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];
}
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,
// 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;
uint32_t j;
// initialize bitsets on first call
if (!valid_df_short_bitset)
init_bitsets();
unsigned char *bestmsg;
int bestscore, bestphase;
@ -173,7 +211,7 @@ void demodulate2400(struct mag_buf *mag)
bestmsg = NULL; bestscore = SR_NOT_SET; bestphase = -1;
for (try_phase = 4; try_phase <= 8; ++try_phase) {
uint16_t *pPtr;
int phase, i, score;
int phase, score;
// Decode all the next 112 bits, regardless of the actual message
// 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);
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;
switch (phase) {
@ -263,6 +302,22 @@ void demodulate2400(struct mag_buf *mag)
}
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.

View File

@ -337,6 +337,7 @@ static void showHelp(void)
"--no-fix Disable error correction using CRC\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"
"--enable-df24 Enable decoding of DF24 Comm-D ELM messages\n"
"--mlat display raw messages in Beast ascii mode\n"
"--stats With --ifile print stats at exit. No other output\n"
"--stats-range Collect/show range histogram\n"
@ -547,6 +548,8 @@ int main(int argc, char **argv) {
Modes.nfix_crc = 1;
} else if (!strcmp(argv[j],"--fix-2bit")) {
Modes.nfix_crc = 2;
} else if (!strcmp(argv[j],"--enable-df24")) {
Modes.enable_df24 = 1;
} else if (!strcmp(argv[j],"--no-fix")) {
Modes.nfix_crc = 0;
} 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 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 enable_df24; // Enable decoding of DF24..DF31 (Comm-D ELM)
int raw; // Raw output format
int mode_ac; // Enable decoding of SSR Modes A & C
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
}
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
// 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
};
*short_syndrome = UNCHECKED_SYNDROME;
*long_syndrome = UNCHECKED_SYNDROME;
// Try to correct, including corrections to the initial 5 bit DF field
// that determines message format
@ -275,27 +280,27 @@ static int correctMessage(const unsigned char *in, unsigned char *out)
struct errorinfo *long_ei = NULL;
if (df_correctable_long[fix_df_bits] & df_bit) {
uint32_t long_syndrome = modesChecksum(in, MODES_LONG_MSG_BITS);
if (isLongPIMessage(in) && long_syndrome == 0) {
*long_syndrome = modesChecksum(in, MODES_LONG_MSG_BITS);
if (isLongPIMessage(in) && *long_syndrome == 0) {
// DF17/18 message with correct checksum
memcpy(out, in, MODES_LONG_MSG_BYTES);
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;
if (df_correctable_short[fix_df_bits] & df_bit) {
uint32_t short_syndrome = modesChecksum(in, MODES_SHORT_MSG_BITS);
if (isShortPIMessage(in) && (short_syndrome & 0xFFFF80) == 0) {
*short_syndrome = modesChecksum(in, MODES_SHORT_MSG_BITS);
if (isShortPIMessage(in) && (*short_syndrome & 0xFFFF80) == 0) {
// DF11 message with correct checksum
// (low 7 bits may be IID)
memcpy(out, in, MODES_SHORT_MSG_BYTES);
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
@ -344,29 +349,38 @@ static int correctMessage(const unsigned char *in, unsigned char *out)
// The more positive, the more reliable the message is.
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
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;
// 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
switch (df) {
case 0: // short air-air surveillance
case 4: // surveillance, altitude reply
case 5: // surveillance, altitude reply
{
uint32_t addr = modesChecksum(corrected, MODES_SHORT_MSG_BITS);
bool recent = icaoFilterTest(addr);
if (short_syndrome == UNCHECKED_SYNDROME)
short_syndrome = modesChecksum(corrected, MODES_SHORT_MSG_BITS);
bool recent = icaoFilterTest(short_syndrome);
return recent ? SR_UNRELIABLE_KNOWN : SR_UNRELIABLE_UNKNOWN;
}
case 16: // long air-air surveillance
case 20: // Comm-B, altitude 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 25: // 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 31: // Comm-D (ELM)
{
uint32_t addr = modesChecksum(corrected, MODES_LONG_MSG_BITS);
bool recent = icaoFilterTest(addr);
if (!Modes.enable_df24)
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;
}
@ -385,7 +402,9 @@ score_rank scoreModesMessage(const unsigned char *uncorrected)
{
// DF11 All-call reply
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);
switch (corrections) {
@ -513,13 +532,23 @@ int decodeModesMessage(struct modesMessage *mm, const unsigned char *in)
memcpy(mm->verbatim, in, MODES_LONG_MSG_BYTES);
// 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;
// Get the message type ASAP as other operations depend on this
mm->msgtype = getbits(msg, 1, 5); // Downlink Format
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->addr = 0;