Skip to content

added some minor options #23

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 156 additions & 21 deletions src/rtl_fm.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* Copyright (C) 2012 by Hoernchen <[email protected]>
* Copyright (C) 2012 by Kyle Keen <[email protected]>
* Copyright (C) 2013 by Elias Oenal <[email protected]>
* Copyright (C) 2015 by Hayati Ayguen <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -92,6 +93,13 @@ static int *atan_lut = NULL;
static int atan_lut_size = 131072; /* 512 KB */
static int atan_lut_coef = 8;

static int verbosity = 0;
static int printLevels = 0;
static int printLevelNo = 1;
static int levelMax = 0;
static int levelMaxMax = 0;
static double levelSum = 0.0;

struct dongle_state
{
int exit_flag;
Expand All @@ -101,7 +109,7 @@ struct dongle_state
uint32_t freq;
uint32_t rate;
int gain;
uint16_t buf16[MAXIMUM_BUF_LENGTH];
int16_t buf16[MAXIMUM_BUF_LENGTH];
uint32_t buf_len;
int ppm_error;
int offset_tuning;
Expand Down Expand Up @@ -138,7 +146,8 @@ struct demod_state
int deemph, deemph_a;
int now_lpr;
int prev_lpr_index;
int dc_block, dc_avg;
int dc_block_audio, dc_avg, adc_block_const;
int dc_block_raw, dc_avgI, dc_avgQ, rdc_block_const;
void (*mode_demod)(struct demod_state*);
pthread_rwlock_t rw;
pthread_cond_t ready;
Expand Down Expand Up @@ -187,24 +196,32 @@ void usage(void)
"\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[-v verbosity (default: 0)]\n"
"\t[-M modulation (default: fm)]\n"
"\t fm, wbfm, raw, am, usb, lsb\n"
"\t fm or nbfm or nfm, wbfm or wfm, raw or iq, 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[-L N prints levels every N calculations]\n"
"\t output are comma separated values (csv):\n"
"\t mean since last output, max since last output, overall max, squelch\n"
"\t[-c de-emphasis_time_constant in us for wbfm. 'us' or 'eu' for 75/50 us (default: us)]\n"
//"\t for fm squelch is inverted\n"
//"\t[-o oversampling (default: 1, 4 recommended)]\n"
"\t[-o oversampling (default: 1, 4 recommended)]\n"
"\t[-p ppm_error (default: 0)]\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 rdc: enable dc blocking filter on raw I/Q data at capture rate\n"
"\t adc: enable dc blocking filter on demodulated audio\n"
"\t dc: same as adc\n"
"\t deemp: enable de-emphasis filter\n"
"\t direct: enable direct sampling\n"
"\t offset: enable offset tuning\n"
"\t[-q dc_avg_factor for option rdc (default: 9)]\n"
"\tfilename ('-' means stdout)\n"
"\t omitting the filename also uses stdout\n\n"
"Experimental options:\n"
Expand Down Expand Up @@ -278,6 +295,27 @@ double log2(double n)
}
#endif

void rotate16_90(int16_t *buf, uint32_t len)
/* 90 rotation is 1+0j, 0+1j, -1+0j, 0-1j
or [0, 1, -3, 2, -4, -5, 7, -6] */
{
uint32_t i;
int16_t tmp;
for (i=0; i<len; i+=8) {
tmp = - buf[i+3];
buf[i+3] = buf[i+2];
buf[i+2] = tmp;

buf[i+4] = - buf[i+4];
buf[i+5] = - buf[i+5];

tmp = - buf[i+6];
buf[i+6] = buf[i+7];
buf[i+7] = tmp;
}
}


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] */
Expand Down Expand Up @@ -610,21 +648,44 @@ void deemph_filter(struct demod_state *fm)
}
}

void dc_block_filter(struct demod_state *fm)
void dc_block_audio_filter(struct demod_state *fm)
{
int i, avg;
int64_t sum = 0;
for (i=0; i < fm->result_len; i++) {
sum += fm->result[i];
}
avg = sum / fm->result_len;
avg = (avg + fm->dc_avg * 9) / 10;
avg = (avg + fm->dc_avg * fm->adc_block_const) / ( fm->adc_block_const + 1 );
for (i=0; i < fm->result_len; i++) {
fm->result[i] -= avg;
}
fm->dc_avg = avg;
}

