362 lines
14 KiB
C++
362 lines
14 KiB
C++
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <stdarg.h>
|
|
#include <stdint.h>
|
|
#include <math.h>
|
|
#include <time.h>
|
|
#include <signal.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <sys/mman.h>
|
|
#include <sndfile.h>
|
|
extern "C"
|
|
{
|
|
#include "rds.h"
|
|
#include "fm_mpx.h"
|
|
#include "control_pipe.h"
|
|
}
|
|
#include <librpitx/librpitx.h>
|
|
ngfmdmasync *fmmod;
|
|
#define DATA_SIZE 5000
|
|
static void terminate(int num)
|
|
{
|
|
delete fmmod;
|
|
fm_mpx_close();
|
|
close_control_pipe();
|
|
exit(num);
|
|
}
|
|
|
|
static void fatal(char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
vfprintf(stderr, fmt, ap);
|
|
va_end(ap);
|
|
terminate(0);
|
|
}
|
|
|
|
int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt, char *control_pipe, int pty, int *af_array, int raw, int drds, double preemp, int power, int rawSampleRate, int rawChannels, int deviation, int ta, int tp, float cutoff_freq, float gaim, float compressor_decay, float compressor_attack, float compressor_max_gain_recip, int enablecompressor, int rds_ct_enabled, float rds_volume, float pilot_volume, int disablestereo, int log, float limiter_threshold) {
|
|
struct sigaction sa;
|
|
memset(&sa, 0, sizeof(sa));
|
|
sa.sa_handler = terminate;
|
|
sigaction(SIGTERM, &sa, NULL);
|
|
sigaction(SIGINT, &sa, NULL);
|
|
sigaction(SIGQUIT, &sa, NULL);
|
|
sigaction(SIGKILL, &sa, NULL);
|
|
sigaction(SIGHUP, &sa, NULL); //https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html
|
|
sigaction(SIGPWR, &sa, NULL);
|
|
sigaction(SIGTSTP, &sa, NULL);
|
|
sigaction(SIGSEGV, &sa, NULL); //seg fault
|
|
|
|
// Data structures for baseband data
|
|
float data[DATA_SIZE];
|
|
float devfreq[DATA_SIZE];
|
|
int data_len = 0;
|
|
int data_index = 0;
|
|
|
|
int generate_multiplex = 1;
|
|
int dstereo = disablestereo;
|
|
|
|
//set the power
|
|
padgpio gpiopad;
|
|
gpiopad.setlevel(power);
|
|
|
|
// Initialize the baseband generator
|
|
if(fm_mpx_open(audio_file, DATA_SIZE, raw, preemp, rawSampleRate, rawChannels, cutoff_freq) < 0) return 1;
|
|
|
|
// Initialize the RDS modulator
|
|
char myps[9] = {0};
|
|
set_rds_pi(pi);
|
|
set_rds_ps(ps);
|
|
set_rds_rt(rt);
|
|
set_rds_pty(pty);
|
|
set_rds_ab(0);
|
|
set_rds_ms(1);
|
|
set_rds_tp(tp);
|
|
set_rds_ta(ta);
|
|
uint16_t count = 0;
|
|
uint16_t count2 = 0;
|
|
if(log) {
|
|
if(drds == 1) {
|
|
printf("RDS Disabled (you can enable with control fifo with the RDS command)\n");
|
|
} else {
|
|
printf("PI: %04X, PS: \"%s\".\n", pi, ps);
|
|
printf("RT: \"%s\"\n", rt);
|
|
|
|
if(af_array[0]) {
|
|
set_rds_af(af_array);
|
|
printf("AF: ");
|
|
int f;
|
|
for(f = 1; f < af_array[0]+1; f++) {
|
|
printf("%f Mhz ", (float)(af_array[f]+875)/10);
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Initialize the control pipe reader
|
|
if(control_pipe) {
|
|
if(open_control_pipe(control_pipe) == 0) {
|
|
if(log) printf("Reading control commands on %s.\n", control_pipe);
|
|
} else {
|
|
if(log) printf("Failed to open control pipe: %s.\n", control_pipe);
|
|
control_pipe = NULL;
|
|
}
|
|
}
|
|
if(log) printf("Starting to transmit on %3.1f MHz.\n", carrier_freq/1e6);
|
|
float deviation_scale_factor;
|
|
// The deviation specifies how wide the signal is (from its lowest bandwidht to its highest, but not including sub-carriers).
|
|
// Use 75kHz for WFM (broadcast radio, or 50khz can be used)
|
|
// and about 2.5kHz for NFM (walkie-talkie style radio)
|
|
deviation_scale_factor= 0.1 * (deviation) ; // todo PPM
|
|
int paused = 0;
|
|
for (;;)
|
|
{
|
|
if(control_pipe) {
|
|
ResultAndArg pollResult = poll_control_pipe(log);
|
|
if(pollResult.res == CONTROL_PIPE_RDS_SET) {
|
|
drds = pollResult.arg_int;
|
|
} else if(pollResult.res == CONTROL_PIPE_PWR_SET) {
|
|
padgpio gpiopad;
|
|
gpiopad.setlevel(pollResult.arg_int);
|
|
} else if(pollResult.res == CONTROL_PIPE_DEVIATION_SET) {
|
|
deviation = std::stoi(pollResult.arg);
|
|
deviation_scale_factor= 0.1 * (deviation );
|
|
} else if(pollResult.res == CONTROL_PIPE_STEREO_SET) {
|
|
dstereo = pollResult.arg_int;
|
|
} else if(pollResult.res == CONTROL_PIPE_GAIN_SET) {
|
|
gaim = std::stof(pollResult.arg);
|
|
} else if(pollResult.res == CONTROL_PIPE_COMPRESSORDECAY_SET) {
|
|
compressor_decay = std::stof(pollResult.arg);
|
|
} else if(pollResult.res == CONTROL_PIPE_COMPRESSORATTACK_SET) {
|
|
compressor_attack = std::stof(pollResult.arg);
|
|
} else if(pollResult.res == CONTROL_PIPE_CT_SET) {
|
|
rds_ct_enabled = pollResult.arg_int;
|
|
} else if(pollResult.res == CONTROL_PIPE_RDSVOL_SET) {
|
|
rds_volume = std::stof(pollResult.arg);
|
|
} else if(pollResult.res == CONTROL_PIPE_PAUSE_SET) {
|
|
paused = pollResult.arg_int;
|
|
} else if(pollResult.res == CONTROL_PIPE_PILVOL_SET) {
|
|
pilot_volume = std::stof(pollResult.arg);
|
|
} else if(pollResult.res == CONTROL_PIPE_MPXGEN_SET) {
|
|
generate_multiplex = pollResult.arg_int;
|
|
} else if(pollResult.res == CONTROL_PIPE_COMPRESSOR_SET) {
|
|
enablecompressor = pollResult.arg_int;
|
|
} else if(pollResult.res == CONTROL_PIPE_COMPRESSORMAXGAINRECIP_SET) {
|
|
compressor_max_gain_recip = std::stof(pollResult.arg);
|
|
} else if(pollResult.res == CONTROL_PIPE_LIMITERTHRESHOLD_SET) {
|
|
limiter_threshold = std::stof(pollResult.arg);
|
|
}
|
|
}
|
|
if(fm_mpx_get_samples(data, drds, compressor_decay, compressor_attack, compressor_max_gain_recip, dstereo, gaim, enablecompressor, rds_ct_enabled, rds_volume, paused, pilot_volume, generate_multiplex, limiter_threshold) < 0 ) terminate(0);
|
|
data_len = DATA_SIZE;
|
|
for(int i=0;i< data_len;i++) {
|
|
devfreq[i] = data[i]*deviation_scale_factor;
|
|
}
|
|
fmmod->SetFrequencySamples(devfreq,data_len);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int main(int argc, char **argv) {
|
|
char *audio_file = NULL;
|
|
char *control_pipe = NULL;
|
|
uint32_t carrier_freq = 100000000; //100 mhz
|
|
char *ps = "Pi-FmSa";
|
|
char *rt = "Broadcasting on a Raspberry Pi: Simply Advanced";
|
|
uint16_t pi = 0x1234;
|
|
int pty = 0;
|
|
float compressor_decay = 0.999995;
|
|
float compressor_attack = 1.0;
|
|
float compressor_max_gain_recip = 0.01;
|
|
int enable_compressor = 1;
|
|
float rds_volume = 1.0;
|
|
float pilot_volume = 0.9;
|
|
float limiter_threshold = 0.8;
|
|
int log = 1;
|
|
int ta = 0;
|
|
int tp = 0;
|
|
int af_size = 0;
|
|
int gpiopin = 4;
|
|
int raw = 0;
|
|
int drds = 0;
|
|
int dstereo = 0;
|
|
int power = 7;
|
|
float gain = 1;
|
|
int rawSampleRate = 44100;
|
|
int rawChannels = 2;
|
|
int compressorchanges = 0;
|
|
int limiterchanges = 0;
|
|
double preemp = 50e-6; //eu
|
|
int deviation = 75000;
|
|
int alternative_freq[100] = {};
|
|
int bypassfreqrange = 0;
|
|
int ct = 1;
|
|
float cutofffreq = 16200;
|
|
// Parse command-line arguments
|
|
for(int i=1; i<argc; i++) {
|
|
char *arg = argv[i];
|
|
char *param = NULL;
|
|
if(arg[0] == '-' && i+1 < argc) param = argv[i+1];
|
|
if((strcmp("-audio", arg)==0) && param != NULL) {
|
|
i++;
|
|
audio_file = param;
|
|
} else if(strcmp("-freq", arg)==0 && param != NULL) {
|
|
i++;
|
|
carrier_freq = 1e6 * atof(param);
|
|
if((carrier_freq < 64e6 || carrier_freq > 108e6) && !bypassfreqrange) fatal("Incorrect frequency specification. Must be in megahertz, of the form 107.9, between 64 and 108. (going that low for UKF radios, such as the UNITRA Jowita or other old band FM Radios)\n");
|
|
} else if(strcmp("-pi", arg)==0 && param != NULL) {
|
|
i++;
|
|
pi = (uint16_t) strtol(param, NULL, 16);
|
|
} else if(strcmp("-ps", arg)==0 && param != NULL) {
|
|
i++;
|
|
ps = param;
|
|
} else if(strcmp("-rt", arg)==0 && param != NULL) {
|
|
i++;
|
|
rt = param;
|
|
} else if(strcmp("-compressordecay", arg)==0 && param != NULL) {
|
|
i++;
|
|
compressor_decay = atof(param);
|
|
compressorchanges = 1;
|
|
} else if(strcmp("-compressorattack", arg)==0 && param != NULL) {
|
|
i++;
|
|
compressor_attack = atof(param);
|
|
compressorchanges = 1;
|
|
} else if(strcmp("-compressormaxgainrecip", arg)==0 && param != NULL) {
|
|
i++;
|
|
compressor_max_gain_recip = atof(param);
|
|
compressorchanges = 1;
|
|
} else if(strcmp("-limiterthreshold", arg)==0 && param != NULL) {
|
|
i++;
|
|
limiter_threshold = atof(param);
|
|
limiterchanges = 1;
|
|
if(1 && limiter_threshold > 10) { //if you dont want this for some reason than change the 1 to a 0
|
|
fatal("Nuh uh (limiter threshold cant be bigger than 10)");
|
|
}
|
|
} else if(strcmp("-rdsvolume", arg)==0 && param != NULL) {
|
|
i++;
|
|
rds_volume = atof(param);
|
|
} else if(strcmp("-pilotvolume", arg)==0 && param != NULL) {
|
|
i++;
|
|
pilot_volume = atof(param);
|
|
} else if(strcmp("-pty", arg)==0 && param != NULL) {
|
|
i++;
|
|
pty = atoi(param);
|
|
} else if(strcmp("-gpiopin", arg)==0 && param != NULL) {
|
|
i++;
|
|
printf("GPIO pin setting disabled, mod librpitx and pifmsa (pifm simply advanced) for this");
|
|
// int pinnum = atoi(param);
|
|
// if (!(pinnum == 4 || pinnum == 20 || pinnum == 32 || pinnum == 34 || pinnum == 6)) {
|
|
// fatal("Invalid gpio pin, allowed: 4, 20, 32, 34, 6");
|
|
// } else {
|
|
// gpiopin = pinnum;
|
|
// }
|
|
} else if(strcmp("-disablelogging", arg)==0) {
|
|
i++;
|
|
log = 0;
|
|
} else if(strcmp("-ta", arg)==0) {
|
|
i++;
|
|
ta = 1;
|
|
} else if(strcmp("-bfr", arg)==0) {
|
|
i++;
|
|
bypassfreqrange = 1;
|
|
} else if(strcmp("-tp", arg)==0) {
|
|
i++;
|
|
tp = 1;
|
|
} else if(strcmp("-ctl", arg)==0 && param != NULL) {
|
|
i++;
|
|
control_pipe = param;
|
|
} else if(strcmp("-deviation", arg)==0 && param != NULL) {
|
|
i++;
|
|
if(strcmp("small", param)==0) {
|
|
deviation = 50000;
|
|
} else if(strcmp("nfm", param)==0) {
|
|
deviation = 2500;
|
|
}
|
|
else {
|
|
deviation = atoi(param);
|
|
}
|
|
} else if(strcmp("-rawchannels", arg)==0 && param != NULL) {
|
|
i++;
|
|
rawChannels = atoi(param);
|
|
} else if(strcmp("-rawsamplerate", arg)==0 && param != NULL) {
|
|
i++;
|
|
rawSampleRate = atoi(param);
|
|
} else if(strcmp("-cutofffreq", arg)==0 && param != NULL) {
|
|
i++;
|
|
cutofffreq = atoi(param);
|
|
} else if(strcmp("-audiogain", arg)==0 && param != NULL) {
|
|
i++;
|
|
gain = atoi(param);
|
|
} else if(strcmp("-power", arg)==0 && param != NULL) {
|
|
i++;
|
|
int tpower = atoi(param);
|
|
if(tpower > 7 || tpower < 0) fatal("Power can be between 0 and 7");
|
|
else power = tpower;
|
|
} else if(strcmp("-raw", arg)==0) {
|
|
i++;
|
|
raw = 1;
|
|
} else if(strcmp("-disablerds", arg)==0) {
|
|
i++;
|
|
drds = 1;
|
|
} else if(strcmp("-disablestereo", arg)==0) {
|
|
i++;
|
|
dstereo = 1;
|
|
} else if(strcmp("-disablecompressor", arg)==0) {
|
|
i++;
|
|
enable_compressor = 0;
|
|
} else if(strcmp("-disablect", arg)==0) {
|
|
i++;
|
|
ct = 0;
|
|
} else if(strcmp("-preemphasis", arg)==0 && param != NULL) {
|
|
i++;
|
|
if(strcmp("us", param)==0) {
|
|
preemp = 75e-6; //usa
|
|
} else if(strcmp("eu", param)==0) {
|
|
printf("premp eu default but ok\n");
|
|
preemp = 50e-6;
|
|
} else if(strcmp("off", param)==0 || strcmp("0", param)==0) {
|
|
preemp = 0; //disabled
|
|
} else {
|
|
preemp = atof(param) * 1e-6;
|
|
}
|
|
} else if(strcmp("-af", arg)==0 && param != NULL) {
|
|
i++;
|
|
af_size++;
|
|
alternative_freq[af_size] = (int)(10*atof(param))-875;
|
|
if(alternative_freq[af_size] < 1 || alternative_freq[af_size] > 204)
|
|
fatal("Alternative Frequency has to be set in range of 87.6 Mhz - 107.9 Mhz\n"); //honestly i have no idea why 87.5 and 108 isn't in here, i copied this code, okay?
|
|
}
|
|
else {
|
|
fatal("Unrecognised argument: %s.\n"
|
|
"Syntax: pi_fm_rds [-freq freq] [-audio file] [-pi pi_code]\n"
|
|
" [-ps ps_text] [-rt rt_text] [-ctl control_pipe] [-pty program_type] [-raw play raw audio from stdin] [-disablerds] [-af alt freq] [-preemphasis us] [-rawchannels when using the raw option you can change this] [-rawsamplerate same business] [-deviation the deviation, default is 75000] [-tp] [-ta]\n", arg);
|
|
}
|
|
}
|
|
if(compressorchanges) {
|
|
printf("You've changed the compressor settings, just don't set it too low, so the deviation won't go crazy\n");
|
|
}
|
|
if(limiterchanges) {
|
|
if(limiter_threshold > 0.8) {
|
|
printf("You changed the limiter settings, audio might be quiet now");
|
|
} else { //we've incremented it!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! (69 lol)
|
|
printf("You changed the limiter settings, be careful.");
|
|
}
|
|
}
|
|
if(!enable_compressor) {
|
|
printf("DUDE YOU ARE CRAZY?\n");
|
|
}
|
|
alternative_freq[0] = af_size;
|
|
int FifoSize=DATA_SIZE*2;
|
|
//fmmod=new ngfmdmasync(carrier_freq,228000,14,FifoSize, false, gpiopin); //you can mod
|
|
fmmod=new ngfmdmasync(carrier_freq,228000,14,FifoSize, false);
|
|
int errcode = tx(carrier_freq, audio_file, pi, ps, rt, control_pipe, pty, alternative_freq, raw, drds, preemp, power, rawSampleRate, rawChannels, deviation, ta, tp, cutofffreq, gain, compressor_decay, compressor_attack, compressor_max_gain_recip, enable_compressor, ct, rds_volume, pilot_volume, dstereo, log, limiter_threshold);
|
|
terminate(errcode);
|
|
}
|