Major re-write of decodeModesMessage

Lots of changes to the decodeModesMessages() function, and associated
helper functions, with the aim of speeding the code up by reducing the
number of calls to the CRC functions. Also, only update the mm variables
which are part of the message we're decoding.

Change the way CRC's are generated and checked.
Change the way single and double bit error is implemented.
General tidy up of the function to make it more readable.
This commit is contained in:
Malcolm Robb 2013-04-29 18:53:11 +01:00
parent e9b463d25c
commit 50cedd7fe2

View file

@ -303,8 +303,8 @@ void modesSendRawOutput(struct modesMessage *mm);
void modesSendBeastOutput(struct modesMessage *mm); void modesSendBeastOutput(struct modesMessage *mm);
void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a); void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a);
void useModesMessage(struct modesMessage *mm); void useModesMessage(struct modesMessage *mm);
int fixSingleBitErrors(unsigned char *msg, int bits, struct modesMessage *mm); int fixSingleBitErrors(unsigned char *msg, int bits);
int fixTwoBitsErrors(unsigned char *msg, int bits, struct modesMessage *mm); int fixTwoBitsErrors(unsigned char *msg, int bits);
int modesMessageLenByType(int type); int modesMessageLenByType(int type);
/* ============================= Utility functions ========================== */ /* ============================= Utility functions ========================== */
@ -700,15 +700,13 @@ void dumpRawMessage(char *descr, unsigned char *msg,
uint16_t *m, uint32_t offset) uint16_t *m, uint32_t offset)
{ {
int j; int j;
int msgtype = msg[0]>>3; int msgtype = msg[0] >> 3;
int fixable = -1; int fixable = -1;
if (msgtype == 11 || msgtype == 17) { if (msgtype == 17) {
int msgbits = (msgtype == 11) ? MODES_SHORT_MSG_BITS : fixable = fixSingleBitErrors(msg, MODES_LONG_MSG_BITS);
MODES_LONG_MSG_BITS;
fixable = fixSingleBitErrors(msg, msgbits, NULL);
if (fixable == -1) if (fixable == -1)
fixable = fixTwoBitsErrors(msg, msgbits, NULL); fixable = fixTwoBitsErrors(msg, MODES_LONG_MSG_BITS);
} }
if (Modes.debug & MODES_DEBUG_JS) { if (Modes.debug & MODES_DEBUG_JS) {
@ -1111,6 +1109,7 @@ uint32_t modes_checksum_table[112] = {
uint32_t modesChecksum(unsigned char *msg, int bits) { uint32_t modesChecksum(unsigned char *msg, int bits) {
uint32_t crc = 0; uint32_t crc = 0;
uint32_t rem = 0;
int offset = (bits == 112) ? 0 : (112-56); int offset = (bits == 112) ? 0 : (112-56);
uint8_t theByte = *msg; uint8_t theByte = *msg;
uint32_t * pCRCTable = &modes_checksum_table[offset]; uint32_t * pCRCTable = &modes_checksum_table[offset];
@ -1118,14 +1117,14 @@ uint32_t modesChecksum(unsigned char *msg, int bits) {
for(j = 0; j < bits; j++) { for(j = 0; j < bits; j++) {
if ((j & 7) == 0) if ((j & 7) == 0)
{theByte = *msg++;} {theByte = *msg++; rem = (rem << 8) | theByte;}
// If bit is set, xor with corresponding table entry. // If bit is set, xor with corresponding table entry.
if (theByte & 0x80) {crc ^= *pCRCTable;} if (theByte & 0x80) {crc ^= *pCRCTable;}
pCRCTable++; pCRCTable++;
theByte = theByte << 1; theByte = theByte << 1;
} }
return crc; // 24 bit checksum. return ((crc ^ rem) & 0x00FFFFFF); // 24 bit checksum.
} }
// //
// Given the Downlink Format (DF) of the message, return the message length in bits. // Given the Downlink Format (DF) of the message, return the message length in bits.
@ -1137,97 +1136,79 @@ uint32_t modesChecksum(unsigned char *msg, int bits) {
int modesMessageLenByType(int type) { int modesMessageLenByType(int type) {
return (type & 0x10) ? MODES_LONG_MSG_BITS : MODES_SHORT_MSG_BITS ; return (type & 0x10) ? MODES_LONG_MSG_BITS : MODES_SHORT_MSG_BITS ;
} }
//
/* Try to fix single bit errors using the checksum. On success modifies // Try to fix single bit errors using the checksum. On success modifies
* the original buffer with the fixed version, and returns the position // the original buffer with the fixed version, and returns the position
* of the error bit. Otherwise if fixing failed -1 is returned. */ // of the error bit. Otherwise if fixing failed -1 is returned.
int fixSingleBitErrors(unsigned char *msg, int bits, struct modesMessage *mm) { //
int fixSingleBitErrors(unsigned char *msg, int bits) {
int j; int j;
unsigned char aux[MODES_LONG_MSG_BYTES]; unsigned char aux[MODES_LONG_MSG_BYTES];
memcpy(aux, msg,bits/8); memcpy(aux, msg, bits/8);
for (j = 0; j < bits; j++) { // Do not attempt to error correct Bits 0-4. These contain the DF, and must
// be correct because we can only error correct DF17
for (j = 5; j < bits; j++) {
int byte = j/8; int byte = j/8;
int bitmask = 1 << (7-(j%8)); int bitmask = 1 << (7 - (j & 7));
uint32_t crc1, crc2;
aux[byte] ^= bitmask; /* Flip j-th bit. */ aux[byte] ^= bitmask; // Flip j-th bit
crc1 = ((uint32_t)aux[(bits/8)-3] << 16) | if (0 == modesChecksum(aux, bits)) {
((uint32_t)aux[(bits/8)-2] << 8) | // The error is fixed. Overwrite the original buffer with the
(uint32_t)aux[(bits/8)-1]; // corrected sequence, and returns the error bit position
crc2 = modesChecksum(aux,bits); msg[byte] = aux[byte];
return (j);
if (crc1 == crc2) {
/* The error is fixed. Overwrite the original buffer with
* the corrected sequence, and returns the error bit
* position. */
memcpy(msg,aux,bits/8);
if (mm)
{
mm->crc = crc2;
mm->iid = 0;
mm->crcok = 1;
}
return j;
} }
aux[byte] ^= bitmask; /* Flip j-th bit back again. */ aux[byte] ^= bitmask; // Flip j-th bit back again
} }
return -1; return (-1);
} }
//
/* Similar to fixSingleBitErrors() but try every possible two bit combination. // Similar to fixSingleBitErrors() but try every possible two bit combination.
* This is very slow and should be tried only against DF17 messages that // This is very slow and should be tried only against DF17 messages that
* don't pass the checksum, and only in Aggressive Mode. */ // don't pass the checksum, and only in Aggressive Mode.
int fixTwoBitsErrors(unsigned char *msg, int bits, struct modesMessage *mm) { //
int fixTwoBitsErrors(unsigned char *msg, int bits) {
int j, i; int j, i;
unsigned char aux[MODES_LONG_MSG_BYTES]; unsigned char aux[MODES_LONG_MSG_BYTES];
memcpy(aux,msg, bits/8); memcpy(aux, msg, bits/8);
for (j = 0; j < bits; j++) { // Do not attempt to error correct Bits 0-4. These contain the DF, and must
// be correct because we can only error correct DF17
for (j = 5; j < bits; j++) {
int byte1 = j/8; int byte1 = j/8;
int bitmask1 = 1 << (7-(j%8)); int bitmask1 = 1 << (7 - (j & 7));
aux[byte1] ^= bitmask1; /* Flip j-th bit. */ aux[byte1] ^= bitmask1; // Flip j-th bit
/* Don't check the same pairs multiple times, so i starts from j+1 */ // Don't check the same pairs multiple times, so i starts from j+1
for (i = j+1; i < bits; i++) { for (i = j+1; i < bits; i++) {
int byte2 = i/8; int byte2 = i/8;
int bitmask2 = 1 << (7-(i%8)); int bitmask2 = 1 << (7 - (i & 7));
uint32_t crc1, crc2;
aux[byte2] ^= bitmask2; /* Flip i-th bit. */ aux[byte2] ^= bitmask2; // Flip i-th bit
crc1 = ((uint32_t)aux[(bits/8)-3] << 16) | if (0 == modesChecksum(aux, bits)) {
((uint32_t)aux[(bits/8)-2] << 8) | // The error is fixed. Overwrite the original buffer with
(uint32_t)aux[(bits/8)-1]; // the corrected sequence, and returns the error bit position
crc2 = modesChecksum(aux,bits); msg[byte1] = aux[byte1];
msg[byte2] = aux[byte2];
if (crc1 == crc2) { // We return the two bits as a 16 bit integer by shifting
/* The error is fixed. Overwrite the original buffer with // 'i' on the left. This is possible since 'i' will always
* the corrected sequence, and returns the error bit // be non-zero because i starts from j+1
* position. */ return (j | (i << 8));
memcpy(msg,aux,bits/8);
if (mm)
{
mm->crc = crc2;
mm->iid = 0;
mm->crcok = 1;
}
/* We return the two bits as a 16 bit integer by shifting
* 'i' on the left. This is possible since 'i' will always
* be non-zero because i starts from j+1. */
return j | (i<<8);
aux[byte2] ^= bitmask2; /* Flip i-th bit back. */ aux[byte2] ^= bitmask2; // Flip i-th bit back
} }
aux[byte1] ^= bitmask1; /* Flip j-th bit back. */ aux[byte1] ^= bitmask1; // Flip j-th bit back
} }
} }
return -1; return (-1);
} }
/* Hash the ICAO address to index our cache of MODES_ICAO_CACHE_LEN /* Hash the ICAO address to index our cache of MODES_ICAO_CACHE_LEN
@ -1260,61 +1241,6 @@ int ICAOAddressWasRecentlySeen(uint32_t addr) {
return a && a == addr && time(NULL)-t <= MODES_ICAO_CACHE_TTL; return a && a == addr && time(NULL)-t <= MODES_ICAO_CACHE_TTL;
} }
/* If the message type has the checksum xored with the ICAO address, try to
* brute force it using a list of recently seen ICAO addresses.
*
* Do this in a brute-force fashion by xoring the predicted CRC with
* the address XOR checksum field in the message. This will recover the
* address: if we found it in our cache, we can assume the message is ok.
*
* This function expects mm->msgtype and mm->msgbits to be correctly
* populated by the caller.
*
* On success the correct ICAO address is stored in the modesMessage
* structure in the addr field.
*
* If the function successfully recovers a message with a correct checksum
* it returns 1. Otherwise 0 is returned. */
int bruteForceAP(unsigned char *msg, struct modesMessage *mm) {
unsigned char aux[MODES_LONG_MSG_BYTES];
int msgtype = mm->msgtype;
int msgbits = mm->msgbits;
if (msgtype == 0 || /* Short air surveillance */
msgtype == 4 || /* Surveillance, altitude reply */
msgtype == 5 || /* Surveillance, identity reply */
msgtype == 16 || /* Long Air-Air survillance */
msgtype == 20 || /* Comm-A, altitude request */
msgtype == 21 || /* Comm-A, identity request */
msgtype == 24) /* Comm-C ELM */
{
uint32_t addr;
uint32_t crc;
int lastbyte = (msgbits/8)-1;
/* Work on a copy. */
memcpy(aux,msg,msgbits/8);
/* Compute the CRC of the message and XOR it with the AP field
* so that we recover the address, because:
*
* (ADDR xor CRC) xor CRC = ADDR. */
crc = modesChecksum(aux,msgbits);
aux[lastbyte] ^= crc & 0xff;
aux[lastbyte-1] ^= (crc >> 8) & 0xff;
aux[lastbyte-2] ^= (crc >> 16) & 0xff;
/* If the obtained address exists in our cache we consider
* the message valid. */
addr = aux[lastbyte] | (aux[lastbyte-1] << 8) | (aux[lastbyte-2] << 16);
if (ICAOAddressWasRecentlySeen(addr)) {
mm->addr = addr;
return (1);
}
}
return (0);
}
// //
// In the squawk (identity) field bits are interleaved as follows in // In the squawk (identity) field bits are interleaved as follows in
// (message bit 20 to bit 32): // (message bit 20 to bit 32):
@ -1452,39 +1378,29 @@ char *getMEDescription(int metype, int mesub) {
mename = "Aircraft Operational Status Message"; mename = "Aircraft Operational Status Message";
return mename; return mename;
} }
//
/* Decode a raw Mode S message demodulated as a stream of bytes by // Decode a raw Mode S message demodulated as a stream of bytes by detectModeS(),
* detectModeS(), and split it into fields populating a modesMessage // and split it into fields populating a modesMessage structure.
* structure. */ //
void decodeModesMessage(struct modesMessage *mm, unsigned char *msg) { void decodeModesMessage(struct modesMessage *mm, unsigned char *msg) {
uint32_t crc2; /* Computed CRC, used to verify the message CRC. */
char *ais_charset = "?ABCDEFGHIJKLMNOPQRSTUVWXYZ????? ???????????????0123456789??????"; char *ais_charset = "?ABCDEFGHIJKLMNOPQRSTUVWXYZ????? ???????????????0123456789??????";
/* Work on our local copy */ // Work on our local copy
memcpy(mm->msg, msg, MODES_LONG_MSG_BYTES); memcpy(mm->msg, msg, MODES_LONG_MSG_BYTES);
msg = mm->msg; msg = mm->msg;
/* Get the message type ASAP as other operations depend on this */ // If we havn't already got it, get the message type ASAP as other operations depend on this
mm->msgtype = msg[0] >> 3; /* Downlink Format */ if (mm->msgbits == 0) {
mm->msgtype = msg[0] >> 3; // Downlink Format
mm->msgbits = modesMessageLenByType(mm->msgtype); mm->msgbits = modesMessageLenByType(mm->msgtype);
}
/* CRC is always the last three bytes. */ mm->errorbit = -1; // No errors fixed
mm->crc = ((uint32_t)msg[(mm->msgbits/8)-3] << 16) | mm->phase_corrected = 0;
((uint32_t)msg[(mm->msgbits/8)-2] << 8) | mm->crc = modesChecksum(msg, mm->msgbits);
(uint32_t)msg[(mm->msgbits/8)-1];
crc2 = modesChecksum(msg, mm->msgbits);
mm->iid = (mm->crc ^ crc2);
/* Check CRC and fix single bit errors using the CRC when if ((mm->crc) && (Modes.fix_errors) && (mm->msgtype == 17)) {
* possible (DF 11 and 17). */ // if ((mm->crc) && (Modes.fix_errors) && ((mm->msgtype == 11) || (mm->msgtype == 17))) {
mm->errorbit = -1; /* No error */
if (mm->msgtype == 11)
{mm->crcok = (mm->iid < 80);}
else
{mm->crcok = (mm->iid == 0);}
if (!mm->crcok && Modes.fix_errors && (mm->msgtype == 17)){
// if (!mm->crcok && Modes.fix_errors && ((mm->msgtype == 11) || (mm->msgtype == 17))){
// //
// Fixing single bit errors in DF-11 is a bit dodgy because we have no way to // Fixing single bit errors in DF-11 is a bit dodgy because we have no way to
// know for sure if the crc is supposed to be 0 or not - it could be any value // know for sure if the crc is supposed to be 0 or not - it could be any value
@ -1495,51 +1411,56 @@ void decodeModesMessage(struct modesMessage *mm, unsigned char *msg) {
// using the results. Perhaps check the ICAO against known aircraft, and check // using the results. Perhaps check the ICAO against known aircraft, and check
// IID against known good IID's. That's a TODO. // IID against known good IID's. That's a TODO.
// //
mm->errorbit = fixSingleBitErrors(msg, mm->msgbits, mm); mm->errorbit = fixSingleBitErrors(msg, mm->msgbits);
if ((mm->errorbit == -1) && (Modes.aggressive)) { if ((mm->errorbit == -1) && (Modes.aggressive)) {
mm->errorbit = fixTwoBitsErrors(msg, mm->msgbits, mm); mm->errorbit = fixTwoBitsErrors(msg, mm->msgbits);
} }
} }
//
/* Note that most of the other computation happens *after* we fix // Note that most of the other computation happens *after* we fix the
* the single bit errors, otherwise we would need to recompute the // single/two bit errors, otherwise we would need to recompute the fields again.
* fields again. */ //
mm->ca = msg[0] & 7; /* Responder capabilities. */ if (mm->msgtype == 11) { // DF 11
mm->crcok = (mm->crc < 80);
// ICAO address mm->iid = mm->crc;
mm->addr = (msg[1] << 16) | (msg[2] << 8) | (msg[3]); mm->addr = (msg[1] << 16) | (msg[2] << 8) | (msg[3]);
/* DF 17 type (assuming this is a DF17, otherwise not used) */ if (0 == mm->crc) {
mm->metype = msg[4] >> 3; /* Extended squitter message type. */ // DF 11 : if crc == 0 try to populate our ICAO addresses whitelist.
mm->mesub = msg[4] & 7; /* Extended squitter message subtype. */
/* Fields for DF4,5,20,21 */
mm->fs = msg[0] & 7; /* Flight status for DF4,5,20,21 */
mm->dr = msg[1] >> 3 & 31; /* Request extraction of downlink request. */
mm->um = ((msg[1] & 7)<<3)| /* Request extraction of downlink request. */
msg[2]>>5;
mm->modeA = decodeGillhamField((msg[2] << 8) | msg[3]);
/* DF 11 & 17: try to populate our ICAO addresses whitelist.
* DFs with an AP field (xored addr and crc), try to decode it. */
if (mm->msgtype != 11 && mm->msgtype != 17) {
/* Check if we can check the checksum for the Downlink Formats where
* the checksum is xored with the aircraft ICAO address. We try to
* brute force it using a list of recently seen aircraft addresses. */
if (bruteForceAP(msg,mm)) {
/* We recovered the message, mark the checksum as valid. */
mm->crcok = 1;
} else {
mm->crcok = 0;
}
} else {
/* If this is DF 11 or DF 17 and the checksum was ok,
* we can add this address to the list of recently seen
* addresses. */
if (mm->crcok && mm->errorbit == -1) {
addRecentlySeenICAOAddr(mm->addr); addRecentlySeenICAOAddr(mm->addr);
} }
} else if (mm->msgtype == 17) { // DF 17
mm->crcok = (mm->crc == 0);
mm->addr = (msg[1] << 16) | (msg[2] << 8) | (msg[3]);
if (-1 != mm->errorbit) {
// DF 17 : if (error corrected) force crc = 0 but do not try to add this address
// to the whitelist of recently seen ICAO addresses.
mm->crc = 0;
} else if (0 == mm->crc) {
// DF 17 : if uncorrected and crc == 0 add this address to the whitelist of
// recently seen ICAO addresses.
addRecentlySeenICAOAddr(mm->addr);
}
} else { // All other DF's
// Compare the checksum with the whitelist of recently seen ICAO
// addresses. If it matches one, then declare the message as valid
mm->addr = mm->crc;
mm->crcok = ICAOAddressWasRecentlySeen(mm->crc);
}
// Fields for DF4,5,20,21
mm->ca = // Responder capabilities
mm->fs = msg[0] & 7; // Flight status for DF4,5,20,21
mm->dr = (msg[1] >> 3) & 0x1F; // Request extraction of downlink request
mm->um = ((msg[1] & 7) << 3) | (msg[2] >> 5); // Request extraction of downlink request
// Fields for DF5,21 = Gillham encoded Squawk
if (mm->msgtype == 5 || mm->msgtype == 21) {
mm->modeA = decodeGillhamField((msg[2] << 8) | msg[3]);
} }
// Fields for DF0, DF4, DF16, DF20 13 bit altitude // Fields for DF0, DF4, DF16, DF20 13 bit altitude
@ -1548,9 +1469,12 @@ void decodeModesMessage(struct modesMessage *mm, unsigned char *msg) {
mm->altitude = decodeAC13Field(((msg[2] << 8) | msg[3]), &mm->unit); mm->altitude = decodeAC13Field(((msg[2] << 8) | msg[3]), &mm->unit);
} }
/* Decode extended squitter specific stuff. */ // Fields for DF17 squitter
if (mm->msgtype == 17) { if (mm->msgtype == 17) {
/* Decode the extended squitter message. */ mm->metype = msg[4] >> 3; // Extended squitter message type
mm->mesub = msg[4] & 7; // Extended squitter message subtype
// Decode the extended squitter message
if (mm->metype >= 1 && mm->metype <= 4) { if (mm->metype >= 1 && mm->metype <= 4) {
/* Aircraft Identification and Category */ /* Aircraft Identification and Category */
@ -1612,7 +1536,6 @@ void decodeModesMessage(struct modesMessage *mm, unsigned char *msg) {
} }
} }
} }
mm->phase_corrected = 0; /* Set to 1 by the caller if needed. */
} }
// //
// This function gets a decoded Mode S Message and prints it on the screen // This function gets a decoded Mode S Message and prints it on the screen
@ -2082,7 +2005,9 @@ void detectModeS(uint16_t *m, uint32_t mlen) {
mm.timestampMsg = Modes.timestampBlk + (j*6); mm.timestampMsg = Modes.timestampBlk + (j*6);
sigStrength = (sigStrength + 0x7F) >> 8; sigStrength = (sigStrength + 0x7F) >> 8;
mm.signalLevel = ((sigStrength < 255) ? sigStrength : 255); mm.signalLevel = ((sigStrength < 255) ? sigStrength : 255);
decodeModesMessage(&mm,msg); mm.msgbits = msglen;
mm.msgtype = msg[0] >> 3;
decodeModesMessage(&mm, msg);
/* Update statistics. */ /* Update statistics. */
if (mm.crcok || use_correction) { if (mm.crcok || use_correction) {
@ -3070,6 +2995,7 @@ int decodeHexMessage(struct client *c) {
// Non timemarked internet data has indeterminate delay // Non timemarked internet data has indeterminate delay
mm.timestampMsg = -1; mm.timestampMsg = -1;
mm.signalLevel = -1; mm.signalLevel = -1;
mm.msgbits = 0;
// Remove spaces on the left and on the right // Remove spaces on the left and on the right
while(l && isspace(hex[l-1])) { while(l && isspace(hex[l-1])) {