From 632482623d4821ac5748b91e9cadda557fac3e3f Mon Sep 17 00:00:00 2001 From: Steve Markgraf Date: Wed, 18 Apr 2018 23:07:54 +0200 Subject: fl2k_fm: add stereo and RDS support Signed-off-by: Steve Markgraf --- src/fl2k_fm.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 141 insertions(+), 10 deletions(-) (limited to 'src/fl2k_fm.c') diff --git a/src/fl2k_fm.c b/src/fl2k_fm.c index 6d619dc..74953ab 100644 --- a/src/fl2k_fm.c +++ b/src/fl2k_fm.c @@ -30,6 +30,7 @@ #ifndef _WIN32 #include #include +#include #else #include #include @@ -41,6 +42,7 @@ #include #include "osmo-fl2k.h" +#include "rds_mod.h" #define BUFFER_SAMPLES_SHIFT 16 #define BUFFER_SAMPLES (1 << BUFFER_SAMPLES_SHIFT) @@ -66,10 +68,15 @@ int8_t *buf2 = NULL; uint32_t samp_rate = 100000000; /* default signal parameters */ +#define PILOT_FREQ 19000 /* In Hz */ +#define STEREO_CARRIER 38000 /* In Hz */ + int delta_freq = 75000; int carrier_freq = 97000000; int carrier_per_signal; int input_freq = 44100; +int stereo_flag = 0; +int rds_flag = 0; double *freqbuf; double *slopebuf; @@ -83,7 +90,7 @@ void usage(void) "\t[-d device index (default: 0)]\n" "\t[-c carrier frequency (default: 9.7 MHz)]\n" "\t[-f FM deviation (default: 75000 Hz, WBFM)]\n" - "\t[-i input audio sample rate (default: 44100 Hz)]\n" + "\t[-i input audio sample rate (default: 44100 Hz for mono FM)]\n" "\t[-s samplerate in Hz (default: 100 MS/s)]\n" "\tfilename (use '-' to read from stdin)\n\n" ); @@ -295,15 +302,16 @@ inline double modulate_sample(int lastwritepos, double lastfreq, double sample) return freq; } -void modulator(void) +void fm_modulator_mono(int use_rds) { unsigned int i; size_t len; double freq; double lastfreq = carrier_freq; - double slope; int16_t audio_buf[AUDIO_BUF_SIZE]; uint32_t lastwritepos = writepos; + double sample; + double rds_samples[AUDIO_BUF_SIZE]; while (!do_exit) { len = writelen(AUDIO_BUF_SIZE); @@ -313,10 +321,88 @@ void modulator(void) if (len == 0) do_exit = 1; + if (use_rds) + get_rds_samples(rds_samples, len); + for (i = 0; i < len; i++) { + sample = audio_buf[i] / 32767.0; + + if (use_rds) { + sample *= 4; + sample += rds_samples[i]; + sample /= 5; + } + /* Modulate and buffer the sample */ - lastfreq = modulate_sample(lastwritepos, lastfreq, - audio_buf[i]/32767.0); + lastfreq = modulate_sample(lastwritepos, lastfreq, sample); + lastwritepos = writepos++; + writepos %= BUFFER_SAMPLES; + } + } else { + pthread_cond_wait(&fm_cond, &fm_mutex); + } + } +} + +void fm_modulator_stereo(int use_rds) +{ + unsigned int i; + size_t len, sample_cnt; + double freq; + double lastfreq = carrier_freq; + int16_t audio_buf[AUDIO_BUF_SIZE]; + uint32_t lastwritepos = writepos; + + dds_t pilot, stereo; + double L, R, LpR, LmR, sample; + double rds_samples[AUDIO_BUF_SIZE]; + + /* Prepare stereo carriers */ + pilot = dds_init(input_freq, PILOT_FREQ, 0); + stereo = dds_init(input_freq, STEREO_CARRIER, 0); + + while (!do_exit) { + len = writelen(AUDIO_BUF_SIZE); + if (len > 1 && !(len % 2)) { + len = fread(audio_buf, 2, len, file); + + if (len == 0) + do_exit = 1; + + /* stereo => two audio samples per baseband sample */ + sample_cnt = len/2; + + if (use_rds) + get_rds_samples(rds_samples, sample_cnt); + + for (i = 0; i < sample_cnt; i++) { + /* Get samples for both channels, and calculate the + * mono (L+R) and the difference signal used to recreate + * the stereo data (L-R). */ + L = audio_buf[i*2] / 32767.0; + R = audio_buf[i*2+1] / 32767.0; + LpR = (L + R) / 2; + LmR = (L - R) / 2; + + /* Create a composite sample consisting of the mono + * signal at baseband, a 19kHz pilot and a the difference + * signal DSB-SC modulated on a 38kHz carrier */ + sample = 4.05 * LpR; /* Mono signal */ + sample += 0.9 * (dds_real(&pilot)/127.0); /* Pilot */ + sample += 4.05 * LmR * (dds_real(&stereo)/127.0); /* DSB-SC stereo */ + + if (use_rds) { + /* add RDS signal */ + sample += rds_samples[i]; + + /* Normalize so we get the signal within [-1, 1] */ + sample /= 10; + } else { + sample /= 9; + } + + lastfreq = modulate_sample(lastwritepos, lastfreq, sample); + lastwritepos = writepos++; writepos %= BUFFER_SAMPLES; } @@ -346,13 +432,30 @@ int main(int argc, char **argv) int dev_index = 0; pthread_attr_t attr; char *filename = NULL; + int option_index = 0; + int input_freq_specified = 0; #ifndef _WIN32 struct sigaction sigact, sigign; #endif - while ((opt = getopt(argc, argv, "d:c:f:i:s:")) != -1) { + static struct option long_options[] = + { + {"stereo", no_argument, &stereo_flag, 1}, + {"rds", no_argument, &rds_flag, 1}, + {0, 0, 0, 0} + }; + + while (1) { + opt = getopt_long(argc, argv, "d:c:f:i:s:", long_options, &option_index); + + /* end of options reached */ + if (opt == -1) + break; + switch (opt) { + case 0: + break; case 'd': dev_index = (uint32_t)atoi(optarg); break; @@ -364,6 +467,7 @@ int main(int argc, char **argv) break; case 'i': input_freq = (uint32_t)atof(optarg); + input_freq_specified = 1; break; case 's': samp_rate = (uint32_t)atof(optarg); @@ -384,7 +488,24 @@ int main(int argc, char **argv) exit(1); } - if(strcmp(filename, "-") == 0) { /* Read samples from stdin */ + if (rds_flag && input_freq_specified) { + if (input_freq != RDS_MODULATOR_RATE) { + fprintf(stderr, "RDS modulator only works with " + "228 kHz audio sample rate!\n"); + exit(1); + } + } else if (rds_flag && !input_freq_specified) { + input_freq = RDS_MODULATOR_RATE; + } + + if (stereo_flag && input_freq < (RDS_MODULATOR_RATE/2)) { + fprintf(stderr, "Audio sample rate needs to be at least " + "114 kHz for stereo FM!\n"); + exit(1); + } + + + if (strcmp(filename, "-") == 0) { /* Read samples from stdin */ file = stdin; #ifdef _WIN32 _setmode(_fileno(stdin), _O_BINARY); @@ -452,8 +573,10 @@ int main(int argc, char **argv) /* Calculate needed constants */ carrier_per_signal = samp_rate / input_freq; - carrier_freq = samp_rate - carrier_freq; - + /* Set RDS parameters */ + set_rds_pi(0x0dac); + set_rds_ps("fl2k_fm"); + set_rds_rt("VGA FM transmitter"); #ifndef _WIN32 sigact.sa_handler = sighandler; @@ -468,7 +591,15 @@ int main(int argc, char **argv) SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); #endif - modulator(); + if (stereo_flag) { + fm_modulator_stereo(rds_flag); + } else { + if (rds_flag) + fprintf(stderr, "Warning: RDS with mono (without 19 kHz pilot" + " tone) doesn't work with all receivers!\n"); + + fm_modulator_mono(rds_flag); + } out: fl2k_close(dev); -- cgit v1.2.3