|
|
@ -26,8 +26,7 @@ |
|
|
|
* replace atan2 with a fast approximation |
|
|
|
* replace atan2 with a fast approximation |
|
|
|
* in-place array operations |
|
|
|
* in-place array operations |
|
|
|
* wide band support |
|
|
|
* wide band support |
|
|
|
* squelch |
|
|
|
* refactor to look like rtl_tcp |
|
|
|
* better threading |
|
|
|
|
|
|
|
*/ |
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
#include <errno.h> |
|
|
|
#include <errno.h> |
|
|
@ -63,6 +62,7 @@ struct fm_state |
|
|
|
int prev_index; |
|
|
|
int prev_index; |
|
|
|
int downsample; /* min 4, max 256 */ |
|
|
|
int downsample; /* min 4, max 256 */ |
|
|
|
int output_scale; |
|
|
|
int output_scale; |
|
|
|
|
|
|
|
int squelch_level; |
|
|
|
int signal[2048]; /* 16 bit signed i/q pairs */ |
|
|
|
int signal[2048]; /* 16 bit signed i/q pairs */ |
|
|
|
int16_t signal2[2048]; /* signal has lowpass, signal2 has demod */ |
|
|
|
int16_t signal2[2048]; /* signal has lowpass, signal2 has demod */ |
|
|
|
int signal_len; |
|
|
|
int signal_len; |
|
|
@ -82,6 +82,7 @@ void usage(void) |
|
|
|
"\t[-s samplerate (default: 24000 Hz)]\n" |
|
|
|
"\t[-s samplerate (default: 24000 Hz)]\n" |
|
|
|
"\t[-d device_index (default: 0)]\n" |
|
|
|
"\t[-d device_index (default: 0)]\n" |
|
|
|
"\t[-g tuner_gain (default: -1dB)]\n" |
|
|
|
"\t[-g tuner_gain (default: -1dB)]\n" |
|
|
|
|
|
|
|
"\t[-l squelch_level (default: 150)]\n" |
|
|
|
"\tfilename (a '-' dumps samples to stdout)\n\n" |
|
|
|
"\tfilename (a '-' dumps samples to stdout)\n\n" |
|
|
|
"Produces signed 16 bit ints, use sox to hear them.\n" |
|
|
|
"Produces signed 16 bit ints, use sox to hear them.\n" |
|
|
|
"\trtl_fm ... | play -t raw -r 24k -e signed-integer -b 16 -c 1 -\n\n"); |
|
|
|
"\trtl_fm ... | play -t raw -r 24k -e signed-integer -b 16 -c 1 -\n\n"); |
|
|
@ -116,9 +117,8 @@ void rotate_90(unsigned char *buf, uint32_t len) |
|
|
|
{ |
|
|
|
{ |
|
|
|
uint32_t i; |
|
|
|
uint32_t i; |
|
|
|
unsigned char tmp; |
|
|
|
unsigned char tmp; |
|
|
|
for (i=0; i<len; i+=8) |
|
|
|
for (i=0; i<len; i+=8) { |
|
|
|
{ |
|
|
|
/* uint8_t negation = 255 - x */ |
|
|
|
/* uint_8 negation = 255 - x */ |
|
|
|
|
|
|
|
tmp = 255 - buf[i+3]; |
|
|
|
tmp = 255 - buf[i+3]; |
|
|
|
buf[i+3] = buf[i+2]; |
|
|
|
buf[i+3] = buf[i+2]; |
|
|
|
buf[i+2] = tmp; |
|
|
|
buf[i+2] = tmp; |
|
|
@ -168,9 +168,6 @@ int polar_discriminant(int ar, int aj, int br, int bj) |
|
|
|
int cr, cj; |
|
|
|
int cr, cj; |
|
|
|
double angle; |
|
|
|
double angle; |
|
|
|
multiply(ar, aj, br, -bj, &cr, &cj); |
|
|
|
multiply(ar, aj, br, -bj, &cr, &cj); |
|
|
|
/* fake squelch */ |
|
|
|
|
|
|
|
//if (abs(cr) + abs(cj) < 50000)
|
|
|
|
|
|
|
|
// {return 0;}
|
|
|
|
|
|
|
|
angle = atan2((double)cj, (double)cr); |
|
|
|
angle = atan2((double)cj, (double)cr); |
|
|
|
return (int)(angle / 3.14159 * (1<<14)); |
|
|
|
return (int)(angle / 3.14159 * (1<<14)); |
|
|
|
} |
|
|
|
} |
|
|
@ -190,16 +187,49 @@ void fm_demod(struct fm_state *fm) |
|
|
|
fm->pre_j = fm->signal[fm->signal_len - 1]; |
|
|
|
fm->pre_j = fm->signal[fm->signal_len - 1]; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int mad(int *samples, int len, int step) |
|
|
|
|
|
|
|
/* mean average deviation */ |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
int i=0, sum=0, ave=0; |
|
|
|
|
|
|
|
for (i=0; i<len; i+=step) { |
|
|
|
|
|
|
|
sum += samples[i]; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
ave = sum / (len * step); |
|
|
|
|
|
|
|
sum = 0; |
|
|
|
|
|
|
|
for (i=0; i<len; i+=step) { |
|
|
|
|
|
|
|
sum += abs(samples[i] - ave); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return sum / (len * step); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void post_squelch(struct fm_state *fm) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
int i, i2, 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)) { |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
/* weak signal, kill it entirely */ |
|
|
|
|
|
|
|
for (i=0; i<len; i++) { |
|
|
|
|
|
|
|
fm->signal2[i/2] = 0; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) |
|
|
|
static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) |
|
|
|
{ |
|
|
|
{ |
|
|
|
struct fm_state *fm2; |
|
|
|
struct fm_state *fm2; |
|
|
|
if (!ctx) { |
|
|
|
if (!ctx) { |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
fm2 = (struct fm_struct*)(ctx); // bad conversion?
|
|
|
|
fm2 = (struct fm_struct*)(ctx); // warning?
|
|
|
|
rotate_90(buf, len); |
|
|
|
rotate_90(buf, len); |
|
|
|
low_pass(fm2, buf, len); |
|
|
|
low_pass(fm2, buf, len); |
|
|
|
fm_demod(fm2); |
|
|
|
fm_demod(fm2); |
|
|
|
|
|
|
|
post_squelch(fm2); |
|
|
|
/* ignore under runs for now */ |
|
|
|
/* ignore under runs for now */ |
|
|
|
fwrite(fm2->signal2, 2, fm2->signal_len/2, fm2->file); |
|
|
|
fwrite(fm2->signal2, 2, fm2->signal_len/2, fm2->file); |
|
|
|
} |
|
|
|
} |
|
|
@ -246,8 +276,9 @@ int main(int argc, char **argv) |
|
|
|
uint32_t out_block_size = DEFAULT_BUF_LENGTH; |
|
|
|
uint32_t out_block_size = DEFAULT_BUF_LENGTH; |
|
|
|
int device_count; |
|
|
|
int device_count; |
|
|
|
char vendor[256], product[256], serial[256]; |
|
|
|
char vendor[256], product[256], serial[256]; |
|
|
|
|
|
|
|
fm.squelch_level = 150; |
|
|
|
#ifndef _WIN32 |
|
|
|
#ifndef _WIN32 |
|
|
|
while ((opt = getopt(argc, argv, "d:f:g:s:b:S::")) != -1) { |
|
|
|
while ((opt = getopt(argc, argv, "d:f:g:s:b:l:S::")) != -1) { |
|
|
|
switch (opt) { |
|
|
|
switch (opt) { |
|
|
|
case 'd': |
|
|
|
case 'd': |
|
|
|
dev_index = atoi(optarg); |
|
|
|
dev_index = atoi(optarg); |
|
|
@ -258,6 +289,9 @@ int main(int argc, char **argv) |
|
|
|
case 'g': |
|
|
|
case 'g': |
|
|
|
gain = (int)(atof(optarg) * 10); |
|
|
|
gain = (int)(atof(optarg) * 10); |
|
|
|
break; |
|
|
|
break; |
|
|
|
|
|
|
|
case 'l': |
|
|
|
|
|
|
|
fm.squelch_level = (int)atof(optarg); |
|
|
|
|
|
|
|
break; |
|
|
|
case 's': |
|
|
|
case 's': |
|
|
|
samp_rate = (uint32_t)atof(optarg); |
|
|
|
samp_rate = (uint32_t)atof(optarg); |
|
|
|
break; |
|
|
|
break; |
|
|
|