kopia lustrzana https://github.com/keenerd/rtl-sdr
rtl_fm: in-place demodulators
rodzic
8189495888
commit
7a98f12bcc
95
src/rtl_fm.c
95
src/rtl_fm.c
|
@ -107,7 +107,7 @@ struct dongle_state
|
||||||
uint32_t freq;
|
uint32_t freq;
|
||||||
uint32_t rate;
|
uint32_t rate;
|
||||||
int gain;
|
int gain;
|
||||||
uint16_t buf16[MAXIMUM_BUF_LENGTH];
|
int16_t buf16[MAXIMUM_BUF_LENGTH];
|
||||||
uint32_t buf_len;
|
uint32_t buf_len;
|
||||||
int ppm_error;
|
int ppm_error;
|
||||||
int offset_tuning;
|
int offset_tuning;
|
||||||
|
@ -124,10 +124,8 @@ struct demod_state
|
||||||
int lp_len;
|
int lp_len;
|
||||||
int16_t lp_i_hist[10][6];
|
int16_t lp_i_hist[10][6];
|
||||||
int16_t lp_q_hist[10][6];
|
int16_t lp_q_hist[10][6];
|
||||||
int16_t result[MAXIMUM_BUF_LENGTH];
|
|
||||||
int16_t droop_i_hist[9];
|
int16_t droop_i_hist[9];
|
||||||
int16_t droop_q_hist[9];
|
int16_t droop_q_hist[9];
|
||||||
int result_len;
|
|
||||||
int rate_in;
|
int rate_in;
|
||||||
int rate_out;
|
int rate_out;
|
||||||
int rate_out2;
|
int rate_out2;
|
||||||
|
@ -353,22 +351,23 @@ void low_pass_real(struct demod_state *s)
|
||||||
/* simple square window FIR */
|
/* simple square window FIR */
|
||||||
// add support for upsampling?
|
// add support for upsampling?
|
||||||
{
|
{
|
||||||
|
int16_t *lp = s->lowpassed;
|
||||||
int i=0, i2=0;
|
int i=0, i2=0;
|
||||||
int fast = (int)s->rate_out;
|
int fast = (int)s->rate_out;
|
||||||
int slow = s->rate_out2;
|
int slow = s->rate_out2;
|
||||||
while (i < s->result_len) {
|
while (i < s->lp_len) {
|
||||||
s->now_lpr += s->result[i];
|
s->now_lpr += lp[i];
|
||||||
i++;
|
i++;
|
||||||
s->prev_lpr_index += slow;
|
s->prev_lpr_index += slow;
|
||||||
if (s->prev_lpr_index < fast) {
|
if (s->prev_lpr_index < fast) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
s->result[i2] = (int16_t)(s->now_lpr / (fast/slow));
|
lp[i2] = (int16_t)(s->now_lpr / (fast/slow));
|
||||||
s->prev_lpr_index -= fast;
|
s->prev_lpr_index -= fast;
|
||||||
s->now_lpr = 0;
|
s->now_lpr = 0;
|
||||||
i2 += 1;
|
i2 += 1;
|
||||||
}
|
}
|
||||||
s->result_len = i2;
|
s->lp_len = i2;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fifth_order(int16_t *data, int length, int16_t *hist)
|
void fifth_order(int16_t *data, int length, int16_t *hist)
|
||||||
|
@ -546,35 +545,32 @@ int esbensen(int ar, int aj, int br, int bj)
|
||||||
|
|
||||||
void fm_demod(struct demod_state *fm)
|
void fm_demod(struct demod_state *fm)
|
||||||
{
|
{
|
||||||
int i, pcm;
|
int i, pcm = 0;
|
||||||
int16_t *lp = fm->lowpassed;
|
int16_t *lp = fm->lowpassed;
|
||||||
pcm = polar_discriminant(lp[0], lp[1],
|
int16_t pr = fm->pre_r;
|
||||||
fm->pre_r, fm->pre_j);
|
int16_t pj = fm->pre_j;
|
||||||
fm->result[0] = (int16_t)pcm;
|
for (i = 0; i < (fm->lp_len-1); i += 2) {
|
||||||
for (i = 2; i < (fm->lp_len-1); i += 2) {
|
|
||||||
switch (fm->custom_atan) {
|
switch (fm->custom_atan) {
|
||||||
case 0:
|
case 0:
|
||||||
pcm = polar_discriminant(lp[i], lp[i+1],
|
pcm = polar_discriminant(lp[i], lp[i+1], pr, pj);
|
||||||
lp[i-2], lp[i-1]);
|
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
pcm = polar_disc_fast(lp[i], lp[i+1],
|
pcm = polar_disc_fast(lp[i], lp[i+1], pr, pj);
|
||||||
lp[i-2], lp[i-1]);
|
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
pcm = polar_disc_lut(lp[i], lp[i+1],
|
pcm = polar_disc_lut(lp[i], lp[i+1], pr, pj);
|
||||||
lp[i-2], lp[i-1]);
|
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
pcm = esbensen(lp[i], lp[i+1],
|
pcm = esbensen(lp[i], lp[i+1], pr, pj);
|
||||||
lp[i-2], lp[i-1]);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
fm->result[i/2] = (int16_t)pcm;
|
pr = lp[i];
|
||||||
|
pj = lp[i+1];
|
||||||
|
fm->lowpassed[i/2] = (int16_t)pcm;
|
||||||
}
|
}
|
||||||
fm->pre_r = lp[fm->lp_len - 2];
|
fm->pre_r = pr;
|
||||||
fm->pre_j = lp[fm->lp_len - 1];
|
fm->pre_j = pj;
|
||||||
fm->result_len = fm->lp_len/2;
|
fm->lp_len = fm->lp_len / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
void am_demod(struct demod_state *fm)
|
void am_demod(struct demod_state *fm)
|
||||||
|
@ -582,15 +578,14 @@ void am_demod(struct demod_state *fm)
|
||||||
{
|
{
|
||||||
int i, pcm;
|
int i, pcm;
|
||||||
int16_t *lp = fm->lowpassed;
|
int16_t *lp = fm->lowpassed;
|
||||||
int16_t *r = fm->result;
|
|
||||||
for (i = 0; i < fm->lp_len; i += 2) {
|
for (i = 0; i < fm->lp_len; i += 2) {
|
||||||
// hypot uses floats but won't overflow
|
// hypot uses floats but won't overflow
|
||||||
//r[i/2] = (int16_t)hypot(lp[i], lp[i+1]);
|
//r[i/2] = (int16_t)hypot(lp[i], lp[i+1]);
|
||||||
pcm = lp[i] * lp[i];
|
pcm = lp[i] * lp[i];
|
||||||
pcm += lp[i+1] * lp[i+1];
|
pcm += lp[i+1] * lp[i+1];
|
||||||
r[i/2] = (int16_t)sqrt(pcm) * fm->output_scale;
|
lp[i/2] = (int16_t)sqrt(pcm) * fm->output_scale;
|
||||||
}
|
}
|
||||||
fm->result_len = fm->lp_len/2;
|
fm->lp_len = fm->lp_len / 2;
|
||||||
// lowpass? (3khz) highpass? (dc)
|
// lowpass? (3khz) highpass? (dc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -598,49 +593,44 @@ void usb_demod(struct demod_state *fm)
|
||||||
{
|
{
|
||||||
int i, pcm;
|
int i, pcm;
|
||||||
int16_t *lp = fm->lowpassed;
|
int16_t *lp = fm->lowpassed;
|
||||||
int16_t *r = fm->result;
|
|
||||||
for (i = 0; i < fm->lp_len; i += 2) {
|
for (i = 0; i < fm->lp_len; i += 2) {
|
||||||
pcm = lp[i] + lp[i+1];
|
pcm = lp[i] + lp[i+1];
|
||||||
r[i/2] = (int16_t)pcm * fm->output_scale;
|
lp[i/2] = (int16_t)pcm * fm->output_scale;
|
||||||
}
|
}
|
||||||
fm->result_len = fm->lp_len/2;
|
fm->lp_len = fm->lp_len / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lsb_demod(struct demod_state *fm)
|
void lsb_demod(struct demod_state *fm)
|
||||||
{
|
{
|
||||||
int i, pcm;
|
int i, pcm;
|
||||||
int16_t *lp = fm->lowpassed;
|
int16_t *lp = fm->lowpassed;
|
||||||
int16_t *r = fm->result;
|
|
||||||
for (i = 0; i < fm->lp_len; i += 2) {
|
for (i = 0; i < fm->lp_len; i += 2) {
|
||||||
pcm = lp[i] - lp[i+1];
|
pcm = lp[i] - lp[i+1];
|
||||||
r[i/2] = (int16_t)pcm * fm->output_scale;
|
lp[i/2] = (int16_t)pcm * fm->output_scale;
|
||||||
}
|
}
|
||||||
fm->result_len = fm->lp_len/2;
|
fm->lp_len = fm->lp_len / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
void raw_demod(struct demod_state *fm)
|
void raw_demod(struct demod_state *fm)
|
||||||
{
|
{
|
||||||
int i;
|
return;
|
||||||
for (i = 0; i < fm->lp_len; i++) {
|
|
||||||
fm->result[i] = (int16_t)fm->lowpassed[i];
|
|
||||||
}
|
|
||||||
fm->result_len = fm->lp_len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void deemph_filter(struct demod_state *fm)
|
void deemph_filter(struct demod_state *fm)
|
||||||
{
|
{
|
||||||
static int avg; // cheating...
|
static int avg; // cheating, not threadsafe
|
||||||
int i, d;
|
int i, d;
|
||||||
|
int16_t *lp = fm->lowpassed;
|
||||||
// de-emph IIR
|
// de-emph IIR
|
||||||
// avg = avg * (1 - alpha) + sample * alpha;
|
// avg = avg * (1 - alpha) + sample * alpha;
|
||||||
for (i = 0; i < fm->result_len; i++) {
|
for (i = 0; i < fm->lp_len; i++) {
|
||||||
d = fm->result[i] - avg;
|
d = lp[i] - avg;
|
||||||
if (d > 0) {
|
if (d > 0) {
|
||||||
avg += (d + fm->deemph_a/2) / fm->deemph_a;
|
avg += (d + fm->deemph_a/2) / fm->deemph_a;
|
||||||
} else {
|
} else {
|
||||||
avg += (d - fm->deemph_a/2) / fm->deemph_a;
|
avg += (d - fm->deemph_a/2) / fm->deemph_a;
|
||||||
}
|
}
|
||||||
fm->result[i] = (int16_t)avg;
|
lp[i] = (int16_t)avg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -648,13 +638,14 @@ void dc_block_filter(struct demod_state *fm)
|
||||||
{
|
{
|
||||||
int i, avg;
|
int i, avg;
|
||||||
int64_t sum = 0;
|
int64_t sum = 0;
|
||||||
for (i=0; i < fm->result_len; i++) {
|
int16_t *lp = fm->lowpassed;
|
||||||
sum += fm->result[i];
|
for (i=0; i < fm->lp_len; i++) {
|
||||||
|
sum += lp[i];
|
||||||
}
|
}
|
||||||
avg = sum / fm->result_len;
|
avg = sum / fm->lp_len;
|
||||||
avg = (avg + fm->dc_avg * 9) / 10;
|
avg = (avg + fm->dc_avg * 9) / 10;
|
||||||
for (i=0; i < fm->result_len; i++) {
|
for (i=0; i < fm->lp_len; i++) {
|
||||||
fm->result[i] -= avg;
|
lp[i] -= avg;
|
||||||
}
|
}
|
||||||
fm->dc_avg = avg;
|
fm->dc_avg = avg;
|
||||||
}
|
}
|
||||||
|
@ -812,21 +803,21 @@ void full_demod(struct demod_state *d)
|
||||||
} else {
|
} else {
|
||||||
d->squelch_hits = 0;}
|
d->squelch_hits = 0;}
|
||||||
}
|
}
|
||||||
d->mode_demod(d); /* lowpassed -> result */
|
d->mode_demod(d); /* lowpassed -> lowpassed */
|
||||||
if (d->mode_demod == &raw_demod) {
|
if (d->mode_demod == &raw_demod) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* todo, fm noise squelch */
|
/* todo, fm noise squelch */
|
||||||
// use nicer filter here too?
|
// use nicer filter here too?
|
||||||
if (d->post_downsample > 1) {
|
if (d->post_downsample > 1) {
|
||||||
d->result_len = low_pass_simple(d->result, d->result_len, d->post_downsample);}
|
d->lp_len = low_pass_simple(d->lowpassed, d->lp_len, d->post_downsample);}
|
||||||
if (d->deemph) {
|
if (d->deemph) {
|
||||||
deemph_filter(d);}
|
deemph_filter(d);}
|
||||||
if (d->dc_block) {
|
if (d->dc_block) {
|
||||||
dc_block_filter(d);}
|
dc_block_filter(d);}
|
||||||
if (d->rate_out2 > 0) {
|
if (d->rate_out2 > 0) {
|
||||||
low_pass_real(d);
|
low_pass_real(d);
|
||||||
//arbitrary_resample(d->result, d->result, d->result_len, d->result_len * d->rate_out2 / d->rate_out);
|
//arbitrary_resample(d->lowpassed, d->lowpassed, d->lp_len, d->lp_len * d->rate_out2 / d->rate_out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -881,8 +872,8 @@ static void *demod_thread_fn(void *arg)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
pthread_rwlock_wrlock(&o->rw);
|
pthread_rwlock_wrlock(&o->rw);
|
||||||
memcpy(o->result, d->result, 2*d->result_len);
|
memcpy(o->result, d->lowpassed, 2*d->lp_len);
|
||||||
o->result_len = d->result_len;
|
o->result_len = d->lp_len;
|
||||||
pthread_rwlock_unlock(&o->rw);
|
pthread_rwlock_unlock(&o->rw);
|
||||||
safe_cond_signal(&o->ready, &o->ready_m);
|
safe_cond_signal(&o->ready, &o->ready_m);
|
||||||
}
|
}
|
||||||
|
|
Ładowanie…
Reference in New Issue