Merge pull request #8 from MalcolmRobb/master

Pulling in from MalcomRobb's Master
This commit is contained in:
Brian Davenport 2013-05-23 04:45:30 -07:00
commit b0e0f96c94

View file

@ -56,7 +56,7 @@
// MinorVer changes when additional features are added, but not for bug fixes (range 00-99) // MinorVer changes when additional features are added, but not for bug fixes (range 00-99)
// DayDate & Year changes for all changes, including for bug fixes. It represent the release date of the update // DayDate & Year changes for all changes, including for bug fixes. It represent the release date of the update
// //
#define MODES_DUMP1090_VERSION "1.06.1405.13" #define MODES_DUMP1090_VERSION "1.06.2205.13"
#define MODES_USER_LATITUDE_DFLT (0.0) #define MODES_USER_LATITUDE_DFLT (0.0)
#define MODES_USER_LONGITUDE_DFLT (0.0) #define MODES_USER_LONGITUDE_DFLT (0.0)
@ -227,6 +227,7 @@ struct {
/* Configuration */ /* Configuration */
char *filename; /* Input form file, --ifile option. */ char *filename; /* Input form file, --ifile option. */
int phase_enhance; /* Enable phase enhancement if true */
int fix_errors; /* Single bit error correction if true. */ int fix_errors; /* Single bit error correction if true. */
int check_crc; /* Only display messages with good CRC. */ int check_crc; /* Only display messages with good CRC. */
int raw; /* Raw output format. */ int raw; /* Raw output format. */
@ -264,7 +265,10 @@ struct {
/* Statistics */ /* Statistics */
unsigned int stat_valid_preamble; unsigned int stat_valid_preamble;
unsigned int stat_demodulated; unsigned int stat_demodulated0;
unsigned int stat_demodulated1;
unsigned int stat_demodulated2;
unsigned int stat_demodulated3;
unsigned int stat_goodcrc; unsigned int stat_goodcrc;
unsigned int stat_badcrc; unsigned int stat_badcrc;
unsigned int stat_fixed; unsigned int stat_fixed;
@ -273,6 +277,15 @@ struct {
unsigned int stat_http_requests; unsigned int stat_http_requests;
unsigned int stat_sbs_connections; unsigned int stat_sbs_connections;
unsigned int stat_out_of_phase; unsigned int stat_out_of_phase;
unsigned int stat_ph_demodulated0;
unsigned int stat_ph_demodulated1;
unsigned int stat_ph_demodulated2;
unsigned int stat_ph_demodulated3;
unsigned int stat_ph_goodcrc;
unsigned int stat_ph_badcrc;
unsigned int stat_ph_fixed;
unsigned int stat_ph_single_bit_fix;
unsigned int stat_ph_two_bits_fix;
unsigned int stat_DF_Len_Corrected; unsigned int stat_DF_Len_Corrected;
unsigned int stat_DF_Type_Corrected; unsigned int stat_DF_Type_Corrected;
unsigned int stat_ModeAC; unsigned int stat_ModeAC;
@ -296,7 +309,7 @@ struct modesMessage {
int ca; // Responder capabilities int ca; // Responder capabilities
int iid; int iid;
// DF 17 // DF 17, DF 18
int metype; // Extended squitter message type. int metype; // Extended squitter message type.
int mesub; // Extended squitter message subtype. int mesub; // Extended squitter message subtype.
int heading; // Reported by aircraft, or computed from from EW and NS velocity int heading; // Reported by aircraft, or computed from from EW and NS velocity
@ -347,7 +360,8 @@ static uint64_t mstime(void) {
void sigintHandler(int dummy) { void sigintHandler(int dummy) {
MODES_NOTUSED(dummy); MODES_NOTUSED(dummy);
Modes.exit = 1; // Signal to threads that we are done signal(SIGINT, SIG_DFL); // reset signal handler - bit extra safety
Modes.exit = 1; // Signal to threads that we are done
} }
/* =============================== Initialization =========================== */ /* =============================== Initialization =========================== */
@ -549,6 +563,7 @@ void readDataFromFile(void) {
ssize_t nread, toread; ssize_t nread, toread;
unsigned char *p; unsigned char *p;
if (Modes.exit == 1) break;
if (Modes.data_ready) { if (Modes.data_ready) {
pthread_cond_wait(&Modes.data_cond,&Modes.data_mutex); pthread_cond_wait(&Modes.data_cond,&Modes.data_mutex);
continue; continue;
@ -596,7 +611,11 @@ void *readerThreadEntryPoint(void *arg) {
} else { } else {
readDataFromFile(); readDataFromFile();
} }
return NULL; /* Signal to the other thread that new data is ready - dummy really so threads don't mutually lock */
Modes.data_ready = 1;
pthread_cond_signal(&Modes.data_cond);
pthread_mutex_unlock(&Modes.data_mutex);
pthread_exit(NULL);
} }
/* ============================== Debugging ================================= */ /* ============================== Debugging ================================= */
@ -992,7 +1011,6 @@ int detectModeA(uint16_t *m, struct modesMessage *mm)
if ((ModeABits < 3) || (ModeABits & 0xFFFF8808) || (ModeAErrs) ) if ((ModeABits < 3) || (ModeABits & 0xFFFF8808) || (ModeAErrs) )
{return (ModeABits = 0);} {return (ModeABits = 0);}
memset(mm, 0, sizeof(*mm));
fSig = (fSig + 0x7F) >> 8; fSig = (fSig + 0x7F) >> 8;
mm->signalLevel = ((fSig < 255) ? fSig : 255); mm->signalLevel = ((fSig < 255) ? fSig : 255);
@ -1109,16 +1127,20 @@ uint32_t modesChecksum(unsigned char *msg, int bits) {
uint32_t * pCRCTable = &modes_checksum_table[offset]; uint32_t * pCRCTable = &modes_checksum_table[offset];
int j; int j;
// We don't really need to include the checksum itself
bits -= 24;
for(j = 0; j < bits; j++) { for(j = 0; j < bits; j++) {
if ((j & 7) == 0) if ((j & 7) == 0)
{theByte = *msg++; rem = (rem << 8) | theByte;} theByte = *msg++;
// 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 ^ rem) & 0x00FFFFFF); // 24 bit checksum.
rem = (msg[0] << 16) | (msg[1] << 8) | msg[2]; // message checksum
return ((crc ^ rem) & 0x00FFFFFF); // 24 bit checksum syndrome.
} }
// //
// 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.
@ -1258,28 +1280,24 @@ void modesInitErrorInfo() {
uint32_t crc; uint32_t crc;
n = 0; n = 0;
memset(bitErrorTable, 0, sizeof(bitErrorTable)); memset(bitErrorTable, 0, sizeof(bitErrorTable));
/* First, insert infos about all possible single bit errors */ memset(msg, 0, MODES_LONG_MSG_BYTES);
for (i = 0; i < MODES_LONG_MSG_BITS; i++) { // Add all possible single and double bit errors
int bytepos = (i >> 3); // don't include errors in first 5 bits (DF type)
int mask = 1 << (7 - (i & 7)); for (i = 5; i < MODES_LONG_MSG_BITS; i++) {
memset(msg, 0, MODES_LONG_MSG_BYTES); int bytepos0 = (i >> 3);
msg[bytepos] ^= mask; int mask0 = 1 << (7 - (i & 7));
msg[bytepos0] ^= mask0; // create error0
crc = modesChecksum(msg, MODES_LONG_MSG_BITS); crc = modesChecksum(msg, MODES_LONG_MSG_BITS);
bitErrorTable[n].syndrome = crc; bitErrorTable[n].syndrome = crc; // single bit error case
bitErrorTable[n].pos0 = i; bitErrorTable[n].pos0 = i;
bitErrorTable[n].pos1 = -1; bitErrorTable[n].pos1 = -1;
n += 1; n += 1;
}
/* Add also all double bit errors */ if (Modes.aggressive) {
for (i = 0; i < MODES_LONG_MSG_BITS; i++) { for (j = i+1; j < MODES_LONG_MSG_BITS; j++) {
int bytepos0 = (i >> 3);
int mask0 = 1 << (7 - (i & 7));
memset(msg, 0, MODES_LONG_MSG_BYTES);
msg[bytepos0] ^= mask0;
for (j = i+1; j < MODES_LONG_MSG_BITS; j++) {
int bytepos1 = (j >> 3); int bytepos1 = (j >> 3);
int mask1 = 1 << (7 - (j & 7)); int mask1 = 1 << (7 - (j & 7));
msg[bytepos1] ^= mask1; msg[bytepos1] ^= mask1; // create error1
crc = modesChecksum(msg, MODES_LONG_MSG_BITS); crc = modesChecksum(msg, MODES_LONG_MSG_BITS);
if (n >= NERRORINFO) { if (n >= NERRORINFO) {
/* /*
@ -1289,11 +1307,14 @@ void modesInitErrorInfo() {
*/ */
break; break;
} }
bitErrorTable[n].syndrome = crc; bitErrorTable[n].syndrome = crc; // two bit error case
bitErrorTable[n].pos0 = i; bitErrorTable[n].pos0 = i;
bitErrorTable[n].pos1 = j; bitErrorTable[n].pos1 = j;
n += 1; n += 1;
msg[bytepos1] ^= mask1; // revert error1
}
} }
msg[bytepos0] ^= mask0; // revert error0
} }
qsort(bitErrorTable, NERRORINFO, qsort(bitErrorTable, NERRORINFO,
sizeof(struct errorinfo), cmpErrorInfo); sizeof(struct errorinfo), cmpErrorInfo);
@ -1319,10 +1340,10 @@ void modesInitErrorInfo() {
} }
*/ */
} }
//
/* Flip a bit, but make sure that the DF field (first 5 bits) // Flip a bit, but make sure that the DF field (first 5 bits)
* is never changed // is never changed
*/ /*
int flipBit(unsigned char *msg, int nbits, int bit) { int flipBit(unsigned char *msg, int nbits, int bit) {
int bytepos, mask; int bytepos, mask;
if ((bit < 0) || (bit >= nbits)) { if ((bit < 0) || (bit >= nbits)) {
@ -1336,50 +1357,41 @@ int flipBit(unsigned char *msg, int nbits, int bit) {
msg[bytepos] ^= mask; msg[bytepos] ^= mask;
return 1; return 1;
} }
*/
/* Search syndrome in table and, if an entry is found, flip the necessary // Search syndrome in table and, if an entry is found, flip the necessary
* bits. Make sure the indices fit into the array, and for 2-bit errors, // bits. Make sure the indices fit into the array, and for 2-bit errors,
* are different. // are different.
* Return number of fixed bits. // Return number of fixed bits.
*/ //
int fixBitErrors(unsigned char *msg, int bits) { int fixBitErrors(unsigned char *msg, int bits) {
struct errorinfo *pei; struct errorinfo *pei;
struct errorinfo ei; struct errorinfo ei;
int bitpos0, bitpos1, offset, res; int bitpos0, bitpos1, offset, res;
ei.syndrome = modesChecksum(msg, bits); ei.syndrome = modesChecksum(msg, bits);
ei.pos0 = -1; ei.pos0 = -1;
ei.pos1 = -1; ei.pos1 = -1;
pei = bsearch(&ei, bitErrorTable, NERRORINFO, pei = bsearch(&ei, bitErrorTable, NERRORINFO,
sizeof(struct errorinfo), cmpErrorInfo); sizeof(struct errorinfo), cmpErrorInfo);
if (pei == NULL) { if (pei == NULL) {
/* Nothing found */ return 0; // No syndrome found
return 0; }
} res = 0;
offset = MODES_LONG_MSG_BITS-bits; offset = MODES_LONG_MSG_BITS-bits;
bitpos0 = pei->pos0; bitpos0 = pei->pos0 - offset;
bitpos1 = pei->pos1; if ((bitpos0 < 0) || (bitpos0 >= bits)) {
res = 0; return 0;
if (bitpos1 >= 0) { /* two-bit error pattern */ }
bitpos0 -= offset; msg[(bitpos0 >> 3)] ^= (1 << (7 - (bitpos0 & 7)));
bitpos1 -= offset; res++;
if ((bitpos0 < 0) || (bitpos0 >= bits) || if (pei->pos1 >= 0) { /* two-bit error pattern */
(bitpos1 < 0) || (bitpos1 >= bits)) { bitpos1 = pei->pos1 - offset;
return res; if ((bitpos1 < 0) || (bitpos1 >= bits)) {
} return 0;
res +=flipBit(msg, bits, bitpos0);
if (bitpos0 != bitpos1) {
res += flipBit(msg, bits, bitpos1);
return res;
}
return res;
} else {
bitpos0 -= offset;
if ((bitpos0 < 0) || (bitpos0 >= bits)) {
return res;
}
res += flipBit(msg, bits, bitpos0);
return res;
} }
msg[(bitpos1 >> 3)] ^= (1 << (7 - (bitpos1 & 7)));
res++;
}
return res;
} }
/* Code for testing the timing: run all possible 1- and 2-bit error /* Code for testing the timing: run all possible 1- and 2-bit error
@ -1653,6 +1665,18 @@ char *ca_str[8] = {
/* 7 */ "Level 7 ???" /* 7 */ "Level 7 ???"
}; };
// DF 18 Control field table.
char *cf_str[8] = {
/* 0 */ "ADS-B ES/NT device with ICAO 24-bit address",
/* 1 */ "ADS-B ES/NT device with other address",
/* 2 */ "Fine format TIS-B",
/* 3 */ "Coarse format TIS-B",
/* 4 */ "TIS-B managment message",
/* 5 */ "TIS-B relay of ADS-B message with other address",
/* 6 */ "ADS-B rebroadcast using DF-17 message format",
/* 7 */ "Reserved"
};
/* Flight status table. */ /* Flight status table. */
char *fs_str[8] = { char *fs_str[8] = {
/* 0 */ "Normal, Airborne", /* 0 */ "Normal, Airborne",
@ -1706,11 +1730,9 @@ void decodeModesMessage(struct modesMessage *mm, unsigned char *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 = msg[0] >> 3; // Downlink Format mm->msgtype = msg[0] >> 3; // Downlink Format
mm->msgbits = modesMessageLenByType(mm->msgtype); mm->msgbits = modesMessageLenByType(mm->msgtype);
mm->correctedbits = 0; // No errors fixed
mm->phase_corrected = 0;
mm->crc = modesChecksum(msg, mm->msgbits); mm->crc = modesChecksum(msg, mm->msgbits);
if ((mm->crc) && (Modes.fix_errors) && (mm->msgtype == 17)) { if ((mm->crc) && (Modes.fix_errors) && ((mm->msgtype == 17) || (mm->msgtype == 18))) {
// if ((mm->crc) && (Modes.fix_errors) && ((mm->msgtype == 11) || (mm->msgtype == 17))) { // if ((mm->crc) && (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
@ -1723,9 +1745,12 @@ void decodeModesMessage(struct modesMessage *mm, unsigned char *msg) {
// IID against known good IID's. That's a TODO. // IID against known good IID's. That's a TODO.
// //
mm->correctedbits = fixBitErrors(msg, mm->msgbits); mm->correctedbits = fixBitErrors(msg, mm->msgbits);
//if ((mm->errorbit == -1) && (Modes.aggressive)) { // If we correct, validate ICAO addr to help filter birthday paradox solutions.
// mm->errorbit = fixTwoBitsErrors(msg, mm->msgbits); if (mm->correctedbits) {
//} uint32_t addr = (msg[1] << 16) | (msg[2] << 8) | (msg[3]);
if (!ICAOAddressWasRecentlySeen(addr))
mm->correctedbits = 0;
}
} }
// //
// Note that most of the other computation happens *after* we fix the // Note that most of the other computation happens *after* we fix the
@ -1735,6 +1760,7 @@ void decodeModesMessage(struct modesMessage *mm, unsigned char *msg) {
mm->crcok = (mm->crc < 80); mm->crcok = (mm->crc < 80);
mm->iid = mm->crc; mm->iid = mm->crc;
mm->addr = (msg[1] << 16) | (msg[2] << 8) | (msg[3]); mm->addr = (msg[1] << 16) | (msg[2] << 8) | (msg[3]);
mm->ca = (msg[0] & 0x07); // Responder capabilities
if (0 == mm->crc) { if (0 == mm->crc) {
// DF 11 : if crc == 0 try to populate our ICAO addresses whitelist. // DF 11 : if crc == 0 try to populate our ICAO addresses whitelist.
@ -1744,15 +1770,20 @@ void decodeModesMessage(struct modesMessage *mm, unsigned char *msg) {
} else if (mm->msgtype == 17) { // DF 17 } else if (mm->msgtype == 17) { // DF 17
mm->crcok = (mm->crc == 0); mm->crcok = (mm->crc == 0);
mm->addr = (msg[1] << 16) | (msg[2] << 8) | (msg[3]); mm->addr = (msg[1] << 16) | (msg[2] << 8) | (msg[3]);
mm->ca = (msg[0] & 0x07); // Responder capabilities
if (0 != mm->correctedbits) { if (0 == mm->crc) {
// DF 17 : if (error corrected) force crc = 0 but do not try to add this address // DF 17 : if crc == 0 try to populate our ICAO addresses whitelist.
// to the whitelist of recently seen ICAO addresses. addRecentlySeenICAOAddr(mm->addr);
mm->crc = 0; }
} else if (0 == mm->crc) { } else if (mm->msgtype == 18) { // DF 18
// DF 17 : if uncorrected and crc == 0 add this address to the whitelist of mm->crcok = (mm->crc == 0);
// recently seen ICAO addresses. mm->addr = (msg[1] << 16) | (msg[2] << 8) | (msg[3]);
mm->ca = (msg[0] & 0x07); // Control Field
if (0 == mm->crc) {
// DF 18 : if crc == 0 try to populate our ICAO addresses whitelist.
addRecentlySeenICAOAddr(mm->addr); addRecentlySeenICAOAddr(mm->addr);
} }
@ -1773,8 +1804,7 @@ void decodeModesMessage(struct modesMessage *mm, unsigned char *msg) {
} }
// Fields for DF11, DF17 // Fields for DF11, DF17
if (mm->msgtype == 11 || mm->msgtype == 17) { if (mm->msgtype == 11 || mm->msgtype == 17) {
mm->ca = msg[0] & 0x07; // Responder capabilities
if (mm->ca == 4) { if (mm->ca == 4) {
mm->bFlags |= MODES_ACFLAGS_AOG_VALID | MODES_ACFLAGS_AOG; mm->bFlags |= MODES_ACFLAGS_AOG_VALID | MODES_ACFLAGS_AOG;
} else if (mm->ca == 5) { } else if (mm->ca == 5) {
@ -1813,8 +1843,9 @@ void decodeModesMessage(struct modesMessage *mm, unsigned char *msg) {
} }
} }
// Fields for DF17 squitter // Fields for DF17, DF18_CF0, DF18_CF1, DF18_CF6 squitters
if (mm->msgtype == 17) { if ( (mm->msgtype == 17)
|| ((mm->msgtype == 18) && ((mm->ca == 0) || (mm->ca == 1) || (mm->ca == 6)) )) {
int metype = mm->metype = msg[4] >> 3; // Extended squitter message type int metype = mm->metype = msg[4] >> 3; // Extended squitter message type
int mesub = mm->mesub = msg[4] & 7; // Extended squitter message subtype int mesub = mm->mesub = msg[4] & 7; // Extended squitter message subtype
@ -1971,7 +2002,7 @@ void displayModesMessage(struct modesMessage *mm) {
} }
// Show the raw message. // Show the raw message.
if (Modes.mlat) { if (Modes.mlat && mm->timestampMsg) {
printf("@"); printf("@");
pTimeStamp = (unsigned char *) &mm->timestampMsg; pTimeStamp = (unsigned char *) &mm->timestampMsg;
for (j=5; j>=0;j--) { for (j=5; j>=0;j--) {
@ -2107,6 +2138,65 @@ void displayModesMessage(struct modesMessage *mm) {
} else if (mm->msgtype == 18) { // DF 18 } else if (mm->msgtype == 18) { // DF 18
printf("DF 18: Extended Squitter.\n"); printf("DF 18: Extended Squitter.\n");
printf(" Control Field : %d (%s)\n", mm->ca, cf_str[mm->ca]);
if ((mm->ca == 0) || (mm->ca == 1) || (mm->ca == 6)) {
if (mm->ca == 1) {
printf(" Other Address : %06x\n", mm->addr);
} else {
printf(" ICAO Address : %06x\n", mm->addr);
}
printf(" Extended Squitter Type: %d\n", mm->metype);
printf(" Extended Squitter Sub : %d\n", mm->mesub);
printf(" Extended Squitter Name: %s\n", getMEDescription(mm->metype, mm->mesub));
// Decode the extended squitter message
if (mm->metype >= 1 && mm->metype <= 4) { // Aircraft identification
printf(" Aircraft Type : %c%d\n", ('A' + 4 - mm->metype), mm->mesub);
printf(" Identification : %s\n", mm->flight);
//} else if (mm->metype >= 5 && mm->metype <= 8) { // Surface position
} else if (mm->metype >= 9 && mm->metype <= 18) { // Airborne position Baro
printf(" F flag : %s\n", (mm->msg[6] & 0x04) ? "odd" : "even");
printf(" T flag : %s\n", (mm->msg[6] & 0x08) ? "UTC" : "non-UTC");
printf(" Altitude : %d feet\n", mm->altitude);
if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID) {
printf(" Latitude : %f\n", mm->fLat);
printf(" Longitude: %f\n", mm->fLon);
} else {
printf(" Latitude : %d (not decoded)\n", mm->raw_latitude);
printf(" Longitude: %d (not decoded)\n", mm->raw_longitude);
}
} else if (mm->metype == 19) { // Airborne Velocity
if (mm->mesub == 1 || mm->mesub == 2) {
printf(" EW status : %s\n", (mm->bFlags & MODES_ACFLAGS_EWSPEED_VALID) ? "Valid" : "Unavailable");
printf(" EW velocity : %d\n", mm->ew_velocity);
printf(" NS status : %s\n", (mm->bFlags & MODES_ACFLAGS_NSSPEED_VALID) ? "Valid" : "Unavailable");
printf(" NS velocity : %d\n", mm->ns_velocity);
printf(" Vertical status : %s\n", (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) ? "Valid" : "Unavailable");
printf(" Vertical rate src : %d\n", ((mm->msg[8] >> 4) & 1));
printf(" Vertical rate : %d\n", mm->vert_rate);
} else if (mm->mesub == 3 || mm->mesub == 4) {
printf(" Heading status : %s\n", (mm->bFlags & MODES_ACFLAGS_HEADING_VALID) ? "Valid" : "Unavailable");
printf(" Heading : %d\n", mm->heading);
printf(" Airspeed status : %s\n", (mm->bFlags & MODES_ACFLAGS_SPEED_VALID) ? "Valid" : "Unavailable");
printf(" Airspeed : %d\n", mm->velocity);
printf(" Vertical status : %s\n", (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) ? "Valid" : "Unavailable");
printf(" Vertical rate src : %d\n", ((mm->msg[8] >> 4) & 1));
printf(" Vertical rate : %d\n", mm->vert_rate);
} else {
printf(" Unrecognized ME subtype: %d subtype: %d\n", mm->metype, mm->mesub);
}
//} else if (mm->metype >= 20 && mm->metype <= 22) { // Airborne position GNSS
} else {
printf(" Unrecognized ME type: %d subtype: %d\n", mm->metype, mm->mesub);
}
}
} else if (mm->msgtype == 19) { // DF 19 } else if (mm->msgtype == 19) { // DF 19
printf("DF 19: Military Extended Squitter.\n"); printf("DF 19: Military Extended Squitter.\n");
@ -2208,11 +2298,14 @@ void applyPhaseCorrection(uint16_t *pPayload) {
* size 'mlen' bytes. Every detected Mode S message is convert it into a * size 'mlen' bytes. Every detected Mode S message is convert it into a
* stream of bits and passed to the function to display it. */ * stream of bits and passed to the function to display it. */
void detectModeS(uint16_t *m, uint32_t mlen) { void detectModeS(uint16_t *m, uint32_t mlen) {
struct modesMessage mm;
unsigned char msg[MODES_LONG_MSG_BYTES], *pMsg; unsigned char msg[MODES_LONG_MSG_BYTES], *pMsg;
uint16_t aux[MODES_LONG_MSG_SAMPLES]; uint16_t aux[MODES_LONG_MSG_SAMPLES];
uint32_t j; uint32_t j;
int use_correction = 0; int use_correction = 0;
memset(&mm, 0, sizeof(mm));
/* The Mode S preamble is made of impulses of 0.5 microseconds at /* The Mode S preamble is made of impulses of 0.5 microseconds at
* the following time offsets: * the following time offsets:
* *
@ -2238,7 +2331,6 @@ void detectModeS(uint16_t *m, uint32_t mlen) {
*/ */
for (j = 0; j < mlen; j++) { for (j = 0; j < mlen; j++) {
int high, i, errors, errors56, errorsTy; int high, i, errors, errors56, errorsTy;
int good_message = 0;
uint16_t *pPreamble, *pPayload, *pPtr; uint16_t *pPreamble, *pPayload, *pPtr;
uint8_t theByte, theErrs; uint8_t theByte, theErrs;
int msglen, scanlen, sigStrength; int msglen, scanlen, sigStrength;
@ -2246,12 +2338,18 @@ void detectModeS(uint16_t *m, uint32_t mlen) {
pPreamble = &m[j]; pPreamble = &m[j];
pPayload = &m[j+MODES_PREAMBLE_SAMPLES]; pPayload = &m[j+MODES_PREAMBLE_SAMPLES];
// Rather than clear the whole mm structure, just clear the parts which are required. The clear
// is required for every bit of the input stream, and we don't want to be memset-ing the whole
// modesMessage structure two million times per second if we don't have to..
mm.bFlags =
mm.crcok =
mm.correctedbits = 0;
if (!use_correction) // This is not a re-try with phase correction if (!use_correction) // This is not a re-try with phase correction
{ // so try to find a new preamble { // so try to find a new preamble
if (Modes.mode_ac) if (Modes.mode_ac)
{ {
struct modesMessage mm;
int ModeA = detectModeA(pPreamble, &mm); int ModeA = detectModeA(pPreamble, &mm);
if (ModeA) // We have found a valid ModeA/C in the data if (ModeA) // We have found a valid ModeA/C in the data
@ -2449,24 +2547,54 @@ void detectModeS(uint16_t *m, uint32_t mlen) {
if ( (msglen) if ( (msglen)
&& (sigStrength > MODES_MSG_SQUELCH_LEVEL) && (sigStrength > MODES_MSG_SQUELCH_LEVEL)
&& (errors <= MODES_MSG_ENCODER_ERRS) ) { && (errors <= MODES_MSG_ENCODER_ERRS) ) {
struct modesMessage mm;
memset(&mm, 0, sizeof(mm));
// Set initial mm structure details // Set initial mm structure details
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);
mm.phase_corrected = use_correction;
// Decode the received message // Decode the received message
decodeModesMessage(&mm, msg); decodeModesMessage(&mm, msg);
// Update statistics // Update statistics
if (Modes.stats) { if (Modes.stats) {
if (mm.crcok || use_correction) { if (mm.crcok || use_correction || mm.correctedbits) {
if (errors == 0) Modes.stat_demodulated++;
if (use_correction) {
switch (errors) {
case 0: {Modes.stat_ph_demodulated0++; break;}
case 1: {Modes.stat_ph_demodulated1++; break;}
case 2: {Modes.stat_ph_demodulated2++; break;}
default:{Modes.stat_ph_demodulated3++; break;}
}
} else {
switch (errors) {
case 0: {Modes.stat_demodulated0++; break;}
case 1: {Modes.stat_demodulated1++; break;}
case 2: {Modes.stat_demodulated2++; break;}
default:{Modes.stat_demodulated3++; break;}
}
}
if (mm.correctedbits == 0) { if (mm.correctedbits == 0) {
if (mm.crcok) {Modes.stat_goodcrc++;} if (use_correction) {
else {Modes.stat_badcrc++;} if (mm.crcok) {Modes.stat_ph_goodcrc++;}
else {Modes.stat_ph_badcrc++;}
} else {
if (mm.crcok) {Modes.stat_goodcrc++;}
else {Modes.stat_badcrc++;}
}
} else if (use_correction) {
Modes.stat_ph_badcrc++;
Modes.stat_ph_fixed++;
if (mm.correctedbits == 1) {
Modes.stat_ph_single_bit_fix++;
} else if (mm.correctedbits == 2) {
Modes.stat_ph_two_bits_fix++;
}
} else { } else {
Modes.stat_badcrc++; Modes.stat_badcrc++;
Modes.stat_fixed++; Modes.stat_fixed++;
@ -2495,9 +2623,6 @@ void detectModeS(uint16_t *m, uint32_t mlen) {
// Skip this message if we are sure it's fine // Skip this message if we are sure it's fine
if (mm.crcok) { if (mm.crcok) {
j += (MODES_PREAMBLE_US+msglen)*2; j += (MODES_PREAMBLE_US+msglen)*2;
good_message = 1;
if (use_correction)
mm.phase_corrected = 1;
} }
// Pass data to the next layer // Pass data to the next layer
@ -2510,8 +2635,8 @@ void detectModeS(uint16_t *m, uint32_t mlen) {
} }
} }
// Retry with phase correction if possible. // Retry with phase correction if enabled, necessary and possible.
if (!good_message && !use_correction && j && detectOutOfPhase(pPreamble)) { if (Modes.phase_enhance && !mm.crcok && !mm.correctedbits && !use_correction && j && detectOutOfPhase(pPreamble)) {
use_correction = 1; j--; use_correction = 1; j--;
} else { } else {
use_correction = 0; use_correction = 0;
@ -2539,7 +2664,7 @@ void detectModeS(uint16_t *m, uint32_t mlen) {
// processing and visualization // processing and visualization
// //
void useModesMessage(struct modesMessage *mm) { void useModesMessage(struct modesMessage *mm) {
if ((Modes.check_crc == 0) || (mm->crcok)) { if ((Modes.check_crc == 0) || (mm->crcok) || (mm->correctedbits)) { // not checking, ok or fixed
// Track aircrafts if... // Track aircrafts if...
if ( (Modes.interactive) // in interactive mode if ( (Modes.interactive) // in interactive mode
@ -2913,7 +3038,9 @@ int decodeCPRrelative(struct aircraft *a, int fflag, int surface) {
struct aircraft *interactiveReceiveData(struct modesMessage *mm) { struct aircraft *interactiveReceiveData(struct modesMessage *mm) {
struct aircraft *a, *aux; struct aircraft *a, *aux;
if (Modes.check_crc && mm->crcok == 0) return NULL; // Return if (checking crc) AND (not crcok) AND (not fixed)
if (Modes.check_crc && (mm->crcok == 0) && (mm->correctedbits == 0))
return NULL;
// Loookup our aircraft or create a new one // Loookup our aircraft or create a new one
a = interactiveFindAircraft(mm->addr); a = interactiveFindAircraft(mm->addr);
@ -3192,14 +3319,14 @@ void snipMode(int level) {
} }
/* ============================= Networking ================================= /* ============================= Networking =================================
* Note: here we risregard any kind of good coding practice in favor of * Note: here we disregard any kind of good coding practice in favor of
* extreme simplicity, that is: * extreme simplicity, that is:
* *
* 1) We only rely on the kernel buffers for our I/O without any kind of * 1) We only rely on the kernel buffers for our I/O without any kind of
* user space buffering. * user space buffering.
* 2) We don't register any kind of event handler, from time to time a * 2) We don't register any kind of event handler, from time to time a
* function gets called and we accept new connections. All the rest is * function gets called and we accept new connections. All the rest is
* handled via non-blocking I/O and manually pullign clients to see if * handled via non-blocking I/O and manually polling clients to see if
* they have something new to share with us when reading is needed. * they have something new to share with us when reading is needed.
*/ */
@ -3354,14 +3481,14 @@ void modesSendRawOutput(struct modesMessage *mm) {
int j; int j;
unsigned char * pTimeStamp; unsigned char * pTimeStamp;
if (Modes.mlat) { if (Modes.mlat && mm->timestampMsg) {
*p++ = '@'; *p++ = '@';
pTimeStamp = (unsigned char *) &mm->timestampMsg; pTimeStamp = (unsigned char *) &mm->timestampMsg;
for (j = 5; j >= 0; j--) { for (j = 5; j >= 0; j--) {
sprintf(p, "%02X", pTimeStamp[j]); sprintf(p, "%02X", pTimeStamp[j]);
p += 2; p += 2;
} }
Modes.rawOutUsed += 12; // additional 12 characters for timestamp Modes.rawOutUsed += 12; // additional 12 characters for timestamp
} else } else
*p++ = '*'; *p++ = '*';
@ -3426,7 +3553,7 @@ void modesSendSBSOutput(struct modesMessage *mm) {
p += sprintf(p, "MSG,%d,111,11111,%06X,111111,", msgType, mm->addr); p += sprintf(p, "MSG,%d,111,11111,%06X,111111,", msgType, mm->addr);
// Fields 7 & 8 are the current time and date // Fields 7 & 8 are the current time and date
if (mm->timestampMsg != (uint64_t)(-1)) { // Make sure the records' timestamp is valid before outputing it if (mm->timestampMsg) { // Make sure the records' timestamp is valid before outputing it
epocTime = Modes.stSystemTimeBlk; // This is the time of the start of the Block we're processing epocTime = Modes.stSystemTimeBlk; // This is the time of the start of the Block we're processing
offset = (int) (mm->timestampMsg - Modes.timestampBlk); // This is the time (in 12Mhz ticks) into the Block offset = (int) (mm->timestampMsg - Modes.timestampBlk); // This is the time (in 12Mhz ticks) into the Block
offset = offset / 12000; // convert to milliseconds offset = offset / 12000; // convert to milliseconds
@ -3556,7 +3683,7 @@ int decodeHexMessage(struct client *c) {
// Mixing of data from two or more different receivers and publishing // Mixing of data from two or more different receivers and publishing
// as coming from one would lead to corrupt mlat data // as coming from one would lead to corrupt mlat data
// Non timemarked internet data has indeterminate delay // Non timemarked internet data has indeterminate delay
mm.timestampMsg = -1; mm.timestampMsg = 0;
mm.signalLevel = -1; mm.signalLevel = -1;
// Remove spaces on the left and on the right // Remove spaces on the left and on the right
@ -3569,7 +3696,7 @@ int decodeHexMessage(struct client *c) {
// Turn the message into binary. // Turn the message into binary.
// Accept *-AVR raw @-AVR/BEAST timeS+raw %-AVR timeS+raw (CRC good) <-BEAST timeS+sigL+raw // Accept *-AVR raw @-AVR/BEAST timeS+raw %-AVR timeS+raw (CRC good) <-BEAST timeS+sigL+raw
// and some AVR recorer that we can understand // and some AVR records that we can understand
if (hex[l-1] != ';') {return (0);} // not complete - abort if (hex[l-1] != ';') {return (0);} // not complete - abort
switch(hex[0]) { switch(hex[0]) {
@ -3578,11 +3705,9 @@ int decodeHexMessage(struct client *c) {
hex += 15; l -= 16; // Skip <, timestamp and siglevel, and ; hex += 15; l -= 16; // Skip <, timestamp and siglevel, and ;
break;} break;}
case '@': case '@': // No CRC check
case '%': case '%': { // CRC is OK
case '#': hex += 13; l -= 14; // Skip @,%, and timestamp, and ;
case '$': {
hex += 13; l -= 14; // Skip @,%,#,$, and timestamp, and ;
break;} break;}
case '*': case '*':
@ -3905,6 +4030,7 @@ void showHelp(void) {
"--fix Enable single-bits error correction using CRC\n" "--fix Enable single-bits error correction using CRC\n"
"--no-fix Disable single-bits error correction using CRC\n" "--no-fix Disable single-bits error correction using CRC\n"
"--no-crc-check Disable messages with broken CRC (discouraged)\n" "--no-crc-check Disable messages with broken CRC (discouraged)\n"
"--phase-enhance Enable phase enhancement\n"
"--aggressive More CPU for more messages (two bits fixes, ...)\n" "--aggressive More CPU for more messages (two bits fixes, ...)\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"
@ -3983,6 +4109,8 @@ int main(int argc, char **argv) {
Modes.aggressive = 0; Modes.aggressive = 0;
} else if (!strcmp(argv[j],"--no-crc-check")) { } else if (!strcmp(argv[j],"--no-crc-check")) {
Modes.check_crc = 0; Modes.check_crc = 0;
} else if (!strcmp(argv[j],"--phase-enhance")) {
Modes.phase_enhance = 1;
} else if (!strcmp(argv[j],"--raw")) { } else if (!strcmp(argv[j],"--raw")) {
Modes.raw = 1; Modes.raw = 1;
} else if (!strcmp(argv[j],"--net")) { } else if (!strcmp(argv[j],"--net")) {
@ -4126,21 +4254,38 @@ int main(int argc, char **argv) {
// If --stats were given, print statistics // If --stats were given, print statistics
if (Modes.stats) { if (Modes.stats) {
printf("\n\n"); printf("\n\n");
printf("%d ModeA/C detected\n", Modes.stat_ModeAC); printf("%d ModeA/C detected\n", Modes.stat_ModeAC);
printf("%d valid preambles\n", Modes.stat_valid_preamble); printf("%d valid Mode-S preambles\n", Modes.stat_valid_preamble);
printf("%d DF-?? fields corrected for length\n", Modes.stat_DF_Len_Corrected); printf("%d DF-?? fields corrected for length\n", Modes.stat_DF_Len_Corrected);
printf("%d DF-?? fields corrected for type\n", Modes.stat_DF_Type_Corrected); printf("%d DF-?? fields corrected for type\n", Modes.stat_DF_Type_Corrected);
printf("%d demodulated again after phase correction\n", Modes.stat_out_of_phase); printf("%d demodulated with 0 errors\n", Modes.stat_demodulated0);
printf("%d demodulated with zero errors\n", Modes.stat_demodulated); printf("%d demodulated with 1 error\n", Modes.stat_demodulated1);
printf("%d with good crc\n", Modes.stat_goodcrc); printf("%d demodulated with 2 errors\n", Modes.stat_demodulated2);
printf("%d with bad crc\n", Modes.stat_badcrc); printf("%d demodulated with > 2 errors\n", Modes.stat_demodulated3);
printf("%d errors corrected\n", Modes.stat_fixed); printf("%d with good crc\n", Modes.stat_goodcrc);
printf("%d single bit errors\n", Modes.stat_single_bit_fix); printf("%d with bad crc\n", Modes.stat_badcrc);
printf("%d two bits errors\n", Modes.stat_two_bits_fix); printf("%d errors corrected\n", Modes.stat_fixed);
printf("%d total usable messages\n", Modes.stat_goodcrc + Modes.stat_fixed); printf("%d single bit errors\n", Modes.stat_single_bit_fix);
printf("%d two bits errors\n", Modes.stat_two_bits_fix);
printf("%d phase enhancement attempts\n", Modes.stat_out_of_phase);
printf("%d phase enhanced demodulated with 0 errors\n", Modes.stat_ph_demodulated0);
printf("%d phase enhanced demodulated with 1 error\n", Modes.stat_ph_demodulated1);
printf("%d phase enhanced demodulated with 2 errors\n", Modes.stat_ph_demodulated2);
printf("%d phase enhanced demodulated with > 2 errors\n", Modes.stat_ph_demodulated3);
printf("%d phase enhanced with good crc\n", Modes.stat_ph_goodcrc);
printf("%d phase enhanced with bad crc\n", Modes.stat_ph_badcrc);
printf("%d phase enhanced errors corrected\n", Modes.stat_ph_fixed);
printf("%d phase enhanced single bit errors\n", Modes.stat_ph_single_bit_fix);
printf("%d phase enhanced two bits errors\n", Modes.stat_ph_two_bits_fix);
printf("%d total usable messages\n", Modes.stat_goodcrc + Modes.stat_ph_goodcrc + Modes.stat_fixed + Modes.stat_ph_fixed);
} }
rtlsdr_cancel_async(Modes.dev); // Cancel rtlsdr_read_async will cause data input thread to terminate cleanly if (Modes.filename == NULL) {
rtlsdr_close(Modes.dev); rtlsdr_cancel_async(Modes.dev); // Cancel rtlsdr_read_async will cause data input thread to terminate cleanly
exit (0); rtlsdr_close(Modes.dev);
}
pthread_cond_destroy(&Modes.data_cond); // Thread cleanup
pthread_mutex_destroy(&Modes.data_mutex);
pthread_join(Modes.reader_thread,NULL); // Wait on reader thread exit
pthread_exit(0);
} }