void dc_block_raw_filter(struct demod_state *fm, int16_t *buf, int len)
{
/* derived from dc_block_audio_filter,
running over the raw I/Q components
*/
int i, avgI, avgQ;
int64_t sumI = 0;
int64_t sumQ = 0;
for (i = 0; i < len; i += 2) {
sumI += buf[i];
sumQ += buf[i+1];
}
avgI = sumI / ( len / 2 );
avgQ = sumQ / ( len / 2 );
avgI = (avgI + fm->dc_avgI * fm->rdc_block_const) / ( fm->rdc_block_const + 1 );
avgQ = (avgQ + fm->dc_avgQ * fm->rdc_block_const) / ( fm->rdc_block_const + 1 );
for (i = 0; i < len; i += 2) {
buf[i] -= avgI;
buf[i+1] -= avgQ;
}
fm->dc_avgI = avgI;
fm->dc_avgQ = avgQ;
}
int mad(int16_t *samples, int len, int step)
/* mean average deviation */
{
Expand Down Expand Up @@ -759,6 +820,23 @@ void full_demod(struct demod_state *d)
} else {
d->squelch_hits = 0;}
}

if (printLevels) {
if (!sr)
sr = rms(d->lowpassed, d->lp_len, 1);
--printLevelNo;
if (printLevels) {
levelSum += sr;
if (levelMax < sr) levelMax = sr;
if (levelMaxMax < sr) levelMaxMax = sr;
if (!printLevelNo) {
printLevelNo = printLevels;
fprintf(stderr, "%f, %d, %d, %d\n", (levelSum / printLevels), levelMax, levelMaxMax, d->squelch_level );
levelMax = 0;
levelSum = 0;
}
}
}
d->mode_demod(d); /* lowpassed -> result */
if (d->mode_demod == &raw_demod) {
return;
Expand All @@ -769,8 +847,8 @@ void full_demod(struct demod_state *d)
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->dc_block_audio) {
dc_block_audio_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);
Expand All @@ -792,10 +870,19 @@ static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx)
buf[i] = 127;}
s->mute = 0;
}
if (!s->offset_tuning) {
rotate_90(buf, len);}
/* 1st: convert to 16 bit - to allow easier calculation of DC */
for (i=0; i<(int)len; i++) {
s->buf16[i] = (int16_t)buf[i] - 127;}
s->buf16[i] = ( (int16_t)buf[i] - 127 );
}
/* 2nd: do DC filtering BEFORE up-mixing */
if (d->dc_block_raw) {
dc_block_raw_filter(d, s->buf16, (int)len);
}
/* 3rd: up-mixing */
if (!s->offset_tuning) {
rotate16_90(s->buf16, (int)len);
/* rotate_90(buf, len); */
}
pthread_rwlock_wrlock(&d->rw);
memcpy(d->lowpassed, s->buf16, 2*len);
d->lp_len = len;
Expand Down Expand Up @@ -862,18 +949,30 @@ static void optimal_settings(int freq, int rate)
dm->downsample_passes = (int)log2(dm->downsample) + 1;
dm->downsample = 1 << dm->downsample_passes;
}
if (verbosity) {
fprintf(stderr, "downsample_passes = %d (= # of fifth_order() iterations), downsample = %d\n", dm->downsample_passes, dm->downsample );
}
capture_freq = freq;
capture_rate = dm->downsample * dm->rate_in;
if (verbosity)
fprintf(stderr, "capture_rate = dm->downsample * dm->rate_in = %d * %d = %d\n", dm->downsample, dm->rate_in, capture_rate );
if (!d->offset_tuning) {
capture_freq = freq + capture_rate/4;}
capture_freq = freq + capture_rate/4;
if (verbosity)
fprintf(stderr, "optimal_settings(freq = %d): capture_freq = freq + capture_rate/4 = %d\n", freq, capture_freq );
}
capture_freq += cs->edge * dm->rate_in / 2;
if (verbosity)
fprintf(stderr, "optimal_settings(freq = %d): capture_freq += cs->edge * dm->rate_in / 2 = %d * %d / 2 = %d\n", freq, cs->edge, dm->rate_in, capture_freq );
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;
if (verbosity)
fprintf(stderr, "optimal_settings(freq = %d) delivers freq %.0f, rate %.0f\n", freq, (double)d->freq, (double)d->rate );
}

static void *controller_thread_fn(void *arg)
Expand All @@ -884,6 +983,8 @@ static void *controller_thread_fn(void *arg)
struct controller_state *s = arg;

if (s->wb_mode) {
if (verbosity)
fprintf(stderr, "wbfm: adding 16000 Hz to every intput frequency\n");
for (i=0; i < s->freq_len; i++) {
s->freqs[i] += 16000;}
}
Expand All @@ -896,13 +997,19 @@ static void *controller_thread_fn(void *arg)
verbose_offset_tuning(dongle.dev);}

