Reduce CPU further in --no-fix-df mode. Add --enable-df24 option.

This reinstates the fastpath in the 2.4MHz demodulator that
aborts message demodulation early if the DF bits don't match a message
type that we know how to decode, or stops early if the DF bits can
only correspond to a short 56-bit message.

Do this in a more general form where we test against a set of valid
DF values, rather than a switch/case. Then populate the sets based on
the current error-correction settings (e.g. if we are allowed to repair
DF damage with 1 bit error correction, then a DF17 message could appear
with any of DF=17, DF=16, DF=19, DF=21, DF=25, or DF=1 and still be
correctable to a valid DF17 message)

In the default case this produces a small CPU improvement as short
messages might be able to stop earlier and a few DF values can be ignored
early. With --no-fix-df it produces a larger improvement as relatively few
DF values are valid in that mode.

To further reduce CPU, the default behaviour has changed to _not_
decode DF24 messages (Comm-D ELM messages). These are rarely seen in
the wild and dump1090 can't do anything useful with them. Disabling
them allows us to further reduce the set of valid DF values as they
effectively use all DF values from 24..31, 25% of all possible values.
To re-enable DF24 decoding, use the new `--enable-df24` option.

Finally, avoid doing some CRC calculations twice. This was only happening
once per valid decoded message, not per demodulation attempt, so it's not
such a large win.
This commit is contained in:
Oliver Jowett 2021-03-19 16:52:42 +08:00
parent 455896e86d
commit c433463392
4 changed files with 109 additions and 21 deletions

View File

