aboutsummaryrefslogtreecommitdiffstats
path: root/src/fl2k_fm.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fl2k_fm.c')
-rw-r--r--src/fl2k_fm.c151
1 files changed, 141 insertions, 10 deletions
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 <unistd.h>
#include <fcntl.h>
+#include <getopt.h>
#else
#include <windows.h>
#include <io.h>
@@ -41,6 +42,7 @@
#include <pthread.h>
#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);