Skip to content

Commit 2fe6bfa

Browse files
committed
msk/acars: add support for noise floor estimator
This introduces a basic, low cpu-cost NF estimator which works by using an exponential moving average over the magnitude of the received signal updated only outside of ACARS message processing. The computational overhead of this extra estimator is virtually nil, as the signal power estimator has been reworked to use the same input as the noise floor estimator. Fixes: TLeconte#84
1 parent 4ef656a commit 2fe6bfa

File tree

4 files changed

+28
-18
lines changed

4 files changed

+28
-18
lines changed

acars.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ static void *blk_thread(void *arg)
213213
{ .type = STATSD_UCOUNTER, .name = "decoder.msg.good", .value.u = 1 },
214214
{ .type = STATSD_LGAUGE, .name = "decoder.msg.errs", .value.l = blk->err },
215215
{ .type = STATSD_FGAUGE, .name = "decoder.msg.lvl", .value.f = blk->lvl },
216+
{ .type = STATSD_FGAUGE, .name = "decoder.msg.nf", .value.f = blk->nf },
216217
{ .type = STATSD_LGAUGE, .name = "decoder.msg.len", .value.l = blk->txtlen },
217218
};
218219
// use the frequency if available, else the channel number
@@ -251,18 +252,22 @@ int initAcars(channel_t *ch)
251252

