Selaa lähdekoodia

rtl_fm: fix scanning

Signed-off-by: Steve Markgraf <steve@steve-m.de>
Kyle Keen 11 vuotta sitten
vanhempi
commit
c4fcfbb46e
1 muutettua tiedostoa jossa 94 lisäystä ja 33 poistoa
  1. 94 33
      src/rtl_fm.c

+ 94 - 33
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