From c4fcfbb46e0a432902a2b78db4951bd20f68b9b2 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Sat, 17 Aug 2013 09:49:28 -0400 Subject: rtl_fm: fix scanning Signed-off-by: Steve Markgraf --- src/rtl_fm.c | 127 +++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 94 insertions(+), 33 deletions(-) (limited to 'src/rtl_fm.c') diff --git a/src/rtl_fm.c b/src/rtl_fm.c index e8ebb77..c3bd72c 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -34,6 +34,11 @@ * pad output on hop * nearest gain approx * frequency ranges could be stored better + * 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 */ #include @@ -66,12 +71,16 @@ #define MAXIMUM_BUF_LENGTH (MAXIMUM_OVERSAMPLE * DEFAULT_BUF_LENGTH) #define AUTO_GAIN -100 +#define FREQUENCIES_LIMIT 1000 + static pthread_t demod_thread; -static pthread_mutex_t data_ready; /* locked when no fresh data available */ -static pthread_mutex_t data_write; /* locked when r/w buffer */ -static int do_exit = 0; +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; static int *atan_lut = NULL; static int atan_lut_size = 131072; /* 512 KB */ @@ -95,7 +104,7 @@ struct fm_state int signal2_len; FILE *file; int edge; - uint32_t freqs[1000]; + uint32_t freqs[FREQUENCIES_LIMIT]; int freq_len; int freq_now; uint32_t sample_rate; @@ -145,7 +154,7 @@ void usage(void) "\t[-C enables DC blocking of output (default: off)]\n" "\t[-A std/fast/lut choose atan math (default: std)]\n\n" "Produces signed 16 bit ints, use Sox or aplay to hear them.\n" - "\trtl_fm ... - | play -t raw -r 24k -e signed-integer -b 16 -c 1 -V1 -\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"); exit(1); @@ -158,7 +167,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(dev); return TRUE; } return FALSE; @@ -168,10 +177,14 @@ static void sighandler(int signum) { fprintf(stderr, "Signal caught, exiting!\n"); do_exit = 1; - rtlsdr_cancel_async(dev); + //rtlsdr_cancel_async(dev); } #endif +/* more cond dumbness */ +#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) + void rotate_90(unsigned char *buf, uint32_t len) /* 90 rotation is 1+0j, 0+1j, -1+0j, 0-1j or [0, 1, -3, 2, -4, -5, 7, -6] */ @@ -205,8 +218,8 @@ void low_pass(struct fm_state *fm, unsigned char *buf, uint32_t len) 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->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; @@ -248,8 +261,8 @@ void low_pass_fir(struct fm_state *fm, unsigned char *buf, uint32_t len) 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->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; @@ -432,7 +445,7 @@ void am_demod(struct fm_state *fm) //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; + fm->signal2[i/2] = (int16_t)sqrt(pcm) * fm->output_scale; } fm->signal2_len = fm->signal_len/2; // lowpass? (3khz) highpass? (dc) @@ -443,7 +456,7 @@ void usb_demod(struct fm_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; + fm->signal2[i/2] = (int16_t)pcm * fm->output_scale; } fm->signal2_len = fm->signal_len/2; } @@ -453,7 +466,7 @@ void lsb_demod(struct fm_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; + fm->signal2[i/2] = (int16_t)pcm * fm->output_scale; } fm->signal2_len = fm->signal_len/2; } @@ -545,7 +558,8 @@ static void optimal_settings(struct fm_state *fm, int freq, int hopping) fm->output_scale = (1<<15) / (128 * fm->downsample); if (fm->output_scale < 1) { 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) { @@ -553,7 +567,7 @@ static void optimal_settings(struct fm_state *fm, int freq, int hopping) 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 * lcm_post[fm->post_downsample] * (float)DEFAULT_BUF_LENGTH / (float)capture_rate); + 1000 * 0.5 * (float)ACTUAL_BUF_LENGTH / (float)capture_rate); if (r < 0) { fprintf(stderr, "WARNING: Failed to set center freq.\n");} else { @@ -574,15 +588,16 @@ static void optimal_settings(struct fm_state *fm, int freq, int hopping) void full_demod(struct fm_state *fm) { int i, sr, freq_next, 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); } else { low_pass(fm, fm->buf, fm->buf_len); } - pthread_mutex_unlock(&data_write); + pthread_rwlock_unlock(&data_rw); fm->mode_demod(fm); - if (fm->mode_demod == &raw_demod) { + if (fm->mode_demod == &raw_demod) { fwrite(fm->signal2, 2, fm->signal2_len, fm->file); return; } @@ -625,23 +640,41 @@ static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) return;} if (!ctx) { return;} - pthread_mutex_lock(&data_write); + pthread_rwlock_wrlock(&data_rw); memcpy(fm2->buf, buf, len); fm2->buf_len = len; - pthread_mutex_unlock(&data_ready); + pthread_rwlock_unlock(&data_rw); + safe_cond_signal(&data_ready, &data_mutex); /* single threaded uses 25% less CPU? */ /* full_demod(fm2); */ } +static void sync_read(unsigned char *buf, uint32_t len, struct fm_state *fm) +{ + 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); +} + static void *demod_thread_fn(void *arg) { struct fm_state *fm2 = arg; while (!do_exit) { - pthread_mutex_lock(&data_ready); + safe_cond_wait(&data_ready, &data_mutex); full_demod(fm2); if (fm2->exit_flag) { do_exit = 1; - rtlsdr_cancel_async(dev);} + //rtlsdr_cancel_async(dev); + } } return 0; } @@ -650,7 +683,7 @@ double atofs(char* f) /* standard suffixes */ { char* chop; - double suff = 1.0; + double suff = 1.0; chop = malloc((strlen(f)+1)*sizeof(char)); strncpy(chop, f, strlen(f)-1); switch (f[strlen(f)-1]) { @@ -660,7 +693,7 @@ double atofs(char* f) suff *= 1e3; case 'k': suff *= 1e3; - suff *= atof(chop);} + suff *= atof(chop);} free(chop); if (suff != 1.0) { return suff;} @@ -680,6 +713,8 @@ void frequency_range(struct fm_state *fm, char *arg) { fm->freqs[fm->freq_len] = (uint32_t)i; fm->freq_len++; + if (fm->freq_len >= FREQUENCIES_LIMIT) { + break;} } stop[-1] = ':'; step[-1] = ':'; @@ -725,8 +760,9 @@ int main(int argc, char **argv) int ppm_error = 0; char vendor[256], product[256], serial[256]; fm_init(&fm); - pthread_mutex_init(&data_ready, NULL); - pthread_mutex_init(&data_write, NULL); + 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:NWMULRDC")) != -1) { switch (opt) { @@ -734,6 +770,8 @@ int main(int argc, char **argv) dev_index = atoi(optarg); break; case 'f': + if (fm.freq_len >= FREQUENCIES_LIMIT) { + break;} if (strchr(optarg, ':')) {frequency_range(&fm, optarg);} else @@ -828,18 +866,28 @@ int main(int argc, char **argv) exit(1); } + if (fm.freq_len >= FREQUENCIES_LIMIT) { + fprintf(stderr, "Too many channels, maximum %i.\n", FREQUENCIES_LIMIT); + exit(1); + } + + if (fm.freq_len > 1 && fm.squelch_level == 0) { + fprintf(stderr, "Please specify a squelch level. Required for scanning multiple frequencies.\n"); + exit(1); + } + if (fm.freq_len > 1) { fm.terminate_on_squelch = 0; } if (argc <= optind) { - //usage(); filename = "-"; } else { filename = argv[optind]; } - buffer = malloc(lcm_post[fm.post_downsample] * DEFAULT_BUF_LENGTH * sizeof(uint8_t)); + ACTUAL_BUF_LENGTH = lcm_post[fm.post_downsample] * DEFAULT_BUF_LENGTH; + buffer = malloc(ACTUAL_BUF_LENGTH * sizeof(uint8_t)); device_count = rtlsdr_get_device_count(); if (!device_count) { @@ -875,6 +923,8 @@ int main(int argc, char **argv) #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; } @@ -921,17 +971,26 @@ int main(int argc, char **argv) fprintf(stderr, "WARNING: Failed to reset buffers.\n");} pthread_create(&demod_thread, NULL, demod_thread_fn, (void *)(&fm)); - rtlsdr_read_async(dev, rtlsdr_callback, (void *)(&fm), + /*rtlsdr_read_async(dev, rtlsdr_callback, (void *)(&fm), DEFAULT_ASYNC_BUF_NUMBER, - lcm_post[fm.post_downsample] * DEFAULT_BUF_LENGTH); + ACTUAL_BUF_LENGTH);*/ + + while (!do_exit) { + sync_read(buffer, ACTUAL_BUF_LENGTH, &fm); + } if (do_exit) { fprintf(stderr, "\nUser cancel, exiting...\n");} else { fprintf(stderr, "\nLibrary error %d, exiting...\n", r);} - rtlsdr_cancel_async(dev); - pthread_mutex_destroy(&data_ready); - pthread_mutex_destroy(&data_write); + + //rtlsdr_cancel_async(dev); + safe_cond_signal(&data_ready, &data_mutex); + pthread_join(demod_thread, NULL); + + pthread_cond_destroy(&data_ready); + pthread_rwlock_destroy(&data_rw); + pthread_mutex_destroy(&data_mutex); if (fm.file != stdout) { fclose(fm.file);} @@ -940,3 +999,5 @@ int main(int argc, char **argv) free (buffer); return r >= 0 ? r : -r; } + +// vim: tabstop=8:softtabstop=8:shiftwidth=8:noexpandtab -- cgit v1.2.3