252253
void decodeAcars(channel_t *ch)
253254
{
255+
const float mag = ch->MskMag;
254256
uint8_t r = ch->outbits;
255257
//vprerr("#%d r: %x, count: %d, st: %d\n", ch->chn+1, r, ch->count, ch->Acarsstate);
256258

257259
ch->nbits = 8; // by default we'll read another byte next
258260

261+
/* update power level exp moving average. Average over last 16 bytes */
262+
ch->MskPwr = ch->MskPwr - (1.0F/16.0F * (ch->MskPwr - mag * mag));
263+
259264
switch (ch->Acarsstate) {
260265
case PREKEY:
261266
if (ch->count >= 16 && 0xFF != r) { // we have our first non-0xFF byte after a sequence
262267
uint8_t q = ~r; // avoid type promotion in calling ffs(~r)
263268
int l = ffs(q); // find the first (LSb) 0 in r
264269

265-
vprerr("#%d synced, count: %d, r: %x, fz: %d, lvl: %5.1f\n", ch->chn+1, ch->count, r, l, 10 * log10(ch->MskLvl));
270+
vprerr("#%d synced, count: %d, r: %x, fz: %d, lvl: %5.1f\n", ch->chn+1, ch->count, r, l, 10 * log10(ch->MskPwr));
266271
ch->count = 0;
267272
ch->Acarsstate = SYNC;
268273

@@ -299,6 +304,8 @@ void decodeAcars(channel_t *ch)
299304
ch->count++;
300305
break;
301306
default:
307+
/* outside of msgs, update noise floor moving average: long term (10^3 'bytes') magnitude exp moving average */
308+
ch->MskNF = ch->MskNF - (1e-3F * (ch->MskNF - mag));
302309
ch->count = 0;
303310
break;
304311
}
@@ -347,7 +354,8 @@ void decodeAcars(channel_t *ch)
347354
}
348355
gettimeofday(&(ch->blk->tv), NULL);
349356
ch->Acarsstate = TXT;
350-
ch->blk->lvl = 10 * log10(ch->MskLvl);
357+
ch->blk->lvl = 10 * log10(ch->MskPwr);
358+
ch->blk->nf = 20 * log10(ch->MskNF);
351359
ch->blk->chn = ch->chn;
352360
ch->blk->txtlen = 0;
353361
ch->blk->err = 0;

acarsdec.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
typedef struct mskblk_s {
5555
struct mskblk_s *prev;
5656
struct timeval tv;
57-
float lvl;
57+
float lvl, nf;
5858
uint8_t chn; // there will never be 255 channels
5959
uint8_t txtlen;
6060
uint8_t err;
@@ -88,7 +88,7 @@ typedef struct {
8888
char be;
8989
char msn[4]; // only for libacars - null-terminated copy of msg.no[0-3]
9090
int err;
91-
float lvl;
91+
float lvl, nf;
9292
#ifdef HAVE_LIBACARS
9393
la_reasm_status reasm_status;
9494
la_proto_node *decoded_tree;
@@ -104,7 +104,9 @@ typedef struct {
104104
float complex *inb;
105105
double MskPhi;
106106
double MskDf;
107-
float MskLvl;
107+
float MskMag; // signal magnitude moving average
108+
float MskPwr; // signal power moving average (average of magnitude squared)
109+
float MskNF; // noise floor moving average (average of magnitude outside of msg blocks)
108110
float MskClk;
109111
unsigned int MskS, idx;
110112

msk.c

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,10 @@ int initMsk(channel_t *ch)
5454
static inline void putbit(float v, channel_t *ch)
5555
{
5656
ch->outbits >>= 1;
57-
if (v > 0) {
57+
if (v > 0)
5858
ch->outbits |= 0x80;
59-
}
6059

61-
ch->nbits--;
62-
if (ch->nbits <= 0)
60+
if (--ch->nbits == 0)
6361
decodeAcars(ch);
6462
}
6563

@@ -106,12 +104,11 @@ void demodMSK(channel_t *ch, int len)
106104
v += h[o] * ch->inb[(j + idx) % FLEN];
107105

108106
/* normalize */
109-
lvl = cabsf(v);
110-
v /= lvl + 1e-8;
107+
lvl = cabsf(v) + 1e-8F;
108+
v /= lvl;
111109

112-
/* update level exp moving average. Average over last 16*8 bits */
113-
lvl = lvl * lvl;
114-
ch->MskLvl = ch->MskLvl - (1.0F/128.0F * (ch->MskLvl - lvl));
110+
/* update magnitude exp moving average. Average over last 8 bits */
111+
ch->MskMag = ch->MskMag - (1.0F/8.0F * (ch->MskMag - lvl));
115112

116113
if (ch->MskS & 1) {
117114
vo = cimagf(v);

output.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -391,10 +391,10 @@ static int fmt_msg(acarsmsg_t *msg, int chn, struct timeval tv, char *buf, size_
391391
int len = 0;
392392

393393
if (R.inmode >= IN_RTL)
394-
len += snprintf(buf + len, bufsz - len, "[#%1d (F:%3.3f L:%+5.1f E:%1d) ", chn + 1,
395-
R.channels[chn].Fr / 1000000.0, msg->lvl, msg->err);
394+
len += snprintf(buf + len, bufsz - len, "[#%1d (F:%3.3f L:%+5.1f/%.1f E:%1d) ", chn + 1,
395+
R.channels[chn].Fr / 1000000.0, msg->lvl, msg->nf, msg->err);
396396
else
397-
len += snprintf(buf + len, bufsz - len, "[#%1d (L:%+5.1f E:%1d) ", chn + 1, msg->lvl, msg->err);
397+
len += snprintf(buf + len, bufsz - len, "[#%1d (L:%+5.1f/%.1f E:%1d) ", chn + 1, msg->lvl, msg->nf, msg->err);
398398

399399
if (R.inmode != IN_SNDFILE)
400400
len += fmt_date(tv, buf + len, bufsz - len);
@@ -472,7 +472,7 @@ static int fmt_oneline(acarsmsg_t *msg, int chn, struct timeval tv, char *buf, s
472472
if (*pstr == '\n' || *pstr == '\r')
473473
*pstr = ' ';
474474

475-
len = snprintf(buf, bufsz, "#%1d (L:%+5.1f E:%1d) ", chn + 1, msg->lvl, msg->err);
475+
len = snprintf(buf, bufsz, "#%1d (L:%+5.1f/%.1f E:%1d) ", chn + 1, msg->lvl, msg->nf, msg->err);
476476

477477
if (R.inmode != IN_SNDFILE)
478478
len += fmt_date(tv, buf + len, bufsz - len);
@@ -575,6 +575,8 @@ static int fmt_json(acarsmsg_t *msg, int chn, struct timeval tv, char *buf, size
575575
cJSON_AddRawToObject(json_obj, "freq", convert_tmp);
576576
snprintf(convert_tmp, sizeof(convert_tmp), "%2.1f", msg->lvl);
577577
cJSON_AddRawToObject(json_obj, "level", convert_tmp);
578+
snprintf(convert_tmp, sizeof(convert_tmp), "%.1f", msg->nf);
579+
cJSON_AddRawToObject(json_obj, "noise", convert_tmp);
578580
cJSON_AddNumberToObject(json_obj, "error", msg->err);
579581
snprintf(convert_tmp, sizeof(convert_tmp), "%c", msg->mode);
580582
cJSON_AddStringToObject(json_obj, "mode", convert_tmp);
@@ -727,6 +729,7 @@ void outputmsg(const msgblk_t *blk)
727729
/* fill msg struct */
728730
memset(&msg, 0, sizeof(msg));
729731
msg.lvl = blk->lvl;
732+
msg.nf = blk->nf;
730733
msg.err = blk->err;
731734

732735
msg.mode = blk->txt.d.mode;

0 commit comments

Comments
 (0)