aboutsummaryrefslogtreecommitdiffstats
path: root/src/rtl_fm.c
diff options
context:
space:
mode:
authorKyle Keen <keenerd@gmail.com>2013-08-17 09:49:28 -0400
committerSteve Markgraf <steve@steve-m.de>2013-09-13 18:31:49 +0200
commitc4fcfbb46e0a432902a2b78db4951bd20f68b9b2 (patch)
tree735c4dbaa1e3aa8723eac06174d65ed2425006b8 /src/rtl_fm.c
parent8c3a99c8f7a88d7d2a05845d4b20cfcdacac4054 (diff)
rtl_fm: fix scanning
Signed-off-by: Steve Markgraf <steve@steve-m.de>
Diffstat (limited to 'src/rtl_fm.c')
-rw-r--r--src/rtl_fm.c127
1 files changed, 94 insertions, 33 deletions
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 <errno.h>
@@ -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