Skip to content

Commit df2a9c0

Browse files
committed
rtl_fm: software agc
1 parent 90706d4 commit df2a9c0

File tree

1 file changed

+34
-34
lines changed

1 file changed

+34
-34
lines changed

src/rtl_fm.c

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
*
3131
* todo:
3232
* sanity checks
33-
* scale squelch to other input parameters
3433
* pad output on hop
3534
* frequency ranges could be stored better
3635
* auto-hop after time limit
@@ -124,10 +123,9 @@ struct dongle_state
124123

125124
struct agc_state
126125
{
127-
int64_t gain_num;
128-
int64_t gain_den;
129-
int64_t gain_max;
130-
int gain_int;
126+
int32_t gain_num;
127+
int32_t gain_den;
128+
int32_t gain_max;
131129
int peak_target;
132130
int attack_step;
133131
int decay_step;
@@ -232,9 +230,9 @@ void usage(void)
232230
"\t[-E enable_option (default: none)]\n"
233231
"\t use multiple -E to enable multiple options\n"
234232
"\t edge: enable lower edge tuning\n"
235-
"\t dc: enable dc blocking filter\n"
233+
"\t no-dc: disable dc blocking filter\n"
236234
"\t deemp: enable de-emphasis filter\n"
237-
"\t swagc: enable software agc (only for AM, broken)\n"
235+
"\t swagc: enable software agc (only for AM modes)\n"
238236
"\t direct: enable direct sampling\n"
239237
"\t no-mod: enable no-mod direct sampling\n"
240238
"\t offset: enable offset tuning\n"
@@ -633,7 +631,7 @@ void fm_demod(struct demod_state *fm)
633631
void am_demod(struct demod_state *fm)
634632
// todo, fix this extreme laziness
635633
{
636-
int i, pcm;
634+
int32_t i, pcm;
637635
int16_t *lp = fm->lowpassed;
638636
for (i = 0; i < fm->lp_len; i += 2) {
639637
// hypot uses floats but won't overflow
@@ -643,7 +641,7 @@ void am_demod(struct demod_state *fm)
643641
lp[i/2] = (int16_t)sqrt(pcm) * fm->output_scale;
644642
}
645643
fm->lp_len = fm->lp_len / 2;
646-
// lowpass? (3khz) highpass? (dc)
644+
// lowpass? (3khz)
647645
}
648646

649647
void usb_demod(struct demod_state *fm)
@@ -764,29 +762,35 @@ int squelch_to_rms(int db, struct dongle_state *dongle, struct demod_state *demo
764762
}
765763

766764
void software_agc(struct demod_state *d)
767-
/* ignores complex pairs, indirectly calculates power squelching */
768765
{
769766
int i = 0;
770-
int output;
767+
int peaked = 0;
768+
int32_t output;
771769
struct agc_state *agc = d->agc;
772770
int16_t *lp = d->lowpassed;
773771

774772
for (i=0; i < d->lp_len; i++) {
775-
output = (int)((int64_t)lp[i] * agc->gain_num / agc->gain_den);
773+
output = ((int32_t)lp[i] * agc->gain_num / agc->gain_den);
776774

777-
if (abs(output) < agc->peak_target) {
778-
agc->gain_num += agc->decay_step;
775+
if (!peaked && abs(output) > agc->peak_target) {
776+
peaked = 1;}
777+
if (peaked) {
778+
agc->gain_num += agc->attack_step;
779779
} else {
780-
agc->gain_num -= agc->attack_step;
780+
agc->gain_num += agc->decay_step;
781781
}
782782

783783
if (agc->gain_num < agc->gain_den) {
784784
agc->gain_num = agc->gain_den;}
785785
if (agc->gain_num > agc->gain_max) {
786786
agc->gain_num = agc->gain_max;}
787787

788-
agc->gain_int = (int)(agc->gain_num / agc->gain_den);
789-
lp[i] = output;
788+
if (output >= (1<<15)) {
789+
output = (1<<15) - 1;}
790+
if (output < -(1<<15)) {
791+
output = -(1<<15) + 1;}
792+
793+
lp[i] = (int16_t)output;
790794
}
791795
}
792796

@@ -812,12 +816,8 @@ void full_demod(struct demod_state *d)
812816
} else {
813817
low_pass(d);
814818
}
815-
if (d->agc_enable) {
816-
software_agc(d);
817-
if(d->squelch_level && d->agc->gain_int > d->squelch_level) {
818-
do_squelch = 1;}
819819
/* power squelch */
820-
} else if (d->squelch_level) {
820+
if (d->squelch_level) {
821821
sr = rms(d->lowpassed, d->lp_len, 1);
822822
if (sr < d->squelch_level) {
823823
do_squelch = 1;}
@@ -835,16 +835,17 @@ void full_demod(struct demod_state *d)
835835
}
836836
d->mode_demod(d); /* lowpassed -> lowpassed */
837837
if (d->mode_demod == &raw_demod) {
838-
return;
839-
}
838+
return;}
839+
if (d->dc_block) {
840+
dc_block_filter(d);}
841+
if (d->agc_enable) {
842+
software_agc(d);}
840843
/* todo, fm noise squelch */
841844
// use nicer filter here too?
842845
if (d->post_downsample > 1) {
843846
d->lp_len = low_pass_simple(d->lowpassed, d->lp_len, d->post_downsample);}
844847
if (d->deemph) {
845848
deemph_filter(d);}
846-
if (d->dc_block) {
847-
dc_block_filter(d);}
848849
if (d->rate_out2 > 0) {
849850
low_pass_real(d);
850851
//arbitrary_resample(d->lowpassed, d->lowpassed, d->lp_len, d->lp_len * d->rate_out2 / d->rate_out);
@@ -1136,7 +1137,7 @@ void demod_init(struct demod_state *s)
11361137
s->prev_lpr_index = 0;
11371138
s->deemph_a = 0;
11381139
s->now_lpr = 0;
1139-
s->dc_block = 0;
1140+
s->dc_block = 1;
11401141
s->dc_avg = 0;
11411142
pthread_rwlock_init(&s->rw, NULL);
11421143
pthread_cond_init(&s->ready, NULL);
@@ -1211,13 +1212,12 @@ int agc_init(struct demod_state *s)
12111212
agc = malloc(sizeof(struct agc_state));
12121213
s->agc = agc;
12131214

1214-
agc->gain_den = 1<<13;
1215+
agc->gain_den = 1<<16;
12151216
agc->gain_num = agc->gain_den;
1216-
agc->gain_int = (int)(agc->gain_num / agc->gain_den);
1217-
agc->peak_target = 1<<13;
1218-
agc->gain_max = 1<<10 * agc->gain_num;
1219-
agc->attack_step = 2;
1217+
agc->peak_target = 1<<14;
1218+
agc->gain_max = 256 * agc->gain_den;
12201219
agc->decay_step = 1;
1220+
agc->attack_step = -2;
12211221
return 0;
12221222
}
12231223

@@ -1320,8 +1320,8 @@ int main(int argc, char **argv)
13201320
case 'E':
13211321
if (strcmp("edge", optarg) == 0) {
13221322
controller.edge = 1;}
1323-
if (strcmp("dc", optarg) == 0) {
1324-
demod.dc_block = 1;}
1323+
if (strcmp("no-dc", optarg) == 0) {
1324+
demod.dc_block = 0;}
13251325
if (strcmp("deemp", optarg) == 0) {
13261326
demod.deemph = 1;}
13271327
if (strcmp("swagc", optarg) == 0) {

0 commit comments

Comments
 (0)