/* Set the frequency */
if (verbosity) {
fprintf(stderr, "verbose_set_frequency(%.0f Hz)\n", (double)dongle.freq);
fprintf(stderr, " frequency is away from parametrized one, to avoid negative impact from dc\n");
}
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 */
if (verbosity)
fprintf(stderr, "verbose_set_sample_rate(%.0f Hz)\n", (double)dongle.rate);
verbose_set_sample_rate(dongle.dev, dongle.rate);
fprintf(stderr, "Output at %u Hz.\n", demod.rate_in/demod.post_downsample);

Expand Down Expand Up @@ -969,8 +1076,13 @@ void demod_init(struct demod_state *s)
s->prev_lpr_index = 0;
s->deemph_a = 0;
s->now_lpr = 0;
s->dc_block = 0;
s->dc_block_audio = 0;
s->dc_avg = 0;
s->adc_block_const = 9;
s->dc_block_raw = 0;
s->dc_avgI = 0;
s->dc_avgQ = 0;
s->rdc_block_const = 9;
pthread_rwlock_init(&s->rw, NULL);
pthread_cond_init(&s->ready, NULL);
pthread_mutex_init(&s->ready_m, NULL);
Expand Down Expand Up @@ -1042,12 +1154,13 @@ int main(int argc, char **argv)
int r, opt;
int dev_given = 0;
int custom_ppm = 0;
int timeConstant = 75; /* default: U.S. 75 uS */
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) {
while ((opt = getopt(argc, argv, "d:f:g:s:b:l:L:o:t:r:p:E:q:F:A:M:c:v:h")) != -1) {
switch (opt) {
case 'd':
dongle.dev_index = verbose_device_search(optarg);
Expand All @@ -1070,6 +1183,9 @@ int main(int argc, char **argv)
case 'l':
demod.squelch_level = (int)atof(optarg);
break;
case 'L':
printLevels = (int)atof(optarg);
break;
case 's':
demod.rate_in = (uint32_t)atofs(optarg);
demod.rate_out = (uint32_t)atofs(optarg);
Expand Down Expand Up @@ -1098,15 +1214,20 @@ int main(int argc, char **argv)
case 'E':
if (strcmp("edge", optarg) == 0) {
controller.edge = 1;}
if (strcmp("dc", optarg) == 0) {
demod.dc_block = 1;}
if (strcmp("dc", optarg) == 0 || strcmp("adc", optarg) == 0) {
demod.dc_block_audio = 1;}
if (strcmp("rdc", optarg) == 0) {
demod.dc_block_raw = 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 'q':
demod.rdc_block_const = atoi(optarg);
break;
case 'F':
demod.downsample_passes = 1; /* truthy placeholder */
demod.comp_fir_size = atoi(optarg);
Expand All @@ -1121,17 +1242,17 @@ int main(int argc, char **argv)
demod.custom_atan = 2;}
break;
case 'M':
if (strcmp("fm", optarg) == 0) {
if (strcmp("nbfm", optarg) == 0 || strcmp("nfm", optarg) == 0 || strcmp("fm", optarg) == 0) {
demod.mode_demod = &fm_demod;}
if (strcmp("raw", optarg) == 0) {
if (strcmp("raw", optarg) == 0 || strcmp("iq", 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) {
if (strcmp("wbfm", optarg) == 0 || strcmp("wfm", optarg) == 0) {
controller.wb_mode = 1;
demod.mode_demod = &fm_demod;
demod.rate_in = 170000;
Expand All @@ -1142,6 +1263,17 @@ int main(int argc, char **argv)
demod.deemph = 1;
demod.squelch_level = 0;}
break;
case 'c':
if (strcmp("us", optarg) == 0)
timeConstant = 75;
else if (strcmp("eu", optarg) == 0)
timeConstant = 50;
else
timeConstant = (int)atof(optarg);
break;
case 'v':
verbosity = (int)atof(optarg);
break;
case 'h':
default:
usage();
Expand Down Expand Up @@ -1194,7 +1326,10 @@ int main(int argc, char **argv)
#endif

if (demod.deemph) {
demod.deemph_a = (int)round(1.0/((1.0-exp(-1.0/(demod.rate_out * 75e-6)))));
double tc = (double)timeConstant * 1e-6;
demod.deemph_a = (int)round(1.0/((1.0-exp(-1.0/(demod.rate_out * tc)))));
if (verbosity)
fprintf(stderr, "using wbfm deemphasis filter with time constant %d us\n", timeConstant );
}

/* Set the tuner gain */
Expand Down