@ -58,6 +58,40 @@ static inline int slice_phase4(uint16_t *m) {
return m[0] + 5 * m[1] - 5 * m[2] - m[3]; return m[0] + 5 * m[1] - 5 * m[2] - m[3];
} }
static uint32_t valid_df_short_bitset; // set of acceptable DF values for short messages
static uint32_t valid_df_long_bitset; // set of acceptable DF values for long messages
static uint32_t generate_damage_set(uint8_t df, unsigned damage_bits)
{
uint32_t result = (1 << df);
if (!damage_bits)
return result;
for (unsigned bit = 0; bit < 5; ++bit) {
unsigned damaged_df = df ^ (1 << bit);
result |= generate_damage_set(damaged_df, damage_bits - 1);
}
return result;
}
static void init_bitsets()
{
// DFs that we directly understand without correction
valid_df_short_bitset = (1 << 0) | (1 << 4) | (1 << 5) | (1 << 11);
valid_df_long_bitset = (1 << 16) | (1 << 17) | (1 << 18) | (1 << 20) | (1 << 21);
if (Modes.enable_df24)
valid_df_long_bitset |= (1 << 24) | (1 << 25) | (1 << 26) | (1 << 27) | (1 << 28) | (1 << 29) | (1 << 30) | (1 << 31);
// if we can also repair DF damage, include those corrections
if (Modes.fix_df && Modes.nfix_crc) {
valid_df_short_bitset |= generate_damage_set(11, 1);
valid_df_long_bitset |= generate_damage_set(17, Modes.nfix_crc);
valid_df_long_bitset |= generate_damage_set(18, Modes.nfix_crc);
}
}
// //
// Given 'mlen' magnitude samples in 'm', sampled at 2.4MHz, // Given 'mlen' magnitude samples in 'm', sampled at 2.4MHz,
// try to demodulate some Mode S messages. // try to demodulate some Mode S messages.
@ -69,6 +103,10 @@ void demodulate2400(struct mag_buf *mag)
unsigned char msg1[MODES_LONG_MSG_BYTES], msg2[MODES_LONG_MSG_BYTES], *msg; unsigned char msg1[MODES_LONG_MSG_BYTES], msg2[MODES_LONG_MSG_BYTES], *msg;
uint32_t j; uint32_t j;
// initialize bitsets on first call
if (!valid_df_short_bitset)
init_bitsets();
unsigned char *bestmsg; unsigned char *bestmsg;
int bestscore, bestphase; int bestscore, bestphase;
@ -173,7 +211,7 @@ void demodulate2400(struct mag_buf *mag)
bestmsg = NULL; bestscore = SR_NOT_SET; bestphase = -1; bestmsg = NULL; bestscore = SR_NOT_SET; bestphase = -1;
for (try_phase = 4; try_phase <= 8; ++try_phase) { for (try_phase = 4; try_phase <= 8; ++try_phase) {
uint16_t *pPtr; uint16_t *pPtr;
int phase, i, score; int phase, score;
// Decode all the next 112 bits, regardless of the actual message // Decode all the next 112 bits, regardless of the actual message
// size. We'll check the actual message type later // size. We'll check the actual message type later
@ -181,7 +219,8 @@ void demodulate2400(struct mag_buf *mag)
pPtr = &m[j+19] + (try_phase/5); pPtr = &m[j+19] + (try_phase/5);
phase = try_phase % 5; phase = try_phase % 5;
for (i = 0; i < MODES_LONG_MSG_BYTES; ++i) { unsigned bytelen = 1;
for (unsigned i = 0; i < bytelen; ++i) {
uint8_t theByte = 0; uint8_t theByte = 0;
switch (phase) { switch (phase) {
@ -263,6 +302,22 @@ void demodulate2400(struct mag_buf *mag)
} }
msg[i] = theByte; msg[i] = theByte;
if (i == 0) {
// inspect DF field early, only continue processing
// messages where the DF appears valid
unsigned df = theByte >> 3;
if (valid_df_long_bitset & (1 << df))
bytelen = MODES_LONG_MSG_BYTES;
else if (valid_df_short_bitset & (1 << df))
bytelen = MODES_SHORT_MSG_BYTES;
}
}
if (bytelen == 1) {
// rejected early by the DF filter
Modes.stats_current.demod_rejected_bad++;
continue;
} }
// Score the mode S message and see if it's any good. // Score the mode S message and see if it's any good.

View File

@ -337,6 +337,7 @@ static void showHelp(void)
"--no-fix Disable error correction using CRC\n" "--no-fix Disable error correction using CRC\n"
"--no-fix-df Disable error correction of the DF message field (reduces CPU requirements)\n" "--no-fix-df Disable error correction of the DF message field (reduces CPU requirements)\n"
"--no-crc-check Disable messages with broken CRC (discouraged)\n" "--no-crc-check Disable messages with broken CRC (discouraged)\n"
"--enable-df24 Enable decoding of DF24 Comm-D ELM messages\n"
"--mlat display raw messages in Beast ascii mode\n" "--mlat display raw messages in Beast ascii mode\n"
"--stats With --ifile print stats at exit. No other output\n" "--stats With --ifile print stats at exit. No other output\n"
"--stats-range Collect/show range histogram\n" "--stats-range Collect/show range histogram\n"
@ -547,6 +548,8 @@ int main(int argc, char **argv) {
Modes.nfix_crc = 1; Modes.nfix_crc = 1;
} else if (!strcmp(argv[j],"--fix-2bit")) { } else if (!strcmp(argv[j],"--fix-2bit")) {
Modes.nfix_crc = 2; Modes.nfix_crc = 2;
} else if (!strcmp(argv[j],"--enable-df24")) {
Modes.enable_df24 = 1;
} else if (!strcmp(argv[j],"--no-fix")) { } else if (!strcmp(argv[j],"--no-fix")) {
Modes.nfix_crc = 0; Modes.nfix_crc = 0;
} else if (!strcmp(argv[j],"--no-fix-df")) { } else if (!strcmp(argv[j],"--no-fix-df")) {

View File

@ -325,6 +325,7 @@ struct _Modes { // Internal state
int nfix_crc; // Number of crc bit error(s) to correct int nfix_crc; // Number of crc bit error(s) to correct
int check_crc; // Only display messages with good CRC int check_crc; // Only display messages with good CRC
int fix_df; // Try to correct damage to the DF field, as well as the main message body int fix_df; // Try to correct damage to the DF field, as well as the main message body
int enable_df24; // Enable decoding of DF24..DF31 (Comm-D ELM)
int raw; // Raw output format int raw; // Raw output format
int mode_ac; // Enable decoding of SSR Modes A & C int mode_ac; // Enable decoding of SSR Modes A & C
int mode_ac_auto; // allow toggling of A/C by Beast commands int mode_ac_auto; // allow toggling of A/C by Beast commands

View File

@ -247,7 +247,9 @@ static bool isShortPIMessage(const unsigned char *msg)
return (df == 11); // assume IID==0 return (df == 11); // assume IID==0
} }
static int correctMessage(const unsigned char *in, unsigned char *out) #define UNCHECKED_SYNDROME 0xFFFFFFFFU
static int correctMessage(const unsigned char *in, unsigned char *out, uint32_t *short_syndrome, uint32_t *long_syndrome)
{ {
// Possible DF values of the first byte of a message that could be a valid DF11/17/18 // Possible DF values of the first byte of a message that could be a valid DF11/17/18
// message after correction. See tools/df-correction-arrays.py for generator code. // message after correction. See tools/df-correction-arrays.py for generator code.
@ -262,6 +264,9 @@ static int correctMessage(const unsigned char *in, unsigned char *out)
0x00060000, 0x066f0006, 0x6fff066f 0x00060000, 0x066f0006, 0x6fff066f
}; };
*short_syndrome = UNCHECKED_SYNDROME;
*long_syndrome = UNCHECKED_SYNDROME;
// Try to correct, including corrections to the initial 5 bit DF field // Try to correct, including corrections to the initial 5 bit DF field
// that determines message format // that determines message format
@ -275,27 +280,27 @@ static int correctMessage(const unsigned char *in, unsigned char *out)
struct errorinfo *long_ei = NULL; struct errorinfo *long_ei = NULL;
if (df_correctable_long[fix_df_bits] & df_bit) { if (df_correctable_long[fix_df_bits] & df_bit) {
uint32_t long_syndrome = modesChecksum(in, MODES_LONG_MSG_BITS); *long_syndrome = modesChecksum(in, MODES_LONG_MSG_BITS);
if (isLongPIMessage(in) && long_syndrome == 0) { if (isLongPIMessage(in) && *long_syndrome == 0) {
// DF17/18 message with correct checksum // DF17/18 message with correct checksum
memcpy(out, in, MODES_LONG_MSG_BYTES); memcpy(out, in, MODES_LONG_MSG_BYTES);
return 0; return 0;
} }
long_ei = modesChecksumDiagnose(long_syndrome, MODES_LONG_MSG_BITS); long_ei = modesChecksumDiagnose(*long_syndrome, MODES_LONG_MSG_BITS);
} }
struct errorinfo *short_ei = NULL; struct errorinfo *short_ei = NULL;
if (df_correctable_short[fix_df_bits] & df_bit) { if (df_correctable_short[fix_df_bits] & df_bit) {
uint32_t short_syndrome = modesChecksum(in, MODES_SHORT_MSG_BITS); *short_syndrome = modesChecksum(in, MODES_SHORT_MSG_BITS);
if (isShortPIMessage(in) && (short_syndrome & 0xFFFF80) == 0) { if (isShortPIMessage(in) && (*short_syndrome & 0xFFFF80) == 0) {
// DF11 message with correct checksum // DF11 message with correct checksum
// (low 7 bits may be IID) // (low 7 bits may be IID)
memcpy(out, in, MODES_SHORT_MSG_BYTES); memcpy(out, in, MODES_SHORT_MSG_BYTES);
return 0; return 0;
} }
short_ei = modesChecksumDiagnose(short_syndrome, MODES_SHORT_MSG_BITS); // assume IID == 0 short_ei = modesChecksumDiagnose(*short_syndrome, MODES_SHORT_MSG_BITS); // assume IID == 0
} }
// Might be a damaged DF11/17/18, or might be another message type that doesn't have a full CRC // Might be a damaged DF11/17/18, or might be another message type that doesn't have a full CRC
@ -344,29 +349,38 @@ static int correctMessage(const unsigned char *in, unsigned char *out)
// The more positive, the more reliable the message is. // The more positive, the more reliable the message is.
score_rank scoreModesMessage(const unsigned char *uncorrected) score_rank scoreModesMessage(const unsigned char *uncorrected)
{ {
// try to produce a corrected DF11/17/18, including correcting the DF bits
unsigned char corrected[14];
int corrections = correctMessage(uncorrected, corrected);
// This is a "valid" DF0 message, but it's not useful; we discard these messages // This is a "valid" DF0 message, but it's not useful; we discard these messages
static const unsigned char all_zeros[MODES_SHORT_MSG_BYTES] = { 0, 0, 0, 0, 0, 0, 0 }; static const unsigned char all_zeros[MODES_SHORT_MSG_BYTES] = { 0, 0, 0, 0, 0, 0, 0 };
if (!memcmp(all_zeros, corrected, sizeof(all_zeros))) if (!memcmp(all_zeros, uncorrected, sizeof(all_zeros)))
return SR_ALL_ZEROS; return SR_ALL_ZEROS;
// try to produce a corrected DF11/17/18, including correcting the DF bits
unsigned char corrected[14];
uint32_t short_syndrome, long_syndrome;
int corrections = correctMessage(uncorrected, corrected, &short_syndrome, &long_syndrome);
unsigned df = getbits(corrected, 1, 5); // Downlink Format unsigned df = getbits(corrected, 1, 5); // Downlink Format
switch (df) { switch (df) {
case 0: // short air-air surveillance case 0: // short air-air surveillance
case 4: // surveillance, altitude reply case 4: // surveillance, altitude reply
case 5: // surveillance, altitude reply case 5: // surveillance, altitude reply
{ {
uint32_t addr = modesChecksum(corrected, MODES_SHORT_MSG_BITS); if (short_syndrome == UNCHECKED_SYNDROME)
bool recent = icaoFilterTest(addr); short_syndrome = modesChecksum(corrected, MODES_SHORT_MSG_BITS);
bool recent = icaoFilterTest(short_syndrome);
return recent ? SR_UNRELIABLE_KNOWN : SR_UNRELIABLE_UNKNOWN; return recent ? SR_UNRELIABLE_KNOWN : SR_UNRELIABLE_UNKNOWN;
} }
case 16: // long air-air surveillance case 16: // long air-air surveillance
case 20: // Comm-B, altitude reply case 20: // Comm-B, altitude reply
case 21: // Comm-B, identity reply case 21: // Comm-B, identity reply
{
if (long_syndrome == UNCHECKED_SYNDROME)
long_syndrome = modesChecksum(corrected, MODES_LONG_MSG_BITS);
bool recent = icaoFilterTest(long_syndrome);
return recent ? SR_UNRELIABLE_KNOWN : SR_UNRELIABLE_UNKNOWN;
}
case 24: // Comm-D (ELM) case 24: // Comm-D (ELM)
case 25: // Comm-D (ELM) case 25: // Comm-D (ELM)
case 26: // Comm-D (ELM) case 26: // Comm-D (ELM)
@ -376,8 +390,11 @@ score_rank scoreModesMessage(const unsigned char *uncorrected)
case 30: // Comm-D (ELM) case 30: // Comm-D (ELM)
case 31: // Comm-D (ELM) case 31: // Comm-D (ELM)
{ {
uint32_t addr = modesChecksum(corrected, MODES_LONG_MSG_BITS); if (!Modes.enable_df24)
bool recent = icaoFilterTest(addr); return SR_UNCORRECTABLE;
if (long_syndrome == UNCHECKED_SYNDROME)
long_syndrome = modesChecksum(corrected, MODES_LONG_MSG_BITS);
bool recent = icaoFilterTest(long_syndrome);
return recent ? SR_UNRELIABLE_KNOWN : SR_UNRELIABLE_UNKNOWN; return recent ? SR_UNRELIABLE_KNOWN : SR_UNRELIABLE_UNKNOWN;
} }
@ -385,7 +402,9 @@ score_rank scoreModesMessage(const unsigned char *uncorrected)
{ {
// DF11 All-call reply // DF11 All-call reply
uint32_t addr = getbits(corrected, 9, 32); uint32_t addr = getbits(corrected, 9, 32);
uint32_t iid = modesChecksum(corrected, MODES_SHORT_MSG_BITS) & 0x7F; if (short_syndrome == UNCHECKED_SYNDROME)
short_syndrome = modesChecksum(corrected, MODES_SHORT_MSG_BITS);
uint32_t iid = short_syndrome & 0x7F;
bool recent = icaoFilterTest(addr); bool recent = icaoFilterTest(addr);
switch (corrections) { switch (corrections) {
@ -513,13 +532,23 @@ int decodeModesMessage(struct modesMessage *mm, const unsigned char *in)
memcpy(mm->verbatim, in, MODES_LONG_MSG_BYTES); memcpy(mm->verbatim, in, MODES_LONG_MSG_BYTES);
// Apply corrections to our local copy // Apply corrections to our local copy
int corrections = correctMessage(in, mm->msg); uint32_t short_syndrome, long_syndrome;
int corrections = correctMessage(in, mm->msg, &short_syndrome, &long_syndrome);
const unsigned char *msg = mm->msg; const unsigned char *msg = mm->msg;
// Get the message type ASAP as other operations depend on this // Get the message type ASAP as other operations depend on this
mm->msgtype = getbits(msg, 1, 5); // Downlink Format mm->msgtype = getbits(msg, 1, 5); // Downlink Format
mm->msgbits = modesMessageLenByType(mm->msgtype); mm->msgbits = modesMessageLenByType(mm->msgtype);
mm->crc = modesChecksum(msg, mm->msgbits); if (mm->msgtype & 16) {
if (long_syndrome == UNCHECKED_SYNDROME)
long_syndrome = modesChecksum(mm->msg, MODES_LONG_MSG_BITS);
mm->crc = long_syndrome;
} else {
if (short_syndrome == UNCHECKED_SYNDROME)
short_syndrome = modesChecksum(mm->msg, MODES_SHORT_MSG_BITS);
mm->crc = short_syndrome;
}
mm->correctedbits = corrections > 0 ? corrections : 0; mm->correctedbits = corrections > 0 ? corrections : 0;
mm->addr = 0; mm->addr = 0;