Skip to content

Commit

Permalink
msk/acars: add support for noise floor estimator
Browse files Browse the repository at this point in the history
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
  • Loading branch information
f00b4r0 committed Sep 26, 2024
1 parent 81758de commit 27c40e5
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 18 deletions.
12 changes: 10 additions & 2 deletions acars.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 >= 12 && 0xFF != r) { // we have our first non-0xFF byte after a sequence - XXX REVISIT: expect at least 16: adjust count depending on how fast the MSK PLL locks
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;

Expand Down Expand Up @@ -298,6 +303,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;
}
Expand Down Expand Up @@ -346,7 +353,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;
Expand Down
8 changes: 5 additions & 3 deletions acarsdec.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;

Expand Down
15 changes: 6 additions & 9 deletions msk.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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);
Expand Down
11 changes: 7 additions & 4 deletions output.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 27c40e5

Please sign in to comment.