From a2d9677b37743b135421950b2368e75db635a8a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibaut=20VAR=C3=88NE?= Date: Thu, 5 Sep 2024 18:04:43 +0200 Subject: [PATCH] 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: https://github.com/TLeconte/acarsdec/issues/84 --- acars.c | 12 ++++++++++-- acarsdec.h | 8 +++++--- msk.c | 15 ++++++--------- output.c | 11 +++++++---- 4 files changed, 28 insertions(+), 18 deletions(-) diff --git a/acars.c b/acars.c index 4b0528d..e25c672 100644 --- a/acars.c +++ b/acars.c @@ -213,6 +213,7 @@ static void *blk_thread(void *arg) { .type = STATSD_UCOUNTER, .name = "decoder.msg.good", .value.u = 1 }, { .type = STATSD_LGAUGE, .name = "decoder.msg.errs", .value.l = blk->err }, { .type = STATSD_FGAUGE, .name = "decoder.msg.lvl", .value.f = blk->lvl }, + { .type = STATSD_FGAUGE, .name = "decoder.msg.nf", .value.f = blk->nf }, { .type = STATSD_LGAUGE, .name = "decoder.msg.len", .value.l = blk->txtlen }, }; // use the frequency if available, else the channel number @@ -251,18 +252,22 @@ int initAcars(channel_t *ch) void decodeAcars(channel_t *ch) { + const float mag = ch->MskMag; uint8_t r = ch->outbits; //vprerr("#%d r: %x, count: %d, st: %d\n", ch->chn+1, r, ch->count, ch->Acarsstate); ch->nbits = 8; // by default we'll read another byte next + /* update power level exp moving average. Average over last 16 bytes */ + ch->MskPwr = ch->MskPwr - (1.0F/16.0F * (ch->MskPwr - mag * mag)); + switch (ch->Acarsstate) { case PREKEY: if (ch->count >= 16 && 0xFF != r) { // we have our first non-0xFF byte after a sequence uint8_t q = ~r; // avoid type promotion in calling ffs(~r) int l = ffs(q); // find the first (LSb) 0 in r - vprerr("#%d synced, count: %d, r: %x, fz: %d, lvl: %5.1f\n", ch->chn+1, ch->count, r, l, 10 * log10(ch->MskLvl)); + vprerr("#%d synced, count: %d, r: %x, fz: %d, lvl: %5.1f\n", ch->chn+1, ch->count, r, l, 10 * log10(ch->MskPwr)); ch->count = 0; ch->Acarsstate = SYNC; @@ -299,6 +304,8 @@ void decodeAcars(channel_t *ch) ch->count++; break; default: + /* outside of msgs, update noise floor moving average: long term (10^3 'bytes') magnitude exp moving average */ + ch->MskNF = ch->MskNF - (1e-3F * (ch->MskNF - mag)); ch->count = 0; break; } @@ -347,7 +354,8 @@ void decodeAcars(channel_t *ch) } gettimeofday(&(ch->blk->tv), NULL); ch->Acarsstate = TXT; - ch->blk->lvl = 10 * log10(ch->MskLvl); + ch->blk->lvl = 10 * log10(ch->MskPwr); + ch->blk->nf = 20 * log10(ch->MskNF); ch->blk->chn = ch->chn; ch->blk->txtlen = 0; ch->blk->err = 0; diff --git a/acarsdec.h b/acarsdec.h index e3ec7ff..7521b64 100644 --- a/acarsdec.h +++ b/acarsdec.h @@ -54,7 +54,7 @@ typedef struct mskblk_s { struct mskblk_s *prev; struct timeval tv; - float lvl; + float lvl, nf; uint8_t chn; // there will never be 255 channels uint8_t txtlen; uint8_t err; @@ -88,7 +88,7 @@ typedef struct { char be; char msn[4]; // only for libacars - null-terminated copy of msg.no[0-3] int err; - float lvl; + float lvl, nf; #ifdef HAVE_LIBACARS la_reasm_status reasm_status; la_proto_node *decoded_tree; @@ -104,7 +104,9 @@ typedef struct { float complex *inb; double MskPhi; double MskDf; - float MskLvl; + float MskMag; // signal magnitude moving average + float MskPwr; // signal power moving average (average of magnitude squared) + float MskNF; // noise floor moving average (average of magnitude outside of msg blocks) float MskClk; unsigned int MskS, idx; diff --git a/msk.c b/msk.c index 16b6143..7a2e4cd 100644 --- a/msk.c +++ b/msk.c @@ -54,12 +54,10 @@ int initMsk(channel_t *ch) static inline void putbit(float v, channel_t *ch) { ch->outbits >>= 1; - if (v > 0) { + if (v > 0) ch->outbits |= 0x80; - } - ch->nbits--; - if (ch->nbits <= 0) + if (--ch->nbits == 0) decodeAcars(ch); } @@ -106,12 +104,11 @@ void demodMSK(channel_t *ch, int len) v += h[o] * ch->inb[(j + idx) % FLEN]; /* normalize */ - lvl = cabsf(v); - v /= lvl + 1e-8; + lvl = cabsf(v) + 1e-8F; + v /= lvl; - /* update level exp moving average. Average over last 16*8 bits */ - lvl = lvl * lvl; - ch->MskLvl = ch->MskLvl - (1.0F/128.0F * (ch->MskLvl - lvl)); + /* update magnitude exp moving average. Average over last 8 bits */ + ch->MskMag = ch->MskMag - (1.0F/8.0F * (ch->MskMag - lvl)); if (ch->MskS & 1) { vo = cimagf(v); diff --git a/output.c b/output.c index f7c8858..52faef7 100644 --- a/output.c +++ b/output.c @@ -391,10 +391,10 @@ static int fmt_msg(acarsmsg_t *msg, int chn, struct timeval tv, char *buf, size_ int len = 0; if (R.inmode >= IN_RTL) - len += snprintf(buf + len, bufsz - len, "[#%1d (F:%3.3f L:%+5.1f E:%1d) ", chn + 1, - R.channels[chn].Fr / 1000000.0, msg->lvl, msg->err); + len += snprintf(buf + len, bufsz - len, "[#%1d (F:%3.3f L:%+5.1f/%.1f E:%1d) ", chn + 1, + R.channels[chn].Fr / 1000000.0, msg->lvl, msg->nf, msg->err); else - len += snprintf(buf + len, bufsz - len, "[#%1d (L:%+5.1f E:%1d) ", chn + 1, msg->lvl, msg->err); + len += snprintf(buf + len, bufsz - len, "[#%1d (L:%+5.1f/%.1f E:%1d) ", chn + 1, msg->lvl, msg->nf, msg->err); if (R.inmode != IN_SNDFILE) 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 if (*pstr == '\n' || *pstr == '\r') *pstr = ' '; - len = snprintf(buf, bufsz, "#%1d (L:%+5.1f E:%1d) ", chn + 1, msg->lvl, msg->err); + len = snprintf(buf, bufsz, "#%1d (L:%+5.1f/%.1f E:%1d) ", chn + 1, msg->lvl, msg->nf, msg->err); if (R.inmode != IN_SNDFILE) 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 cJSON_AddRawToObject(json_obj, "freq", convert_tmp); snprintf(convert_tmp, sizeof(convert_tmp), "%2.1f", msg->lvl); cJSON_AddRawToObject(json_obj, "level", convert_tmp); + snprintf(convert_tmp, sizeof(convert_tmp), "%.1f", msg->nf); + cJSON_AddRawToObject(json_obj, "noise", convert_tmp); cJSON_AddNumberToObject(json_obj, "error", msg->err); snprintf(convert_tmp, sizeof(convert_tmp), "%c", msg->mode); cJSON_AddStringToObject(json_obj, "mode", convert_tmp); @@ -727,6 +729,7 @@ void outputmsg(const msgblk_t *blk) /* fill msg struct */ memset(&msg, 0, sizeof(msg)); msg.lvl = blk->lvl; + msg.nf = blk->nf; msg.err = blk->err; msg.mode = blk->txt.d.mode;