aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKyle Keen <keenerd@gmail.com>2014-01-23 07:31:47 -0500
committerSteve Markgraf <steve@steve-m.de>2014-01-23 23:28:19 +0100
commite90cd17d3621a0e086acdb5fec9637d7b1631831 (patch)
tree03f5e4d9209f97d6881073cfb52499e1c5ec5320
parentf459214894f1eb7d633a7f71165065b9974540b9 (diff)
rtl_fm: overhaul
Signed-off-by: Steve Markgraf <steve@steve-m.de>
-rw-r--r--src/rtl_fm.c1161
1 files changed, 716 insertions, 445 deletions
diff --git a/src/rtl_fm.c b/src/rtl_fm.c
index f732bca..5847d27 100644
--- a/src/rtl_fm.c
+++ b/src/rtl_fm.c
@@ -24,11 +24,12 @@
* written because people could not do real time
* FM demod on Atom hardware with GNU radio
* based on rtl_sdr.c and rtl_tcp.c
- * todo: realtime ARMv5
- * remove float math (disqualifies complex.h)
- * in-place array operations
+ *
+ * lots of locks, but that is okay
+ * (no many-to-many locks)
+ *
+ * todo:
* sanity checks
- * nicer FIR than square
* scale squelch to other input parameters
* test all the demodulations
* pad output on hop
@@ -36,11 +37,15 @@
* scaled AM demod amplification
* auto-hop after time limit
* peak detector to tune onto stronger signals
- * use slower sample rates (250k) for nbfm
- * offset tuning
* fifo for active hop frequency
* clips
- * real squelch math
+ * noise squelch
+ * merge stereo patch
+ * merge soft agc patch
+ * merge udp patch
+ * testmode to detect overruns
+ * watchdog to reset bad dongle
+ * fix oversampling
*/
#include <errno.h>
@@ -80,12 +85,7 @@
#define FREQUENCIES_LIMIT 1000
-static pthread_t demod_thread;
-static pthread_cond_t data_ready; /* shared buffer filled */
-static pthread_rwlock_t data_rw; /* lock for shared buffer */
-static pthread_mutex_t data_mutex; /* because conds are dumb */
static volatile int do_exit = 0;
-static rtlsdr_dev_t *dev = NULL;
static int lcm_post[17] = {1,1,1,3,1,5,3,7,1,9,5,11,3,13,7,15,1};
static int ACTUAL_BUF_LENGTH;
@@ -93,8 +93,39 @@ static int *atan_lut = NULL;
static int atan_lut_size = 131072; /* 512 KB */
static int atan_lut_coef = 8;
-struct fm_state
+struct dongle_state
+{
+ int exit_flag;
+ pthread_t thread;
+ rtlsdr_dev_t *dev;
+ int dev_index;
+ uint32_t freq;
+ uint32_t rate;
+ int gain;
+ uint16_t buf16[MAXIMUM_BUF_LENGTH];
+ uint32_t buf_len;
+ int ppm_error;
+ int offset_tuning;
+ int direct_sampling;
+ int mute;
+ struct demod_state *demod_target;
+};
+
+struct demod_state
{
+ int exit_flag;
+ pthread_t thread;
+ int16_t lowpassed[MAXIMUM_BUF_LENGTH];
+ int lp_len;
+ int16_t lp_i_hist[10][6];
+ int16_t lp_q_hist[10][6];
+ int16_t result[MAXIMUM_BUF_LENGTH];
+ int16_t droop_i_hist[9];
+ int16_t droop_q_hist[9];
+ int result_len;
+ int rate_in;
+ int rate_out;
+ int rate_out2;
int now_r, now_j;
int pre_r, pre_j;
int prev_index;
@@ -102,63 +133,88 @@ struct fm_state
int post_downsample;
int output_scale;
int squelch_level, conseq_squelch, squelch_hits, terminate_on_squelch;
- int exit_flag;
- uint8_t buf[MAXIMUM_BUF_LENGTH];
- uint32_t buf_len;
- int signal[MAXIMUM_BUF_LENGTH]; /* 16 bit signed i/q pairs */
- int16_t signal2[MAXIMUM_BUF_LENGTH]; /* signal has lowpass, signal2 has demod */
- int signal_len;
- int signal2_len;
- FILE *file;
- int edge;
- uint32_t freqs[FREQUENCIES_LIMIT];
- int freq_len;
- int freq_now;
- uint32_t sample_rate;
- int output_rate;
- int fir_enable;
- int fir[256]; /* fir_len == downsample */
- int fir_sum;
+ int downsample_passes;
+ int comp_fir_size;
int custom_atan;
int deemph, deemph_a;
int now_lpr;
int prev_lpr_index;
int dc_block, dc_avg;
- void (*mode_demod)(struct fm_state*);
+ void (*mode_demod)(struct demod_state*);
+ pthread_rwlock_t rw;
+ pthread_cond_t ready;
+ pthread_mutex_t ready_m;
+ struct output_state *output_target;
};
+struct output_state
+{
+ int exit_flag;
+ pthread_t thread;
+ FILE *file;
+ char *filename;
+ int16_t result[MAXIMUM_BUF_LENGTH];
+ int result_len;
+ int rate;
+ pthread_rwlock_t rw;
+ pthread_cond_t ready;
+ pthread_mutex_t ready_m;
+};
+
+struct controller_state
+{
+ int exit_flag;
+ pthread_t thread;
+ uint32_t freqs[FREQUENCIES_LIMIT];
+ int freq_len;
+ int freq_now;
+ int edge;
+ int wb_mode;
+ pthread_cond_t hop;
+ pthread_mutex_t hop_m;
+};
+
+// multiple of these, eventually
+struct dongle_state dongle;
+struct demod_state demod;
+struct output_state output;
+struct controller_state controller;
+
void usage(void)
{
fprintf(stderr,
"rtl_fm, a simple narrow band FM demodulator for RTL2832 based DVB-T receivers\n\n"
"Use:\trtl_fm -f freq [-options] [filename]\n"
"\t-f frequency_to_tune_to [Hz]\n"
- "\t (use multiple -f for scanning, requires squelch)\n"
- "\t (ranges supported, -f 118M:137M:25k)\n"
+ "\t use multiple -f for scanning (requires squelch)\n"
+ "\t ranges supported, -f 118M:137M:25k\n"
+ "\t[-M modulation (default: fm)]\n"
+ "\t fm, wbfm, raw, am, usb, lsb\n"
+ "\t wbfm == -M fm -s 170k -o 4 -A fast -r 32k -l 0 -E deemp\n"
+ "\t raw mode outputs 2x16 bit IQ pairs\n"
"\t[-s sample_rate (default: 24k)]\n"
"\t[-d device_index (default: 0)]\n"
"\t[-g tuner_gain (default: automatic)]\n"
"\t[-l squelch_level (default: 0/off)]\n"
- "\t[-o oversampling (default: 1, 4 recommended)]\n"
+ //"\t for fm squelch is inverted\n"
+ //"\t[-o oversampling (default: 1, 4 recommended)]\n"
"\t[-p ppm_error (default: 0)]\n"
- "\t[-E sets lower edge tuning (default: center)]\n"
- "\t[-N enables NBFM mode (default: on)]\n"
- "\t[-W enables WBFM mode (default: off)]\n"
- "\t (-N -s 170k -o 4 -A fast -r 32k -l 0 -D)\n"
- "\tfilename (a '-' dumps samples to stdout)\n"
- "\t (omitting the filename also uses stdout)\n\n"
+ "\t[-E enable_option (default: none)]\n"
+ "\t use multiple -E to enable multiple options\n"
+ "\t edge: enable lower edge tuning\n"
+ "\t dc: enable dc blocking filter\n"
+ "\t deemp: enable de-emphasis filter\n"
+ "\t direct: enable direct sampling\n"
+ "\t offset: enable offset tuning\n"
+ "\tfilename ('-' means stdout)\n"
+ "\t omitting the filename also uses stdout\n\n"
"Experimental options:\n"
- "\t[-r output_rate (default: same as -s)]\n"
- "\t[-t squelch_delay (default: 20)]\n"
- "\t (+values will mute/scan, -values will exit)\n"
- "\t[-M enables AM mode (default: off)]\n"
- "\t[-L enables LSB mode (default: off)]\n"
- "\t[-U enables USB mode (default: off)]\n"
- //"\t[-D enables DSB mode (default: off)]\n"
- "\t[-R enables raw mode (default: off, 2x16 bit output)]\n"
- "\t[-F enables Hamming FIR (default: off/square)]\n"
- "\t[-D enables de-emphasis (default: off)]\n"
- "\t[-C enables DC blocking of output (default: off)]\n"
+ "\t[-r resample_rate (default: none / same as -s)]\n"
+ "\t[-t squelch_delay (default: 10)]\n"
+ "\t +values will mute/scan, -values will exit\n"
+ "\t[-F fir_size (default: off)]\n"
+ "\t enables low-leakage downsample filter\n"
+ "\t size can be 0 or 9. 0 has bad roll off\n"
"\t[-A std/fast/lut choose atan math (default: std)]\n"
//"\t[-C clip_path (default: off)\n"
//"\t (create time stamped raw clips, requires squelch)\n"
@@ -167,9 +223,10 @@ void usage(void)
//"\t (fifo will contain the active frequency)\n"
"\n"
"Produces signed 16 bit ints, use Sox or aplay to hear them.\n"
- "\trtl_fm ... - | play -t raw -r 24k -es -b 16 -c 1 -V1 -\n"
- "\t | aplay -r 24k -f S16_LE -t raw -c 1\n"
- "\t -s 22.5k - | multimon -t raw /dev/stdin\n\n");
+ "\trtl_fm ... | play -t raw -r 24k -es -b 16 -c 1 -V1 -\n"
+ "\t | aplay -r 24k -f S16_LE -t raw -c 1\n"
+ "\t -M wbfm | play -r 32k ... \n"
+ "\t -s 22050 | multimon -t raw /dev/stdin\n\n");
exit(1);
}
@@ -180,7 +237,7 @@ sighandler(int signum)
if (CTRL_C_EVENT == signum) {
fprintf(stderr, "Signal caught, exiting!\n");
do_exit = 1;
- //rtlsdr_cancel_async(dev);
+ rtlsdr_cancel_async(dongle.dev);
return TRUE;
}
return FALSE;
@@ -190,7 +247,7 @@ static void sighandler(int signum)
{
fprintf(stderr, "Signal caught, exiting!\n");
do_exit = 1;
- //rtlsdr_cancel_async(dev);
+ rtlsdr_cancel_async(dongle.dev);
}
#endif
@@ -198,6 +255,23 @@ static void sighandler(int signum)
#define safe_cond_signal(n, m) pthread_mutex_lock(m); pthread_cond_signal(n); pthread_mutex_unlock(m)
#define safe_cond_wait(n, m) pthread_mutex_lock(m); pthread_cond_wait(n, m); pthread_mutex_unlock(m)
+/* {length, coef, coef, coef} and scaled by 2^15
+ for now, only length 9, optimal way to get +85% bandwidth */
+#define CIC_TABLE_MAX 10
+int cic_9_tables[][10] = {
+ {0,},
+ {9, -156, -97, 2798, -15489, 61019, -15489, 2798, -97, -156},
+ {9, -128, -568, 5593, -24125, 74126, -24125, 5593, -568, -128},
+ {9, -129, -639, 6187, -26281, 77511, -26281, 6187, -639, -129},
+ {9, -122, -612, 6082, -26353, 77818, -26353, 6082, -612, -122},
+ {9, -120, -602, 6015, -26269, 77757, -26269, 6015, -602, -120},
+ {9, -120, -582, 5951, -26128, 77542, -26128, 5951, -582, -120},
+ {9, -119, -580, 5931, -26094, 77505, -26094, 5931, -580, -119},
+ {9, -119, -578, 5921, -26077, 77484, -26077, 5921, -578, -119},
+ {9, -119, -577, 5917, -26067, 77473, -26067, 5917, -577, -119},
+ {9, -199, -362, 5303, -25505, 77489, -25505, 5303, -362, -199},
+};
+
#ifdef _MSC_VER
double log2(double n)
{
@@ -226,74 +300,26 @@ void rotate_90(unsigned char *buf, uint32_t len)
}
}
-void low_pass(struct fm_state *fm, unsigned char *buf, uint32_t len)
+void low_pass(struct demod_state *d)
/* simple square window FIR */
{
int i=0, i2=0;
- while (i < (int)len) {
- fm->now_r += ((int)buf[i] - 127);
- fm->now_j += ((int)buf[i+1] - 127);
- i += 2;
- fm->prev_index++;
- if (fm->prev_index < fm->downsample) {
- continue;
- }
- fm->signal[i2] = fm->now_r; // * fm->output_scale;
- fm->signal[i2+1] = fm->now_j; // * fm->output_scale;
- fm->prev_index = 0;
- fm->now_r = 0;
- fm->now_j = 0;
- i2 += 2;
- }
- fm->signal_len = i2;
-}
-
-void build_fir(struct fm_state *fm)
-/* hamming */
-/* point = sum(sample[i] * fir[i] * fir_len / fir_sum) */
-{
- double a, b, w, N1;
- int i, len;
- len = fm->downsample;
- a = 25.0/46.0;
- b = 21.0/46.0;
- N1 = (double)(len-1);
- for(i = 0; i < len; i++) {
- w = a - b*cos(2*i*M_PI/N1);
- fm->fir[i] = (int)(w * 255);
- }
- fm->fir_sum = 0;
- for(i = 0; i < len; i++) {
- fm->fir_sum += fm->fir[i];
- }
-}
-
-void low_pass_fir(struct fm_state *fm, unsigned char *buf, uint32_t len)
-/* perform an arbitrary FIR, doubles CPU use */
-// possibly bugged, or overflowing
-{
- int i=0, i2=0, i3=0;
- while (i < (int)len) {
- i3 = fm->prev_index;
- fm->now_r += ((int)buf[i] - 127) * fm->fir[i3];
- fm->now_j += ((int)buf[i+1] - 127) * fm->fir[i3];
+ while (i < d->lp_len) {
+ d->now_r += d->lowpassed[i];
+ d->now_j += d->lowpassed[i+1];
i += 2;
- fm->prev_index++;
- if (fm->prev_index < fm->downsample) {
+ d->prev_index++;
+ if (d->prev_index < d->downsample) {
continue;
}
- fm->now_r *= fm->downsample;
- fm->now_j *= fm->downsample;
- fm->now_r /= fm->fir_sum;
- fm->now_j /= fm->fir_sum;
- fm->signal[i2] = fm->now_r; //* fm->output_scale;
- fm->signal[i2+1] = fm->now_j; //* fm->output_scale;
- fm->prev_index = 0;
- fm->now_r = 0;
- fm->now_j = 0;
+ d->lowpassed[i2] = d->now_r; // * d->output_scale;
+ d->lowpassed[i2+1] = d->now_j; // * d->output_scale;
+ d->prev_index = 0;
+ d->now_r = 0;
+ d->now_j = 0;
i2 += 2;
}
- fm->signal_len = i2;
+ d->lp_len = i2;
}
int low_pass_simple(int16_t *signal2, int len, int step)
@@ -312,26 +338,82 @@ int low_pass_simple(int16_t *signal2, int len, int step)
return len / step;
}
-void low_pass_real(struct fm_state *fm)
+void low_pass_real(struct demod_state *s)
/* simple square window FIR */
// add support for upsampling?
{
int i=0, i2=0;
- int fast = (int)fm->sample_rate / fm->post_downsample;
- int slow = fm->output_rate;
- while (i < fm->signal2_len) {
- fm->now_lpr += fm->signal2[i];
+ int fast = (int)s->rate_out;
+ int slow = s->rate_out2;
+ while (i < s->result_len) {
+ s->now_lpr += s->result[i];
i++;
- fm->prev_lpr_index += slow;
- if (fm->prev_lpr_index < fast) {
+ s->prev_lpr_index += slow;
+ if (s->prev_lpr_index < fast) {
continue;
}
- fm->signal2[i2] = (int16_t)(fm->now_lpr / (fast/slow));
- fm->prev_lpr_index -= fast;
- fm->now_lpr = 0;
+ s->result[i2] = (int16_t)(s->now_lpr / (fast/slow));
+ s->prev_lpr_index -= fast;
+ s->now_lpr = 0;
i2 += 1;
}
- fm->signal2_len = i2;
+ s->result_len = i2;
+}
+
+void fifth_order(int16_t *data, int length, int16_t *hist)
+/* for half of interleaved data */
+{
+ int i;
+ int16_t a, b, c, d, e, f;
+ a = hist[1];
+ b = hist[2];
+ c = hist[3];
+ d = hist[4];
+ e = hist[5];
+ f = data[0];
+ /* a downsample should improve resolution, so don't fully shift */
+ data[0] = (a + (b+e)*5 + (c+d)*10 + f) >> 4;
+ for (i=4; i<length; i+=4) {
+ a = c;
+ b = d;
+ c = e;
+ d = f;
+ e = data[i-2];
+ f = data[i];
+ data[i/2] = (a + (b+e)*5 + (c+d)*10 + f) >> 4;
+ }
+ /* archive */
+ hist[0] = a;
+ hist[1] = b;
+ hist[2] = c;
+ hist[3] = d;
+ hist[4] = e;
+ hist[5] = f;
+}
+
+void generic_fir(int16_t *data, int length, int *fir, int16_t *hist)
+/* Okay, not at all generic. Assumes length 9, fix that eventually. */
+{
+ int d, f, temp, sum;
+ for (d=0; d<length; d+=2) {
+ temp = data[d];
+ sum = 0;
+ sum += (hist[0] + hist[8]) * fir[1];
+ sum += (hist[1] + hist[7]) * fir[2];
+ sum += (hist[2] + hist[6]) * fir[3];
+ sum += (hist[3] + hist[5]) * fir[4];
+ sum += hist[4] * fir[5];
+ data[d] = sum >> 15 ;
+ hist[0] = hist[1];
+ hist[1] = hist[2];
+ hist[2] = hist[3];
+ hist[3] = hist[4];
+ hist[4] = hist[5];
+ hist[5] = hist[6];
+ hist[6] = hist[7];
+ hist[7] = hist[8];
+ hist[8] = temp;
+ }
}
/* define our own complex math ops
@@ -382,7 +464,7 @@ int polar_disc_fast(int ar, int aj, int br, int bj)
return fast_atan2(cj, cr);
}
-int atan_lut_init()
+int atan_lut_init(void)
{
int i = 0;
@@ -433,112 +515,118 @@ int polar_disc_lut(int ar, int aj, int br, int bj)
return 0;
}
-void fm_demod(struct fm_state *fm)
+void fm_demod(struct demod_state *fm)
{
int i, pcm;
- pcm = polar_discriminant(fm->signal[0], fm->signal[1],
+ int16_t *lp = fm->lowpassed;
+ pcm = polar_discriminant(lp[0], lp[1],
fm->pre_r, fm->pre_j);
- fm->signal2[0] = (int16_t)pcm;
- for (i = 2; i < (fm->signal_len); i += 2) {
+ fm->result[0] = (int16_t)pcm;
+ for (i = 2; i < (fm->lp_len-1); i += 2) {
switch (fm->custom_atan) {
case 0:
- pcm = polar_discriminant(fm->signal[i], fm->signal[i+1],
- fm->signal[i-2], fm->signal[i-1]);
+ pcm = polar_discriminant(lp[i], lp[i+1],
+ lp[i-2], lp[i-1]);
break;
case 1:
- pcm = polar_disc_fast(fm->signal[i], fm->signal[i+1],
- fm->signal[i-2], fm->signal[i-1]);
+ pcm = polar_disc_fast(lp[i], lp[i+1],
+ lp[i-2], lp[i-1]);
break;
case 2:
- pcm = polar_disc_lut(fm->signal[i], fm->signal[i+1],
- fm->signal[i-2], fm->signal[i-1]);
+ pcm = polar_disc_lut(lp[i], lp[i+1],
+ lp[i-2], lp[i-1]);
break;
}
- fm->signal2[i/2] = (int16_t)pcm;
+ fm->result[i/2] = (int16_t)pcm;
}
- fm->pre_r = fm->signal[fm->signal_len - 2];
- fm->pre_j = fm->signal[fm->signal_len - 1];
- fm->signal2_len = fm->signal_len/2;
+ fm->pre_r = lp[fm->lp_len - 2];
+ fm->pre_j = lp[fm->lp_len - 1];
+ fm->result_len = fm->lp_len/2;
}
-void am_demod(struct fm_state *fm)
+void am_demod(struct demod_state *fm)
// todo, fix this extreme laziness
{
int i, pcm;
- for (i = 0; i < (fm->signal_len); i += 2) {
+ int16_t *lp = fm->lowpassed;
+ int16_t *r = fm->result;
+ for (i = 0; i < fm->lp_len; i += 2) {
// hypot uses floats but won't overflow
- //fm->signal2[i/2] = (int16_t)hypot(fm->signal[i], fm->signal[i+1]);
- pcm = fm->signal[i] * fm->signal[i];
- pcm += fm->signal[i+1] * fm->signal[i+1];
- fm->signal2[i/2] = (int16_t)sqrt(pcm) * fm->output_scale;
+ //r[i/2] = (int16_t)hypot(lp[i], lp[i+1]);
+ pcm = lp[i] * lp[i];
+ pcm += lp[i+1] * lp[i+1];
+ r[i/2] = (int16_t)sqrt(pcm) * fm->output_scale;
}
- fm->signal2_len = fm->signal_len/2;
+ fm->result_len = fm->lp_len/2;
// lowpass? (3khz) highpass? (dc)
}
-void usb_demod(struct fm_state *fm)
+void usb_demod(struct demod_state *fm)
{
int i, pcm;
- for (i = 0; i < (fm->signal_len); i += 2) {
- pcm = fm->signal[i] + fm->signal[i+1];
- fm->signal2[i/2] = (int16_t)pcm * fm->output_scale;
+ int16_t *lp = fm->lowpassed;
+ int16_t *r = fm->result;
+ for (i = 0; i < fm->lp_len; i += 2) {
+ pcm = lp[i] + lp[i+1];
+ r[i/2] = (int16_t)pcm * fm->output_scale;
}
- fm->signal2_len = fm->signal_len/2;
+ fm->result_len = fm->lp_len/2;
}
-void lsb_demod(struct fm_state *fm)
+void lsb_demod(struct demod_state *fm)
{
int i, pcm;
- for (i = 0; i < (fm->signal_len); i += 2) {
- pcm = fm->signal[i] - fm->signal[i+1];
- fm->signal2[i/2] = (int16_t)pcm * fm->output_scale;
+ int16_t *lp = fm->lowpassed;
+ int16_t *r = fm->result;
+ for (i = 0; i < fm->lp_len; i += 2) {
+ pcm = lp[i] - lp[i+1];
+ r[i/2] = (int16_t)pcm * fm->output_scale;
}
- fm->signal2_len = fm->signal_len/2;
+ fm->result_len = fm->lp_len/2;
}
-void raw_demod(struct fm_state *fm)
+void raw_demod(struct demod_state *fm)
{
- /* hacky and pointless code */
int i;
- for (i = 0; i < (fm->signal_len); i++) {
- fm->signal2[i] = (int16_t)fm->signal[i];
+ for (i = 0; i < fm->lp_len; i++) {
+ fm->result[i] = (int16_t)fm->lowpassed[i];
}
- fm->signal2_len = fm->signal_len;
+ fm->result_len = fm->lp_len;
}
-void deemph_filter(struct fm_state *fm)
+void deemph_filter(struct demod_state *fm)
{
static int avg; // cheating...
int i, d;
// de-emph IIR
// avg = avg * (1 - alpha) + sample * alpha;
- for (i = 0; i < fm->signal2_len; i++) {
- d = fm->signal2[i] - avg;
+ for (i = 0; i < fm->result_len; i++) {
+ d = fm->result[i] - avg;
if (d > 0) {
avg += (d + fm->deemph_a/2) / fm->deemph_a;
} else {
avg += (d - fm->deemph_a/2) / fm->deemph_a;
}
- fm->signal2[i] = (int16_t)avg;
+ fm->result[i] = (int16_t)avg;
}
}
-void dc_block_filter(struct fm_state *fm)
+void dc_block_filter(struct demod_state *fm)
{
int i, avg;
int64_t sum = 0;
- for (i=0; i < fm->signal2_len; i++) {
- sum += fm->signal2[i];
+ for (i=0; i < fm->result_len; i++) {
+ sum += fm->result[i];
}
- avg = sum / fm->signal2_len;
+ avg = sum / fm->result_len;
avg = (avg + fm->dc_avg * 9) / 10;
- for (i=0; i < fm->signal2_len; i++) {
- fm->signal2[i] -= avg;
+ for (i=0; i < fm->result_len; i++) {
+ fm->result[i] -= avg;
}
fm->dc_avg = avg;
}
-int mad(int *samples, int len, int step)
+int mad(int16_t *samples, int len, int step)
/* mean average deviation */
{
int i=0, sum=0, ave=0;
@@ -555,159 +643,285 @@ int mad(int *samples, int len, int step)
return sum / (len / step);
}
-int post_squelch(struct fm_state *fm)
-/* returns 1 for active signal, 0 for no signal */
+int rms(int16_t *samples, int len, int step)
+/* largely lifted from rtl_power */
{
- int dev_r, dev_j, len, sq_l;
- /* only for small samples, big samples need chunk processing */
- len = fm->signal_len;
- sq_l = fm->squelch_level;
- dev_r = mad(&(fm->signal[0]), len, 2);
- dev_j = mad(&(fm->signal[1]), len, 2);
- if ((dev_r > sq_l) || (dev_j > sq_l)) {
- fm->squelch_hits = 0;
- return 1;
+ int i;
+ long p, t, s;
+ double dc, err;
+
+ p = t = 0L;
+ for (i=0; i<len; i+=step) {
+ s = (long)samples[i];
+ t += s;
+ p += s * s;
}
- fm->squelch_hits++;
- return 0;
+ /* correct for dc offset in squares */
+ dc = (double)(t*step) / (double)len;
+ err = t * 2 * dc - dc * dc * len;
+
+ return (int)sqrt((p-err) / len);
}
-static void optimal_settings(struct fm_state *fm, int freq, int hopping)
+void arbitrary_upsample(int16_t *buf1, int16_t *buf2, int len1, int len2)
+/* linear interpolation, len1 < len2 */
{
- int r, capture_freq, capture_rate;
- fm->downsample = (1000000 / fm->sample_rate) + 1;
- fm->freq_now = freq;
- capture_rate = fm->downsample * fm->sample_rate;
- capture_freq = fm->freqs[freq] + capture_rate/4;
- capture_freq += fm->edge * fm->sample_rate / 2;
- fm->output_scale = (1<<15) / (128 * fm->downsample);
- if (fm->output_scale < 1) {
- fm->output_scale = 1;}
- if (fm->mode_demod == &fm_demod) {
- fm->output_scale = 1;}
- /* Set the frequency */
- r = rtlsdr_set_center_freq(dev, (uint32_t)capture_freq);
- if (hopping) {
- return;}
- fprintf(stderr, "Oversampling input by: %ix.\n", fm->downsample);
- fprintf(stderr, "Oversampling output by: %ix.\n", fm->post_downsample);
- fprintf(stderr, "Buffer size: %0.2fms\n",
- 1000 * 0.5 * (float)ACTUAL_BUF_LENGTH / (float)capture_rate);
- if (r < 0) {
- fprintf(stderr, "WARNING: Failed to set center freq.\n");}
- else {
- fprintf(stderr, "Tuned to %u Hz.\n", capture_freq);}
+ int i = 1;
+ int j = 0;
+ int tick = 0;
+ double frac; // use integers...
+ while (j < len2) {
+ frac = (double)tick / (double)len2;
+ buf2[j] = (int16_t)(buf1[i-1]*(1-frac) + buf1[i]*frac);
+ j++;
+ tick += len1;
+ if (tick > len2) {
+ tick -= len2;
+ i++;
+ }
+ if (i >= len1) {
+ i = len1 - 1;
+ tick = len2;
+ }
+ }
+}
- /* Set the sample rate */
- fprintf(stderr, "Sampling at %u Hz.\n", capture_rate);
- if (fm->output_rate > 0) {
- fprintf(stderr, "Output at %u Hz.\n", fm->output_rate);
- } else {
- fprintf(stderr, "Output at %u Hz.\n", fm->sample_rate/fm->post_downsample);}
- r = rtlsdr_set_sample_rate(dev, (uint32_t)capture_rate);
- if (r < 0) {
- fprintf(stderr, "WARNING: Failed to set sample rate.\n");}
+void arbitrary_downsample(int16_t *buf1, int16_t *buf2, int len1, int len2)
+/* fractional boxcar lowpass, len1 > len2 */
+{
+ int i = 1;
+ int j = 0;
+ int tick = 0;
+ double remainder = 0;
+ double frac; // use integers...
+ buf2[0] = 0;
+ while (j < len2) {
+ frac = 1.0;
+ if ((tick + len2) > len1) {
+ frac = (double)(len1 - tick) / (double)len2;}
+ buf2[j] += (int16_t)((double)buf1[i] * frac + remainder);
+ remainder = (double)buf1[i] * (1.0-frac);
+ tick += len2;
+ i++;
+ if (tick > len1) {
+ j++;
+ buf2[j] = 0;
+ tick -= len1;
+ }
+ if (i >= len1) {
+ i = len1 - 1;
+ tick = len1;
+ }
+ }
+ for (j=0; j<len2; j++) {
+ buf2[j] = buf2[j] * len2 / len1;}
+}
+void arbitrary_resample(int16_t *buf1, int16_t *buf2, int len1, int len2)
+/* up to you to calculate lengths and make sure it does not go OOB
+ * okay for buffers to overlap, if you are downsampling */
+{
+ if (len1 < len2) {
+ arbitrary_upsample(buf1, buf2, len1, len2);
+ } else {
+ arbitrary_downsample(buf1, buf2, len1, len2);
+ }
}
-void full_demod(struct fm_state *fm)
+void full_demod(struct demod_state *d)
{
uint8_t dump[BUFFER_DUMP];
- int i, sr, freq_next, n_read, hop = 0;
- pthread_rwlock_wrlock(&data_rw);
- rotate_90(fm->buf, fm->buf_len);
- if (fm->fir_enable) {
- low_pass_fir(fm, fm->buf, fm->buf_len);
+ int i, ds, ds_p, freq_next, n_read;
+ int sr = 0;
+ ds_p = d->downsample_passes;
+ if (ds_p) {
+ for (i=0; i < ds_p; i++) {
+ fifth_order(d->lowpassed, (d->lp_len >> i), d->lp_i_hist[i]);
+ fifth_order(d->lowpassed+1, (d->lp_len >> i) - 1, d->lp_q_hist[i]);
+ }
+ d->lp_len = d->lp_len >> ds_p;
+ /* droop compensation */
+ if (d->comp_fir_size == 9 && ds_p <= CIC_TABLE_MAX) {
+ generic_fir(d->lowpassed, d->lp_len,
+ cic_9_tables[ds_p], d->droop_i_hist);
+ generic_fir(d->lowpassed+1, d->lp_len-1,
+ cic_9_tables[ds_p], d->droop_q_hist);
+ }
} else {
- low_pass(fm, fm->buf, fm->buf_len);
+ low_pass(d);
}
- pthread_rwlock_unlock(&data_rw);
- fm->mode_demod(fm);
- if (fm->mode_demod == &raw_demod) {
- fwrite(fm->signal2, 2, fm->signal2_len, fm->file);
+ /* power squelch */
+ if (d->squelch_level) {
+ sr = rms(d->lowpassed, d->lp_len, 1);
+ if (sr < d->squelch_level) {
+ d->squelch_hits++;
+ for (i=0; i<d->lp_len; i++) {
+ d->lowpassed[i] = 0;
+ }
+ } else {
+ d->squelch_hits = 0;}
+ }
+ d->mode_demod(d); /* lowpassed -> result */
+ if (d->mode_demod == &raw_demod) {
return;
}
- sr = post_squelch(fm);
- if (!sr && fm->squelch_hits > fm->conseq_squelch) {
- if (fm->terminate_on_squelch) {
- fm->exit_flag = 1;}
- if (fm->freq_len == 1) { /* mute */
- for (i=0; i<fm->signal_len; i++) {
- fm->signal2[i] = 0;}
- }
- else {
- hop = 1;}
- }
- if (fm->post_downsample > 1) {
- fm->signal2_len = low_pass_simple(fm->signal2, fm->signal2_len, fm->post_downsample);}
- if (fm->output_rate > 0) {
- low_pass_real(fm);
- }
- if (fm->deemph) {
- deemph_filter(fm);}
- if (fm->dc_block) {
- dc_block_filter(fm);}
- /* ignore under runs for now */
- fwrite(fm->signal2, 2, fm->signal2_len, fm->file);
- if (hop) {
- freq_next = (fm->freq_now + 1) % fm->freq_len;
- optimal_settings(fm, freq_next, 1);
- fm->squelch_hits = fm->conseq_squelch + 1; /* hair trigger */
- /* wait for settling and flush buffer */
- usleep(5000);
- rtlsdr_read_sync(dev, &dump, BUFFER_DUMP, &n_read);
- if (n_read != BUFFER_DUMP) {
- fprintf(stderr, "Error: bad retune.\n");}
+ /* todo, fm noise squelch */
+ // use nicer filter here too?
+ if (d->post_downsample > 1) {
+ d->result_len = low_pass_simple(d->result, d->result_len, d->post_downsample);}
+ if (d->deemph) {
+ deemph_filter(d);}
+ if (d->dc_block) {
+ dc_block_filter(d);}
+ if (d->rate_out2 > 0) {
+ low_pass_real(d);
+ //arbitrary_resample(d->result, d->result, d->result_len, d->result_len * d->rate_out2 / d->rate_out);
}
}
static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx)
{
- struct fm_state *fm2 = ctx;
+ int i;
+ struct dongle_state *s = ctx;
if (do_exit) {
return;}
if (!ctx) {
return;}
- pthread_rwlock_wrlock(&data_rw);
- memcpy(fm2->buf, buf, len);
- fm2->buf_len = len;
- pthread_rwlock_unlock(&data_rw);
- safe_cond_signal(&data_ready, &data_mutex);
- /* single threaded uses 25% less CPU? */
- /* full_demod(fm2); */
+ struct demod_state *d = s->demod_target;
+ if (s->mute) {
+ for (i=0; i<s->mute; i++) {
+ buf[i] = 127;}
+ s->mute = 0;
+ }
+ if (!s->offset_tuning) {
+ rotate_90(buf, len);}
+ for (i=0; i<(int)len; i++) {
+ s->buf16[i] = (int16_t)buf[i] - 127;}
+ pthread_rwlock_wrlock(&d->rw);
+ memcpy(d->lowpassed, s->buf16, 2*len);
+ d->lp_len = len;
+ pthread_rwlock_unlock(&d->rw);
+ safe_cond_signal(&d->ready, &d->ready_m);
}
-static void sync_read(unsigned char *buf, uint32_t len, struct fm_state *fm)
+static void *dongle_thread_fn(void *arg)
{
- int r, n_read;
- r = rtlsdr_read_sync(dev, buf, len, &n_read);
- if (r < 0) {
- fprintf(stderr, "WARNING: sync read failed.\n");
- return;
- }
- pthread_rwlock_wrlock(&data_rw);
- memcpy(fm->buf, buf, len);
- fm->buf_len = len;
- pthread_rwlock_unlock(&data_rw);
- safe_cond_signal(&data_ready, &data_mutex);
- //full_demod(fm);
+ struct dongle_state *s = arg;
+ rtlsdr_read_async(s->dev, rtlsdr_callback, s,
+ DEFAULT_ASYNC_BUF_NUMBER, s->buf_len);
+ return 0;
}
static void *demod_thread_fn(void *arg)
{
- struct fm_state *fm2 = arg;
+ struct demod_state *d = arg;
+ struct output_state *o = d->output_target;
while (!do_exit) {
- safe_cond_wait(&data_ready, &data_mutex);
- full_demod(fm2);
- if (fm2->exit_flag) {
+ safe_cond_wait(&d->ready, &d->ready_m);
+ pthread_rwlock_wrlock(&d->rw);
+ full_demod(d);
+ pthread_rwlock_unlock(&d->rw);
+ if (d->exit_flag) {
do_exit = 1;
- //rtlsdr_cancel_async(dev);
}
+ if (d->squelch_level && d->squelch_hits > d->conseq_squelch) {
+ d->squelch_hits = d->conseq_squelch + 1; /* hair trigger */
+ safe_cond_signal(&controller.hop, &controller.hop_m);
+ continue;
+ }
+ pthread_rwlock_wrlock(&o->rw);
+ memcpy(o->result, d->result, 2*d->result_len);
+ o->result_len = d->result_len;
+ pthread_rwlock_unlock(&o->rw);
+ safe_cond_signal(&o->ready, &o->ready_m);
+ }
+ return 0;
+}
+
+static void *output_thread_fn(void *arg)
+{
+ struct output_state *s = arg;
+ while (!do_exit) {
+ // use timedwait and pad out under runs
+ safe_cond_wait(&s->ready, &s->ready_m);
+ pthread_rwlock_rdlock(&s->rw);
+ fwrite(s->result, 2, s->result_len, s->file);
+ pthread_rwlock_unlock(&s->rw);
}
return 0;
}
-void frequency_range(struct fm_state *fm, char *arg)
+static void optimal_settings(int freq, int rate)
+{
+ // giant ball of hacks
+ // seems unable to do a single pass, 2:1
+ int r, capture_freq, capture_rate;
+ struct dongle_state *d = &dongle;
+ struct demod_state *dm = &demod;
+ struct controller_state *cs = &controller;
+ dm->downsample = (1000000 / dm->rate_in) + 1;
+ if (dm->downsample_passes) {
+ dm->downsample_passes = (int)log2(dm->downsample) + 1;
+ dm->downsample = 1 << dm->downsample_passes;
+ }
+ capture_freq = freq;
+ capture_rate = dm->downsample * dm->rate_in;
+ if (!d->offset_tuning) {
+ capture_freq = freq + capture_rate/4;}
+ capture_freq += cs->edge * dm->rate_in / 2;
+ dm->output_scale = (1<<15) / (128 * dm->downsample);
+ if (dm->output_scale < 1) {
+ dm->output_scale = 1;}
+ if (dm->mode_demod == &fm_demod) {
+ dm->output_scale = 1;}
+ d->freq = (uint32_t)capture_freq;
+ d->rate = (uint32_t)capture_rate;
+}
+
+static void *controller_thread_fn(void *arg)
+{
+ // thoughts for multiple dongles
+ // might be no good using a controller thread if retune/rate blocks
+ int i, r;
+ struct controller_state *s = arg;
+
+ if (s->wb_mode) {
+ for (i=0; i < s->freq_len; i++) {
+ s->freqs[i] += 16000;}
+ }
+
+ /* set up primary channel */
+ optimal_settings(s->freqs[0], demod.rate_in);
+ if (dongle.direct_sampling) {
+ verbose_direct_sampling(dongle.dev, 1);}
+ if (dongle.offset_tuning) {
+ verbose_offset_tuning(dongle.dev);}
+
+ /* Set the frequency */
+ verbose_set_frequency(dongle.dev, dongle.freq);
+ fprintf(stderr, "Oversampling input by: %ix.\n", demod.downsample);
+ fprintf(stderr, "Oversampling output by: %ix.\n", demod.post_downsample);
+ fprintf(stderr, "Buffer size: %0.2fms\n",
+ 1000 * 0.5 * (float)ACTUAL_BUF_LENGTH / (float)dongle.rate);
+
+ /* Set the sample rate */
+ verbose_set_sample_rate(dongle.dev, dongle.rate);
+ fprintf(stderr, "Output at %u Hz.\n", demod.rate_in/demod.post_downsample);
+
+ while (!do_exit) {
+ safe_cond_wait(&s->hop, &s->hop_m);
+ if (s->freq_len <= 1) {
+ continue;}
+ /* hacky hopping */
+ s->freq_now = (s->freq_now + 1) % s->freq_len;
+ optimal_settings(s->freqs[s->freq_now], demod.rate_in);
+ rtlsdr_set_center_freq(dongle.dev, dongle.freq);
+ dongle.mute = BUFFER_DUMP;
+ }
+ return 0;
+}
+
+void frequency_range(struct controller_state *s, char *arg)
{
char *start, *stop, *step;
int i;
@@ -718,39 +932,108 @@ void frequency_range(struct fm_state *fm, char *arg)
step[-1] = '\0';
for(i=(int)atofs(start); i<=(int)atofs(stop); i+=(int)atofs(step))
{
- fm->freqs[fm->freq_len] = (uint32_t)i;
- fm->freq_len++;
- if (fm->freq_len >= FREQUENCIES_LIMIT) {
+ s->freqs[s->freq_len] = (uint32_t)i;
+ s->freq_len++;
+ if (s->freq_len >= FREQUENCIES_LIMIT) {
break;}
}
stop[-1] = ':';
step[-1] = ':';
}
+void dongle_init(struct dongle_state *s)
+{
+ s->rate = DEFAULT_SAMPLE_RATE;
+ s->gain = AUTO_GAIN; // tenths of a dB
+ s->mute = 0;
+ s->direct_sampling = 0;
+ s->offset_tuning = 0;
+ s->demod_target = &demod;
+}
+
+void demod_init(struct demod_state *s)
+{
+ s->rate_in = DEFAULT_SAMPLE_RATE;
+ s->rate_out = DEFAULT_SAMPLE_RATE;
+ s->squelch_level = 0;
+ s->conseq_squelch = 10;
+ s->terminate_on_squelch = 0;
+ s->squelch_hits = 11;
+ s->downsample_passes = 0;
+ s->comp_fir_size = 0;
+ s->prev_index = 0;
+ s->post_downsample = 1; // once this works, default = 4
+ s->custom_atan = 0;
+ s->deemph = 0;
+ s->rate_out2 = -1; // flag for disabled
+ s->mode_demod = &fm_demod;
+ s->pre_j = s->pre_r = s->now_r = s->now_j = 0;
+ s->prev_lpr_index = 0;
+ s->deemph_a = 0;
+ s->now_lpr = 0;
+ s->dc_block = 0;
+ s->dc_avg = 0;
+ pthread_rwlock_init(&s->rw, NULL);
+ pthread_cond_init(&s->ready, NULL);
+ pthread_mutex_init(&s->ready_m, NULL);
+ s->output_target = &output;
+}
+
+void demod_cleanup(struct demod_state *s)
+{
+ pthread_rwlock_destroy(&s->rw);
+ pthread_cond_destroy(&s->ready);
+ pthread_mutex_destroy(&s->ready_m);
+}
+
+void output_init(struct output_state *s)
+{
+ s->rate = DEFAULT_SAMPLE_RATE;
+ pthread_rwlock_init(&s->rw, NULL);
+ pthread_cond_init(&s->ready, NULL);
+ pthread_mutex_init(&s->ready_m, NULL);
+}
+
+void output_cleanup(struct output_state *s)
+{
+ pthread_rwlock_destroy(&s->rw);
+ pthread_cond_destroy(&s->ready);
+ pthread_mutex_destroy(&s->ready_m);
+}
+
+void controller_init(struct controller_state *s)
+{
+ s->freqs[0] = 100000000;
+ s->freq_len = 0;
+ s->edge = 0;
+ s->wb_mode = 0;
+ pthread_cond_init(&s->hop, NULL);
+ pthread_mutex_init(&s->hop_m, NULL);
+}
+
+void controller_cleanup(struct controller_state *s)
+{
+ pthread_cond_destroy(&s->hop);
+ pthread_mutex_destroy(&s->hop_m);
+}
+
+void sanity_checks(void)
+{
+ if (controller.freq_len == 0) {
+ fprintf(stderr, "Please specify a frequency.\n");
+ exit(1);
+ }
+
+ if (controller.freq_len >= FREQUENCIES_LIMIT) {
+ fprintf(stderr, "Too many channels, maximum %i.\n", FREQUENCIES_LIMIT);
+ exit(1);
+ }
+
+ if (controller.freq_len > 1 && demod.squelch_level == 0) {
+ fprintf(stderr, "Please specify a squelch level. Required for scanning multiple frequencies.\n");
+ exit(1);
+ }
-void fm_init(struct fm_state *fm)
-{
- fm->freqs[0] = 100000000;
- fm->sample_rate = DEFAULT_SAMPLE_RATE;
- fm->squelch_level = 0;
- fm->conseq_squelch = 20;
- fm->terminate_on_squelch = 0;
- fm->squelch_hits = 0;
- fm->freq_len = 0;
- fm->edge = 0;
- fm->fir_enable = 0;
- fm->prev_index = 0;
- fm->post_downsample = 1; // once this works, default = 4
- fm->custom_atan = 0;
- fm->deemph = 0;
- fm->output_rate = -1; // flag for disabled
- fm->mode_demod = &fm_demod;
- fm->pre_j = fm->pre_r = fm->now_r = fm->now_j = 0;
- fm->prev_lpr_index = 0;
- fm->deemph_a = 0;
- fm->now_lpr = 0;
- fm->dc_block = 0;
- fm->dc_avg = 0;
}
int main(int argc, char **argv)
@@ -758,109 +1041,109 @@ int main(int argc, char **argv)
#ifndef _WIN32
struct sigaction sigact;
#endif
- struct fm_state fm;
- char *filename = NULL;
- int n_read, r, opt, wb_mode = 0;
- int i, gain = AUTO_GAIN; // tenths of a dB
- uint8_t *buffer;
- int32_t dev_index = 0;
+ int n_read, r, opt;
+ int i;
int dev_given = 0;
- int ppm_error = 0;
- char vendor[256], product[256], serial[256];
- fm_init(&fm);
- pthread_cond_init(&data_ready, NULL);
- pthread_rwlock_init(&data_rw, NULL);
- pthread_mutex_init(&data_mutex, NULL);
-
- while ((opt = getopt(argc, argv, "d:f:g:s:b:l:o:t:r:p:EFA:NWMULRDCh")) != -1) {
+ int custom_ppm = 0;
+ dongle_init(&dongle);
+ demod_init(&demod);
+ output_init(&output);
+ controller_init(&controller);
+
+ while ((opt = getopt(argc, argv, "d:f:g:s:b:l:o:t:r:p:E:F:A:M:h")) != -1) {
switch (opt) {
case 'd':
- dev_index = verbose_device_search(optarg);
+ dongle.dev_index = verbose_device_search(optarg);
dev_given = 1;
break;
case 'f':
- if (fm.freq_len >= FREQUENCIES_LIMIT) {
+ if (controller.freq_len >= FREQUENCIES_LIMIT) {
break;}
if (strchr(optarg, ':'))
- {frequency_range(&fm, optarg);}
+ {frequency_range(&controller, optarg);}
else
{
- fm.freqs[fm.freq_len] = (uint32_t)atofs(optarg);
- fm.freq_len++;
+ controller.freqs[controller.freq_len] = (uint32_t)atofs(optarg);
+ controller.freq_len++;
}
break;
case 'g':
- gain = (int)(atof(optarg) * 10);
+ dongle.gain = (int)(atof(optarg) * 10);
break;
case 'l':
- fm.squelch_level = (int)atof(optarg);
+ demod.squelch_level = (int)atof(optarg);
break;
case 's':
- fm.sample_rate = (uint32_t)atofs(optarg);
+ demod.rate_in = (uint32_t)atofs(optarg);
+ demod.rate_out = (uint32_t)atofs(optarg);
break;
case 'r':
- fm.output_rate = (int)atofs(optarg);
+ output.rate = (int)atofs(optarg);
+ demod.rate_out2 = (int)atofs(optarg);
break;
case 'o':
- fm.post_downsample = (int)atof(optarg);
- if (fm.post_downsample < 1 || fm.post_downsample > MAXIMUM_OVERSAMPLE) {
+ fprintf(stderr, "Warning: -o is very buggy\n");
+ demod.post_downsample = (int)atof(optarg);
+ if (demod.post_downsample < 1 || demod.post_downsample > MAXIMUM_OVERSAMPLE) {
fprintf(stderr, "Oversample must be between 1 and %i\n", MAXIMUM_OVERSAMPLE);}
break;
case 't':
- fm.conseq_squelch = (int)atof(optarg);
- if (fm.conseq_squelch < 0) {
- fm.conseq_squelch = -fm.conseq_squelch;
- fm.terminate_on_squelch = 1;
+ demod.conseq_squelch = (int)atof(optarg);
+ if (demod.conseq_squelch < 0) {
+ demod.conseq_squelch = -demod.conseq_squelch;
+ demod.terminate_on_squelch = 1;
}
break;
case 'p':
- ppm_error = atoi(optarg);
+ dongle.ppm_error = atoi(optarg);
+ custom_ppm = 1;
break;
case 'E':
- fm.edge = 1;
+ if (strcmp("edge", optarg) == 0) {
+ controller.edge = 1;}
+ if (strcmp("dc", optarg) == 0) {
+ demod.dc_block = 1;}
+ if (strcmp("deemp", optarg) == 0) {
+ demod.deemph = 1;}
+ if (strcmp("direct", optarg) == 0) {
+ dongle.direct_sampling = 1;}
+ if (strcmp("offset", optarg) == 0) {
+ dongle.offset_tuning = 1;}
break;
case 'F':
- fm.fir_enable = 1;
+ demod.downsample_passes = 1; /* truthy placeholder */
+ demod.comp_fir_size = atoi(optarg);
break;
case 'A':
if (strcmp("std", optarg) == 0) {
- fm.custom_atan = 0;}
+ demod.custom_atan = 0;}
if (strcmp("fast", optarg) == 0) {
- fm.custom_atan = 1;}
+ demod.custom_atan = 1;}
if (strcmp("lut", optarg) == 0) {
atan_lut_init();
- fm.custom_atan = 2;}
- break;
- case 'D':
- fm.deemph = 1;
- break;
- case 'C':
- fm.dc_block = 1;
- break;
- case 'N':
- fm.mode_demod = &fm_demod;
- break;
- case 'W':
- wb_mode = 1;
- fm.mode_demod = &fm_demod;
- fm.sample_rate = 170000;
- fm.output_rate = 32000;
- fm.custom_atan = 1;
- fm.post_downsample = 4;
- fm.deemph = 1;
- fm.squelch_level = 0;
+ demod.custom_atan = 2;}
break;
case 'M':
- fm.mode_demod = &am_demod;
- break;
- case 'U':
- fm.mode_demod = &usb_demod;
- break;
- case 'L':
- fm.mode_demod = &lsb_demod;
- break;
- case 'R':
- fm.mode_demod = &raw_demod;
+ if (strcmp("fm", optarg) == 0) {
+ demod.mode_demod = &fm_demod;}
+ if (strcmp("raw", optarg) == 0) {
+ demod.mode_demod = &raw_demod;}
+ if (strcmp("am", optarg) == 0) {
+ demod.mode_demod = &am_demod;}
+ if (strcmp("usb", optarg) == 0) {
+ demod.mode_demod = &usb_demod;}
+ if (strcmp("lsb", optarg) == 0) {
+ demod.mode_demod = &lsb_demod;}
+ if (strcmp("wbfm", optarg) == 0) {
+ controller.wb_mode = 1;
+ demod.mode_demod = &fm_demod;
+ demod.rate_in = 170000;
+ demod.rate_out = 170000;
+ demod.rate_out2 = 32000;
+ demod.custom_atan = 1;
+ //demod.post_downsample = 4;
+ demod.deemph = 1;
+ demod.squelch_level = 0;}
break;
case 'h':
default:
@@ -868,47 +1151,37 @@ int main(int argc, char **argv)
break;
}
}
- /* quadruple sample_rate to limit to Δθ to ±π/2 */
- fm.sample_rate *= fm.post_downsample;
- if (fm.freq_len == 0) {
- fprintf(stderr, "Please specify a frequency.\n");
- exit(1);
- }
+ /* quadruple sample_rate to limit to Δθ to ±π/2 */
+ demod.rate_in *= demod.post_downsample;
- if (fm.freq_len >= FREQUENCIES_LIMIT) {
- fprintf(stderr, "Too many channels, maximum %i.\n", FREQUENCIES_LIMIT);
- exit(1);
- }
+ if (!output.rate) {
+ output.rate = demod.rate_out;}
- if (fm.freq_len > 1 && fm.squelch_level == 0) {
- fprintf(stderr, "Please specify a squelch level. Required for scanning multiple frequencies.\n");
- exit(1);
- }
+ sanity_checks();
- if (fm.freq_len > 1) {
- fm.terminate_on_squelch = 0;
- }
+ if (controller.freq_len > 1) {
+ demod.terminate_on_squelch = 0;}
if (argc <= optind) {
- filename = "-";
+ output.filename = "-";
} else {
- filename = argv[optind];
+ output.filename = argv[optind];
}
- ACTUAL_BUF_LENGTH = lcm_post[fm.post_downsample] * DEFAULT_BUF_LENGTH;
- buffer = malloc(ACTUAL_BUF_LENGTH * sizeof(uint8_t));
+ ACTUAL_BUF_LENGTH = lcm_post[demod.post_downsample] * DEFAULT_BUF_LENGTH;
if (!dev_given) {
- dev_index = verbose_device_search("0");
+ dongle.dev_index = verbose_device_search("0");
}
- fprintf(stderr, "Using device %d: %s\n",
- dev_index, rtlsdr_get_device_name(dev_index));
+ if (dongle.dev_index < 0) {
+ exit(1);
+ }
- r = rtlsdr_open(&dev, dev_index);
+ r = rtlsdr_open(&dongle.dev, (uint32_t)dongle.dev_index);
if (r < 0) {
- fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dev_index);
+ fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dongle.dev_index);
exit(1);
}
#ifndef _WIN32
@@ -923,53 +1196,46 @@ int main(int argc, char **argv)
SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE );
#endif
- /* WBFM is special */
- // I really should loop over everything
- // but you are more wrong for scanning broadcast FM
- if (wb_mode) {
- fm.freqs[0] += 16000;
- }
-
- if (fm.deemph) {
- fm.deemph_a = (int)round(1.0/((1.0-exp(-1.0/(fm.output_rate * 75e-6)))));
+ if (demod.deemph) {
+ demod.deemph_a = (int)round(1.0/((1.0-exp(-1.0/(demod.rate_out * 75e-6)))));
}
- optimal_settings(&fm, 0, 0);
- build_fir(&fm);
-
/* Set the tuner gain */
- if (gain == AUTO_GAIN) {
- verbose_auto_gain(dev);
+ if (dongle.gain == AUTO_GAIN) {
+ verbose_auto_gain(dongle.dev);
} else {
- gain = nearest_gain(dev, gain);
- verbose_gain_set(dev, gain);
+ dongle.gain = nearest_gain(dongle.dev, dongle.gain);
+ verbose_gain_set(dongle.dev, dongle.gain);
}
- verbose_ppm_set(dev, ppm_error);
+ verbose_ppm_set(dongle.dev, dongle.ppm_error);
- if (strcmp(filename, "-") == 0) { /* Write samples to stdout */
- fm.file = stdout;
+ if (strcmp(output.filename, "-") == 0) { /* Write samples to stdout */
+ output.file = stdout;
#ifdef _WIN32
- _setmode(_fileno(fm.file), _O_BINARY);
+ _setmode(_fileno(output.file), _O_BINARY);
#endif
} else {
- fm.file = fopen(filename, "wb");
- if (!fm.file) {
- fprintf(stderr, "Failed to open %s\n", filename);
+ output.file = fopen(output.filename, "wb");
+ if (!output.file) {
+ fprintf(stderr, "Failed to open %s\n", output.filename);
exit(1);
}
}
+ //r = rtlsdr_set_testmode(dongle.dev, 1);
+
/* Reset endpoint before we start reading from it (mandatory) */
- verbose_reset_buffer(dev);
+ verbose_reset_buffer(dongle.dev);
- pthread_create(&demod_thread, NULL, demod_thread_fn, (void *)(&fm));
- /*rtlsdr_read_async(dev, rtlsdr_callback, (void *)(&fm),
- DEFAULT_ASYNC_BUF_NUMBER,
- ACTUAL_BUF_LENGTH);*/
+ pthread_create(&controller.thread, NULL, controller_thread_fn, (void *)(&controller));
+ usleep(100000);
+ pthread_create(&output.thread, NULL, output_thread_fn, (void *)(&output));
+ pthread_create(&demod.thread, NULL, demod_thread_fn, (void *)(&demod));
+ pthread_create(&dongle.thread, NULL, dongle_thread_fn, (void *)(&dongle));
while (!do_exit) {
- sync_read(buffer, ACTUAL_BUF_LENGTH, &fm);
+ usleep(100000);
}
if (do_exit) {
@@ -977,19 +1243,24 @@ int main(int argc, char **argv)
else {
fprintf(stderr, "\nLibrary error %d, exiting...\n", r);}
- //rtlsdr_cancel_async(dev);
- safe_cond_signal(&data_ready, &data_mutex);
- pthread_join(demod_thread, NULL);
+ rtlsdr_cancel_async(dongle.dev);
+ pthread_join(dongle.thread, NULL);
+ safe_cond_signal(&demod.ready, &demod.ready_m);
+ pthread_join(demod.thread, NULL);
+ safe_cond_signal(&output.ready, &output.ready_m);
+ pthread_join(output.thread, NULL);
+ safe_cond_signal(&controller.hop, &controller.hop_m);
+ pthread_join(controller.thread, NULL);
- pthread_cond_destroy(&data_ready);
- pthread_rwlock_destroy(&data_rw);
- pthread_mutex_destroy(&data_mutex);
+ //dongle_cleanup(&dongle);
+ demod_cleanup(&demod);
+ output_cleanup(&output);
+ controller_cleanup(&controller);
- if (fm.file != stdout) {
- fclose(fm.file);}
+ if (output.file != stdout) {
+ fclose(output.file);}
- rtlsdr_close(dev);
- free (buffer);
+ rtlsdr_close(dongle.dev);
return r >= 0 ? r : -r;
}