diff --git a/Makefile b/Makefile index 8382ff0..51a7756 100644 --- a/Makefile +++ b/Makefile @@ -20,8 +20,8 @@ all: dump1090 %.o: %.c $(CC) $(CFLAGS) $(EXTRACFLAGS) -c $< -dump1090: dump1090.o anet.o - $(CC) -g -o dump1090 dump1090.o anet.o $(LIBS) +dump1090: dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o + $(CC) -g -o dump1090 dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o $(LIBS) clean: rm -f *.o dump1090 diff --git a/dump1090.c b/dump1090.c index 50e5c64..2778f31 100644 --- a/dump1090.c +++ b/dump1090.c @@ -32,16 +32,6 @@ // // ============================= Utility functions ========================== // -static uint64_t mstime(void) { - struct timeval tv; - uint64_t mst; - - gettimeofday(&tv, NULL); - mst = ((uint64_t)tv.tv_sec)*1000; - mst += tv.tv_usec/1000; - return mst; -} - void sigintHandler(int dummy) { MODES_NOTUSED(dummy); signal(SIGINT, SIG_DFL); // reset signal handler - bit extra safety @@ -70,7 +60,9 @@ void modesInitConfig(void) { Modes.fUserLat = MODES_USER_LATITUDE_DFLT; Modes.fUserLon = MODES_USER_LONGITUDE_DFLT; } - +// +//========================================================================= +// void modesInit(void) { int i, q; @@ -169,9 +161,9 @@ void modesInit(void) { // Prepare error correction tables modesInitErrorInfo(); } - -/* =============================== RTLSDR handling ========================== */ - +// +// =============================== RTLSDR handling ========================== +// void modesInitRTLSDR(void) { int j; int device_count; @@ -196,12 +188,12 @@ void modesInitRTLSDR(void) { exit(1); } - /* Set gain, frequency, sample rate, and reset the device. */ + // Set gain, frequency, sample rate, and reset the device rtlsdr_set_tuner_gain_mode(Modes.dev, (Modes.gain == MODES_AUTO_GAIN) ? 0 : 1); if (Modes.gain != MODES_AUTO_GAIN) { if (Modes.gain == MODES_MAX_GAIN) { - /* Find the maximum gain available. */ + // Find the maximum gain available int numgains; int gains[100]; @@ -222,29 +214,37 @@ void modesInitRTLSDR(void) { fprintf(stderr, "Gain reported by device: %.2f\n", rtlsdr_get_tuner_gain(Modes.dev)/10.0); } - -/* We use a thread reading data in background, while the main thread - * handles decoding and visualization of data to the user. - * - * The reading thread calls the RTLSDR API to read data asynchronously, and - * uses a callback to populate the data buffer. - * A Mutex is used to avoid races with the decoding thread. */ +// +//========================================================================= +// +// We use a thread reading data in background, while the main thread +// handles decoding and visualization of data to the user. +// +// The reading thread calls the RTLSDR API to read data asynchronously, and +// uses a callback to populate the data buffer. +// +// A Mutex is used to avoid races with the decoding thread. +// void rtlsdrCallback(unsigned char *buf, uint32_t len, void *ctx) { + MODES_NOTUSED(ctx); pthread_mutex_lock(&Modes.data_mutex); ftime(&Modes.stSystemTimeRTL); if (len > MODES_ASYNC_BUF_SIZE) len = MODES_ASYNC_BUF_SIZE; - /* Read the new data. */ + // Read the new data memcpy(Modes.data, buf, len); Modes.data_ready = 1; - /* Signal to the other thread that new data is ready */ + // Signal to the other thread that new data is ready pthread_cond_signal(&Modes.data_cond); pthread_mutex_unlock(&Modes.data_mutex); } - -/* This is used when --ifile is specified in order to read data from file - * instead of using an RTLSDR device. */ +// +//========================================================================= +// +// This is used when --ifile is specified in order to read data from file +// instead of using an RTLSDR device +// void readDataFromFile(void) { pthread_mutex_lock(&Modes.data_mutex); while(1) { @@ -258,8 +258,8 @@ void readDataFromFile(void) { } if (Modes.interactive) { - /* When --ifile and --interactive are used together, slow down - * playing at the natural rate of the RTLSDR received. */ + // When --ifile and --interactive are used together, slow down + // playing at the natural rate of the RTLSDR received. pthread_mutex_unlock(&Modes.data_mutex); usleep(64000); pthread_mutex_lock(&Modes.data_mutex); @@ -270,25 +270,27 @@ void readDataFromFile(void) { while(toread) { nread = read(Modes.fd, p, toread); if (nread <= 0) { - Modes.exit = 1; /* Signal the other thread to exit. */ + Modes.exit = 1; // Signal the other threads to exit. break; } p += nread; toread -= nread; } if (toread) { - /* Not enough data on file to fill the buffer? Pad with - * no signal. */ + // Not enough data on file to fill the buffer? Pad with no signal. memset(p,127,toread); } Modes.data_ready = 1; - /* Signal to the other thread that new data is ready */ + // Signal to the other thread that new data is ready pthread_cond_signal(&Modes.data_cond); } } - -/* We read data using a thread, so the main thread only handles decoding - * without caring about data acquisition. */ +// +//========================================================================= +// +// We read data using a thread, so the main thread only handles decoding +// without caring about data acquisition +// void *readerThreadEntryPoint(void *arg) { MODES_NOTUSED(arg); @@ -299,7 +301,7 @@ void *readerThreadEntryPoint(void *arg) { } else { readDataFromFile(); } - /* Signal to the other thread that new data is ready - dummy really so threads don't mutually lock */ + // 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); @@ -309,2730 +311,12 @@ void *readerThreadEntryPoint(void *arg) { return NULL; #endif } - -/* ============================== Debugging ================================= */ - -/* Helper function for dumpMagnitudeVector(). - * It prints a single bar used to display raw signals. - * - * Since every magnitude sample is between 0-255, the function uses - * up to 63 characters for every bar. Every character represents - * a length of 4, 3, 2, 1, specifically: - * - * "O" is 4 - * "o" is 3 - * "-" is 2 - * "." is 1 - */ -void dumpMagnitudeBar(int index, int magnitude) { - char *set = " .-o"; - char buf[256]; - int div = magnitude / 256 / 4; - int rem = magnitude / 256 % 4; - - memset(buf,'O',div); - buf[div] = set[rem]; - buf[div+1] = '\0'; - - if (index >= 0) - printf("[%.3d] |%-66s %d\n", index, buf, magnitude); - else - printf("[%.2d] |%-66s %d\n", index, buf, magnitude); -} - -/* Display an ASCII-art alike graphical representation of the undecoded - * message as a magnitude signal. - * - * The message starts at the specified offset in the "m" buffer. - * The function will display enough data to cover a short 56 bit message. - * - * If possible a few samples before the start of the messsage are included - * for context. */ - -void dumpMagnitudeVector(uint16_t *m, uint32_t offset) { - uint32_t padding = 5; /* Show a few samples before the actual start. */ - uint32_t start = (offset < padding) ? 0 : offset-padding; - uint32_t end = offset + (MODES_PREAMBLE_SAMPLES)+(MODES_SHORT_MSG_SAMPLES) - 1; - uint32_t j; - - for (j = start; j <= end; j++) { - dumpMagnitudeBar(j-offset, m[j]); - } -} - -/* Produce a raw representation of the message as a Javascript file - * loadable by debug.html. */ -void dumpRawMessageJS(char *descr, unsigned char *msg, - uint16_t *m, uint32_t offset, int fixable, char *bitpos) -{ - int padding = 5; /* Show a few samples before the actual start. */ - int start = offset - padding; - int end = offset + (MODES_PREAMBLE_SAMPLES)+(MODES_LONG_MSG_SAMPLES) - 1; - FILE *fp; - int j; - - MODES_NOTUSED(fixable); - if ((fp = fopen("frames.js","a")) == NULL) { - fprintf(stderr, "Error opening frames.js: %s\n", strerror(errno)); - exit(1); - } - - fprintf(fp,"frames.push({\"descr\": \"%s\", \"mag\": [", descr); - for (j = start; j <= end; j++) { - fprintf(fp,"%d", j < 0 ? 0 : m[j]); - if (j != end) fprintf(fp,","); - } - fprintf(fp,"], \"fix1\": %d, \"fix2\": %d, \"bits\": %d, \"hex\": \"", - bitpos[0], bitpos[1] , modesMessageLenByType(msg[0]>>3)); - for (j = 0; j < MODES_LONG_MSG_BYTES; j++) - fprintf(fp,"\\x%02x",msg[j]); - fprintf(fp,"\"});\n"); - fclose(fp); -} - -/* This is a wrapper for dumpMagnitudeVector() that also show the message - * in hex format with an additional description. - * - * descr is the additional message to show to describe the dump. - * msg points to the decoded message - * m is the original magnitude vector - * offset is the offset where the message starts - * - * The function also produces the Javascript file used by debug.html to - * display packets in a graphical format if the Javascript output was - * enabled. - */ -void dumpRawMessage(char *descr, unsigned char *msg, - uint16_t *m, uint32_t offset) -{ - int j; - int msgtype = msg[0] >> 3; - int fixable = 0; - char bitpos[MODES_MAX_BITERRORS]; - - for (j = 0; j < MODES_MAX_BITERRORS; j++) { - bitpos[j] = -1; - } - if (msgtype == 17) { - fixable = fixBitErrors(msg, MODES_LONG_MSG_BITS, MODES_MAX_BITERRORS, bitpos); - } - - if (Modes.debug & MODES_DEBUG_JS) { - dumpRawMessageJS(descr, msg, m, offset, fixable, bitpos); - return; - } - - printf("\n--- %s\n ", descr); - for (j = 0; j < MODES_LONG_MSG_BYTES; j++) { - printf("%02x",msg[j]); - if (j == MODES_SHORT_MSG_BYTES-1) printf(" ... "); - } - printf(" (DF %d, Fixable: %d)\n", msgtype, fixable); - dumpMagnitudeVector(m,offset); - printf("---\n\n"); -} // -// ===================== Mode A/C detection and decoding =================== +// ============================== Snip mode ================================= // +// Get raw IQ samples and filter everything is < than the specified level +// for more than 256 samples in order to reduce example file size // -// This table is used to build the Mode A/C variable called ModeABits.Each -// bit period is inspected, and if it's value exceeds the threshold limit, -// then the value in this table is or-ed into ModeABits. -// -// At the end of message processing, ModeABits will be the decoded ModeA value. -// -// We can also flag noise in bits that should be zeros - the xx bits. Noise in -// these bits cause bits (31-16) in ModeABits to be set. Then at the end of message -// processing we can test for errors by looking at these bits. -// -uint32_t ModeABitTable[24] = { -0x00000000, // F1 = 1 -0x00000010, // C1 -0x00001000, // A1 -0x00000020, // C2 -0x00002000, // A2 -0x00000040, // C4 -0x00004000, // A4 -0x40000000, // xx = 0 Set bit 30 if we see this high -0x00000100, // B1 -0x00000001, // D1 -0x00000200, // B2 -0x00000002, // D2 -0x00000400, // B4 -0x00000004, // D4 -0x00000000, // F2 = 1 -0x08000000, // xx = 0 Set bit 27 if we see this high -0x04000000, // xx = 0 Set bit 26 if we see this high -0x00000080, // SPI -0x02000000, // xx = 0 Set bit 25 if we see this high -0x01000000, // xx = 0 Set bit 24 if we see this high -0x00800000, // xx = 0 Set bit 23 if we see this high -0x00400000, // xx = 0 Set bit 22 if we see this high -0x00200000, // xx = 0 Set bit 21 if we see this high -0x00100000, // xx = 0 Set bit 20 if we see this high -}; -// -// This table is used to produce an error variable called ModeAErrs.Each -// inter-bit period is inspected, and if it's value falls outside of the -// expected range, then the value in this table is or-ed into ModeAErrs. -// -// At the end of message processing, ModeAErrs will indicate if we saw -// any inter-bit anomolies, and the bits that are set will show which -// bits had them. -// -uint32_t ModeAMidTable[24] = { -0x80000000, // F1 = 1 Set bit 31 if we see F1_C1 error -0x00000010, // C1 Set bit 4 if we see C1_A1 error -0x00001000, // A1 Set bit 12 if we see A1_C2 error -0x00000020, // C2 Set bit 5 if we see C2_A2 error -0x00002000, // A2 Set bit 13 if we see A2_C4 error -0x00000040, // C4 Set bit 6 if we see C3_A4 error -0x00004000, // A4 Set bit 14 if we see A4_xx error -0x40000000, // xx = 0 Set bit 30 if we see xx_B1 error -0x00000100, // B1 Set bit 8 if we see B1_D1 error -0x00000001, // D1 Set bit 0 if we see D1_B2 error -0x00000200, // B2 Set bit 9 if we see B2_D2 error -0x00000002, // D2 Set bit 1 if we see D2_B4 error -0x00000400, // B4 Set bit 10 if we see B4_D4 error -0x00000004, // D4 Set bit 2 if we see D4_F2 error -0x20000000, // F2 = 1 Set bit 29 if we see F2_xx error -0x08000000, // xx = 0 Set bit 27 if we see xx_xx error -0x04000000, // xx = 0 Set bit 26 if we see xx_SPI error -0x00000080, // SPI Set bit 15 if we see SPI_xx error -0x02000000, // xx = 0 Set bit 25 if we see xx_xx error -0x01000000, // xx = 0 Set bit 24 if we see xx_xx error -0x00800000, // xx = 0 Set bit 23 if we see xx_xx error -0x00400000, // xx = 0 Set bit 22 if we see xx_xx error -0x00200000, // xx = 0 Set bit 21 if we see xx_xx error -0x00100000, // xx = 0 Set bit 20 if we see xx_xx error -}; -// -// The "off air" format is,, -// _F1_C1_A1_C2_A2_C4_A4_xx_B1_D1_B2_D2_B4_D4_F2_xx_xx_SPI_ -// -// Bit spacing is 1.45uS, with 0.45uS high, and 1.00us low. This is a problem -// because we ase sampling at 2Mhz (500nS) so we are below Nyquist. -// -// The bit spacings are.. -// F1 : 0.00, -// 1.45, 2.90, 4.35, 5.80, 7.25, 8.70, -// X : 10.15, -// : 11.60, 13.05, 14.50, 15.95, 17.40, 18.85, -// F2 : 20.30, -// X : 21.75, 23.20, 24.65 -// -// This equates to the following sample point centers at 2Mhz. -// [ 0.0], -// [ 2.9], [ 5.8], [ 8.7], [11.6], [14.5], [17.4], -// [20.3], -// [23.2], [26.1], [29.0], [31.9], [34.8], [37.7] -// [40.6] -// [43.5], [46.4], [49.3] -// -// We know that this is a supposed to be a binary stream, so the signal -// should either be a 1 or a 0. Therefore, any energy above the noise level -// in two adjacent samples must be from the same pulse, so we can simply -// add the values together.. -// -int detectModeA(uint16_t *m, struct modesMessage *mm) - { - int j, lastBitWasOne; - int ModeABits = 0; - int ModeAErrs = 0; - int byte, bit; - int thisSample, lastBit, lastSpace = 0; - int m0, m1, m2, m3, mPhase; - int n0, n1, n2 ,n3; - int F1_sig, F1_noise; - int F2_sig, F2_noise; - int fSig, fNoise, fLevel, fLoLo; - - // m[0] contains the energy from 0 -> 499 nS - // m[1] contains the energy from 500 -> 999 nS - // m[2] contains the energy from 1000 -> 1499 nS - // m[3] contains the energy from 1500 -> 1999 nS - // - // We are looking for a Frame bit (F1) whose width is 450nS, followed by - // 1000nS of quiet. - // - // The width of the frame bit is 450nS, which is 90% of our sample rate. - // Therefore, in an ideal world, all the energy for the frame bit will be - // in a single sample, preceeded by (at least) one zero, and followed by - // two zeros, Best case we can look for ... - // - // 0 - 1 - 0 - 0 - // - // However, our samples are not phase aligned, so some of the energy from - // each bit could be spread over two consecutive samples. Worst case is - // that we sample half in one bit, and half in the next. In that case, - // we're looking for - // - // 0 - 0.5 - 0.5 - 0. - - m0 = m[0]; m1 = m[1]; - - if (m0 >= m1) // m1 *must* be bigger than m0 for this to be F1 - {return (0);} - - m2 = m[2]; m3 = m[3]; - - // - // if (m2 <= m0), then assume the sample bob on (Phase == 0), so don't look at m3 - if ((m2 <= m0) || (m2 < m3)) - {m3 = m2; m2 = m0;} - - if ( (m3 >= m1) // m1 must be bigger than m3 - || (m0 > m2) // m2 can be equal to m0 if ( 0,1,0,0 ) - || (m3 > m2) ) // m2 can be equal to m3 if ( 0,1,0,0 ) - {return (0);} - - // m0 = noise - // m1 = noise + (signal * X)) - // m2 = noise + (signal * (1-X)) - // m3 = noise - // - // Hence, assuming all 4 samples have similar amounts of noise in them - // signal = (m1 + m2) - ((m0 + m3) * 2) - // noise = (m0 + m3) / 2 - // - F1_sig = (m1 + m2) - ((m0 + m3) << 1); - F1_noise = (m0 + m3) >> 1; - - if ( (F1_sig < MODEAC_MSG_SQUELCH_LEVEL) // minimum required F1 signal amplitude - || (F1_sig < (F1_noise << 2)) ) // minimum allowable Sig/Noise ratio 4:1 - {return (0);} - - // If we get here then we have a potential F1, so look for an equally valid F2 20.3uS later - // - // Our F1 is centered somewhere between samples m[1] and m[2]. We can guestimate where F2 is - // by comparing the ratio of m1 and m2, and adding on 20.3 uS (40.6 samples) - // - mPhase = ((m2 * 20) / (m1 + m2)); - byte = (mPhase + 812) / 20; - n0 = m[byte++]; n1 = m[byte++]; - - if (n0 >= n1) // n1 *must* be bigger than n0 for this to be F2 - {return (0);} - - n2 = m[byte++]; - // - // if the sample bob on (Phase == 0), don't look at n3 - // - if ((mPhase + 812) % 20) - {n3 = m[byte++];} - else - {n3 = n2; n2 = n0;} - - if ( (n3 >= n1) // n1 must be bigger than n3 - || (n0 > n2) // n2 can be equal to n0 ( 0,1,0,0 ) - || (n3 > n2) ) // n2 can be equal to n3 ( 0,1,0,0 ) - {return (0);} - - F2_sig = (n1 + n2) - ((n0 + n3) << 1); - F2_noise = (n0 + n3) >> 1; - - if ( (F2_sig < MODEAC_MSG_SQUELCH_LEVEL) // minimum required F2 signal amplitude - || (F2_sig < (F2_noise << 2)) ) // maximum allowable Sig/Noise ratio 4:1 - {return (0);} - - fSig = (F1_sig + F2_sig) >> 1; - fNoise = (F1_noise + F2_noise) >> 1; - fLoLo = fNoise + (fSig >> 2); // 1/2 - fLevel = fNoise + (fSig >> 1); - lastBitWasOne = 1; - lastBit = F1_sig; - // - // Now step by a half ModeA bit, 0.725nS, which is 1.45 samples, which is 29/20 - // No need to do bit 0 because we've already selected it as a valid F1 - // Do several bits past the SPI to increase error rejection - // - for (j = 1, mPhase += 29; j < 48; mPhase += 29, j ++) - { - byte = 1 + (mPhase / 20); - - thisSample = m[byte] - fNoise; - if (mPhase % 20) // If the bit is split over two samples... - {thisSample += (m[byte+1] - fNoise);} // add in the second sample's energy - - // If we're calculating a space value - if (j & 1) - {lastSpace = thisSample;} - - else - {// We're calculating a new bit value - bit = j >> 1; - if (thisSample >= fLevel) - {// We're calculating a new bit value, and its a one - ModeABits |= ModeABitTable[bit--]; // or in the correct bit - - if (lastBitWasOne) - { // This bit is one, last bit was one, so check the last space is somewhere less than one - if ( (lastSpace >= (thisSample>>1)) || (lastSpace >= lastBit) ) - {ModeAErrs |= ModeAMidTable[bit];} - } - - else - {// This bit,is one, last bit was zero, so check the last space is somewhere less than one - if (lastSpace >= (thisSample >> 1)) - {ModeAErrs |= ModeAMidTable[bit];} - } - - lastBitWasOne = 1; - } - - - else - {// We're calculating a new bit value, and its a zero - if (lastBitWasOne) - { // This bit is zero, last bit was one, so check the last space is somewhere in between - if (lastSpace >= lastBit) - {ModeAErrs |= ModeAMidTable[bit];} - } - - else - {// This bit,is zero, last bit was zero, so check the last space is zero too - if (lastSpace >= fLoLo) - {ModeAErrs |= ModeAMidTable[bit];} - } - - lastBitWasOne = 0; - } - - lastBit = (thisSample >> 1); - } - } - - // - // Output format is : 00:A4:A2:A1:00:B4:B2:B1:00:C4:C2:C1:00:D4:D2:D1 - // - if ((ModeABits < 3) || (ModeABits & 0xFFFF8808) || (ModeAErrs) ) - {return (ModeABits = 0);} - - fSig = (fSig + 0x7F) >> 8; - mm->signalLevel = ((fSig < 255) ? fSig : 255); - - return ModeABits; - } - -// Input format is : 00:A4:A2:A1:00:B4:B2:B1:00:C4:C2:C1:00:D4:D2:D1 -int ModeAToModeC(unsigned int ModeA) - { - unsigned int FiveHundreds = 0; - unsigned int OneHundreds = 0; - - if ( (ModeA & 0xFFFF888B) // D1 set is illegal. D2 set is > 62700ft which is unlikely - || ((ModeA & 0x000000F0) == 0) ) // C1,,C4 cannot be Zero - {return -9999;} - - if (ModeA & 0x0010) {OneHundreds ^= 0x007;} // C1 - if (ModeA & 0x0020) {OneHundreds ^= 0x003;} // C2 - if (ModeA & 0x0040) {OneHundreds ^= 0x001;} // C4 - - // Remove 7s from OneHundreds (Make 7->5, snd 5->7). - if ((OneHundreds & 5) == 5) {OneHundreds ^= 2;} - - // Check for invalid codes, only 1 to 5 are valid - if (OneHundreds > 5) - {return -9999;} - -//if (ModeA & 0x0001) {FiveHundreds ^= 0x1FF;} // D1 never used for altitude - if (ModeA & 0x0002) {FiveHundreds ^= 0x0FF;} // D2 - if (ModeA & 0x0004) {FiveHundreds ^= 0x07F;} // D4 - - if (ModeA & 0x1000) {FiveHundreds ^= 0x03F;} // A1 - if (ModeA & 0x2000) {FiveHundreds ^= 0x01F;} // A2 - if (ModeA & 0x4000) {FiveHundreds ^= 0x00F;} // A4 - - if (ModeA & 0x0100) {FiveHundreds ^= 0x007;} // B1 - if (ModeA & 0x0200) {FiveHundreds ^= 0x003;} // B2 - if (ModeA & 0x0400) {FiveHundreds ^= 0x001;} // B4 - - // Correct order of OneHundreds. - if (FiveHundreds & 1) {OneHundreds = 6 - OneHundreds;} - - return ((FiveHundreds * 5) + OneHundreds - 13); - } - -void decodeModeAMessage(struct modesMessage *mm, int ModeA) - { - mm->msgtype = 32; // Valid Mode S DF's are DF-00 to DF-31. - // so use 32 to indicate Mode A/C - - mm->msgbits = 16; // Fudge up a Mode S style data stream - mm->msg[0] = (ModeA >> 8); - mm->msg[1] = (ModeA); - - // Fudge an ICAO address based on Mode A (remove the Ident bit) - // Use an upper address byte of FF, since this is ICAO unallocated - mm->addr = 0x00FF0000 | (ModeA & 0x0000FF7F); - - // Set the Identity field to ModeA - mm->modeA = ModeA & 0x7777; - mm->bFlags |= MODES_ACFLAGS_SQUAWK_VALID; - - // Flag ident in flight status - mm->fs = ModeA & 0x0080; - - // Not much else we can tell from a Mode A/C reply. - // Just fudge up a few bits to keep other code happy - mm->crcok = 1; - mm->correctedbits = 0; - } - -/* ===================== Mode S detection and decoding ===================== */ - -/* Parity table for MODE S Messages. - * The table contains 112 elements, every element corresponds to a bit set - * in the message, starting from the first bit of actual data after the - * preamble. - * - * For messages of 112 bit, the whole table is used. - * For messages of 56 bits only the last 56 elements are used. - * - * The algorithm is as simple as xoring all the elements in this table - * for which the corresponding bit on the message is set to 1. - * - * The latest 24 elements in this table are set to 0 as the checksum at the - * end of the message should not affect the computation. - * - * Note: this function can be used with DF11 and DF17, other modes have - * the CRC xored with the sender address as they are reply to interrogations, - * but a casual listener can't split the address from the checksum. - */ -uint32_t modes_checksum_table[112] = { -0x3935ea, 0x1c9af5, 0xf1b77e, 0x78dbbf, 0xc397db, 0x9e31e9, 0xb0e2f0, 0x587178, -0x2c38bc, 0x161c5e, 0x0b0e2f, 0xfa7d13, 0x82c48d, 0xbe9842, 0x5f4c21, 0xd05c14, -0x682e0a, 0x341705, 0xe5f186, 0x72f8c3, 0xc68665, 0x9cb936, 0x4e5c9b, 0xd8d449, -0x939020, 0x49c810, 0x24e408, 0x127204, 0x093902, 0x049c81, 0xfdb444, 0x7eda22, -0x3f6d11, 0xe04c8c, 0x702646, 0x381323, 0xe3f395, 0x8e03ce, 0x4701e7, 0xdc7af7, -0x91c77f, 0xb719bb, 0xa476d9, 0xadc168, 0x56e0b4, 0x2b705a, 0x15b82d, 0xf52612, -0x7a9309, 0xc2b380, 0x6159c0, 0x30ace0, 0x185670, 0x0c2b38, 0x06159c, 0x030ace, -0x018567, 0xff38b7, 0x80665f, 0xbfc92b, 0xa01e91, 0xaff54c, 0x57faa6, 0x2bfd53, -0xea04ad, 0x8af852, 0x457c29, 0xdd4410, 0x6ea208, 0x375104, 0x1ba882, 0x0dd441, -0xf91024, 0x7c8812, 0x3e4409, 0xe0d800, 0x706c00, 0x383600, 0x1c1b00, 0x0e0d80, -0x0706c0, 0x038360, 0x01c1b0, 0x00e0d8, 0x00706c, 0x003836, 0x001c1b, 0xfff409, -0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, -0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, -0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000 -}; - -uint32_t modesChecksum(unsigned char *msg, int bits) { - uint32_t crc = 0; - uint32_t rem = 0; - int offset = (bits == 112) ? 0 : (112-56); - uint8_t theByte = *msg; - uint32_t * pCRCTable = &modes_checksum_table[offset]; - int j; - - // We don't really need to include the checksum itself - bits -= 24; - for(j = 0; j < bits; j++) { - if ((j & 7) == 0) - theByte = *msg++; - - // If bit is set, xor with corresponding table entry. - if (theByte & 0x80) {crc ^= *pCRCTable;} - pCRCTable++; - theByte = theByte << 1; - } - - 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. -// -// All known DF's 16 or greater are long. All known DF's 15 or less are short. -// There are lots of unused codes in both category, so we can assume ICAO will stick to -// these rules, meaning that the most significant bit of the DF indicates the length. -// -int modesMessageLenByType(int type) { - return (type & 0x10) ? MODES_LONG_MSG_BITS : MODES_SHORT_MSG_BITS ; -} -// -// Try to fix single bit errors using the checksum. On success modifies -// the original buffer with the fixed version, and returns the position -// of the error bit. Otherwise if fixing failed -1 is returned. -// -int fixSingleBitErrors(unsigned char *msg, int bits) { - int j; - unsigned char aux[MODES_LONG_MSG_BYTES]; - - memcpy(aux, msg, bits/8); - - // 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 bitmask = 1 << (7 - (j & 7)); - - aux[byte] ^= bitmask; // Flip j-th bit - - if (0 == modesChecksum(aux, bits)) { - // The error is fixed. Overwrite the original buffer with the - // corrected sequence, and returns the error bit position - msg[byte] = aux[byte]; - return (j); - } - - aux[byte] ^= bitmask; // Flip j-th bit back again - } - return (-1); -} -// -// Similar to fixSingleBitErrors() but try every possible two bit combination. -// This is very slow and should be tried only against DF17 messages that -// don't pass the checksum, and only in Aggressive Mode. -/* -int fixTwoBitsErrors(unsigned char *msg, int bits) { - int j, i; - unsigned char aux[MODES_LONG_MSG_BYTES]; - - memcpy(aux, msg, bits/8); - - // 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 bitmask1 = 1 << (7 - (j & 7)); - aux[byte1] ^= bitmask1; // Flip j-th bit - - // Don't check the same pairs multiple times, so i starts from j+1 - for (i = j+1; i < bits; i++) { - int byte2 = i/8; - int bitmask2 = 1 << (7 - (i & 7)); - - aux[byte2] ^= bitmask2; // Flip i-th bit - - if (0 == modesChecksum(aux, bits)) { - // The error is fixed. Overwrite the original buffer with - // the corrected sequence, and returns the error bit position - msg[byte1] = aux[byte1]; - msg[byte2] = aux[byte2]; - - // 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[byte1] ^= bitmask1; // Flip j-th bit back - } - } - return (-1); -} -*/ -/* Code for introducing a less CPU-intensive method of correcting - * single bit errors. - * - * Makes use of the fact that the crc checksum is linear with respect to - * the bitwise xor operation, i.e. - * crc(m^e) = (crc(m)^crc(e) - * where m and e are the message resp. error bit vectors. - * - * Call crc(e) the syndrome. - * - * The code below works by precomputing a table of (crc(e), e) for all - * possible error vectors e (here only single bit and double bit errors), - * search for the syndrome in the table, and correct the then known error. - * The error vector e is represented by one or two bit positions that are - * changed. If a second bit position is not used, it is -1. - * - * Run-time is binary search in a sorted table, plus some constant overhead, - * instead of running through all possible bit positions (resp. pairs of - * bit positions). - * - * - * - */ -struct errorinfo { - uint32_t syndrome; // CRC syndrome - int bits; // Number of bit positions to fix - int pos[MODES_MAX_BITERRORS]; // Bit positions corrected by this syndrome -}; - -#define NERRORINFO \ - (MODES_LONG_MSG_BITS+MODES_LONG_MSG_BITS*(MODES_LONG_MSG_BITS-1)/2) -struct errorinfo bitErrorTable[NERRORINFO]; - -/* Compare function as needed for stdlib's qsort and bsearch functions */ -int cmpErrorInfo(const void *p0, const void *p1) { - struct errorinfo *e0 = (struct errorinfo*)p0; - struct errorinfo *e1 = (struct errorinfo*)p1; - if (e0->syndrome == e1->syndrome) { - return 0; - } else if (e0->syndrome < e1->syndrome) { - return -1; - } else { - return 1; - } -} - -/* Compute the table of all syndromes for 1-bit and 2-bit error vectors */ -void modesInitErrorInfo() { - unsigned char msg[MODES_LONG_MSG_BYTES]; - int i, j, n; - uint32_t crc; - n = 0; - memset(bitErrorTable, 0, sizeof(bitErrorTable)); - memset(msg, 0, MODES_LONG_MSG_BYTES); - // Add all possible single and double bit errors - // don't include errors in first 5 bits (DF type) - for (i = 5; i < MODES_LONG_MSG_BITS; i++) { - int bytepos0 = (i >> 3); - int mask0 = 1 << (7 - (i & 7)); - msg[bytepos0] ^= mask0; // create error0 - crc = modesChecksum(msg, MODES_LONG_MSG_BITS); - bitErrorTable[n].syndrome = crc; // single bit error case - bitErrorTable[n].bits = 1; - bitErrorTable[n].pos[0] = i; - bitErrorTable[n].pos[1] = -1; - n += 1; - - if (Modes.nfix_crc > 1) { - for (j = i+1; j < MODES_LONG_MSG_BITS; j++) { - int bytepos1 = (j >> 3); - int mask1 = 1 << (7 - (j & 7)); - msg[bytepos1] ^= mask1; // create error1 - crc = modesChecksum(msg, MODES_LONG_MSG_BITS); - if (n >= NERRORINFO) { - //fprintf(stderr, "Internal error, too many entries, fix NERRORINFO\n"); - break; - } - bitErrorTable[n].syndrome = crc; // two bit error case - bitErrorTable[n].bits = 2; - bitErrorTable[n].pos[0] = i; - bitErrorTable[n].pos[1] = j; - n += 1; - msg[bytepos1] ^= mask1; // revert error1 - } - } - msg[bytepos0] ^= mask0; // revert error0 - } - qsort(bitErrorTable, NERRORINFO, sizeof(struct errorinfo), cmpErrorInfo); - - // Test code: report if any syndrome appears at least twice. In this - // case the correction cannot be done without ambiguity. - // Tried it, does not happen for 1- and 2-bit errors. - /* - for (i = 1; i < NERRORINFO; i++) { - if (bitErrorTable[i-1].syndrome == bitErrorTable[i].syndrome) { - fprintf(stderr, "modesInitErrorInfo: Collision for syndrome %06x\n", - (int)bitErrorTable[i].syndrome); - } - } - - for (i = 0; i < NERRORINFO; i++) { - printf("syndrome %06x bit0 %3d bit1 %3d\n", - bitErrorTable[i].syndrome, - bitErrorTable[i].pos0, bitErrorTable[i].pos1); - } - */ -} -// -// Search for syndrome in table and if an entry is found, flip the necessary -// bits. Make sure the indices fit into the array -// Additional parameter: fix only less than maxcorrected bits, and record -// fixed bit positions in corrected[]. This array can be NULL, otherwise -// must be of length at least maxcorrected. -// Return number of fixed bits. -// -int fixBitErrors(unsigned char *msg, int bits, int maxfix, char *fixedbits) { - struct errorinfo *pei; - struct errorinfo ei; - int bitpos, offset, res, i; - memset(&ei, 0, sizeof(struct errorinfo)); - ei.syndrome = modesChecksum(msg, bits); - pei = bsearch(&ei, bitErrorTable, NERRORINFO, - sizeof(struct errorinfo), cmpErrorInfo); - if (pei == NULL) { - return 0; // No syndrome found - } - - // Check if the syndrome fixes more bits than we allow - if (maxfix < pei->bits) { - return 0; - } - - // Check that all bit positions lie inside the message length - offset = MODES_LONG_MSG_BITS-bits; - for (i = 0; i < pei->bits; i++) { - bitpos = pei->pos[i] - offset; - if ((bitpos < 0) || (bitpos >= bits)) { - return 0; - } - } - - // Fix the bits - for (i = res = 0; i < pei->bits; i++) { - bitpos = pei->pos[i] - offset; - msg[bitpos >> 3] ^= (1 << (7 - (bitpos & 7))); - if (fixedbits) { - fixedbits[res++] = bitpos; - } - } - return res; -} - -/* Code for testing the timing: run all possible 1- and 2-bit error - * the test message by all 1-bit errors. Run the old code against - * all of them, and new the code. - * - * Example measurements: - * Timing old vs. new crc correction code: - * Old code: 1-bit errors on 112 msgs: 3934 usecs - * New code: 1-bit errors on 112 msgs: 104 usecs - * Old code: 2-bit errors on 6216 msgs: 407743 usecs - * New code: 2-bit errors on 6216 msgs: 5176 usecs - * indicating a 37-fold resp. 78-fold improvement in speed for 1-bit resp. - * 2-bit error. - */ -unsigned char tmsg0[MODES_LONG_MSG_BYTES] = { - /* Test data: first ADS-B message from testfiles/modes1.bin */ - 0x8f, 0x4d, 0x20, 0x23, 0x58, 0x7f, 0x34, 0x5e, - 0x35, 0x83, 0x7e, 0x22, 0x18, 0xb2 -}; -#define NTWOBITS (MODES_LONG_MSG_BITS*(MODES_LONG_MSG_BITS-1)/2) -unsigned char tmsg1[MODES_LONG_MSG_BITS][MODES_LONG_MSG_BYTES]; -unsigned char tmsg2[NTWOBITS][MODES_LONG_MSG_BYTES]; -/* Init an array of cloned messages with all possible 1-bit errors present, - * applied to each message at the respective position - */ -void inittmsg1() { - int i, bytepos, mask; - for (i = 0; i < MODES_LONG_MSG_BITS; i++) { - bytepos = i >> 3; - mask = 1 << (7 - (i & 7)); - memcpy(&tmsg1[i][0], tmsg0, MODES_LONG_MSG_BYTES); - tmsg1[i][bytepos] ^= mask; - } -} - -/* Run sanity check on all but first 5 messages / bits, as those bits - * are not corrected. - */ -void checktmsg1(FILE *out) { - int i, k; - uint32_t crc; - for (i = 5; i < MODES_LONG_MSG_BITS; i++) { - crc = modesChecksum(&tmsg1[i][0], MODES_LONG_MSG_BITS); - if (crc != 0) { - fprintf(out, "CRC not fixed for " - "positon %d\n", i); - fprintf(out, " MSG "); - for (k = 0; k < MODES_LONG_MSG_BYTES; k++) { - fprintf(out, "%02x", tmsg1[i][k]); - } - fprintf(out, "\n"); - } - } -} - -void inittmsg2() { - int i, j, n, bytepos0, bytepos1, mask0, mask1; - n = 0; - for (i = 0; i < MODES_LONG_MSG_BITS; i++) { - bytepos0 = i >> 3; - mask0 = 1 << (7 - (i & 7)); - for (j = i+1; j < MODES_LONG_MSG_BITS; j++) { - bytepos1 = j >> 3; - mask1 = 1 << (7 - (j & 7)); - memcpy(&tmsg2[n][0], tmsg0, MODES_LONG_MSG_BYTES); - tmsg2[n][bytepos0] ^= mask0; - tmsg2[n][bytepos1] ^= mask1; - n += 1; - } - } -} - -long difftvusec(struct timeval *t0, struct timeval *t1) { - long res = 0; - res = t1->tv_usec-t0->tv_usec; - res += (t1->tv_sec-t0->tv_sec)*1000000L; - return res; -} - -/* the actual test code */ -void testAndTimeBitCorrection() { - struct timeval starttv, endtv; - int i; - /* Run timing on 1-bit errors */ - printf("Timing old vs. new crc correction code:\n"); - inittmsg1(); - gettimeofday(&starttv, NULL); - for (i = 0; i < MODES_LONG_MSG_BITS; i++) { - fixSingleBitErrors(&tmsg1[i][0], MODES_LONG_MSG_BITS); - } - gettimeofday(&endtv, NULL); - printf(" Old code: 1-bit errors on %d msgs: %ld usecs\n", - MODES_LONG_MSG_BITS, difftvusec(&starttv, &endtv)); - checktmsg1(stdout); - /* Re-init */ - inittmsg1(); - gettimeofday(&starttv, NULL); - for (i = 0; i < MODES_LONG_MSG_BITS; i++) { - fixBitErrors(&tmsg1[i][0], MODES_LONG_MSG_BITS, MODES_MAX_BITERRORS, NULL); - } - gettimeofday(&endtv, NULL); - printf(" New code: 1-bit errors on %d msgs: %ld usecs\n", - MODES_LONG_MSG_BITS, difftvusec(&starttv, &endtv)); - checktmsg1(stdout); - /* Run timing on 2-bit errors */ - inittmsg2(); - gettimeofday(&starttv, NULL); - for (i = 0; i < NTWOBITS; i++) { - fixSingleBitErrors(&tmsg2[i][0], MODES_LONG_MSG_BITS); - } - gettimeofday(&endtv, NULL); - printf(" Old code: 2-bit errors on %d msgs: %ld usecs\n", - NTWOBITS, difftvusec(&starttv, &endtv)); - /* Re-init */ - inittmsg2(); - gettimeofday(&starttv, NULL); - for (i = 0; i < NTWOBITS; i++) { - fixBitErrors(&tmsg2[i][0], MODES_LONG_MSG_BITS, MODES_MAX_BITERRORS, NULL); - } - gettimeofday(&endtv, NULL); - printf(" New code: 2-bit errors on %d msgs: %ld usecs\n", - NTWOBITS, difftvusec(&starttv, &endtv)); -} - - -/* Hash the ICAO address to index our cache of MODES_ICAO_CACHE_LEN - * elements, that is assumed to be a power of two. */ -uint32_t ICAOCacheHashAddress(uint32_t a) { - /* The following three rounds wil make sure that every bit affects - * every output bit with ~ 50% of probability. */ - a = ((a >> 16) ^ a) * 0x45d9f3b; - a = ((a >> 16) ^ a) * 0x45d9f3b; - a = ((a >> 16) ^ a); - return a & (MODES_ICAO_CACHE_LEN-1); -} - -/* Add the specified entry to the cache of recently seen ICAO addresses. - * Note that we also add a timestamp so that we can make sure that the - * entry is only valid for MODES_ICAO_CACHE_TTL seconds. */ -void addRecentlySeenICAOAddr(uint32_t addr) { - uint32_t h = ICAOCacheHashAddress(addr); - Modes.icao_cache[h*2] = addr; - Modes.icao_cache[h*2+1] = (uint32_t) time(NULL); -} - -/* Returns 1 if the specified ICAO address was seen in a DF format with - * proper checksum (not xored with address) no more than * MODES_ICAO_CACHE_TTL - * seconds ago. Otherwise returns 0. */ -int ICAOAddressWasRecentlySeen(uint32_t addr) { - uint32_t h = ICAOCacheHashAddress(addr); - uint32_t a = Modes.icao_cache[h*2]; - uint32_t t = Modes.icao_cache[h*2+1]; - - return a && a == addr && time(NULL)-t <= MODES_ICAO_CACHE_TTL; -} -// -// In the squawk (identity) field bits are interleaved as follows in -// (message bit 20 to bit 32): -// -// C1-A1-C2-A2-C4-A4-ZERO-B1-D1-B2-D2-B4-D4 -// -// So every group of three bits A, B, C, D represent an integer from 0 to 7. -// -// The actual meaning is just 4 octal numbers, but we convert it into a hex -// number tha happens to represent the four octal numbers. -// -// For more info: http://en.wikipedia.org/wiki/Gillham_code -// -int decodeID13Field(int ID13Field) { - int hexGillham = 0; - - if (ID13Field & 0x1000) {hexGillham |= 0x0010;} // Bit 12 = C1 - if (ID13Field & 0x0800) {hexGillham |= 0x1000;} // Bit 11 = A1 - if (ID13Field & 0x0400) {hexGillham |= 0x0020;} // Bit 10 = C2 - if (ID13Field & 0x0200) {hexGillham |= 0x2000;} // Bit 9 = A2 - if (ID13Field & 0x0100) {hexGillham |= 0x0040;} // Bit 8 = C4 - if (ID13Field & 0x0080) {hexGillham |= 0x4000;} // Bit 7 = A4 - //if (ID13Field & 0x0040) {hexGillham |= 0x0800;} // Bit 6 = X or M - if (ID13Field & 0x0020) {hexGillham |= 0x0100;} // Bit 5 = B1 - if (ID13Field & 0x0010) {hexGillham |= 0x0001;} // Bit 4 = D1 or Q - if (ID13Field & 0x0008) {hexGillham |= 0x0200;} // Bit 3 = B2 - if (ID13Field & 0x0004) {hexGillham |= 0x0002;} // Bit 2 = D2 - if (ID13Field & 0x0002) {hexGillham |= 0x0400;} // Bit 1 = B4 - if (ID13Field & 0x0001) {hexGillham |= 0x0004;} // Bit 0 = D4 - - return (hexGillham); - } -// -// Decode the 13 bit AC altitude field (in DF 20 and others). -// Returns the altitude, and set 'unit' to either MODES_UNIT_METERS or MDOES_UNIT_FEETS. -// -int decodeAC13Field(int AC13Field, int *unit) { - int m_bit = AC13Field & 0x0040; // set = meters, clear = feet - int q_bit = AC13Field & 0x0010; // set = 25 ft encoding, clear = Gillham Mode C encoding - - if (!m_bit) { - *unit = MODES_UNIT_FEET; - if (q_bit) { - // N is the 11 bit integer resulting from the removal of bit Q and M - int n = ((AC13Field & 0x1F80) >> 2) | - ((AC13Field & 0x0020) >> 1) | - (AC13Field & 0x000F); - // The final altitude is resulting number multiplied by 25, minus 1000. - return ((n * 25) - 1000); - } else { - // N is an 11 bit Gillham coded altitude - int n = ModeAToModeC(decodeID13Field(AC13Field)); - if (n < -12) {n = 0;} - - return (100 * n); - } - } else { - *unit = MODES_UNIT_METERS; - // TODO: Implement altitude when meter unit is selected - } - return 0; -} -// -// Decode the 12 bit AC altitude field (in DF 17 and others). -// -int decodeAC12Field(int AC12Field, int *unit) { - int q_bit = AC12Field & 0x10; // Bit 48 = Q - - *unit = MODES_UNIT_FEET; - if (q_bit) { - /// N is the 11 bit integer resulting from the removal of bit Q at bit 4 - int n = ((AC12Field & 0x0FE0) >> 1) | - (AC12Field & 0x000F); - // The final altitude is the resulting number multiplied by 25, minus 1000. - return ((n * 25) - 1000); - } else { - // Make N a 13 bit Gillham coded altitude by inserting M=0 at bit 6 - int n = ((AC12Field & 0x0FC0) << 1) | - (AC12Field & 0x003F); - n = ModeAToModeC(decodeID13Field(n)); - if (n < -12) {n = 0;} - - return (100 * n); - } -} -// -// Decode the 7 bit ground movement field PWL exponential style scale -// -int decodeMovementField(int movement) { - int gspeed; - - // Note : movement codes 0,125,126,127 are all invalid, but they are - // trapped for before this function is called. - - if (movement > 123) gspeed = 199; // > 175kt - else if (movement > 108) gspeed = ((movement - 108) * 5) + 100; - else if (movement > 93) gspeed = ((movement - 93) * 2) + 70; - else if (movement > 38) gspeed = ((movement - 38) ) + 15; - else if (movement > 12) gspeed = ((movement - 11) >> 1) + 2; - else if (movement > 8) gspeed = ((movement - 6) >> 2) + 1; - else gspeed = 0; - - return (gspeed); -} - -/* Capability table. */ -char *ca_str[8] = { - /* 0 */ "Level 1 (Survillance Only)", - /* 1 */ "Level 2 (DF0,4,5,11)", - /* 2 */ "Level 3 (DF0,4,5,11,20,21)", - /* 3 */ "Level 4 (DF0,4,5,11,20,21,24)", - /* 4 */ "Level 2+3+4 (DF0,4,5,11,20,21,24,code7 - is on ground)", - /* 5 */ "Level 2+3+4 (DF0,4,5,11,20,21,24,code7 - is on airborne)", - /* 6 */ "Level 2+3+4 (DF0,4,5,11,20,21,24,code7)", - /* 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. */ -char *fs_str[8] = { - /* 0 */ "Normal, Airborne", - /* 1 */ "Normal, On the ground", - /* 2 */ "ALERT, Airborne", - /* 3 */ "ALERT, On the ground", - /* 4 */ "ALERT & Special Position Identification. Airborne or Ground", - /* 5 */ "Special Position Identification. Airborne or Ground", - /* 6 */ "Value 6 is not assigned", - /* 7 */ "Value 7 is not assigned" -}; - -char *getMEDescription(int metype, int mesub) { - char *mename = "Unknown"; - - if (metype >= 1 && metype <= 4) - mename = "Aircraft Identification and Category"; - else if (metype >= 5 && metype <= 8) - mename = "Surface Position"; - else if (metype >= 9 && metype <= 18) - mename = "Airborne Position (Baro Altitude)"; - else if (metype == 19 && mesub >=1 && mesub <= 4) - mename = "Airborne Velocity"; - else if (metype >= 20 && metype <= 22) - mename = "Airborne Position (GNSS Height)"; - else if (metype == 23 && mesub == 0) - mename = "Test Message"; - else if (metype == 24 && mesub == 1) - mename = "Surface System Status"; - else if (metype == 28 && mesub == 1) - mename = "Extended Squitter Aircraft Status (Emergency)"; - else if (metype == 28 && mesub == 2) - mename = "Extended Squitter Aircraft Status (1090ES TCAS RA)"; - else if (metype == 29 && (mesub == 0 || mesub == 1)) - mename = "Target State and Status Message"; - else if (metype == 31 && (mesub == 0 || mesub == 1)) - mename = "Aircraft Operational Status Message"; - return mename; -} -// -// Decode a raw Mode S message demodulated as a stream of bytes by detectModeS(), -// and split it into fields populating a modesMessage structure. -// -void decodeModesMessage(struct modesMessage *mm, unsigned char *msg) { - char *ais_charset = "?ABCDEFGHIJKLMNOPQRSTUVWXYZ????? ???????????????0123456789??????"; - - // Work on our local copy - memcpy(mm->msg, msg, MODES_LONG_MSG_BYTES); - msg = mm->msg; - - // Get the message type ASAP as other operations depend on this - mm->msgtype = msg[0] >> 3; // Downlink Format - mm->msgbits = modesMessageLenByType(mm->msgtype); - mm->crc = modesChecksum(msg, mm->msgbits); - - if ((mm->crc) && (Modes.nfix_crc) && ((mm->msgtype == 17) || (mm->msgtype == 18))) { -// if ((mm->crc) && (Modes.nfix_crc) && ((mm->msgtype == 11) || (mm->msgtype == 17))) { - // - // 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 - // less than 80. Therefore, attempting to fix DF-11 errors can result in a - // multitude of possible crc solutions, only one of which is correct. - // - // We should probably perform some sanity checks on corrected DF-11's before - // using the results. Perhaps check the ICAO against known aircraft, and check - // IID against known good IID's. That's a TODO. - // - mm->correctedbits = fixBitErrors(msg, mm->msgbits, Modes.nfix_crc, mm->corrected); - - // If we correct, validate ICAO addr to help filter birthday paradox solutions. - 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 - // single/two bit errors, otherwise we would need to recompute the fields again. - // - if (mm->msgtype == 11) { // DF 11 - mm->crcok = (mm->crc < 80); - mm->iid = mm->crc; - mm->addr = (msg[1] << 16) | (msg[2] << 8) | (msg[3]); - mm->ca = (msg[0] & 0x07); // Responder capabilities - - if (0 == mm->crc) { - // DF 11 : if crc == 0 try to populate our ICAO addresses whitelist. - 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]); - mm->ca = (msg[0] & 0x07); // Responder capabilities - - if (0 == mm->crc) { - // DF 17 : if crc == 0 try to populate our ICAO addresses whitelist. - addRecentlySeenICAOAddr(mm->addr); - } - - } else if (mm->msgtype == 18) { // DF 18 - mm->crcok = (mm->crc == 0); - 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); - } - - } 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 DF0, DF16 - if (mm->msgtype == 0 || mm->msgtype == 16) { - if (msg[0] & 0x04) { // VS Bit - mm->bFlags |= MODES_ACFLAGS_AOG_VALID | MODES_ACFLAGS_AOG; - } else { - mm->bFlags |= MODES_ACFLAGS_AOG_VALID; - } - } - - // Fields for DF11, DF17 - if (mm->msgtype == 11 || mm->msgtype == 17) { - if (mm->ca == 4) { - mm->bFlags |= MODES_ACFLAGS_AOG_VALID | MODES_ACFLAGS_AOG; - } else if (mm->ca == 5) { - mm->bFlags |= MODES_ACFLAGS_AOG_VALID; - } - } - - // Fields for DF5, DF21 = Gillham encoded Squawk - if (mm->msgtype == 5 || mm->msgtype == 21) { - int ID13Field = ((msg[2] << 8) | msg[3]) & 0x1FFF; - if (ID13Field) { - mm->bFlags |= MODES_ACFLAGS_SQUAWK_VALID; - mm->modeA = decodeID13Field(ID13Field); - } - } - - // Fields for DF0, DF4, DF16, DF20 13 bit altitude - if (mm->msgtype == 0 || mm->msgtype == 4 || - mm->msgtype == 16 || mm->msgtype == 20) { - int AC13Field = ((msg[2] << 8) | msg[3]) & 0x1FFF; - if (AC13Field) { // Only attempt to decode if a valid (non zero) altitude is present - mm->bFlags |= MODES_ACFLAGS_ALTITUDE_VALID; - mm->altitude = decodeAC13Field(AC13Field, &mm->unit); - } - } - - // Fields for DF4, DF5, DF20, DF21 - if ((mm->msgtype == 4) || (mm->msgtype == 20) || - (mm->msgtype == 5) || (mm->msgtype == 21)) { - mm->bFlags |= MODES_ACFLAGS_FS_VALID; - mm->fs = msg[0] & 7; // Flight status for DF4,5,20,21 - if (mm->fs <= 3) { - mm->bFlags |= MODES_ACFLAGS_AOG_VALID; - if (mm->fs & 1) - {mm->bFlags |= MODES_ACFLAGS_AOG;} - } - } - - // Fields for DF17, DF18_CF0, DF18_CF1, DF18_CF6 squitters - 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 mesub = mm->mesub = msg[4] & 7; // Extended squitter message subtype - - // Decode the extended squitter message - - if (metype >= 1 && metype <= 4) { // Aircraft Identification and Category - uint32_t chars; - mm->bFlags |= MODES_ACFLAGS_CALLSIGN_VALID; - - chars = (msg[5] << 16) | (msg[6] << 8) | (msg[7]); - mm->flight[3] = ais_charset[chars & 0x3F]; chars = chars >> 6; - mm->flight[2] = ais_charset[chars & 0x3F]; chars = chars >> 6; - mm->flight[1] = ais_charset[chars & 0x3F]; chars = chars >> 6; - mm->flight[0] = ais_charset[chars & 0x3F]; - - chars = (msg[8] << 16) | (msg[9] << 8) | (msg[10]); - mm->flight[7] = ais_charset[chars & 0x3F]; chars = chars >> 6; - mm->flight[6] = ais_charset[chars & 0x3F]; chars = chars >> 6; - mm->flight[5] = ais_charset[chars & 0x3F]; chars = chars >> 6; - mm->flight[4] = ais_charset[chars & 0x3F]; - - mm->flight[8] = '\0'; - - } else if (metype >= 5 && metype <= 18) { // Position Message - mm->raw_latitude = ((msg[6] & 3) << 15) | (msg[7] << 7) | (msg[8] >> 1); - mm->raw_longitude = ((msg[8] & 1) << 16) | (msg[9] << 8) | (msg[10]); - mm->bFlags |= (mm->msg[6] & 0x04) ? MODES_ACFLAGS_LLODD_VALID - : MODES_ACFLAGS_LLEVEN_VALID; - if (metype >= 9) { // Airborne - int AC12Field = ((msg[5] << 4) | (msg[6] >> 4)) & 0x0FFF; - mm->bFlags |= MODES_ACFLAGS_AOG_VALID; - if (AC12Field) {// Only attempt to decode if a valid (non zero) altitude is present - mm->bFlags |= MODES_ACFLAGS_ALTITUDE_VALID; - mm->altitude = decodeAC12Field(AC12Field, &mm->unit); - } - } else { // Ground - int movement = ((msg[4] << 4) | (msg[5] >> 4)) & 0x007F; - mm->bFlags |= MODES_ACFLAGS_AOG_VALID | MODES_ACFLAGS_AOG; - if ((movement) && (movement < 125)) { - mm->bFlags |= MODES_ACFLAGS_SPEED_VALID; - mm->velocity = decodeMovementField(movement); - } - - if (msg[5] & 0x08) { - mm->bFlags |= MODES_ACFLAGS_HEADING_VALID; - mm->heading = ((((msg[5] << 4) | (msg[6] >> 4)) & 0x007F) * 45) >> 4; - } - } - - } else if (metype == 19) { // Airborne Velocity Message - - // Presumably airborne if we get an Airborne Velocity Message - mm->bFlags |= MODES_ACFLAGS_AOG_VALID; - - if ( (mesub >= 1) && (mesub <= 4) ) { - int vert_rate = ((msg[8] & 0x07) << 6) | (msg[9] >> 2); - if (vert_rate) { - --vert_rate; - if (msg[8] & 0x08) - {vert_rate = 0 - vert_rate;} - mm->vert_rate = vert_rate * 64; - mm->bFlags |= MODES_ACFLAGS_VERTRATE_VALID; - } - } - - if ((mesub == 1) || (mesub == 2)) { - int ew_raw = ((msg[5] & 0x03) << 8) | msg[6]; - int ew_vel = ew_raw - 1; - int ns_raw = ((msg[7] & 0x7F) << 3) | (msg[8] >> 5); - int ns_vel = ns_raw - 1; - - if (mesub == 2) { // If (supersonic) unit is 4 kts - ns_vel = ns_vel << 2; - ew_vel = ew_vel << 2; - } - - if (ew_raw) { // Do East/West - mm->bFlags |= MODES_ACFLAGS_EWSPEED_VALID; - if (msg[5] & 0x04) - {ew_vel = 0 - ew_vel;} - mm->ew_velocity = ew_vel; - } - - if (ns_raw) { // Do North/South - mm->bFlags |= MODES_ACFLAGS_NSSPEED_VALID; - if (msg[7] & 0x80) - {ns_vel = 0 - ns_vel;} - mm->ns_velocity = ns_vel; - } - - if (ew_raw && ns_raw) { - // Compute velocity and angle from the two speed components - mm->bFlags |= (MODES_ACFLAGS_SPEED_VALID | MODES_ACFLAGS_HEADING_VALID | MODES_ACFLAGS_NSEWSPD_VALID); - mm->velocity = (int) sqrt((ns_vel * ns_vel) + (ew_vel * ew_vel)); - - if (mm->velocity) { - mm->heading = (int) (atan2(ew_vel, ns_vel) * 180.0 / M_PI); - // We don't want negative values but a 0-360 scale - if (mm->heading < 0) mm->heading += 360; - } - } - - } else if (mesub == 3 || mesub == 4) { - int airspeed = ((msg[7] & 0x7f) << 3) | (msg[8] >> 5); - if (airspeed) { - mm->bFlags |= MODES_ACFLAGS_SPEED_VALID; - --airspeed; - if (mesub == 4) // If (supersonic) unit is 4 kts - {airspeed = airspeed << 2;} - mm->velocity = airspeed; - } - - if (msg[5] & 0x04) { - mm->bFlags |= MODES_ACFLAGS_HEADING_VALID; - mm->heading = ((((msg[5] & 0x03) << 8) | msg[6]) * 45) >> 7; - } - } - } - } - - // Fields for DF20, DF21 Comm-B - if ((mm->msgtype == 20) || (mm->msgtype == 21)){ - - if (msg[4] == 0x20) { // Aircraft Identification - uint32_t chars; - mm->bFlags |= MODES_ACFLAGS_CALLSIGN_VALID; - - chars = (msg[5] << 16) | (msg[6] << 8) | (msg[7]); - mm->flight[3] = ais_charset[chars & 0x3F]; chars = chars >> 6; - mm->flight[2] = ais_charset[chars & 0x3F]; chars = chars >> 6; - mm->flight[1] = ais_charset[chars & 0x3F]; chars = chars >> 6; - mm->flight[0] = ais_charset[chars & 0x3F]; - - chars = (msg[8] << 16) | (msg[9] << 8) | (msg[10]); - mm->flight[7] = ais_charset[chars & 0x3F]; chars = chars >> 6; - mm->flight[6] = ais_charset[chars & 0x3F]; chars = chars >> 6; - mm->flight[5] = ais_charset[chars & 0x3F]; chars = chars >> 6; - mm->flight[4] = ais_charset[chars & 0x3F]; - - mm->flight[8] = '\0'; - } else { - } - } -} -// -// This function gets a decoded Mode S Message and prints it on the screen -// in a human readable format. -// -void displayModesMessage(struct modesMessage *mm) { - int j; - unsigned char * pTimeStamp; - - // Handle only addresses mode first. - if (Modes.onlyaddr) { - printf("%06x\n", mm->addr); - return; // Enough for --onlyaddr mode - } - - // Show the raw message. - if (Modes.mlat && mm->timestampMsg) { - printf("@"); - pTimeStamp = (unsigned char *) &mm->timestampMsg; - for (j=5; j>=0;j--) { - printf("%02X",pTimeStamp[j]); - } - } else - printf("*"); - - for (j = 0; j < mm->msgbits/8; j++) printf("%02x", mm->msg[j]); - printf(";\n"); - - if (Modes.raw) { - fflush(stdout); // Provide data to the reader ASAP - return; // Enough for --raw mode - } - - if (mm->msgtype < 32) - printf("CRC: %06x (%s)\n", (int)mm->crc, mm->crcok ? "ok" : "wrong"); - - if (mm->correctedbits != 0) - printf("No. of bit errors fixed: %d\n", mm->correctedbits); - - if (mm->msgtype == 0) { // DF 0 - printf("DF 0: Short Air-Air Surveillance.\n"); - printf(" Altitude : %d %s\n", mm->altitude, - (mm->unit == MODES_UNIT_METERS) ? "meters" : "feet"); - printf(" ICAO Address : %06x\n", mm->addr); - - } else if (mm->msgtype == 4 || mm->msgtype == 20) { - printf("DF %d: %s, Altitude Reply.\n", mm->msgtype, - (mm->msgtype == 4) ? "Surveillance" : "Comm-B"); - printf(" Flight Status : %s\n", fs_str[mm->fs]); - printf(" DR : %d\n", ((mm->msg[1] >> 3) & 0x1F)); - printf(" UM : %d\n", (((mm->msg[1] & 7) << 3) | (mm->msg[2] >> 5))); - printf(" Altitude : %d %s\n", mm->altitude, - (mm->unit == MODES_UNIT_METERS) ? "meters" : "feet"); - printf(" ICAO Address : %06x\n", mm->addr); - - if (mm->msgtype == 20) { - printf(" Comm-B BDS : %x\n", mm->msg[4]); - - // Decode the extended squitter message - if ( mm->msg[4] == 0x20) { // BDS 2,0 Aircraft identification - printf(" BDS 2,0 Aircraft Identification : %s\n", mm->flight); - } - } - - } else if (mm->msgtype == 5 || mm->msgtype == 21) { - printf("DF %d: %s, Identity Reply.\n", mm->msgtype, - (mm->msgtype == 5) ? "Surveillance" : "Comm-B"); - printf(" Flight Status : %s\n", fs_str[mm->fs]); - printf(" DR : %d\n", ((mm->msg[1] >> 3) & 0x1F)); - printf(" UM : %d\n", (((mm->msg[1] & 7) << 3) | (mm->msg[2] >> 5))); - printf(" Squawk : %x\n", mm->modeA); - printf(" ICAO Address : %06x\n", mm->addr); - - if (mm->msgtype == 21) { - printf(" Comm-B BDS : %x\n", mm->msg[4]); - - // Decode the extended squitter message - if ( mm->msg[4] == 0x20) { // BDS 2,0 Aircraft identification - printf(" BDS 2,0 Aircraft Identification : %s\n", mm->flight); - } - } - - } else if (mm->msgtype == 11) { // DF 11 - printf("DF 11: All Call Reply.\n"); - printf(" Capability : %d (%s)\n", mm->ca, ca_str[mm->ca]); - printf(" ICAO Address: %06x\n", mm->addr); - if (mm->iid > 16) - {printf(" IID : SI-%02d\n", mm->iid-16);} - else - {printf(" IID : II-%02d\n", mm->iid);} - - } else if (mm->msgtype == 16) { // DF 16 - printf("DF 16: Long Air to Air ACAS\n"); - - } else if (mm->msgtype == 17) { // DF 17 - printf("DF 17: ADS-B message.\n"); - printf(" Capability : %d (%s)\n", mm->ca, ca_str[mm->ca]); - 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 == 18) { // DF 18 - 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 - printf("DF 19: Military Extended Squitter.\n"); - - } else if (mm->msgtype == 22) { // DF 22 - printf("DF 22: Military Use.\n"); - - } else if (mm->msgtype == 24) { // DF 24 - printf("DF 24: Comm D Extended Length Message.\n"); - - } else if (mm->msgtype == 32) { // DF 32 is special code we use for Mode A/C - printf("SSR : Mode A/C Reply.\n"); - if (mm->fs & 0x0080) { - printf(" Mode A : %04x IDENT\n", mm->modeA); - } else { - printf(" Mode A : %04x\n", mm->modeA); - if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) - {printf(" Mode C : %d feet\n", mm->altitude);} - } - - } else { - printf("DF %d: Unknown DF Format.\n", mm->msgtype); - } - - printf("\n"); -} - -/* Turn I/Q samples pointed by Modes.data into the magnitude vector - * pointed by Modes.magnitude. */ -void computeMagnitudeVector(void) { - uint16_t *m = &Modes.magnitude[MODES_PREAMBLE_SAMPLES+MODES_LONG_MSG_SAMPLES]; - uint16_t *p = Modes.data; - uint32_t j; - - memcpy(Modes.magnitude,&Modes.magnitude[MODES_ASYNC_BUF_SAMPLES], MODES_PREAMBLE_SIZE+MODES_LONG_MSG_SIZE); - - /* Compute the magnitudo vector. It's just SQRT(I^2 + Q^2), but - * we rescale to the 0-255 range to exploit the full resolution. */ - for (j = 0; j < MODES_ASYNC_BUF_SAMPLES; j ++) { - *m++ = Modes.maglut[*p++]; - } -} - -/* Return -1 if the message is out of fase left-side - * Return 1 if the message is out of fase right-size - * Return 0 if the message is not particularly out of phase. - * - * Note: this function will access pPreamble[-1], so the caller should make sure to - * call it only if we are not at the start of the current buffer. */ -int detectOutOfPhase(uint16_t *pPreamble) { - if (pPreamble[ 3] > pPreamble[2]/3) return 1; - if (pPreamble[10] > pPreamble[9]/3) return 1; - if (pPreamble[ 6] > pPreamble[7]/3) return -1; - if (pPreamble[-1] > pPreamble[1]/3) return -1; - return 0; -} - -/* This function does not really correct the phase of the message, it just - * applies a transformation to the first sample representing a given bit: - * - * If the previous bit was one, we amplify it a bit. - * If the previous bit was zero, we decrease it a bit. - * - * This simple transformation makes the message a bit more likely to be - * correctly decoded for out of phase messages: - * - * When messages are out of phase there is more uncertainty in - * sequences of the same bit multiple times, since 11111 will be - * transmitted as continuously altering magnitude (high, low, high, low...) - * - * However because the message is out of phase some part of the high - * is mixed in the low part, so that it is hard to distinguish if it is - * a zero or a one. - * - * However when the message is out of phase passing from 0 to 1 or from - * 1 to 0 happens in a very recognizable way, for instance in the 0 -> 1 - * transition, magnitude goes low, high, high, low, and one of of the - * two middle samples the high will be *very* high as part of the previous - * or next high signal will be mixed there. - * - * Applying our simple transformation we make more likely if the current - * bit is a zero, to detect another zero. Symmetrically if it is a one - * it will be more likely to detect a one because of the transformation. - * In this way similar levels will be interpreted more likely in the - * correct way. */ -void applyPhaseCorrection(uint16_t *pPayload) { - int j; - - for (j = 0; j < MODES_LONG_MSG_SAMPLES; j += 2, pPayload += 2) { - if (pPayload[0] > pPayload[1]) { /* One */ - pPayload[2] = (pPayload[2] * 5) / 4; - } else { /* Zero */ - pPayload[2] = (pPayload[2] * 4) / 5; - } - } -} - -/* Detect a Mode S messages inside the magnitude buffer pointed by 'm' and of - * size 'mlen' bytes. Every detected Mode S message is convert it into a - * stream of bits and passed to the function to display it. */ -void detectModeS(uint16_t *m, uint32_t mlen) { - struct modesMessage mm; - unsigned char msg[MODES_LONG_MSG_BYTES], *pMsg; - uint16_t aux[MODES_LONG_MSG_SAMPLES]; - uint32_t j; - int use_correction = 0; - - memset(&mm, 0, sizeof(mm)); - - /* The Mode S preamble is made of impulses of 0.5 microseconds at - * the following time offsets: - * - * 0 - 0.5 usec: first impulse. - * 1.0 - 1.5 usec: second impulse. - * 3.5 - 4 usec: third impulse. - * 4.5 - 5 usec: last impulse. - * - * Since we are sampling at 2 Mhz every sample in our magnitude vector - * is 0.5 usec, so the preamble will look like this, assuming there is - * an impulse at offset 0 in the array: - * - * 0 ----------------- - * 1 - - * 2 ------------------ - * 3 -- - * 4 - - * 5 -- - * 6 - - * 7 ------------------ - * 8 -- - * 9 ------------------- - */ - for (j = 0; j < mlen; j++) { - int high, i, errors, errors56, errorsTy; - uint16_t *pPreamble, *pPayload, *pPtr; - uint8_t theByte, theErrs; - int msglen, scanlen, sigStrength; - - pPreamble = &m[j]; - 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 - { // so try to find a new preamble - - if (Modes.mode_ac) - { - int ModeA = detectModeA(pPreamble, &mm); - - if (ModeA) // We have found a valid ModeA/C in the data - { - mm.timestampMsg = Modes.timestampBlk + ((j+1) * 6); - - // Decode the received message - decodeModeAMessage(&mm, ModeA); - - // Pass data to the next layer - useModesMessage(&mm); - - j += MODEAC_MSG_SAMPLES; - Modes.stat_ModeAC++; - continue; - } - } - - /* First check of relations between the first 10 samples - * representing a valid preamble. We don't even investigate further - * if this simple test is not passed. */ - if (!(pPreamble[0] > pPreamble[1] && - pPreamble[1] < pPreamble[2] && - pPreamble[2] > pPreamble[3] && - pPreamble[3] < pPreamble[0] && - pPreamble[4] < pPreamble[0] && - pPreamble[5] < pPreamble[0] && - pPreamble[6] < pPreamble[0] && - pPreamble[7] > pPreamble[8] && - pPreamble[8] < pPreamble[9] && - pPreamble[9] > pPreamble[6])) - { - if (Modes.debug & MODES_DEBUG_NOPREAMBLE && - *pPreamble > MODES_DEBUG_NOPREAMBLE_LEVEL) - dumpRawMessage("Unexpected ratio among first 10 samples", msg, m, j); - continue; - } - - /* The samples between the two spikes must be < than the average - * of the high spikes level. We don't test bits too near to - * the high levels as signals can be out of phase so part of the - * energy can be in the near samples. */ - high = (pPreamble[0] + pPreamble[2] + pPreamble[7] + pPreamble[9]) / 6; - if (pPreamble[4] >= high || - pPreamble[5] >= high) - { - if (Modes.debug & MODES_DEBUG_NOPREAMBLE && - *pPreamble > MODES_DEBUG_NOPREAMBLE_LEVEL) - dumpRawMessage("Too high level in samples between 3 and 6", msg, m, j); - continue; - } - - /* Similarly samples in the range 11-14 must be low, as it is the - * space between the preamble and real data. Again we don't test - * bits too near to high levels, see above. */ - if (pPreamble[11] >= high || - pPreamble[12] >= high || - pPreamble[13] >= high || - pPreamble[14] >= high) - { - if (Modes.debug & MODES_DEBUG_NOPREAMBLE && - *pPreamble > MODES_DEBUG_NOPREAMBLE_LEVEL) - dumpRawMessage("Too high level in samples between 10 and 15", msg, m, j); - continue; - } - Modes.stat_valid_preamble++; - } - - else { - /* If the previous attempt with this message failed, retry using - * magnitude correction. */ - // Make a copy of the Payload, and phase correct the copy - memcpy(aux, pPayload, sizeof(aux)); - applyPhaseCorrection(aux); - Modes.stat_out_of_phase++; - pPayload = aux; - /* TODO ... apply other kind of corrections. */ - } - - /* Decode all the next 112 bits, regardless of the actual message - * size. We'll check the actual message type later. */ - pMsg = &msg[0]; - pPtr = pPayload; - theByte = 0; - theErrs = 0; errorsTy = 0; - errors = 0; errors56 = 0; - - // We should have 4 'bits' of 0/1 and 1/0 samples in the preamble, - // so include these in the signal strength - sigStrength = (pPreamble[0]-pPreamble[1]) - + (pPreamble[2]-pPreamble[3]) - + (pPreamble[7]-pPreamble[6]) - + (pPreamble[9]-pPreamble[8]); - - msglen = scanlen = MODES_LONG_MSG_BITS; - for (i = 0; i < scanlen; i++) { - uint32_t a = *pPtr++; - uint32_t b = *pPtr++; - - if (a > b) - {theByte |= 1; if (i < 56) {sigStrength += (a-b);}} - else if (a < b) - {/*theByte |= 0;*/ if (i < 56) {sigStrength += (b-a);}} - else if (i >= MODES_SHORT_MSG_BITS) //(a == b), and we're in the long part of a frame - {errors++; /*theByte |= 0;*/} - else if (i >= 5) //(a == b), and we're in the short part of a frame - {scanlen = MODES_LONG_MSG_BITS; errors56 = ++errors;/*theByte |= 0;*/} - else if (i) //(a == b), and we're in the message type part of a frame - {errorsTy = errors56 = ++errors; theErrs |= 1; /*theByte |= 0;*/} - else //(a == b), and we're in the first bit of the message type part of a frame - {errorsTy = errors56 = ++errors; theErrs |= 1; theByte |= 1;} - - if ((i & 7) == 7) - {*pMsg++ = theByte;} - else if (i == 4) { - msglen = modesMessageLenByType(theByte); - if (errors == 0) - {scanlen = msglen;} - } - - theByte = theByte << 1; - if (i < 7) - {theErrs = theErrs << 1;} - - // If we've exceeded the permissible number of encoding errors, abandon ship now - if (errors > MODES_MSG_ENCODER_ERRS) { - - if (i < MODES_SHORT_MSG_BITS) { - msglen = 0; - - } else if ((errorsTy == 1) && (theErrs == 0x80)) { - // If we only saw one error in the first bit of the byte of the frame, then it's possible - // we guessed wrongly about the value of the bit. We may be able to correct it by guessing - // the other way. - // - // We guessed a '1' at bit 7, which is the DF length bit == 112 Bits. - // Inverting bit 7 will change the message type from a long to a short. - // Invert the bit, cross your fingers and carry on. - msglen = MODES_SHORT_MSG_BITS; - msg[0] ^= theErrs; errorsTy = 0; - errors = errors56; // revert to the number of errors prior to bit 56 - Modes.stat_DF_Len_Corrected++; - - } else if (i < MODES_LONG_MSG_BITS) { - msglen = MODES_SHORT_MSG_BITS; - errors = errors56; - - } else { - msglen = MODES_LONG_MSG_BITS; - } - - break; - } - } - - // Ensure msglen is consistent with the DF type - i = modesMessageLenByType(msg[0] >> 3); - if (msglen > i) {msglen = i;} - else if (msglen < i) {msglen = 0;} - - // - // If we guessed at any of the bits in the DF type field, then look to see if our guess was sensible. - // Do this by looking to see if the original guess results in the DF type being one of the ICAO defined - // message types. If it isn't then toggle the guessed bit and see if this new value is ICAO defined. - // if the new value is ICAO defined, then update it in our message. - if ((msglen) && (errorsTy == 1) && (theErrs & 0x78)) { - // We guessed at one (and only one) of the message type bits. See if our guess is "likely" - // to be correct by comparing the DF against a list of known good DF's - int thisDF = ((theByte = msg[0]) >> 3) & 0x1f; - uint32_t validDFbits = 0x017F0831; // One bit per 32 possible DF's. Set bits 0,4,5,11,16.17.18.19,20,21,22,24 - uint32_t thisDFbit = (1 << thisDF); - if (0 == (validDFbits & thisDFbit)) { - // The current DF is not ICAO defined, so is probably an errors. - // Toggle the bit we guessed at and see if the resultant DF is more likely - theByte ^= theErrs; - thisDF = (theByte >> 3) & 0x1f; - thisDFbit = (1 << thisDF); - // if this DF any more likely? - if (validDFbits & thisDFbit) { - // Yep, more likely, so update the main message - msg[0] = theByte; - Modes.stat_DF_Type_Corrected++; - errors--; // decrease the error count so we attempt to use the modified DF. - } - } - } - - // We measured signal strength over the first 56 bits. Don't forget to add 4 - // for the preamble samples, so round up and divide by 60. - sigStrength = (sigStrength + 29) / 60; - - // When we reach this point, if error is small, and the signal strength is large enough - // we may have a Mode S message on our hands. It may still be broken and the CRC may not - // be correct, but this can be handled by the next layer. - if ( (msglen) - && (sigStrength > MODES_MSG_SQUELCH_LEVEL) - && (errors <= MODES_MSG_ENCODER_ERRS) ) { - - // Set initial mm structure details - mm.timestampMsg = Modes.timestampBlk + (j*6); - sigStrength = (sigStrength + 0x7F) >> 8; - mm.signalLevel = ((sigStrength < 255) ? sigStrength : 255); - mm.phase_corrected = use_correction; - - // Decode the received message - decodeModesMessage(&mm, msg); - - // Update statistics - if (Modes.stats) { - if (mm.crcok || use_correction || mm.correctedbits) { - - 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 (use_correction) { - 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) - && (mm.correctedbits <= MODES_MAX_BITERRORS) ) { - Modes.stat_ph_bit_fix[mm.correctedbits-1] += 1; - } - - } else { - Modes.stat_badcrc++; - Modes.stat_fixed++; - if ( (mm.correctedbits) - && (mm.correctedbits <= MODES_MAX_BITERRORS) ) { - Modes.stat_bit_fix[mm.correctedbits-1] += 1; - } - } - } - } - - // Output debug mode info if needed - if (use_correction) { - if (Modes.debug & MODES_DEBUG_DEMOD) - dumpRawMessage("Demodulated with 0 errors", msg, m, j); - else if (Modes.debug & MODES_DEBUG_BADCRC && - mm.msgtype == 17 && - (!mm.crcok || mm.correctedbits != 0)) - dumpRawMessage("Decoded with bad CRC", msg, m, j); - else if (Modes.debug & MODES_DEBUG_GOODCRC && mm.crcok && - mm.correctedbits == 0) - dumpRawMessage("Decoded with good CRC", msg, m, j); - } - - // Skip this message if we are sure it's fine - if (mm.crcok) { - j += (MODES_PREAMBLE_US+msglen)*2; - } - - // Pass data to the next layer - useModesMessage(&mm); - - } else { - if (Modes.debug & MODES_DEBUG_DEMODERR && use_correction) { - printf("The following message has %d demod errors\n", errors); - dumpRawMessage("Demodulated with errors", msg, m, j); - } - } - - // Retry with phase correction if enabled, necessary and possible. - if (Modes.phase_enhance && !mm.crcok && !mm.correctedbits && !use_correction && j && detectOutOfPhase(pPreamble)) { - use_correction = 1; j--; - } else { - use_correction = 0; - } - } - - //Send any remaining partial raw buffers now - if (Modes.rawOutUsed || Modes.beastOutUsed) - { - Modes.net_output_raw_rate_count++; - if (Modes.net_output_raw_rate_count > Modes.net_output_raw_rate) - { - if (Modes.rawOutUsed) { - modesSendAllClients(Modes.ros, Modes.rawOut, Modes.rawOutUsed); - Modes.rawOutUsed = 0; - } - if (Modes.beastOutUsed) { - modesSendAllClients(Modes.bos, Modes.beastOut, Modes.beastOutUsed); - Modes.beastOutUsed = 0; - } - Modes.net_output_raw_rate_count = 0; - } - } -} -// -// When a new message is available, because it was decoded from the RTL device, -// file, or received in the TCP input port, or any other way we can receive a -// decoded message, we call this function in order to use the message. -// -// Basically this function passes a raw message to the upper layers for further -// processing and visualization -// -void useModesMessage(struct modesMessage *mm) { - if ((Modes.check_crc == 0) || (mm->crcok) || (mm->correctedbits)) { // not checking, ok or fixed - - // Track aircrafts if... - if ( (Modes.interactive) // in interactive mode - || (Modes.stat_http_requests) // or if the HTTP interface is enabled - || (Modes.stat_sbs_connections) // or if sbs connections are established - || (Modes.mode_ac) ) { // or if mode A/C decoding is enabled - interactiveReceiveData(mm); - } - - // In non-interactive non-quiet mode, display messages on standard output - if (!Modes.interactive && !Modes.quiet) { - displayModesMessage(mm); - } - - // Feed output clients - if (Modes.stat_sbs_connections) {modesSendSBSOutput(mm);} - if (Modes.stat_beast_connections) {modesSendBeastOutput(mm);} - if (Modes.stat_raw_connections) {modesSendRawOutput(mm);} - } -} -// -// ========================= Interactive mode =============================== -// -// Return a new aircraft structure for the interactive mode linked list -// of aircraft -// -struct aircraft *interactiveCreateAircraft(struct modesMessage *mm) { - struct aircraft *a = (struct aircraft *) malloc(sizeof(*a)); - - // Default everything to zero/NULL - memset(a, 0, sizeof(*a)); - - // Now initialise things that should not be 0/NULL to their defaults - a->addr = mm->addr; - a->lat = a->lon = 0.0; - memset(a->signalLevel, mm->signalLevel, 8); // First time, initialise everything - // to the first signal strength - - // mm->msgtype 32 is used to represent Mode A/C. These values can never change, so - // set them once here during initialisation, and don't bother to set them every - // time this ModeA/C is received again in the future - if (mm->msgtype == 32) { - int modeC = ModeAToModeC(mm->modeA | mm->fs); - a->modeACflags = MODEAC_MSG_FLAG; - if (modeC < -12) { - a->modeACflags |= MODEAC_MSG_MODEA_ONLY; - } else { - mm->altitude = modeC * 100; - mm->bFlags |= MODES_ACFLAGS_ALTITUDE_VALID; - } - } - return (a); -} -// -//========================================================================= -// -// Return the aircraft with the specified address, or NULL if no aircraft -// exists with this address. -// -struct aircraft *interactiveFindAircraft(uint32_t addr) { - struct aircraft *a = Modes.aircrafts; - - while(a) { - if (a->addr == addr) return (a); - a = a->next; - } - return (NULL); -} -// -//========================================================================= -// -// We have received a Mode A or C response. -// -// Search through the list of known Mode-S aircraft and tag them if this Mode A/C -// matches their known Mode S Squawks or Altitudes(+/- 50feet). -// -// A Mode S equipped aircraft may also respond to Mode A and Mode C SSR interrogations. -// We can't tell if this is a Mode A or C, so scan through the entire aircraft list -// looking for matches on Mode A (squawk) and Mode C (altitude). Flag in the Mode S -// records that we have had a potential Mode A or Mode C response from this aircraft. -// -// If an aircraft responds to Mode A then it's highly likely to be responding to mode C -// too, and vice verca. Therefore, once the mode S record is tagged with both a Mode A -// and a Mode C flag, we can be fairly confident that this Mode A/C frame relates to that -// Mode S aircraft. -// -// Mode C's are more likely to clash than Mode A's; There could be several aircraft -// cruising at FL370, but it's less likely (though not impossible) that there are two -// aircraft on the same squawk. Therefore, give precidence to Mode A record matches -// -// Note : It's theoretically possible for an aircraft to have the same value for Mode A -// and Mode C. Therefore we have to check BOTH A AND C for EVERY S. -// -void interactiveUpdateAircraftModeA(struct aircraft *a) { - struct aircraft *b = Modes.aircrafts; - - while(b) { - if ((b->modeACflags & MODEAC_MSG_FLAG) == 0) {// skip any fudged ICAO records - - // If both (a) and (b) have valid squawks... - if ((a->bFlags & b->bFlags) & MODES_ACFLAGS_SQUAWK_VALID) { - // ...check for Mode-A == Mode-S Squawk matches - if (a->modeA == b->modeA) { // If a 'real' Mode-S ICAO exists using this Mode-A Squawk - b->modeAcount = a->messages; - b->modeACflags |= MODEAC_MSG_MODEA_HIT; - a->modeACflags |= MODEAC_MSG_MODEA_HIT; - if ( (b->modeAcount > 0) && - ( (b->modeCcount > 1) - || (a->modeACflags & MODEAC_MSG_MODEA_ONLY)) ) // Allow Mode-A only matches if this Mode-A is invalid Mode-C - {a->modeACflags |= MODEAC_MSG_MODES_HIT;} // flag this ModeA/C probably belongs to a known Mode S - } - } - - // If both (a) and (b) have valid altitudes... - if ((a->bFlags & b->bFlags) & MODES_ACFLAGS_ALTITUDE_VALID) { - // ... check for Mode-C == Mode-S Altitude matches - if ( (a->modeC == b->modeC ) // If a 'real' Mode-S ICAO exists at this Mode-C Altitude - || (a->modeC == b->modeC + 1) // or this Mode-C - 100 ft - || (a->modeC + 1 == b->modeC ) ) { // or this Mode-C + 100 ft - b->modeCcount = a->messages; - b->modeACflags |= MODEAC_MSG_MODEC_HIT; - a->modeACflags |= MODEAC_MSG_MODEC_HIT; - if ( (b->modeAcount > 0) && - (b->modeCcount > 1) ) - {a->modeACflags |= (MODEAC_MSG_MODES_HIT | MODEAC_MSG_MODEC_OLD);} // flag this ModeA/C probably belongs to a known Mode S - } - } - } - b = b->next; - } -} -// -//========================================================================= -// -void interactiveUpdateAircraftModeS() { - struct aircraft *a = Modes.aircrafts; - - while(a) { - int flags = a->modeACflags; - if (flags & MODEAC_MSG_FLAG) { // find any fudged ICAO records - - // clear the current A,C and S hit bits ready for this attempt - a->modeACflags = flags & ~(MODEAC_MSG_MODEA_HIT | MODEAC_MSG_MODEC_HIT | MODEAC_MSG_MODES_HIT); - - interactiveUpdateAircraftModeA(a); // and attempt to match them with Mode-S - } - a = a->next; - } -} -// -//========================================================================= -// -// Always positive MOD operation, used for CPR decoding. -int cprModFunction(int a, int b) { - int res = a % b; - if (res < 0) res += b; - return res; -} -// -//========================================================================= -// -// The NL function uses the precomputed table from 1090-WP-9-14 -// -int cprNLFunction(double lat) { - if (lat < 0) lat = -lat; // Table is simmetric about the equator - if (lat < 10.47047130) return 59; - if (lat < 14.82817437) return 58; - if (lat < 18.18626357) return 57; - if (lat < 21.02939493) return 56; - if (lat < 23.54504487) return 55; - if (lat < 25.82924707) return 54; - if (lat < 27.93898710) return 53; - if (lat < 29.91135686) return 52; - if (lat < 31.77209708) return 51; - if (lat < 33.53993436) return 50; - if (lat < 35.22899598) return 49; - if (lat < 36.85025108) return 48; - if (lat < 38.41241892) return 47; - if (lat < 39.92256684) return 46; - if (lat < 41.38651832) return 45; - if (lat < 42.80914012) return 44; - if (lat < 44.19454951) return 43; - if (lat < 45.54626723) return 42; - if (lat < 46.86733252) return 41; - if (lat < 48.16039128) return 40; - if (lat < 49.42776439) return 39; - if (lat < 50.67150166) return 38; - if (lat < 51.89342469) return 37; - if (lat < 53.09516153) return 36; - if (lat < 54.27817472) return 35; - if (lat < 55.44378444) return 34; - if (lat < 56.59318756) return 33; - if (lat < 57.72747354) return 32; - if (lat < 58.84763776) return 31; - if (lat < 59.95459277) return 30; - if (lat < 61.04917774) return 29; - if (lat < 62.13216659) return 28; - if (lat < 63.20427479) return 27; - if (lat < 64.26616523) return 26; - if (lat < 65.31845310) return 25; - if (lat < 66.36171008) return 24; - if (lat < 67.39646774) return 23; - if (lat < 68.42322022) return 22; - if (lat < 69.44242631) return 21; - if (lat < 70.45451075) return 20; - if (lat < 71.45986473) return 19; - if (lat < 72.45884545) return 18; - if (lat < 73.45177442) return 17; - if (lat < 74.43893416) return 16; - if (lat < 75.42056257) return 15; - if (lat < 76.39684391) return 14; - if (lat < 77.36789461) return 13; - if (lat < 78.33374083) return 12; - if (lat < 79.29428225) return 11; - if (lat < 80.24923213) return 10; - if (lat < 81.19801349) return 9; - if (lat < 82.13956981) return 8; - if (lat < 83.07199445) return 7; - if (lat < 83.99173563) return 6; - if (lat < 84.89166191) return 5; - if (lat < 85.75541621) return 4; - if (lat < 86.53536998) return 3; - if (lat < 87.00000000) return 2; - else return 1; -} -// -//========================================================================= -// -int cprNFunction(double lat, int fflag) { - int nl = cprNLFunction(lat) - (fflag ? 1 : 0); - if (nl < 1) nl = 1; - return nl; -} -// -//========================================================================= -// -double cprDlonFunction(double lat, int fflag, int surface) { - return (surface ? 90.0 : 360.0) / cprNFunction(lat, fflag); -} -// -//========================================================================= -// -// This algorithm comes from: -// http://www.lll.lu/~edward/edward/adsb/DecodingADSBposition.html. -// -// A few remarks: -// 1) 131072 is 2^17 since CPR latitude and longitude are encoded in 17 bits. -// 2) We assume that we always received the odd packet as last packet for -// simplicity. This may provide a position that is less fresh of a few -// seconds. -// -void decodeCPR(struct aircraft *a, int fflag, int surface) { - double AirDlat0 = (surface ? 90.0 : 360.0) / 60.0; - double AirDlat1 = (surface ? 90.0 : 360.0) / 59.0; - double lat0 = a->even_cprlat; - double lat1 = a->odd_cprlat; - double lon0 = a->even_cprlon; - double lon1 = a->odd_cprlon; - - // Compute the Latitude Index "j" - int j = (int) floor(((59*lat0 - 60*lat1) / 131072) + 0.5); - double rlat0 = AirDlat0 * (cprModFunction(j,60) + lat0 / 131072); - double rlat1 = AirDlat1 * (cprModFunction(j,59) + lat1 / 131072); - - if (surface) { - // If we're on the ground, make sure we have our receiver base station Lat/Lon - if (0 == (Modes.bUserFlags & MODES_USER_LATLON_VALID)) - {return;} - rlat0 += floor(Modes.fUserLat / 90.0) * 90.0; // Move from 1st quadrant to our quadrant - rlat1 += floor(Modes.fUserLat / 90.0) * 90.0; - } else { - if (rlat0 >= 270) rlat0 -= 360; - if (rlat1 >= 270) rlat1 -= 360; - } - - // Check that both are in the same latitude zone, or abort. - if (cprNLFunction(rlat0) != cprNLFunction(rlat1)) return; - - // Compute ni and the Longitude Index "m" - if (fflag) { // Use odd packet. - int ni = cprNFunction(rlat1,1); - int m = (int) floor((((lon0 * (cprNLFunction(rlat1)-1)) - - (lon1 * cprNLFunction(rlat1))) / 131072.0) + 0.5); - a->lon = cprDlonFunction(rlat1, 1, surface) * (cprModFunction(m, ni)+lon1/131072); - a->lat = rlat1; - } else { // Use even packet. - int ni = cprNFunction(rlat0,0); - int m = (int) floor((((lon0 * (cprNLFunction(rlat0)-1)) - - (lon1 * cprNLFunction(rlat0))) / 131072) + 0.5); - a->lon = cprDlonFunction(rlat0, 0, surface) * (cprModFunction(m, ni)+lon0/131072); - a->lat = rlat0; - } - - if (surface) { - a->lon += floor(Modes.fUserLon / 90.0) * 90.0; // Move from 1st quadrant to our quadrant - } else if (a->lon > 180) { - a->lon -= 360; - } - - a->seenLatLon = a->seen; - a->timestampLatLon = a->timestamp; - a->bFlags |= (MODES_ACFLAGS_LATLON_VALID | MODES_ACFLAGS_LATLON_REL_OK); -} -// -//========================================================================= -// -// This algorithm comes from: -// 1090-WP29-07-Draft_CPR101 (which also defines decodeCPR() ) -// -// There is an error in this document related to CPR relative decode. -// Should use trunc() rather than the floor() function in Eq 38 and related for deltaZI. -// floor() returns integer less than argument -// trunc() returns integer closer to zero than argument. -// Note: text of document describes trunc() functionality for deltaZI calculation -// but the formulae use floor(). -// -int decodeCPRrelative(struct aircraft *a, int fflag, int surface) { - double AirDlat; - double AirDlon; - double lat; - double lon; - double lonr, latr; - double rlon, rlat; - int j,m; - - if (a->bFlags & MODES_ACFLAGS_LATLON_REL_OK) { // Ok to try aircraft relative first - latr = a->lat; - lonr = a->lon; - } else if (Modes.bUserFlags & MODES_USER_LATLON_VALID) { // Try ground station relative next - latr = Modes.fUserLat; - lonr = Modes.fUserLon; - } else { - return (-1); // Exit with error - can't do relative if we don't have ref. - } - - if (fflag) { // odd - AirDlat = (surface ? 90.0 : 360.0) / 59.0; - lat = a->odd_cprlat; - lon = a->odd_cprlon; - } else { // even - AirDlat = (surface ? 90.0 : 360.0) / 60.0; - lat = a->even_cprlat; - lon = a->even_cprlon; - } - - // Compute the Latitude Index "j" - j = (int) (floor(latr/AirDlat) + - trunc(0.5 + cprModFunction((int)latr, (int)AirDlat)/AirDlat - lat/131072)); - rlat = AirDlat * (j + lat/131072); - if (rlat >= 270) rlat -= 360; - - // Check to see that answer is reasonable - ie no more than 1/2 cell away - if (fabs(rlat - a->lat) > (AirDlat/2)) { - a->bFlags &= ~MODES_ACFLAGS_LATLON_REL_OK; // This will cause a quick exit next time if no global has been done - return (-1); // Time to give up - Latitude error - } - - // Compute the Longitude Index "m" - AirDlon = cprDlonFunction(rlat, fflag, surface); - m = (int) (floor(lonr/AirDlon) + - trunc(0.5 + cprModFunction((int)lonr, (int)AirDlon)/AirDlon - lon/131072)); - rlon = AirDlon * (m + lon/131072); - if (rlon > 180) rlon -= 360; - - // Check to see that answer is reasonable - ie no more than 1/2 cell away - if (fabs(rlon - a->lon) > (AirDlon/2)) { - a->bFlags &= ~MODES_ACFLAGS_LATLON_REL_OK; // This will cause a quick exit next time if no global has been done - return (-1); // Time to give up - Longitude error - } - - a->lat = rlat; - a->lon = rlon; - - a->seenLatLon = a->seen; - a->timestampLatLon = a->timestamp; - a->bFlags |= (MODES_ACFLAGS_LATLON_VALID | MODES_ACFLAGS_LATLON_REL_OK); - return (0); -} -// -//========================================================================= -// -// Receive new messages and populate the interactive mode with more info -// -struct aircraft *interactiveReceiveData(struct modesMessage *mm) { - struct aircraft *a, *aux; - - // Return if (checking crc) AND (not crcok) AND (not fixed) - if (Modes.check_crc && (mm->crcok == 0) && (mm->correctedbits == 0)) - return NULL; - - // Lookup our aircraft or create a new one - a = interactiveFindAircraft(mm->addr); - if (!a) { // If it's a currently unknown aircraft.... - a = interactiveCreateAircraft(mm); // ., create a new record for it, - a->next = Modes.aircrafts; // .. and put it at the head of the list - Modes.aircrafts = a; - } else { - /* If it is an already known aircraft, move it on head - * so we keep aircrafts ordered by received message time. - * - * However move it on head only if at least one second elapsed - * since the aircraft that is currently on head sent a message, - * othewise with multiple aircrafts at the same time we have an - * useless shuffle of positions on the screen. */ - if (0 && Modes.aircrafts != a && (time(NULL) - a->seen) >= 1) { - aux = Modes.aircrafts; - while(aux->next != a) aux = aux->next; - /* Now we are a node before the aircraft to remove. */ - aux->next = aux->next->next; /* removed. */ - /* Add on head */ - a->next = Modes.aircrafts; - Modes.aircrafts = a; - } - } - - a->signalLevel[a->messages & 7] = mm->signalLevel;// replace the 8th oldest signal strength - a->seen = time(NULL); - a->timestamp = mm->timestampMsg; - a->messages++; - - // If a (new) CALLSIGN has been received, copy it to the aircraft structure - if (mm->bFlags & MODES_ACFLAGS_CALLSIGN_VALID) { - memcpy(a->flight, mm->flight, sizeof(a->flight)); - } - - // If a (new) ALTITUDE has been received, copy it to the aircraft structure - if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) { - if ( (a->modeCcount) // if we've a modeCcount already - && (a->altitude != mm->altitude ) ) // and Altitude has changed -// && (a->modeC != mm->modeC + 1) // and Altitude not changed by +100 feet -// && (a->modeC + 1 != mm->modeC ) ) // and Altitude not changes by -100 feet - { - a->modeCcount = 0; //....zero the hit count - a->modeACflags &= ~MODEAC_MSG_MODEC_HIT; - } - a->altitude = mm->altitude; - a->modeC = (mm->altitude + 49) / 100; - } - - // If a (new) SQUAWK has been received, copy it to the aircraft structure - if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) { - if (a->modeA != mm->modeA) { - a->modeAcount = 0; // Squawk has changed, so zero the hit count - a->modeACflags &= ~MODEAC_MSG_MODEA_HIT; - } - a->modeA = mm->modeA; - } - - // If a (new) HEADING has been received, copy it to the aircraft structure - if (mm->bFlags & MODES_ACFLAGS_HEADING_VALID) { - a->track = mm->heading; - } - - // If a (new) SPEED has been received, copy it to the aircraft structure - if (mm->bFlags & MODES_ACFLAGS_SPEED_VALID) { - a->speed = mm->velocity; - } - - // If a (new) Vertical Descent rate has been received, copy it to the aircraft structure - if (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) { - a->vert_rate = mm->vert_rate; - } - - // if the Aircraft has landed or taken off since the last message, clear the even/odd CPR flags - if ((mm->bFlags & MODES_ACFLAGS_AOG_VALID) && ((a->bFlags ^ mm->bFlags) & MODES_ACFLAGS_AOG)) { - a->bFlags &= ~(MODES_ACFLAGS_LLBOTH_VALID | MODES_ACFLAGS_AOG); - - } else if ( (mm->bFlags & MODES_ACFLAGS_LLEITHER_VALID) - && (((mm->bFlags | a->bFlags) & MODES_ACFLAGS_LLEITHER_VALID) == MODES_ACFLAGS_LLBOTH_VALID) ) { - // If it's a new even/odd raw lat/lon, and we now have both even and odd,decode the CPR - int fflag; - - if (mm->bFlags & MODES_ACFLAGS_LLODD_VALID) { - fflag = 1; - a->odd_cprlat = mm->raw_latitude; - a->odd_cprlon = mm->raw_longitude; - a->odd_cprtime = mstime(); - } else { - fflag = 0; - a->even_cprlat = mm->raw_latitude; - a->even_cprlon = mm->raw_longitude; - a->even_cprtime = mstime(); - } - // Try relative CPR first - if (decodeCPRrelative(a, fflag, (mm->bFlags & MODES_ACFLAGS_AOG))) { - // If it fails then try global if the two data are less than 10 seconds apart - if (abs((int)(a->even_cprtime - a->odd_cprtime)) <= 10000) { - decodeCPR(a, fflag, (mm->bFlags & MODES_ACFLAGS_AOG)); - } - } - - //If we sucessfully decoded, back copy the results to mm so that we can print them in list output - if (a->bFlags & MODES_ACFLAGS_LATLON_VALID) { - mm->bFlags |= MODES_ACFLAGS_LATLON_VALID; - mm->fLat = a->lat; - mm->fLon = a->lon; - } - } - - // Update the aircrafts a->bFlags to reflect the newly received mm->bFlags; - a->bFlags |= mm->bFlags; - - if (mm->msgtype == 32) { - int flags = a->modeACflags; - if ((flags & (MODEAC_MSG_MODEC_HIT | MODEAC_MSG_MODEC_OLD)) == MODEAC_MSG_MODEC_OLD) { - // - // This Mode-C doesn't currently hit any known Mode-S, but it used to because MODEAC_MSG_MODEC_OLD is - // set So the aircraft it used to match has either changed altitude, or gone out of our receiver range - // - // We've now received this Mode-A/C again, so it must be a new aircraft. It could be another aircraft - // at the same Mode-C altitude, or it could be a new airctraft with a new Mods-A squawk. - // - // To avoid masking this aircraft from the interactive display, clear the MODEAC_MSG_MODES_OLD flag - // and set messages to 1; - // - a->modeACflags = flags & ~MODEAC_MSG_MODEC_OLD; - a->messages = 1; - } - } - - return (a); -} -// -//========================================================================= -// -// Show the currently captured interactive data on screen. -// -void interactiveShowData(void) { - struct aircraft *a = Modes.aircrafts; - time_t now = time(NULL); - int count = 0; - char progress; - char spinner[4] = "|/-\\"; - - // Refresh screen every (MODES_INTERACTIVE_REFRESH_TIME) miliseconde - if ((mstime() - Modes.interactive_last_update) < MODES_INTERACTIVE_REFRESH_TIME) - {return;} - - Modes.interactive_last_update = mstime(); - - // Attempt to reconsile any ModeA/C with known Mode-S - // We can't condition on Modes.modeac because ModeA/C could be comming - // in from a raw input port which we can't turn off. - interactiveUpdateAircraftModeS(); - - progress = spinner[time(NULL)%4]; - -#ifndef _WIN32 - printf("\x1b[H\x1b[2J"); // Clear the screen -#else - system("cls"); -#endif - - if (Modes.interactive_rtl1090 == 0) { - printf ( -"Hex Mode Sqwk Flight Alt Spd Hdg Lat Long Sig Msgs Ti%c\n", progress); - } else { - printf ( -"Hex Flight Alt V/S GS TT SSR G*456^ Msgs Seen %c\n", progress); - } - printf( -"-------------------------------------------------------------------------------\n"); - - while(a && (count < Modes.interactive_rows)) { - - if ((now - a->seen) < Modes.interactive_display_ttl) - { - int msgs = a->messages; - int flags = a->modeACflags; - - if ( (((flags & (MODEAC_MSG_FLAG )) == 0 ) ) - || (((flags & (MODEAC_MSG_MODES_HIT | MODEAC_MSG_MODEA_ONLY)) == MODEAC_MSG_MODEA_ONLY) && (msgs > 4 ) ) - || (((flags & (MODEAC_MSG_MODES_HIT | MODEAC_MSG_MODEC_OLD )) == 0 ) && (msgs > 127) ) - ) { - int altitude = a->altitude, speed = a->speed; - char strSquawk[5] = " "; - char strFl[6] = " "; - char strTt[5] = " "; - char strGs[5] = " "; - - // Convert units to metric if --metric was specified - if (Modes.metric) { - altitude = (int) (altitude / 3.2828); - speed = (int) (speed * 1.852); - } - - if (a->bFlags & MODES_ACFLAGS_SQUAWK_VALID) { - snprintf(strSquawk,5,"%04x", a->modeA);} - - if (a->bFlags & MODES_ACFLAGS_SPEED_VALID) { - snprintf (strGs, 5,"%3d", speed);} - - if (a->bFlags & MODES_ACFLAGS_HEADING_VALID) { - snprintf (strTt, 5,"%03d", a->track);} - - if (msgs > 99999) { - msgs = 99999;} - - if (Modes.interactive_rtl1090) { // RTL1090 display mode - - if (a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) { - snprintf(strFl,6,"F%03d",(altitude/100)); - } - printf("%06x %-8s %-4s %-3s %-3s %4s %-6d %-2d\n", - a->addr, a->flight, strFl, strGs, strTt, strSquawk, msgs, (int)(now - a->seen)); - - } else { // Dump1090 display mode - char strMode[5] = " "; - char strLat[8] = " "; - char strLon[9] = " "; - unsigned char * pSig = a->signalLevel; - unsigned int signalAverage = (pSig[0] + pSig[1] + pSig[2] + pSig[3] + - pSig[4] + pSig[5] + pSig[6] + pSig[7] + 3) >> 3; - - if ((flags & MODEAC_MSG_FLAG) == 0) { - strMode[0] = 'S'; - } else if (flags & MODEAC_MSG_MODEA_ONLY) { - strMode[0] = 'A'; - } - if (flags & MODEAC_MSG_MODEA_HIT) {strMode[2] = 'a';} - if (flags & MODEAC_MSG_MODEC_HIT) {strMode[3] = 'c';} - - if (a->bFlags & MODES_ACFLAGS_LATLON_VALID) { - snprintf(strLat, 8,"%7.03f", a->lat); - snprintf(strLon, 9,"%8.03f", a->lon); - } - - if (a->bFlags & MODES_ACFLAGS_AOG) { - snprintf(strFl, 6," grnd"); - } else if (a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) { - snprintf(strFl, 6, "%5d", altitude); - } - - printf("%06x %-4s %-4s %-8s %5s %3s %3s %7s %8s %3d %5d %2d\n", - a->addr, strMode, strSquawk, a->flight, strFl, strGs, strTt, - strLat, strLon, signalAverage, msgs, (int)(now - a->seen)); - } - count++; - } - } - a = a->next; - } -} -// -//========================================================================= -// -// When in interactive mode If we don't receive new nessages within -// MODES_INTERACTIVE__DELETE_TTL seconds we remove the aircraft from the list. -// -void interactiveRemoveStaleAircrafts(void) { - struct aircraft *a = Modes.aircrafts; - struct aircraft *prev = NULL; - time_t now = time(NULL); - - while(a) { - if ((now - a->seen) > Modes.interactive_delete_ttl) { - struct aircraft *next = a->next; - // Remove the element from the linked list, with care - // if we are removing the first element - - if (!prev) - Modes.aircrafts = next; - else - prev->next = next; - - free(a); - a = next; - } else { - prev = a; - a = a->next; - } - } -} - -/* ============================== Snip mode ================================= */ - -/* Get raw IQ samples and filter everything is < than the specified level - * for more than 256 samples in order to reduce example file size. */ void snipMode(int level) { int i, q; uint64_t c = 0; @@ -3048,794 +332,9 @@ void snipMode(int level) { putchar(q); } } - -/* ============================= Networking ================================= - * Note: here we disregard any kind of good coding practice in favor of - * extreme simplicity, that is: - * - * 1) We only rely on the kernel buffers for our I/O without any kind of - * user space buffering. - * 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 - * 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. - */ - -/* Networking "stack" initialization. */ -void modesInitNet(void) { - struct { - char *descr; - int *socket; - int port; - } services[6] = { - {"Raw TCP output", &Modes.ros, Modes.net_output_raw_port}, - {"Raw TCP input", &Modes.ris, Modes.net_input_raw_port}, - {"Beast TCP output", &Modes.bos, Modes.net_output_beast_port}, - {"Beast TCP input", &Modes.bis, Modes.net_input_beast_port}, - {"HTTP server", &Modes.https, Modes.net_http_port}, - {"Basestation TCP output", &Modes.sbsos, Modes.net_output_sbs_port} - }; - int j; - - memset(Modes.clients,0,sizeof(Modes.clients)); - Modes.maxfd = -1; - - for (j = 0; j < 6; j++) { - int s = anetTcpServer(Modes.aneterr, services[j].port, NULL); - if (s == -1) { - fprintf(stderr, "Error opening the listening port %d (%s): %s\n", - services[j].port, services[j].descr, strerror(errno)); - exit(1); - } - anetNonBlock(Modes.aneterr, s); - *services[j].socket = s; - } - - signal(SIGPIPE, SIG_IGN); -} - -/* This function gets called from time to time when the decoding thread is - * awakened by new data arriving. This usually happens a few times every - * second. */ -void modesAcceptClients(void) { - int fd, port; - unsigned int j; - struct client *c; - int services[6]; - - services[0] = Modes.ros; - services[1] = Modes.ris; - services[2] = Modes.bos; - services[3] = Modes.bis; - services[4] = Modes.https; - services[5] = Modes.sbsos; - - for (j = 0; j < sizeof(services)/sizeof(int); j++) { - fd = anetTcpAccept(Modes.aneterr, services[j], NULL, &port); - if (fd == -1) continue; - - if (fd >= MODES_NET_MAX_FD) { - close(fd); - return; /* Max number of clients reached. */ - } - - anetNonBlock(Modes.aneterr, fd); - c = (struct client *) malloc(sizeof(*c)); - c->service = services[j]; - c->fd = fd; - c->buflen = 0; - Modes.clients[fd] = c; - anetSetSendBuffer(Modes.aneterr,fd,MODES_NET_SNDBUF_SIZE); - - if (Modes.maxfd < fd) Modes.maxfd = fd; - if (services[j] == Modes.sbsos) Modes.stat_sbs_connections++; - if (services[j] == Modes.ros) Modes.stat_raw_connections++; - if (services[j] == Modes.bos) Modes.stat_beast_connections++; - - j--; /* Try again with the same listening port. */ - - if (Modes.debug & MODES_DEBUG_NET) - printf("Created new client %d\n", fd); - } -} - -/* On error free the client, collect the structure, adjust maxfd if needed. */ -void modesFreeClient(int fd) { - close(fd); - if (Modes.clients[fd]->service == Modes.sbsos) { - if (Modes.stat_sbs_connections) Modes.stat_sbs_connections--; - } - else if (Modes.clients[fd]->service == Modes.ros) { - if (Modes.stat_raw_connections) Modes.stat_raw_connections--; - } - else if (Modes.clients[fd]->service == Modes.bos) { - if (Modes.stat_beast_connections) Modes.stat_beast_connections--; - } - free(Modes.clients[fd]); - Modes.clients[fd] = NULL; - - if (Modes.debug & MODES_DEBUG_NET) - printf("Closing client %d\n", fd); - - /* If this was our maxfd, rescan the full clients array to check what's - * the new max. */ - if (Modes.maxfd == fd) { - int j; - - Modes.maxfd = -1; - for (j = 0; j < MODES_NET_MAX_FD; j++) { - if (Modes.clients[j]) Modes.maxfd = j; - } - } -} - -/* Send the specified message to all clients listening for a given service. */ -void modesSendAllClients(int service, void *msg, int len) { - int j; - struct client *c; - - for (j = 0; j <= Modes.maxfd; j++) { - c = Modes.clients[j]; - if (c && c->service == service) { - int nwritten = write(j, msg, len); - if (nwritten != len) { - modesFreeClient(j); - } - } - } -} - -/* Write raw output in Beast Binary format with Timestamp to TCP clients */ -void modesSendBeastOutput(struct modesMessage *mm) { - char *p = &Modes.beastOut[Modes.beastOutUsed]; - int msgLen = mm->msgbits / 8; - char * pTimeStamp; - int j; - - *p++ = 0x1a; - if (msgLen == MODES_SHORT_MSG_BYTES) - {*p++ = '2';} - else if (msgLen == MODES_LONG_MSG_BYTES) - {*p++ = '3';} - else if (msgLen == MODEAC_MSG_BYTES) - {*p++ = '1';} - else - {return;} - - pTimeStamp = (char *) &mm->timestampMsg; - for (j = 5; j >= 0; j--) { - *p++ = pTimeStamp[j]; - } - - *p++ = mm->signalLevel; - - memcpy(p, mm->msg, msgLen); - - Modes.beastOutUsed += (msgLen + 9); - if (Modes.beastOutUsed >= Modes.net_output_raw_size) - { - modesSendAllClients(Modes.bos, Modes.beastOut, Modes.beastOutUsed); - Modes.beastOutUsed = 0; - Modes.net_output_raw_rate_count = 0; - } -} - -/* Write raw output to TCP clients. */ -void modesSendRawOutput(struct modesMessage *mm) { - char *p = &Modes.rawOut[Modes.rawOutUsed]; - int msgLen = mm->msgbits / 8; - int j; - unsigned char * pTimeStamp; - - if (Modes.mlat && mm->timestampMsg) { - *p++ = '@'; - pTimeStamp = (unsigned char *) &mm->timestampMsg; - for (j = 5; j >= 0; j--) { - sprintf(p, "%02X", pTimeStamp[j]); - p += 2; - } - Modes.rawOutUsed += 12; // additional 12 characters for timestamp - } else - *p++ = '*'; - - for (j = 0; j < msgLen; j++) { - sprintf(p, "%02X", mm->msg[j]); - p += 2; - } - - *p++ = ';'; - *p++ = '\n'; - - Modes.rawOutUsed += ((msgLen*2) + 3); - if (Modes.rawOutUsed >= Modes.net_output_raw_size) - { - modesSendAllClients(Modes.ros, Modes.rawOut, Modes.rawOutUsed); - Modes.rawOutUsed = 0; - Modes.net_output_raw_rate_count = 0; - } -} // -// Write SBS output to TCP clients -// The message structure mm->bFlags tells us what has been updated by this message +// ================================ Main ==================================== // -void modesSendSBSOutput(struct modesMessage *mm) { - char msg[256], *p = msg; - uint32_t offset; - struct timeb epocTime; - struct tm stTime; - int msgType; - - // - // SBS BS style output checked against the following reference - // http://www.homepages.mcb.net/bones/SBS/Article/Barebones42_Socket_Data.htm - seems comprehensive - // - - // Decide on the basic SBS Message Type - if ((mm->msgtype == 4) || (mm->msgtype == 20)) { - msgType = 5; - } else if ((mm->msgtype == 5) || (mm->msgtype == 21)) { - msgType = 6; - } else if ((mm->msgtype == 0) || (mm->msgtype == 16)) { - msgType = 7; - } else if (mm->msgtype == 11) { - msgType = 8; - } else if ((mm->msgtype != 17) && (mm->msgtype != 18)) { - return; - } else if ((mm->metype >= 1) && (mm->metype <= 4)) { - msgType = 1; - } else if ((mm->metype >= 5) && (mm->metype <= 8)) { - if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID) - {msgType = 2;} - else - {msgType = 7;} - } else if ((mm->metype >= 9) && (mm->metype <= 18)) { - if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID) - {msgType = 3;} - else - {msgType = 7;} - } else if (mm->metype != 19) { - return; - } else if ((mm->mesub == 1) || (mm->mesub == 2)) { - msgType = 4; - } else { - return; - } - - // Fields 1 to 6 : SBS message type and ICAO address of the aircraft and some other stuff - p += sprintf(p, "MSG,%d,111,11111,%06X,111111,", msgType, mm->addr); - - // Fields 7 & 8 are the current time and date - 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 - offset = (int) (mm->timestampMsg - Modes.timestampBlk); // This is the time (in 12Mhz ticks) into the Block - offset = offset / 12000; // convert to milliseconds - epocTime.millitm += offset; // add on the offset time to the Block start time - if (epocTime.millitm > 999) // if we've caused an overflow into the next second... - {epocTime.millitm -= 1000; epocTime.time ++;} // ..correct the overflow - stTime = *localtime(&epocTime.time); // convert the time to year, month day, hours, min, sec - p += sprintf(p, "%04d/%02d/%02d,", (stTime.tm_year+1900),(stTime.tm_mon+1), stTime.tm_mday); - p += sprintf(p, "%02d:%02d:%02d.%03d,", stTime.tm_hour, stTime.tm_min, stTime.tm_sec, epocTime.millitm); - } else { - p += sprintf(p, ",,"); - } - - // Fields 9 & 10 are the current time and date - ftime(&epocTime); // get the current system time & date - stTime = *localtime(&epocTime.time); // convert the time to year, month day, hours, min, sec - p += sprintf(p, "%04d/%02d/%02d,", (stTime.tm_year+1900),(stTime.tm_mon+1), stTime.tm_mday); - p += sprintf(p, "%02d:%02d:%02d.%03d", stTime.tm_hour, stTime.tm_min, stTime.tm_sec, epocTime.millitm); - - // Field 11 is the callsign (if we have it) - if (mm->bFlags & MODES_ACFLAGS_CALLSIGN_VALID) {p += sprintf(p, ",%s", mm->flight);} - else {p += sprintf(p, ",");} - - // Field 12 is the altitude (if we have it) - force to zero if we're on the ground - if ((mm->bFlags & MODES_ACFLAGS_AOG_GROUND) == MODES_ACFLAGS_AOG_GROUND) { - p += sprintf(p, ",0"); - } else if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) { - p += sprintf(p, ",%d", mm->altitude); - } else { - p += sprintf(p, ","); - } - - // Field 13 and 14 are the ground Speed and Heading (if we have them) - if (mm->bFlags & MODES_ACFLAGS_NSEWSPD_VALID) {p += sprintf(p, ",%d,%d", mm->velocity, mm->heading);} - else {p += sprintf(p, ",,");} - - // Fields 15 and 16 are the Lat/Lon (if we have it) - if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID) {p += sprintf(p, ",%1.5f,%1.5f", mm->fLat, mm->fLon);} - else {p += sprintf(p, ",,");} - - // Field 17 is the VerticalRate (if we have it) - if (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) {p += sprintf(p, ",%d", mm->vert_rate);} - else {p += sprintf(p, ",");} - - // Field 18 is the Squawk (if we have it) - if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) {p += sprintf(p, ",%x", mm->modeA);} - else {p += sprintf(p, ",");} - - // Field 19 is the Squawk Changing Alert flag (if we have it) - if (mm->bFlags & MODES_ACFLAGS_FS_VALID) { - if ((mm->fs >= 2) && (mm->fs <= 4)) { - p += sprintf(p, ",-1"); - } else { - p += sprintf(p, ",0"); - } - } else { - p += sprintf(p, ","); - } - - // Field 20 is the Squawk Emergency flag (if we have it) - if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) { - if ((mm->modeA == 0x7500) || (mm->modeA == 0x7600) || (mm->modeA == 0x7700)) { - p += sprintf(p, ",-1"); - } else { - p += sprintf(p, ",0"); - } - } else { - p += sprintf(p, ","); - } - - // Field 21 is the Squawk Ident flag (if we have it) - if (mm->bFlags & MODES_ACFLAGS_FS_VALID) { - if ((mm->fs >= 4) && (mm->fs <= 5)) { - p += sprintf(p, ",-1"); - } else { - p += sprintf(p, ",0"); - } - } else { - p += sprintf(p, ","); - } - - // Field 22 is the OnTheGround flag (if we have it) - if (mm->bFlags & MODES_ACFLAGS_AOG_VALID) { - if (mm->bFlags & MODES_ACFLAGS_AOG) { - p += sprintf(p, ",-1"); - } else { - p += sprintf(p, ",0"); - } - } else { - p += sprintf(p, ","); - } - - p += sprintf(p, "\r\n"); - modesSendAllClients(Modes.sbsos, msg, p-msg); -} -// -// This function decodes a Beast binary format message -// -// The message is passed to the higher level layers, so it feeds -// the selected screen output, the network output and so forth. -// -// If the message looks invalid it is silently discarded. -// -// The function always returns 0 (success) to the caller as there is no -// case where we want broken messages here to close the client connection. -int decodeBinMessage(struct client *c, char *p) { - int msgLen = 0; - unsigned char msg[MODES_LONG_MSG_BYTES]; - struct modesMessage mm; - MODES_NOTUSED(c); - memset(&mm, 0, sizeof(mm)); - - if ((*p == '1') && (Modes.mode_ac)) { // skip ModeA/C unless user enables --modes-ac - msgLen = MODEAC_MSG_BYTES; - } else if (*p == '2') { - msgLen = MODES_SHORT_MSG_BYTES; - } else if (*p == '3') { - msgLen = MODES_LONG_MSG_BYTES; - } - - if (msgLen) { - // Mark messages received over the internet as remote so that we don't try to - // pass them off as being received by this instance when forwarding them - mm.remote = 1; - p += 7; // Skip the timestamp - mm.signalLevel = *p++; // Grab the signal level - memcpy(msg, p, msgLen); // and the data - - if (msgLen == MODEAC_MSG_BYTES) { // ModeA or ModeC - decodeModeAMessage(&mm, ((msg[0] << 8) | msg[1])); - } else { - decodeModesMessage(&mm, msg); - } - - useModesMessage(&mm); - } - return (0); -} -// -// Turn an hex digit into its 4 bit decimal value. -// Returns -1 if the digit is not in the 0-F range. -int hexDigitVal(int c) { - c = tolower(c); - if (c >= '0' && c <= '9') return c-'0'; - else if (c >= 'a' && c <= 'f') return c-'a'+10; - else return -1; -} -// -// This function decodes a string representing message in raw hex format -// like: *8D4B969699155600E87406F5B69F; The string is null-terminated. -// -// The message is passed to the higher level layers, so it feeds -// the selected screen output, the network output and so forth. -// -// If the message looks invalid it is silently discarded. -// -// The function always returns 0 (success) to the caller as there is no -// case where we want broken messages here to close the client connection. -int decodeHexMessage(struct client *c, char *hex) { - int l = strlen(hex), j; - unsigned char msg[MODES_LONG_MSG_BYTES]; - struct modesMessage mm; - MODES_NOTUSED(c); - memset(&mm, 0, sizeof(mm)); - - // Mark messages received over the internet as remote so that we don't try to - // pass them off as being received by this instance when forwarding them - mm.remote = 1; - mm.signalLevel = 0xFF; - - // Remove spaces on the left and on the right - while(l && isspace(hex[l-1])) { - hex[l-1] = '\0'; l--; - } - while(isspace(*hex)) { - hex++; l--; - } - - // Turn the message into binary. - // Accept *-AVR raw @-AVR/BEAST timeS+raw %-AVR timeS+raw (CRC good) <-BEAST timeS+sigL+raw - // and some AVR records that we can understand - if (hex[l-1] != ';') {return (0);} // not complete - abort - - switch(hex[0]) { - case '<': { - mm.signalLevel = (hexDigitVal(hex[13])<<4) | hexDigitVal(hex[14]); - hex += 15; l -= 16; // Skip <, timestamp and siglevel, and ; - break;} - - case '@': // No CRC check - case '%': { // CRC is OK - hex += 13; l -= 14; // Skip @,%, and timestamp, and ; - break;} - - case '*': - case ':': { - hex++; l-=2; // Skip * and ; - break;} - - default: { - return (0); // We don't know what this is, so abort - break;} - } - - if ( (l != (MODEAC_MSG_BYTES * 2)) - && (l != (MODES_SHORT_MSG_BYTES * 2)) - && (l != (MODES_LONG_MSG_BYTES * 2)) ) - {return (0);} // Too short or long message... broken - - if ( (0 == Modes.mode_ac) - && (l == (MODEAC_MSG_BYTES * 2)) ) - {return (0);} // Right length for ModeA/C, but not enabled - - for (j = 0; j < l; j += 2) { - int high = hexDigitVal(hex[j]); - int low = hexDigitVal(hex[j+1]); - - if (high == -1 || low == -1) return 0; - msg[j/2] = (high << 4) | low; - } - - if (l == (MODEAC_MSG_BYTES * 2)) { // ModeA or ModeC - decodeModeAMessage(&mm, ((msg[0] << 8) | msg[1])); - } else { // Assume ModeS - decodeModesMessage(&mm, msg); - } - - useModesMessage(&mm); - return (0); -} - -/* Return a description of planes in json. No metric conversion. */ -char *aircraftsToJson(int *len) { - time_t now = time(NULL); - struct aircraft *a = Modes.aircrafts; - int buflen = 1024; /* The initial buffer is incremented as needed. */ - char *buf = (char *) malloc(buflen), *p = buf; - int l; - - l = snprintf(p,buflen,"[\n"); - p += l; buflen -= l; - while(a) { - int position = 0; - int track = 0; - - if (a->modeACflags & MODEAC_MSG_FLAG) { // skip any fudged ICAO records Mode A/C - a = a->next; - continue; - } - - - if (a->bFlags & MODES_ACFLAGS_LATLON_VALID) { - position = 1; - } - - if (a->bFlags & MODES_ACFLAGS_HEADING_VALID) { - track = 1; - } - - // No metric conversion - l = snprintf(p,buflen, - "{\"hex\":\"%06x\", \"squawk\":\"%04x\", \"flight\":\"%s\", \"lat\":%f, " - "\"lon\":%f, \"validposition\":%d, \"altitude\":%d, \"track\":%d, \"validtrack\":%d," - "\"speed\":%d, \"messages\":%ld, \"seen\":%d},\n", - a->addr, a->modeA, a->flight, a->lat, a->lon, position, a->altitude, a->track, track, - a->speed, a->messages, (int)(now - a->seen)); - p += l; buflen -= l; - - /* Resize if needed. */ - if (buflen < 256) { - int used = p-buf; - buflen += 1024; // Our increment. - buf = (char *) realloc(buf,used+buflen); - p = buf+used; - } - - a = a->next; - } - /* Remove the final comma if any, and closes the json array. */ - if (*(p-2) == ',') { - *(p-2) = '\n'; - p--; - buflen++; - } - l = snprintf(p,buflen,"]\n"); - p += l; buflen -= l; - - *len = p-buf; - return buf; -} - -#define MODES_CONTENT_TYPE_HTML "text/html;charset=utf-8" -#define MODES_CONTENT_TYPE_CSS "text/css;charset=utf-8" -#define MODES_CONTENT_TYPE_JSON "application/json;charset=utf-8" -#define MODES_CONTENT_TYPE_JS "application/javascript;charset=utf-8" - -/* Get an HTTP request header and write the response to the client. - * Again here we assume that the socket buffer is enough without doing - * any kind of userspace buffering. - * - * Returns 1 on error to signal the caller the client connection should - * be closed. */ -int handleHTTPRequest(struct client *c, char *p) { - char hdr[512]; - int clen, hdrlen; - int httpver, keepalive; - char *url, *content; - char ctype[48]; - char getFile[1024]; - char *ext; - - if (Modes.debug & MODES_DEBUG_NET) - printf("\nHTTP request: %s\n", c->buf); - - // Minimally parse the request. - httpver = (strstr(p, "HTTP/1.1") != NULL) ? 11 : 10; - if (httpver == 10) { - // HTTP 1.0 defaults to close, unless otherwise specified. - keepalive = strstr(p, "Connection: keep-alive") != NULL; - } else if (httpver == 11) { - // HTTP 1.1 defaults to keep-alive, unless close is specified. - keepalive = strstr(p, "Connection: close") == NULL; - } - - // Identify he URL. - p = strchr(p,' '); - if (!p) return 1; /* There should be the method and a space... */ - url = ++p; /* Now this should point to the requested URL. */ - p = strchr(p, ' '); - if (!p) return 1; /* There should be a space before HTTP/... */ - *p = '\0'; - - if (Modes.debug & MODES_DEBUG_NET) { - printf("\nHTTP keep alive: %d\n", keepalive); - printf("HTTP requested URL: %s\n\n", url); - } - - if (strlen(url) < 2) { - snprintf(getFile, sizeof getFile, "%s/gmap.html", HTMLPATH); // Default file - } else { - snprintf(getFile, sizeof getFile, "%s/%s", HTMLPATH, url); - } - - /* Select the content to send, we have just two so far: - * "/" -> Our google map application. - * "/data.json" -> Our ajax request to update planes. */ - if (strstr(url, "/data.json")) { - content = aircraftsToJson(&clen); - //snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JSON); - } else { - struct stat sbuf; - int fd = -1; - - if (stat(getFile, &sbuf) != -1 && (fd = open(getFile, O_RDONLY)) != -1) { - content = (char *) malloc(sbuf.st_size); - if (read(fd, content, sbuf.st_size) == -1) { - snprintf(content, sbuf.st_size, "Error reading from file: %s", strerror(errno)); - } - clen = sbuf.st_size; - } else { - char buf[128]; - clen = snprintf(buf,sizeof(buf),"Error opening HTML file: %s", strerror(errno)); - content = strdup(buf); - } - - if (fd != -1) { - close(fd); - } - } - - // Get file extension and content type - snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_HTML); // Default content type - ext = strrchr(getFile, '.'); - - if (strlen(ext) > 0) { - if (strstr(ext, ".json")) { - snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JSON); - } else if (strstr(ext, ".css")) { - snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_CSS); - } else if (strstr(ext, ".js")) { - snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JS); - } - } - - /* Create the header and send the reply. */ - hdrlen = snprintf(hdr, sizeof(hdr), - "HTTP/1.1 200 OK\r\n" - "Server: Dump1090\r\n" - "Content-Type: %s\r\n" - "Connection: %s\r\n" - "Content-Length: %d\r\n" - "\r\n", - ctype, - keepalive ? "keep-alive" : "close", - clen); - - if (Modes.debug & MODES_DEBUG_NET) { - printf("HTTP Reply header:\n%s", hdr); - } - - // Send header and content. - if (write(c->fd, hdr, hdrlen) == -1 || write(c->fd, content, clen) == -1) { - free(content); - return 1; - } - free(content); - Modes.stat_http_requests++; - return !keepalive; -} -// -// This function polls the clients using read() in order to receive new -// messages from the net. -// -// The message is supposed to be separated from the next message by the -// separator 'sep', which is a null-terminated C string. -// -// Every full message received is decoded and passed to the higher layers -// calling the function's 'handler'. -// -// The handler returns 0 on success, or 1 to signal this function we should -// close the connection with the client in case of non-recoverable errors. -void modesReadFromClient(struct client *c, char *sep, - int(*handler)(struct client *, char *)) { - int left; - int nread; - int fullmsg; - char *s, *e; - - while(1) { - - fullmsg = 0; - left = MODES_CLIENT_BUF_SIZE - c->buflen; - // If our buffer is full discard it, this is some badly formatted shit - if (left == 0) { - c->buflen = 0; - left = MODES_CLIENT_BUF_SIZE; - // If there is garbage, read more to discard it ASAP - } - nread = read(c->fd, c->buf+c->buflen, left); - - if (nread <= 0) { - if (nread == 0 || errno != EAGAIN) { // Error, or end of file - modesFreeClient(c->fd); - } - break; // Serve next client - } - c->buflen += nread; - - // Always null-term so we are free to use strstr() (it won't affect binary case) - c->buf[c->buflen] = '\0'; - - e = s = c->buf; // Start with the start of buffer, first message - - if (c->service == Modes.bis) { - // This is the Bease Binary scanning case. - // If there is a complete message still in the buffer, there must be the separator 'sep' - // in the buffer, note that we full-scan the buffer at every read for simplicity. - - left = c->buflen; // Length of valid search for memchr() - while (left && ((s = memchr(e, (char) 0x1a, left)) != NULL)) { // In reality the first byte of buffer 'should' be 0x1a - s++; // skip the 0x1a - if (*s == '1') { - e = s + MODEAC_MSG_BYTES + 8; // point past remainder of message - } else if (*s == '2') { - e = s + MODES_SHORT_MSG_BYTES + 8; - } else if (*s == '3') { - e = s + MODES_LONG_MSG_BYTES + 8; - } else { - e = s; // Not a valid beast message, skip - left = &(c->buf[c->buflen]) - e; - continue; - } - left = &(c->buf[c->buflen]) - e; - if (left < 0) { // Incomplete message in buffer - e = s - 1; // point back at last found 0x1a. - break; - } - // Have a 0x1a followed by 1, 2 or 3 - pass message less 0x1a to handler. - if (handler(c, s)) { - modesFreeClient(c->fd); - return; - } - fullmsg = 1; - } - s = e; // For the buffer remainder below - - } else { - // This is the ASCII scanning case, AVR RAW or HTTP at present - // If there is a complete message still in the buffer, there must be the separator 'sep' - // in the buffer, note that we full-scan the buffer at every read for simplicity. - - while ((e = strstr(s, sep)) != NULL) { // end of first message if found - *e = '\0'; // The handler expects null terminated strings - if (handler(c, s)) { // Pass message to handler. - modesFreeClient(c->fd); // Handler returns 1 on error to signal we . - return; // should close the client connection - } - s = e + strlen(sep); // Move to start of next message - fullmsg = 1; - } - } - - if (fullmsg) { // We processed something - so - c->buflen = &(c->buf[c->buflen]) - s; // The unprocessed buffer length - memmove(c->buf, s, c->buflen); // move what's remaining to the start of the buffer - } else { // If no message was decoded process the next client - break; - } - } -} -// -// Read data from clients. This function actually delegates a lower-level -// function that depends on the kind of service (raw, http, ...). -void modesReadFromClients(void) { - int j; - struct client *c; - - for (j = 0; j <= Modes.maxfd; j++) { - if ((c = Modes.clients[j]) == NULL) continue; - if (c->service == Modes.ris) - modesReadFromClient(c,"\n",decodeHexMessage); - else if (c->service == Modes.bis) - modesReadFromClient(c,"",decodeBinMessage); - else if (c->service == Modes.https) - modesReadFromClient(c,"\r\n\r\n",handleHTTPRequest); - } -} - -/* ================================ Main ==================================== */ - void showHelp(void) { printf( "-----------------------------------------------------------------------------\n" @@ -3889,13 +388,15 @@ void showHelp(void) { " j = Log frames to frames.js, loadable by debug.html\n" ); } - -/* This function is called a few times every second by main in order to - * perform tasks we need to do continuously, like accepting new clients - * from the net, refreshing the screen in interactive mode, and so forth. */ +// +//========================================================================= +// +// This function is called a few times every second by main in order to +// perform tasks we need to do continuously, like accepting new clients +// from the net, refreshing the screen in interactive mode, and so forth +// void backgroundTasks(void) { if (Modes.net) { - modesAcceptClients(); modesReadFromClients(); } @@ -3909,7 +410,9 @@ void backgroundTasks(void) { interactiveShowData(); } } - +// +//========================================================================= +// int main(int argc, char **argv) { int j; @@ -3917,14 +420,14 @@ int main(int argc, char **argv) { modesInitConfig(); signal(SIGINT, sigintHandler); // Define Ctrl/C handler (exit program) - /* Parse the command line options */ + // Parse the command line options for (j = 1; j < argc; j++) { - int more = j+1 < argc; /* There are more arguments. */ + int more = j+1 < argc; // There are more arguments if (!strcmp(argv[j],"--device-index") && more) { Modes.dev_index = atoi(argv[++j]); } else if (!strcmp(argv[j],"--gain") && more) { - Modes.gain = (int) atof(argv[++j])*10; /* Gain is in tens of DBs */ + Modes.gain = (int) atof(argv[++j])*10; // Gain is in tens of DBs } else if (!strcmp(argv[j],"--enable-agc")) { Modes.enable_agc++; } else if (!strcmp(argv[j],"--freq") && more) { @@ -4031,9 +534,11 @@ int main(int argc, char **argv) { // Initialization modesInit(); - if (Modes.debug & MODES_DEBUG_BADCRC) { - testAndTimeBitCorrection(); - } + + //if (Modes.debug & MODES_DEBUG_BADCRC) { + // testAndTimeBitCorrection(); + //} + if (Modes.net_only) { fprintf(stderr,"Net-only mode, no RTL device or file open.\n"); } else if (Modes.filename == NULL) { @@ -4048,15 +553,15 @@ int main(int argc, char **argv) { } if (Modes.net) modesInitNet(); - /* If the user specifies --net-only, just run in order to serve network - * clients without reading data from the RTL device. */ + // If the user specifies --net-only, just run in order to serve network + // clients without reading data from the RTL device while (Modes.net_only) { if (Modes.exit) exit(0); // If we exit net_only nothing further in main() backgroundTasks(); usleep(100000); } - /* Create the thread that will read the data from the device. */ + // Create the thread that will read the data from the device. pthread_create(&Modes.reader_thread, NULL, readerThreadEntryPoint, NULL); pthread_mutex_lock(&Modes.data_mutex); @@ -4068,15 +573,15 @@ int main(int argc, char **argv) { computeMagnitudeVector(); Modes.stSystemTimeBlk = Modes.stSystemTimeRTL; - /* Signal to the other thread that we processed the available data - * and we want more (useful for --ifile). */ + // Signal to the other thread that we processed the available data + // and we want more (useful for --ifile) Modes.data_ready = 0; pthread_cond_signal(&Modes.data_cond); - /* Process data after releasing the lock, so that the capturing - * thread can read data while we perform computationally expensive - * stuff * at the same time. (This should only be useful with very - * slow processors). */ + // Process data after releasing the lock, so that the capturing + // thread can read data while we perform computationally expensive + // stuff * at the same time. (This should only be useful with very + // slow processors). pthread_mutex_unlock(&Modes.data_mutex); detectModeS(Modes.magnitude, MODES_ASYNC_BUF_SAMPLES); Modes.timestampBlk += (MODES_ASYNC_BUF_SAMPLES*6); @@ -4088,6 +593,8 @@ int main(int argc, char **argv) { // If --stats were given, print statistics if (Modes.stats) { printf("\n\n"); + if (Modes.interactive) + interactiveShowData(); printf("%d ModeA/C detected\n", Modes.stat_ModeAC); printf("%d valid Mode-S preambles\n", Modes.stat_valid_preamble); printf("%d DF-?? fields corrected for length\n", Modes.stat_DF_Len_Corrected); @@ -4127,3 +634,6 @@ int main(int argc, char **argv) { pthread_join(Modes.reader_thread,NULL); // Wait on reader thread exit pthread_exit(0); } +// +//========================================================================= +// diff --git a/dump1090.h b/dump1090.h index 0992019..dd55c50 100644 --- a/dump1090.h +++ b/dump1090.h @@ -388,29 +388,30 @@ int detectModeA (uint16_t *m, struct modesMessage *mm); void decodeModeAMessage(struct modesMessage *mm, int ModeA); int ModeAToModeC (unsigned int ModeA); +// +// Functions exported from mode_s.c +// +void detectModeS (uint16_t *m, uint32_t mlen); +void decodeModesMessage (struct modesMessage *mm, unsigned char *msg); +void displayModesMessage(struct modesMessage *mm); +void useModesMessage (struct modesMessage *mm); +void computeMagnitudeVector(); +void decodeCPR (struct aircraft *a, int fflag, int surface); +int decodeCPRrelative (struct aircraft *a, int fflag, int surface); +void modesInitErrorInfo (); // // Functions exported from interactive.c // struct aircraft* interactiveReceiveData(struct modesMessage *mm); void interactiveShowData(void); void interactiveRemoveStaleAircrafts(void); - // -// Functions exported from dump1090.c +// Functions exported from net_io.c // +void modesInitNet (void); +void modesReadFromClients (void); void modesSendAllClients (int service, void *msg, int len); -void modesSendRawOutput (struct modesMessage *mm); -void modesSendBeastOutput (struct modesMessage *mm); -void modesSendSBSOutput (struct modesMessage *mm); -void useModesMessage (struct modesMessage *mm); - -void decodeCPR (struct aircraft *a, int fflag, int surface); -int decodeCPRrelative(struct aircraft *a, int fflag, int surface); - -int fixBitErrors (unsigned char *msg, int bits, int maxfix, char *fixedbits); - -void modesInitErrorInfo (); -int modesMessageLenByType(int type); +void modesQueueOutput (struct modesMessage *mm); #ifdef __cplusplus } diff --git a/interactive.c b/interactive.c new file mode 100644 index 0000000..688fc7e --- /dev/null +++ b/interactive.c @@ -0,0 +1,480 @@ +// dump1090, a Mode S messages decoder for RTLSDR devices. +// +// Copyright (C) 2012 by Salvatore Sanfilippo +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include "dump1090.h" +// +// ============================= Utility functions ========================== +// +static uint64_t mstime(void) { + struct timeval tv; + uint64_t mst; + + gettimeofday(&tv, NULL); + mst = ((uint64_t)tv.tv_sec)*1000; + mst += tv.tv_usec/1000; + return mst; +} +// +//========================= Interactive mode =============================== +// +// Return a new aircraft structure for the interactive mode linked list +// of aircraft +// +struct aircraft *interactiveCreateAircraft(struct modesMessage *mm) { + struct aircraft *a = (struct aircraft *) malloc(sizeof(*a)); + + // Default everything to zero/NULL + memset(a, 0, sizeof(*a)); + + // Now initialise things that should not be 0/NULL to their defaults + a->addr = mm->addr; + a->lat = a->lon = 0.0; + memset(a->signalLevel, mm->signalLevel, 8); // First time, initialise everything + // to the first signal strength + + // mm->msgtype 32 is used to represent Mode A/C. These values can never change, so + // set them once here during initialisation, and don't bother to set them every + // time this ModeA/C is received again in the future + if (mm->msgtype == 32) { + int modeC = ModeAToModeC(mm->modeA | mm->fs); + a->modeACflags = MODEAC_MSG_FLAG; + if (modeC < -12) { + a->modeACflags |= MODEAC_MSG_MODEA_ONLY; + } else { + mm->altitude = modeC * 100; + mm->bFlags |= MODES_ACFLAGS_ALTITUDE_VALID; + } + } + return (a); +} +// +//========================================================================= +// +// Return the aircraft with the specified address, or NULL if no aircraft +// exists with this address. +// +struct aircraft *interactiveFindAircraft(uint32_t addr) { + struct aircraft *a = Modes.aircrafts; + + while(a) { + if (a->addr == addr) return (a); + a = a->next; + } + return (NULL); +} +// +//========================================================================= +// +// We have received a Mode A or C response. +// +// Search through the list of known Mode-S aircraft and tag them if this Mode A/C +// matches their known Mode S Squawks or Altitudes(+/- 50feet). +// +// A Mode S equipped aircraft may also respond to Mode A and Mode C SSR interrogations. +// We can't tell if this is a Mode A or C, so scan through the entire aircraft list +// looking for matches on Mode A (squawk) and Mode C (altitude). Flag in the Mode S +// records that we have had a potential Mode A or Mode C response from this aircraft. +// +// If an aircraft responds to Mode A then it's highly likely to be responding to mode C +// too, and vice verca. Therefore, once the mode S record is tagged with both a Mode A +// and a Mode C flag, we can be fairly confident that this Mode A/C frame relates to that +// Mode S aircraft. +// +// Mode C's are more likely to clash than Mode A's; There could be several aircraft +// cruising at FL370, but it's less likely (though not impossible) that there are two +// aircraft on the same squawk. Therefore, give precidence to Mode A record matches +// +// Note : It's theoretically possible for an aircraft to have the same value for Mode A +// and Mode C. Therefore we have to check BOTH A AND C for EVERY S. +// +void interactiveUpdateAircraftModeA(struct aircraft *a) { + struct aircraft *b = Modes.aircrafts; + + while(b) { + if ((b->modeACflags & MODEAC_MSG_FLAG) == 0) {// skip any fudged ICAO records + + // If both (a) and (b) have valid squawks... + if ((a->bFlags & b->bFlags) & MODES_ACFLAGS_SQUAWK_VALID) { + // ...check for Mode-A == Mode-S Squawk matches + if (a->modeA == b->modeA) { // If a 'real' Mode-S ICAO exists using this Mode-A Squawk + b->modeAcount = a->messages; + b->modeACflags |= MODEAC_MSG_MODEA_HIT; + a->modeACflags |= MODEAC_MSG_MODEA_HIT; + if ( (b->modeAcount > 0) && + ( (b->modeCcount > 1) + || (a->modeACflags & MODEAC_MSG_MODEA_ONLY)) ) // Allow Mode-A only matches if this Mode-A is invalid Mode-C + {a->modeACflags |= MODEAC_MSG_MODES_HIT;} // flag this ModeA/C probably belongs to a known Mode S + } + } + + // If both (a) and (b) have valid altitudes... + if ((a->bFlags & b->bFlags) & MODES_ACFLAGS_ALTITUDE_VALID) { + // ... check for Mode-C == Mode-S Altitude matches + if ( (a->modeC == b->modeC ) // If a 'real' Mode-S ICAO exists at this Mode-C Altitude + || (a->modeC == b->modeC + 1) // or this Mode-C - 100 ft + || (a->modeC + 1 == b->modeC ) ) { // or this Mode-C + 100 ft + b->modeCcount = a->messages; + b->modeACflags |= MODEAC_MSG_MODEC_HIT; + a->modeACflags |= MODEAC_MSG_MODEC_HIT; + if ( (b->modeAcount > 0) && + (b->modeCcount > 1) ) + {a->modeACflags |= (MODEAC_MSG_MODES_HIT | MODEAC_MSG_MODEC_OLD);} // flag this ModeA/C probably belongs to a known Mode S + } + } + } + b = b->next; + } +} +// +//========================================================================= +// +void interactiveUpdateAircraftModeS() { + struct aircraft *a = Modes.aircrafts; + + while(a) { + int flags = a->modeACflags; + if (flags & MODEAC_MSG_FLAG) { // find any fudged ICAO records + + // clear the current A,C and S hit bits ready for this attempt + a->modeACflags = flags & ~(MODEAC_MSG_MODEA_HIT | MODEAC_MSG_MODEC_HIT | MODEAC_MSG_MODES_HIT); + + interactiveUpdateAircraftModeA(a); // and attempt to match them with Mode-S + } + a = a->next; + } +} +// +//========================================================================= +// +// Receive new messages and populate the interactive mode with more info +// +struct aircraft *interactiveReceiveData(struct modesMessage *mm) { + struct aircraft *a, *aux; + + // Return if (checking crc) AND (not crcok) AND (not fixed) + if (Modes.check_crc && (mm->crcok == 0) && (mm->correctedbits == 0)) + return NULL; + + // Lookup our aircraft or create a new one + a = interactiveFindAircraft(mm->addr); + if (!a) { // If it's a currently unknown aircraft.... + a = interactiveCreateAircraft(mm); // ., create a new record for it, + a->next = Modes.aircrafts; // .. and put it at the head of the list + Modes.aircrafts = a; + } else { + /* If it is an already known aircraft, move it on head + * so we keep aircrafts ordered by received message time. + * + * However move it on head only if at least one second elapsed + * since the aircraft that is currently on head sent a message, + * othewise with multiple aircrafts at the same time we have an + * useless shuffle of positions on the screen. */ + if (0 && Modes.aircrafts != a && (time(NULL) - a->seen) >= 1) { + aux = Modes.aircrafts; + while(aux->next != a) aux = aux->next; + /* Now we are a node before the aircraft to remove. */ + aux->next = aux->next->next; /* removed. */ + /* Add on head */ + a->next = Modes.aircrafts; + Modes.aircrafts = a; + } + } + + a->signalLevel[a->messages & 7] = mm->signalLevel;// replace the 8th oldest signal strength + a->seen = time(NULL); + a->timestamp = mm->timestampMsg; + a->messages++; + + // If a (new) CALLSIGN has been received, copy it to the aircraft structure + if (mm->bFlags & MODES_ACFLAGS_CALLSIGN_VALID) { + memcpy(a->flight, mm->flight, sizeof(a->flight)); + } + + // If a (new) ALTITUDE has been received, copy it to the aircraft structure + if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) { + if ( (a->modeCcount) // if we've a modeCcount already + && (a->altitude != mm->altitude ) ) // and Altitude has changed +// && (a->modeC != mm->modeC + 1) // and Altitude not changed by +100 feet +// && (a->modeC + 1 != mm->modeC ) ) // and Altitude not changes by -100 feet + { + a->modeCcount = 0; //....zero the hit count + a->modeACflags &= ~MODEAC_MSG_MODEC_HIT; + } + a->altitude = mm->altitude; + a->modeC = (mm->altitude + 49) / 100; + } + + // If a (new) SQUAWK has been received, copy it to the aircraft structure + if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) { + if (a->modeA != mm->modeA) { + a->modeAcount = 0; // Squawk has changed, so zero the hit count + a->modeACflags &= ~MODEAC_MSG_MODEA_HIT; + } + a->modeA = mm->modeA; + } + + // If a (new) HEADING has been received, copy it to the aircraft structure + if (mm->bFlags & MODES_ACFLAGS_HEADING_VALID) { + a->track = mm->heading; + } + + // If a (new) SPEED has been received, copy it to the aircraft structure + if (mm->bFlags & MODES_ACFLAGS_SPEED_VALID) { + a->speed = mm->velocity; + } + + // If a (new) Vertical Descent rate has been received, copy it to the aircraft structure + if (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) { + a->vert_rate = mm->vert_rate; + } + + // if the Aircraft has landed or taken off since the last message, clear the even/odd CPR flags + if ((mm->bFlags & MODES_ACFLAGS_AOG_VALID) && ((a->bFlags ^ mm->bFlags) & MODES_ACFLAGS_AOG)) { + a->bFlags &= ~(MODES_ACFLAGS_LLBOTH_VALID | MODES_ACFLAGS_AOG); + + } else if ( (mm->bFlags & MODES_ACFLAGS_LLEITHER_VALID) + && (((mm->bFlags | a->bFlags) & MODES_ACFLAGS_LLEITHER_VALID) == MODES_ACFLAGS_LLBOTH_VALID) ) { + // If it's a new even/odd raw lat/lon, and we now have both even and odd,decode the CPR + int fflag; + + if (mm->bFlags & MODES_ACFLAGS_LLODD_VALID) { + fflag = 1; + a->odd_cprlat = mm->raw_latitude; + a->odd_cprlon = mm->raw_longitude; + a->odd_cprtime = mstime(); + } else { + fflag = 0; + a->even_cprlat = mm->raw_latitude; + a->even_cprlon = mm->raw_longitude; + a->even_cprtime = mstime(); + } + // Try relative CPR first + if (decodeCPRrelative(a, fflag, (mm->bFlags & MODES_ACFLAGS_AOG))) { + // If it fails then try global if the two data are less than 10 seconds apart + if (abs((int)(a->even_cprtime - a->odd_cprtime)) <= 10000) { + decodeCPR(a, fflag, (mm->bFlags & MODES_ACFLAGS_AOG)); + } + } + + //If we sucessfully decoded, back copy the results to mm so that we can print them in list output + if (a->bFlags & MODES_ACFLAGS_LATLON_VALID) { + mm->bFlags |= MODES_ACFLAGS_LATLON_VALID; + mm->fLat = a->lat; + mm->fLon = a->lon; + } + } + + // Update the aircrafts a->bFlags to reflect the newly received mm->bFlags; + a->bFlags |= mm->bFlags; + + if (mm->msgtype == 32) { + int flags = a->modeACflags; + if ((flags & (MODEAC_MSG_MODEC_HIT | MODEAC_MSG_MODEC_OLD)) == MODEAC_MSG_MODEC_OLD) { + // + // This Mode-C doesn't currently hit any known Mode-S, but it used to because MODEAC_MSG_MODEC_OLD is + // set So the aircraft it used to match has either changed altitude, or gone out of our receiver range + // + // We've now received this Mode-A/C again, so it must be a new aircraft. It could be another aircraft + // at the same Mode-C altitude, or it could be a new airctraft with a new Mods-A squawk. + // + // To avoid masking this aircraft from the interactive display, clear the MODEAC_MSG_MODES_OLD flag + // and set messages to 1; + // + a->modeACflags = flags & ~MODEAC_MSG_MODEC_OLD; + a->messages = 1; + } + } + + return (a); +} +// +//========================================================================= +// +// Show the currently captured interactive data on screen. +// +void interactiveShowData(void) { + struct aircraft *a = Modes.aircrafts; + time_t now = time(NULL); + int count = 0; + char progress; + char spinner[4] = "|/-\\"; + + // Refresh screen every (MODES_INTERACTIVE_REFRESH_TIME) miliseconde + if ((mstime() - Modes.interactive_last_update) < MODES_INTERACTIVE_REFRESH_TIME) + {return;} + + Modes.interactive_last_update = mstime(); + + // Attempt to reconsile any ModeA/C with known Mode-S + // We can't condition on Modes.modeac because ModeA/C could be comming + // in from a raw input port which we can't turn off. + interactiveUpdateAircraftModeS(); + + progress = spinner[time(NULL)%4]; + +#ifndef _WIN32 + printf("\x1b[H\x1b[2J"); // Clear the screen +#else + system("cls"); +#endif + + if (Modes.interactive_rtl1090 == 0) { + printf ( +"Hex Mode Sqwk Flight Alt Spd Hdg Lat Long Sig Msgs Ti%c\n", progress); + } else { + printf ( +"Hex Flight Alt V/S GS TT SSR G*456^ Msgs Seen %c\n", progress); + } + printf( +"-------------------------------------------------------------------------------\n"); + + while(a && (count < Modes.interactive_rows)) { + + if ((now - a->seen) < Modes.interactive_display_ttl) + { + int msgs = a->messages; + int flags = a->modeACflags; + + if (flags & MODEAC_MSG_FLAG) { + if ( (0x3300 != (a->modeA & 0x7700)) // London Mil South 3300 - 3377 + && (0x6400 != (a->modeA & 0x7700)) // London Mil Daventry 6400 - 6477 + && (0x7400 != (a->modeA & 0x7740)) // Yeovilton 7400 - 7437 + && (0x4300 != (a->modeA & 0x7700)) // Yeovilton Lars 4300 - 4377 + && (0x2600 != (a->modeA & 0x7700)) // Boscombe 2600 - 2677 + && (0x7400 != (a->modeA & 0x7477)) ) // Emergency 7400,7500,7600,7700 + msgs = 0; + } + + if ( (((flags & (MODEAC_MSG_FLAG )) == 0 ) ) + || (((flags & (MODEAC_MSG_MODES_HIT | MODEAC_MSG_MODEA_ONLY)) == MODEAC_MSG_MODEA_ONLY) && (msgs > 4 ) ) + || (((flags & (MODEAC_MSG_MODES_HIT | MODEAC_MSG_MODEC_OLD )) == 0 ) && (msgs > 127) ) + ) { + int altitude = a->altitude, speed = a->speed; + char strSquawk[5] = " "; + char strFl[6] = " "; + char strTt[5] = " "; + char strGs[5] = " "; + + // Convert units to metric if --metric was specified + if (Modes.metric) { + altitude = (int) (altitude / 3.2828); + speed = (int) (speed * 1.852); + } + + if (a->bFlags & MODES_ACFLAGS_SQUAWK_VALID) { + snprintf(strSquawk,5,"%04x", a->modeA);} + + if (a->bFlags & MODES_ACFLAGS_SPEED_VALID) { + snprintf (strGs, 5,"%3d", speed);} + + if (a->bFlags & MODES_ACFLAGS_HEADING_VALID) { + snprintf (strTt, 5,"%03d", a->track);} + + if (msgs > 99999) { + msgs = 99999;} + + if (Modes.interactive_rtl1090) { // RTL1090 display mode + + if (a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) { + snprintf(strFl,6,"F%03d",(altitude/100)); + } + printf("%06x %-8s %-4s %-3s %-3s %4s %-6d %-2d\n", + a->addr, a->flight, strFl, strGs, strTt, strSquawk, msgs, (int)(now - a->seen)); + + } else { // Dump1090 display mode + char strMode[5] = " "; + char strLat[8] = " "; + char strLon[9] = " "; + unsigned char * pSig = a->signalLevel; + unsigned int signalAverage = (pSig[0] + pSig[1] + pSig[2] + pSig[3] + + pSig[4] + pSig[5] + pSig[6] + pSig[7] + 3) >> 3; + + if ((flags & MODEAC_MSG_FLAG) == 0) { + strMode[0] = 'S'; + } else if (flags & MODEAC_MSG_MODEA_ONLY) { + strMode[0] = 'A'; + } + if (flags & MODEAC_MSG_MODEA_HIT) {strMode[2] = 'a';} + if (flags & MODEAC_MSG_MODEC_HIT) {strMode[3] = 'c';} + + if (a->bFlags & MODES_ACFLAGS_LATLON_VALID) { + snprintf(strLat, 8,"%7.03f", a->lat); + snprintf(strLon, 9,"%8.03f", a->lon); + } + + if (a->bFlags & MODES_ACFLAGS_AOG) { + snprintf(strFl, 6," grnd"); + } else if (a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) { + snprintf(strFl, 6, "%5d", altitude); + } + + printf("%06x %-4s %-4s %-8s %5s %3s %3s %7s %8s %3d %5d %2d\n", + a->addr, strMode, strSquawk, a->flight, strFl, strGs, strTt, + strLat, strLon, signalAverage, msgs, (int)(now - a->seen)); + } + count++; + } + } + a = a->next; + } +} +// +//========================================================================= +// +// When in interactive mode If we don't receive new nessages within +// MODES_INTERACTIVE__DELETE_TTL seconds we remove the aircraft from the list. +// +void interactiveRemoveStaleAircrafts(void) { + struct aircraft *a = Modes.aircrafts; + struct aircraft *prev = NULL; + time_t now = time(NULL); + + while(a) { + if ((now - a->seen) > Modes.interactive_delete_ttl) { + struct aircraft *next = a->next; + // Remove the element from the linked list, with care + // if we are removing the first element + + if (!prev) + Modes.aircrafts = next; + else + prev->next = next; + + free(a); + a = next; + } else { + prev = a; + a = a->next; + } + } +} +// +//========================================================================= +// diff --git a/mode_ac.c b/mode_ac.c new file mode 100644 index 0000000..881a428 --- /dev/null +++ b/mode_ac.c @@ -0,0 +1,386 @@ +// dump1090, a Mode S messages decoder for RTLSDR devices. +// +// Copyright (C) 2012 by Salvatore Sanfilippo +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include "dump1090.h" +// +// ===================== Mode A/C detection and decoding =================== +// +// +// This table is used to build the Mode A/C variable called ModeABits.Each +// bit period is inspected, and if it's value exceeds the threshold limit, +// then the value in this table is or-ed into ModeABits. +// +// At the end of message processing, ModeABits will be the decoded ModeA value. +// +// We can also flag noise in bits that should be zeros - the xx bits. Noise in +// these bits cause bits (31-16) in ModeABits to be set. Then at the end of message +// processing we can test for errors by looking at these bits. +// +uint32_t ModeABitTable[24] = { +0x00000000, // F1 = 1 +0x00000010, // C1 +0x00001000, // A1 +0x00000020, // C2 +0x00002000, // A2 +0x00000040, // C4 +0x00004000, // A4 +0x40000000, // xx = 0 Set bit 30 if we see this high +0x00000100, // B1 +0x00000001, // D1 +0x00000200, // B2 +0x00000002, // D2 +0x00000400, // B4 +0x00000004, // D4 +0x00000000, // F2 = 1 +0x08000000, // xx = 0 Set bit 27 if we see this high +0x04000000, // xx = 0 Set bit 26 if we see this high +0x00000080, // SPI +0x02000000, // xx = 0 Set bit 25 if we see this high +0x01000000, // xx = 0 Set bit 24 if we see this high +0x00800000, // xx = 0 Set bit 23 if we see this high +0x00400000, // xx = 0 Set bit 22 if we see this high +0x00200000, // xx = 0 Set bit 21 if we see this high +0x00100000, // xx = 0 Set bit 20 if we see this high +}; +// +// This table is used to produce an error variable called ModeAErrs.Each +// inter-bit period is inspected, and if it's value falls outside of the +// expected range, then the value in this table is or-ed into ModeAErrs. +// +// At the end of message processing, ModeAErrs will indicate if we saw +// any inter-bit anomolies, and the bits that are set will show which +// bits had them. +// +uint32_t ModeAMidTable[24] = { +0x80000000, // F1 = 1 Set bit 31 if we see F1_C1 error +0x00000010, // C1 Set bit 4 if we see C1_A1 error +0x00001000, // A1 Set bit 12 if we see A1_C2 error +0x00000020, // C2 Set bit 5 if we see C2_A2 error +0x00002000, // A2 Set bit 13 if we see A2_C4 error +0x00000040, // C4 Set bit 6 if we see C3_A4 error +0x00004000, // A4 Set bit 14 if we see A4_xx error +0x40000000, // xx = 0 Set bit 30 if we see xx_B1 error +0x00000100, // B1 Set bit 8 if we see B1_D1 error +0x00000001, // D1 Set bit 0 if we see D1_B2 error +0x00000200, // B2 Set bit 9 if we see B2_D2 error +0x00000002, // D2 Set bit 1 if we see D2_B4 error +0x00000400, // B4 Set bit 10 if we see B4_D4 error +0x00000004, // D4 Set bit 2 if we see D4_F2 error +0x20000000, // F2 = 1 Set bit 29 if we see F2_xx error +0x08000000, // xx = 0 Set bit 27 if we see xx_xx error +0x04000000, // xx = 0 Set bit 26 if we see xx_SPI error +0x00000080, // SPI Set bit 15 if we see SPI_xx error +0x02000000, // xx = 0 Set bit 25 if we see xx_xx error +0x01000000, // xx = 0 Set bit 24 if we see xx_xx error +0x00800000, // xx = 0 Set bit 23 if we see xx_xx error +0x00400000, // xx = 0 Set bit 22 if we see xx_xx error +0x00200000, // xx = 0 Set bit 21 if we see xx_xx error +0x00100000, // xx = 0 Set bit 20 if we see xx_xx error +}; +// +// The "off air" format is,, +// _F1_C1_A1_C2_A2_C4_A4_xx_B1_D1_B2_D2_B4_D4_F2_xx_xx_SPI_ +// +// Bit spacing is 1.45uS, with 0.45uS high, and 1.00us low. This is a problem +// because we ase sampling at 2Mhz (500nS) so we are below Nyquist. +// +// The bit spacings are.. +// F1 : 0.00, +// 1.45, 2.90, 4.35, 5.80, 7.25, 8.70, +// X : 10.15, +// : 11.60, 13.05, 14.50, 15.95, 17.40, 18.85, +// F2 : 20.30, +// X : 21.75, 23.20, 24.65 +// +// This equates to the following sample point centers at 2Mhz. +// [ 0.0], +// [ 2.9], [ 5.8], [ 8.7], [11.6], [14.5], [17.4], +// [20.3], +// [23.2], [26.1], [29.0], [31.9], [34.8], [37.7] +// [40.6] +// [43.5], [46.4], [49.3] +// +// We know that this is a supposed to be a binary stream, so the signal +// should either be a 1 or a 0. Therefore, any energy above the noise level +// in two adjacent samples must be from the same pulse, so we can simply +// add the values together.. +// +int detectModeA(uint16_t *m, struct modesMessage *mm) + { + int j, lastBitWasOne; + int ModeABits = 0; + int ModeAErrs = 0; + int byte, bit; + int thisSample, lastBit, lastSpace = 0; + int m0, m1, m2, m3, mPhase; + int n0, n1, n2 ,n3; + int F1_sig, F1_noise; + int F2_sig, F2_noise; + int fSig, fNoise, fLevel, fLoLo; + + // m[0] contains the energy from 0 -> 499 nS + // m[1] contains the energy from 500 -> 999 nS + // m[2] contains the energy from 1000 -> 1499 nS + // m[3] contains the energy from 1500 -> 1999 nS + // + // We are looking for a Frame bit (F1) whose width is 450nS, followed by + // 1000nS of quiet. + // + // The width of the frame bit is 450nS, which is 90% of our sample rate. + // Therefore, in an ideal world, all the energy for the frame bit will be + // in a single sample, preceeded by (at least) one zero, and followed by + // two zeros, Best case we can look for ... + // + // 0 - 1 - 0 - 0 + // + // However, our samples are not phase aligned, so some of the energy from + // each bit could be spread over two consecutive samples. Worst case is + // that we sample half in one bit, and half in the next. In that case, + // we're looking for + // + // 0 - 0.5 - 0.5 - 0. + + m0 = m[0]; m1 = m[1]; + + if (m0 >= m1) // m1 *must* be bigger than m0 for this to be F1 + {return (0);} + + m2 = m[2]; m3 = m[3]; + + // + // if (m2 <= m0), then assume the sample bob on (Phase == 0), so don't look at m3 + if ((m2 <= m0) || (m2 < m3)) + {m3 = m2; m2 = m0;} + + if ( (m3 >= m1) // m1 must be bigger than m3 + || (m0 > m2) // m2 can be equal to m0 if ( 0,1,0,0 ) + || (m3 > m2) ) // m2 can be equal to m3 if ( 0,1,0,0 ) + {return (0);} + + // m0 = noise + // m1 = noise + (signal * X)) + // m2 = noise + (signal * (1-X)) + // m3 = noise + // + // Hence, assuming all 4 samples have similar amounts of noise in them + // signal = (m1 + m2) - ((m0 + m3) * 2) + // noise = (m0 + m3) / 2 + // + F1_sig = (m1 + m2) - ((m0 + m3) << 1); + F1_noise = (m0 + m3) >> 1; + + if ( (F1_sig < MODEAC_MSG_SQUELCH_LEVEL) // minimum required F1 signal amplitude + || (F1_sig < (F1_noise << 2)) ) // minimum allowable Sig/Noise ratio 4:1 + {return (0);} + + // If we get here then we have a potential F1, so look for an equally valid F2 20.3uS later + // + // Our F1 is centered somewhere between samples m[1] and m[2]. We can guestimate where F2 is + // by comparing the ratio of m1 and m2, and adding on 20.3 uS (40.6 samples) + // + mPhase = ((m2 * 20) / (m1 + m2)); + byte = (mPhase + 812) / 20; + n0 = m[byte++]; n1 = m[byte++]; + + if (n0 >= n1) // n1 *must* be bigger than n0 for this to be F2 + {return (0);} + + n2 = m[byte++]; + // + // if the sample bob on (Phase == 0), don't look at n3 + // + if ((mPhase + 812) % 20) + {n3 = m[byte++];} + else + {n3 = n2; n2 = n0;} + + if ( (n3 >= n1) // n1 must be bigger than n3 + || (n0 > n2) // n2 can be equal to n0 ( 0,1,0,0 ) + || (n3 > n2) ) // n2 can be equal to n3 ( 0,1,0,0 ) + {return (0);} + + F2_sig = (n1 + n2) - ((n0 + n3) << 1); + F2_noise = (n0 + n3) >> 1; + + if ( (F2_sig < MODEAC_MSG_SQUELCH_LEVEL) // minimum required F2 signal amplitude + || (F2_sig < (F2_noise << 2)) ) // maximum allowable Sig/Noise ratio 4:1 + {return (0);} + + fSig = (F1_sig + F2_sig) >> 1; + fNoise = (F1_noise + F2_noise) >> 1; + fLoLo = fNoise + (fSig >> 2); // 1/2 + fLevel = fNoise + (fSig >> 1); + lastBitWasOne = 1; + lastBit = F1_sig; + // + // Now step by a half ModeA bit, 0.725nS, which is 1.45 samples, which is 29/20 + // No need to do bit 0 because we've already selected it as a valid F1 + // Do several bits past the SPI to increase error rejection + // + for (j = 1, mPhase += 29; j < 48; mPhase += 29, j ++) + { + byte = 1 + (mPhase / 20); + + thisSample = m[byte] - fNoise; + if (mPhase % 20) // If the bit is split over two samples... + {thisSample += (m[byte+1] - fNoise);} // add in the second sample's energy + + // If we're calculating a space value + if (j & 1) + {lastSpace = thisSample;} + + else + {// We're calculating a new bit value + bit = j >> 1; + if (thisSample >= fLevel) + {// We're calculating a new bit value, and its a one + ModeABits |= ModeABitTable[bit--]; // or in the correct bit + + if (lastBitWasOne) + { // This bit is one, last bit was one, so check the last space is somewhere less than one + if ( (lastSpace >= (thisSample>>1)) || (lastSpace >= lastBit) ) + {ModeAErrs |= ModeAMidTable[bit];} + } + + else + {// This bit,is one, last bit was zero, so check the last space is somewhere less than one + if (lastSpace >= (thisSample >> 1)) + {ModeAErrs |= ModeAMidTable[bit];} + } + + lastBitWasOne = 1; + } + + + else + {// We're calculating a new bit value, and its a zero + if (lastBitWasOne) + { // This bit is zero, last bit was one, so check the last space is somewhere in between + if (lastSpace >= lastBit) + {ModeAErrs |= ModeAMidTable[bit];} + } + + else + {// This bit,is zero, last bit was zero, so check the last space is zero too + if (lastSpace >= fLoLo) + {ModeAErrs |= ModeAMidTable[bit];} + } + + lastBitWasOne = 0; + } + + lastBit = (thisSample >> 1); + } + } + + // + // Output format is : 00:A4:A2:A1:00:B4:B2:B1:00:C4:C2:C1:00:D4:D2:D1 + // + if ((ModeABits < 3) || (ModeABits & 0xFFFF8808) || (ModeAErrs) ) + {return (ModeABits = 0);} + + fSig = (fSig + 0x7F) >> 8; + mm->signalLevel = ((fSig < 255) ? fSig : 255); + + return ModeABits; + } +// +//========================================================================= +// +// Input format is : 00:A4:A2:A1:00:B4:B2:B1:00:C4:C2:C1:00:D4:D2:D1 +// +int ModeAToModeC(unsigned int ModeA) + { + unsigned int FiveHundreds = 0; + unsigned int OneHundreds = 0; + + if ( (ModeA & 0xFFFF888B) // D1 set is illegal. D2 set is > 62700ft which is unlikely + || ((ModeA & 0x000000F0) == 0) ) // C1,,C4 cannot be Zero + {return -9999;} + + if (ModeA & 0x0010) {OneHundreds ^= 0x007;} // C1 + if (ModeA & 0x0020) {OneHundreds ^= 0x003;} // C2 + if (ModeA & 0x0040) {OneHundreds ^= 0x001;} // C4 + + // Remove 7s from OneHundreds (Make 7->5, snd 5->7). + if ((OneHundreds & 5) == 5) {OneHundreds ^= 2;} + + // Check for invalid codes, only 1 to 5 are valid + if (OneHundreds > 5) + {return -9999;} + +//if (ModeA & 0x0001) {FiveHundreds ^= 0x1FF;} // D1 never used for altitude + if (ModeA & 0x0002) {FiveHundreds ^= 0x0FF;} // D2 + if (ModeA & 0x0004) {FiveHundreds ^= 0x07F;} // D4 + + if (ModeA & 0x1000) {FiveHundreds ^= 0x03F;} // A1 + if (ModeA & 0x2000) {FiveHundreds ^= 0x01F;} // A2 + if (ModeA & 0x4000) {FiveHundreds ^= 0x00F;} // A4 + + if (ModeA & 0x0100) {FiveHundreds ^= 0x007;} // B1 + if (ModeA & 0x0200) {FiveHundreds ^= 0x003;} // B2 + if (ModeA & 0x0400) {FiveHundreds ^= 0x001;} // B4 + + // Correct order of OneHundreds. + if (FiveHundreds & 1) {OneHundreds = 6 - OneHundreds;} + + return ((FiveHundreds * 5) + OneHundreds - 13); + } +// +//========================================================================= +// +void decodeModeAMessage(struct modesMessage *mm, int ModeA) + { + mm->msgtype = 32; // Valid Mode S DF's are DF-00 to DF-31. + // so use 32 to indicate Mode A/C + + mm->msgbits = 16; // Fudge up a Mode S style data stream + mm->msg[0] = (ModeA >> 8); + mm->msg[1] = (ModeA); + + // Fudge an ICAO address based on Mode A (remove the Ident bit) + // Use an upper address byte of FF, since this is ICAO unallocated + mm->addr = 0x00FF0000 | (ModeA & 0x0000FF7F); + + // Set the Identity field to ModeA + mm->modeA = ModeA & 0x7777; + mm->bFlags |= MODES_ACFLAGS_SQUAWK_VALID; + + // Flag ident in flight status + mm->fs = ModeA & 0x0080; + + // Not much else we can tell from a Mode A/C reply. + // Just fudge up a few bits to keep other code happy + mm->crcok = 1; + mm->correctedbits = 0; + } +// +// ===================== Mode A/C detection and decoding =================== +// \ No newline at end of file diff --git a/mode_s.c b/mode_s.c new file mode 100644 index 0000000..d106989 --- /dev/null +++ b/mode_s.c @@ -0,0 +1,2068 @@ +// dump1090, a Mode S messages decoder for RTLSDR devices. +// +// Copyright (C) 2012 by Salvatore Sanfilippo +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include "dump1090.h" +// +// ===================== Mode S detection and decoding =================== +// +// Parity table for MODE S Messages. +// The table contains 112 elements, every element corresponds to a bit set +// in the message, starting from the first bit of actual data after the +// preamble. +// +// For messages of 112 bit, the whole table is used. +// For messages of 56 bits only the last 56 elements are used. +// +// The algorithm is as simple as xoring all the elements in this table +// for which the corresponding bit on the message is set to 1. +// +// The latest 24 elements in this table are set to 0 as the checksum at the +// end of the message should not affect the computation. +// +// Note: this function can be used with DF11 and DF17, other modes have +// the CRC xored with the sender address as they are reply to interrogations, +// but a casual listener can't split the address from the checksum. +// +uint32_t modes_checksum_table[112] = { +0x3935ea, 0x1c9af5, 0xf1b77e, 0x78dbbf, 0xc397db, 0x9e31e9, 0xb0e2f0, 0x587178, +0x2c38bc, 0x161c5e, 0x0b0e2f, 0xfa7d13, 0x82c48d, 0xbe9842, 0x5f4c21, 0xd05c14, +0x682e0a, 0x341705, 0xe5f186, 0x72f8c3, 0xc68665, 0x9cb936, 0x4e5c9b, 0xd8d449, +0x939020, 0x49c810, 0x24e408, 0x127204, 0x093902, 0x049c81, 0xfdb444, 0x7eda22, +0x3f6d11, 0xe04c8c, 0x702646, 0x381323, 0xe3f395, 0x8e03ce, 0x4701e7, 0xdc7af7, +0x91c77f, 0xb719bb, 0xa476d9, 0xadc168, 0x56e0b4, 0x2b705a, 0x15b82d, 0xf52612, +0x7a9309, 0xc2b380, 0x6159c0, 0x30ace0, 0x185670, 0x0c2b38, 0x06159c, 0x030ace, +0x018567, 0xff38b7, 0x80665f, 0xbfc92b, 0xa01e91, 0xaff54c, 0x57faa6, 0x2bfd53, +0xea04ad, 0x8af852, 0x457c29, 0xdd4410, 0x6ea208, 0x375104, 0x1ba882, 0x0dd441, +0xf91024, 0x7c8812, 0x3e4409, 0xe0d800, 0x706c00, 0x383600, 0x1c1b00, 0x0e0d80, +0x0706c0, 0x038360, 0x01c1b0, 0x00e0d8, 0x00706c, 0x003836, 0x001c1b, 0xfff409, +0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, +0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, +0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000 +}; + +uint32_t modesChecksum(unsigned char *msg, int bits) { + uint32_t crc = 0; + uint32_t rem = 0; + int offset = (bits == 112) ? 0 : (112-56); + uint8_t theByte = *msg; + uint32_t * pCRCTable = &modes_checksum_table[offset]; + int j; + + // We don't really need to include the checksum itself + bits -= 24; + for(j = 0; j < bits; j++) { + if ((j & 7) == 0) + theByte = *msg++; + + // If bit is set, xor with corresponding table entry. + if (theByte & 0x80) {crc ^= *pCRCTable;} + pCRCTable++; + theByte = theByte << 1; + } + + 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. +// +// All known DF's 16 or greater are long. All known DF's 15 or less are short. +// There are lots of unused codes in both category, so we can assume ICAO will stick to +// these rules, meaning that the most significant bit of the DF indicates the length. +// +int modesMessageLenByType(int type) { + return (type & 0x10) ? MODES_LONG_MSG_BITS : MODES_SHORT_MSG_BITS ; +} +// +//========================================================================= +// +// Try to fix single bit errors using the checksum. On success modifies +// the original buffer with the fixed version, and returns the position +// of the error bit. Otherwise if fixing failed -1 is returned. +/* +int fixSingleBitErrors(unsigned char *msg, int bits) { + int j; + unsigned char aux[MODES_LONG_MSG_BYTES]; + + memcpy(aux, msg, bits/8); + + // 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 bitmask = 1 << (7 - (j & 7)); + + aux[byte] ^= bitmask; // Flip j-th bit + + if (0 == modesChecksum(aux, bits)) { + // The error is fixed. Overwrite the original buffer with the + // corrected sequence, and returns the error bit position + msg[byte] = aux[byte]; + return (j); + } + + aux[byte] ^= bitmask; // Flip j-th bit back again + } + return (-1); +} +*/ +//========================================================================= +// +// Similar to fixSingleBitErrors() but try every possible two bit combination. +// This is very slow and should be tried only against DF17 messages that +// don't pass the checksum, and only in Aggressive Mode. +/* +int fixTwoBitsErrors(unsigned char *msg, int bits) { + int j, i; + unsigned char aux[MODES_LONG_MSG_BYTES]; + + memcpy(aux, msg, bits/8); + + // 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 bitmask1 = 1 << (7 - (j & 7)); + aux[byte1] ^= bitmask1; // Flip j-th bit + + // Don't check the same pairs multiple times, so i starts from j+1 + for (i = j+1; i < bits; i++) { + int byte2 = i/8; + int bitmask2 = 1 << (7 - (i & 7)); + + aux[byte2] ^= bitmask2; // Flip i-th bit + + if (0 == modesChecksum(aux, bits)) { + // The error is fixed. Overwrite the original buffer with + // the corrected sequence, and returns the error bit position + msg[byte1] = aux[byte1]; + msg[byte2] = aux[byte2]; + + // 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[byte1] ^= bitmask1; // Flip j-th bit back + } + } + return (-1); +} +*/ +// +//========================================================================= +// +// Code for introducing a less CPU-intensive method of correcting +// single bit errors. +// +// Makes use of the fact that the crc checksum is linear with respect to +// the bitwise xor operation, i.e. +// crc(m^e) = (crc(m)^crc(e) +// where m and e are the message resp. error bit vectors. +// +// Call crc(e) the syndrome. +// +// The code below works by precomputing a table of (crc(e), e) for all +// possible error vectors e (here only single bit and double bit errors), +// search for the syndrome in the table, and correct the then known error. +// The error vector e is represented by one or two bit positions that are +// changed. If a second bit position is not used, it is -1. +// +// Run-time is binary search in a sorted table, plus some constant overhead, +// instead of running through all possible bit positions (resp. pairs of +// bit positions). +// +struct errorinfo { + uint32_t syndrome; // CRC syndrome + int bits; // Number of bit positions to fix + int pos[MODES_MAX_BITERRORS]; // Bit positions corrected by this syndrome +}; + +#define NERRORINFO \ + (MODES_LONG_MSG_BITS+MODES_LONG_MSG_BITS*(MODES_LONG_MSG_BITS-1)/2) +struct errorinfo bitErrorTable[NERRORINFO]; + +// Compare function as needed for stdlib's qsort and bsearch functions +int cmpErrorInfo(const void *p0, const void *p1) { + struct errorinfo *e0 = (struct errorinfo*)p0; + struct errorinfo *e1 = (struct errorinfo*)p1; + if (e0->syndrome == e1->syndrome) { + return 0; + } else if (e0->syndrome < e1->syndrome) { + return -1; + } else { + return 1; + } +} +// +//========================================================================= +// +// Compute the table of all syndromes for 1-bit and 2-bit error vectors +void modesInitErrorInfo() { + unsigned char msg[MODES_LONG_MSG_BYTES]; + int i, j, n; + uint32_t crc; + n = 0; + memset(bitErrorTable, 0, sizeof(bitErrorTable)); + memset(msg, 0, MODES_LONG_MSG_BYTES); + // Add all possible single and double bit errors + // don't include errors in first 5 bits (DF type) + for (i = 5; i < MODES_LONG_MSG_BITS; i++) { + int bytepos0 = (i >> 3); + int mask0 = 1 << (7 - (i & 7)); + msg[bytepos0] ^= mask0; // create error0 + crc = modesChecksum(msg, MODES_LONG_MSG_BITS); + bitErrorTable[n].syndrome = crc; // single bit error case + bitErrorTable[n].bits = 1; + bitErrorTable[n].pos[0] = i; + bitErrorTable[n].pos[1] = -1; + n += 1; + + if (Modes.nfix_crc > 1) { + for (j = i+1; j < MODES_LONG_MSG_BITS; j++) { + int bytepos1 = (j >> 3); + int mask1 = 1 << (7 - (j & 7)); + msg[bytepos1] ^= mask1; // create error1 + crc = modesChecksum(msg, MODES_LONG_MSG_BITS); + if (n >= NERRORINFO) { + //fprintf(stderr, "Internal error, too many entries, fix NERRORINFO\n"); + break; + } + bitErrorTable[n].syndrome = crc; // two bit error case + bitErrorTable[n].bits = 2; + bitErrorTable[n].pos[0] = i; + bitErrorTable[n].pos[1] = j; + n += 1; + msg[bytepos1] ^= mask1; // revert error1 + } + } + msg[bytepos0] ^= mask0; // revert error0 + } + qsort(bitErrorTable, NERRORINFO, sizeof(struct errorinfo), cmpErrorInfo); + + // Test code: report if any syndrome appears at least twice. In this + // case the correction cannot be done without ambiguity. + // Tried it, does not happen for 1- and 2-bit errors. + /* + for (i = 1; i < NERRORINFO; i++) { + if (bitErrorTable[i-1].syndrome == bitErrorTable[i].syndrome) { + fprintf(stderr, "modesInitErrorInfo: Collision for syndrome %06x\n", + (int)bitErrorTable[i].syndrome); + } + } + + for (i = 0; i < NERRORINFO; i++) { + printf("syndrome %06x bit0 %3d bit1 %3d\n", + bitErrorTable[i].syndrome, + bitErrorTable[i].pos0, bitErrorTable[i].pos1); + } + */ +} +// +//========================================================================= +// +// Search for syndrome in table and if an entry is found, flip the necessary +// bits. Make sure the indices fit into the array +// Additional parameter: fix only less than maxcorrected bits, and record +// fixed bit positions in corrected[]. This array can be NULL, otherwise +// must be of length at least maxcorrected. +// Return number of fixed bits. +// +int fixBitErrors(unsigned char *msg, int bits, int maxfix, char *fixedbits) { + struct errorinfo *pei; + struct errorinfo ei; + int bitpos, offset, res, i; + memset(&ei, 0, sizeof(struct errorinfo)); + ei.syndrome = modesChecksum(msg, bits); + pei = bsearch(&ei, bitErrorTable, NERRORINFO, + sizeof(struct errorinfo), cmpErrorInfo); + if (pei == NULL) { + return 0; // No syndrome found + } + + // Check if the syndrome fixes more bits than we allow + if (maxfix < pei->bits) { + return 0; + } + + // Check that all bit positions lie inside the message length + offset = MODES_LONG_MSG_BITS-bits; + for (i = 0; i < pei->bits; i++) { + bitpos = pei->pos[i] - offset; + if ((bitpos < 0) || (bitpos >= bits)) { + return 0; + } + } + + // Fix the bits + for (i = res = 0; i < pei->bits; i++) { + bitpos = pei->pos[i] - offset; + msg[bitpos >> 3] ^= (1 << (7 - (bitpos & 7))); + if (fixedbits) { + fixedbits[res++] = bitpos; + } + } + return res; +} +// +// ============================== Debugging ================================= +// +// Helper function for dumpMagnitudeVector(). +// It prints a single bar used to display raw signals. +// +// Since every magnitude sample is between 0-255, the function uses +// up to 63 characters for every bar. Every character represents +// a length of 4, 3, 2, 1, specifically: +// +// "O" is 4 +// "o" is 3 +// "-" is 2 +// "." is 1 +// +void dumpMagnitudeBar(int index, int magnitude) { + char *set = " .-o"; + char buf[256]; + int div = magnitude / 256 / 4; + int rem = magnitude / 256 % 4; + + memset(buf,'O',div); + buf[div] = set[rem]; + buf[div+1] = '\0'; + + if (index >= 0) + printf("[%.3d] |%-66s %d\n", index, buf, magnitude); + else + printf("[%.2d] |%-66s %d\n", index, buf, magnitude); +} +// +//========================================================================= +// +// Display an ASCII-art alike graphical representation of the undecoded +// message as a magnitude signal. +// +// The message starts at the specified offset in the "m" buffer. +// The function will display enough data to cover a short 56 bit message. +// +// If possible a few samples before the start of the messsage are included +// for context. +// +void dumpMagnitudeVector(uint16_t *m, uint32_t offset) { + uint32_t padding = 5; // Show a few samples before the actual start. + uint32_t start = (offset < padding) ? 0 : offset-padding; + uint32_t end = offset + (MODES_PREAMBLE_SAMPLES)+(MODES_SHORT_MSG_SAMPLES) - 1; + uint32_t j; + + for (j = start; j <= end; j++) { + dumpMagnitudeBar(j-offset, m[j]); + } +} +// +//========================================================================= +// +// Produce a raw representation of the message as a Javascript file +// loadable by debug.html. +// +void dumpRawMessageJS(char *descr, unsigned char *msg, + uint16_t *m, uint32_t offset, int fixable, char *bitpos) +{ + int padding = 5; // Show a few samples before the actual start. + int start = offset - padding; + int end = offset + (MODES_PREAMBLE_SAMPLES)+(MODES_LONG_MSG_SAMPLES) - 1; + FILE *fp; + int j; + + MODES_NOTUSED(fixable); + if ((fp = fopen("frames.js","a")) == NULL) { + fprintf(stderr, "Error opening frames.js: %s\n", strerror(errno)); + exit(1); + } + + fprintf(fp,"frames.push({\"descr\": \"%s\", \"mag\": [", descr); + for (j = start; j <= end; j++) { + fprintf(fp,"%d", j < 0 ? 0 : m[j]); + if (j != end) fprintf(fp,","); + } + fprintf(fp,"], \"fix1\": %d, \"fix2\": %d, \"bits\": %d, \"hex\": \"", + bitpos[0], bitpos[1] , modesMessageLenByType(msg[0]>>3)); + for (j = 0; j < MODES_LONG_MSG_BYTES; j++) + fprintf(fp,"\\x%02x",msg[j]); + fprintf(fp,"\"});\n"); + fclose(fp); +} +// +//========================================================================= +// +// This is a wrapper for dumpMagnitudeVector() that also show the message +// in hex format with an additional description. +// +// descr is the additional message to show to describe the dump. +// msg points to the decoded message +// m is the original magnitude vector +// offset is the offset where the message starts +// +// The function also produces the Javascript file used by debug.html to +// display packets in a graphical format if the Javascript output was +// enabled. +// +void dumpRawMessage(char *descr, unsigned char *msg, uint16_t *m, uint32_t offset) { + int j; + int msgtype = msg[0] >> 3; + int fixable = 0; + char bitpos[MODES_MAX_BITERRORS]; + + for (j = 0; j < MODES_MAX_BITERRORS; j++) { + bitpos[j] = -1; + } + if (msgtype == 17) { + fixable = fixBitErrors(msg, MODES_LONG_MSG_BITS, MODES_MAX_BITERRORS, bitpos); + } + + if (Modes.debug & MODES_DEBUG_JS) { + dumpRawMessageJS(descr, msg, m, offset, fixable, bitpos); + return; + } + + printf("\n--- %s\n ", descr); + for (j = 0; j < MODES_LONG_MSG_BYTES; j++) { + printf("%02x",msg[j]); + if (j == MODES_SHORT_MSG_BYTES-1) printf(" ... "); + } + printf(" (DF %d, Fixable: %d)\n", msgtype, fixable); + dumpMagnitudeVector(m,offset); + printf("---\n\n"); +} +// +//========================================================================= +// +// Code for testing the timing: run all possible 1- and 2-bit error +// the test message by all 1-bit errors. Run the old code against +// all of them, and new the code. +// +// Example measurements: +// Timing old vs. new crc correction code: +// Old code: 1-bit errors on 112 msgs: 3934 usecs +// New code: 1-bit errors on 112 msgs: 104 usecs +// Old code: 2-bit errors on 6216 msgs: 407743 usecs +// New code: 2-bit errors on 6216 msgs: 5176 usecs +// indicating a 37-fold resp. 78-fold improvement in speed for 1-bit resp. +// 2-bit error. +/* +unsigned char tmsg0[MODES_LONG_MSG_BYTES] = { + // Test data: first ADS-B message from testfiles/modes1.bin + 0x8f, 0x4d, 0x20, 0x23, 0x58, 0x7f, 0x34, 0x5e, + 0x35, 0x83, 0x7e, 0x22, 0x18, 0xb2 +}; +#define NTWOBITS (MODES_LONG_MSG_BITS*(MODES_LONG_MSG_BITS-1)/2) +unsigned char tmsg1[MODES_LONG_MSG_BITS][MODES_LONG_MSG_BYTES]; +unsigned char tmsg2[NTWOBITS][MODES_LONG_MSG_BYTES]; +// Init an array of cloned messages with all possible 1-bit errors present, +// applied to each message at the respective position +// +void inittmsg1() { + int i, bytepos, mask; + for (i = 0; i < MODES_LONG_MSG_BITS; i++) { + bytepos = i >> 3; + mask = 1 << (7 - (i & 7)); + memcpy(&tmsg1[i][0], tmsg0, MODES_LONG_MSG_BYTES); + tmsg1[i][bytepos] ^= mask; + } +} + +// Run sanity check on all but first 5 messages / bits, as those bits +// are not corrected. +// +void checktmsg1(FILE *out) { + int i, k; + uint32_t crc; + for (i = 5; i < MODES_LONG_MSG_BITS; i++) { + crc = modesChecksum(&tmsg1[i][0], MODES_LONG_MSG_BITS); + if (crc != 0) { + fprintf(out, "CRC not fixed for " + "positon %d\n", i); + fprintf(out, " MSG "); + for (k = 0; k < MODES_LONG_MSG_BYTES; k++) { + fprintf(out, "%02x", tmsg1[i][k]); + } + fprintf(out, "\n"); + } + } +} + +void inittmsg2() { + int i, j, n, bytepos0, bytepos1, mask0, mask1; + n = 0; + for (i = 0; i < MODES_LONG_MSG_BITS; i++) { + bytepos0 = i >> 3; + mask0 = 1 << (7 - (i & 7)); + for (j = i+1; j < MODES_LONG_MSG_BITS; j++) { + bytepos1 = j >> 3; + mask1 = 1 << (7 - (j & 7)); + memcpy(&tmsg2[n][0], tmsg0, MODES_LONG_MSG_BYTES); + tmsg2[n][bytepos0] ^= mask0; + tmsg2[n][bytepos1] ^= mask1; + n += 1; + } + } +} + +long difftvusec(struct timeval *t0, struct timeval *t1) { + long res = 0; + res = t1->tv_usec-t0->tv_usec; + res += (t1->tv_sec-t0->tv_sec)*1000000L; + return res; +} + +// the actual test code +void testAndTimeBitCorrection() { + struct timeval starttv, endtv; + int i; + // Run timing on 1-bit errors + printf("Timing old vs. new crc correction code:\n"); + inittmsg1(); + gettimeofday(&starttv, NULL); + for (i = 0; i < MODES_LONG_MSG_BITS; i++) { + fixSingleBitErrors(&tmsg1[i][0], MODES_LONG_MSG_BITS); + } + gettimeofday(&endtv, NULL); + printf(" Old code: 1-bit errors on %d msgs: %ld usecs\n", + MODES_LONG_MSG_BITS, difftvusec(&starttv, &endtv)); + checktmsg1(stdout); + // Re-init + inittmsg1(); + gettimeofday(&starttv, NULL); + for (i = 0; i < MODES_LONG_MSG_BITS; i++) { + fixBitErrors(&tmsg1[i][0], MODES_LONG_MSG_BITS, MODES_MAX_BITERRORS, NULL); + } + gettimeofday(&endtv, NULL); + printf(" New code: 1-bit errors on %d msgs: %ld usecs\n", + MODES_LONG_MSG_BITS, difftvusec(&starttv, &endtv)); + checktmsg1(stdout); + // Run timing on 2-bit errors + inittmsg2(); + gettimeofday(&starttv, NULL); + for (i = 0; i < NTWOBITS; i++) { + fixSingleBitErrors(&tmsg2[i][0], MODES_LONG_MSG_BITS); + } + gettimeofday(&endtv, NULL); + printf(" Old code: 2-bit errors on %d msgs: %ld usecs\n", + NTWOBITS, difftvusec(&starttv, &endtv)); + // Re-init + inittmsg2(); + gettimeofday(&starttv, NULL); + for (i = 0; i < NTWOBITS; i++) { + fixBitErrors(&tmsg2[i][0], MODES_LONG_MSG_BITS, MODES_MAX_BITERRORS, NULL); + } + gettimeofday(&endtv, NULL); + printf(" New code: 2-bit errors on %d msgs: %ld usecs\n", + NTWOBITS, difftvusec(&starttv, &endtv)); +} +*/ +//========================================================================= +// +// Hash the ICAO address to index our cache of MODES_ICAO_CACHE_LEN +// elements, that is assumed to be a power of two +// +uint32_t ICAOCacheHashAddress(uint32_t a) { + // The following three rounds wil make sure that every bit affects + // every output bit with ~ 50% of probability. + a = ((a >> 16) ^ a) * 0x45d9f3b; + a = ((a >> 16) ^ a) * 0x45d9f3b; + a = ((a >> 16) ^ a); + return a & (MODES_ICAO_CACHE_LEN-1); +} +// +//========================================================================= +// +// Add the specified entry to the cache of recently seen ICAO addresses. +// Note that we also add a timestamp so that we can make sure that the +// entry is only valid for MODES_ICAO_CACHE_TTL seconds. +// +void addRecentlySeenICAOAddr(uint32_t addr) { + uint32_t h = ICAOCacheHashAddress(addr); + Modes.icao_cache[h*2] = addr; + Modes.icao_cache[h*2+1] = (uint32_t) time(NULL); +} +// +//========================================================================= +// +// Returns 1 if the specified ICAO address was seen in a DF format with +// proper checksum (not xored with address) no more than * MODES_ICAO_CACHE_TTL +// seconds ago. Otherwise returns 0. +// +int ICAOAddressWasRecentlySeen(uint32_t addr) { + uint32_t h = ICAOCacheHashAddress(addr); + uint32_t a = Modes.icao_cache[h*2]; + uint32_t t = Modes.icao_cache[h*2+1]; + + return a && a == addr && time(NULL)-t <= MODES_ICAO_CACHE_TTL; +} +// +//========================================================================= +// +// In the squawk (identity) field bits are interleaved as follows in +// (message bit 20 to bit 32): +// +// C1-A1-C2-A2-C4-A4-ZERO-B1-D1-B2-D2-B4-D4 +// +// So every group of three bits A, B, C, D represent an integer from 0 to 7. +// +// The actual meaning is just 4 octal numbers, but we convert it into a hex +// number tha happens to represent the four octal numbers. +// +// For more info: http://en.wikipedia.org/wiki/Gillham_code +// +int decodeID13Field(int ID13Field) { + int hexGillham = 0; + + if (ID13Field & 0x1000) {hexGillham |= 0x0010;} // Bit 12 = C1 + if (ID13Field & 0x0800) {hexGillham |= 0x1000;} // Bit 11 = A1 + if (ID13Field & 0x0400) {hexGillham |= 0x0020;} // Bit 10 = C2 + if (ID13Field & 0x0200) {hexGillham |= 0x2000;} // Bit 9 = A2 + if (ID13Field & 0x0100) {hexGillham |= 0x0040;} // Bit 8 = C4 + if (ID13Field & 0x0080) {hexGillham |= 0x4000;} // Bit 7 = A4 + //if (ID13Field & 0x0040) {hexGillham |= 0x0800;} // Bit 6 = X or M + if (ID13Field & 0x0020) {hexGillham |= 0x0100;} // Bit 5 = B1 + if (ID13Field & 0x0010) {hexGillham |= 0x0001;} // Bit 4 = D1 or Q + if (ID13Field & 0x0008) {hexGillham |= 0x0200;} // Bit 3 = B2 + if (ID13Field & 0x0004) {hexGillham |= 0x0002;} // Bit 2 = D2 + if (ID13Field & 0x0002) {hexGillham |= 0x0400;} // Bit 1 = B4 + if (ID13Field & 0x0001) {hexGillham |= 0x0004;} // Bit 0 = D4 + + return (hexGillham); + } +// +//========================================================================= +// +// Decode the 13 bit AC altitude field (in DF 20 and others). +// Returns the altitude, and set 'unit' to either MODES_UNIT_METERS or MDOES_UNIT_FEETS. +// +int decodeAC13Field(int AC13Field, int *unit) { + int m_bit = AC13Field & 0x0040; // set = meters, clear = feet + int q_bit = AC13Field & 0x0010; // set = 25 ft encoding, clear = Gillham Mode C encoding + + if (!m_bit) { + *unit = MODES_UNIT_FEET; + if (q_bit) { + // N is the 11 bit integer resulting from the removal of bit Q and M + int n = ((AC13Field & 0x1F80) >> 2) | + ((AC13Field & 0x0020) >> 1) | + (AC13Field & 0x000F); + // The final altitude is resulting number multiplied by 25, minus 1000. + return ((n * 25) - 1000); + } else { + // N is an 11 bit Gillham coded altitude + int n = ModeAToModeC(decodeID13Field(AC13Field)); + if (n < -12) {n = 0;} + + return (100 * n); + } + } else { + *unit = MODES_UNIT_METERS; + // TODO: Implement altitude when meter unit is selected + } + return 0; +} +// +//========================================================================= +// +// Decode the 12 bit AC altitude field (in DF 17 and others). +// +int decodeAC12Field(int AC12Field, int *unit) { + int q_bit = AC12Field & 0x10; // Bit 48 = Q + + *unit = MODES_UNIT_FEET; + if (q_bit) { + /// N is the 11 bit integer resulting from the removal of bit Q at bit 4 + int n = ((AC12Field & 0x0FE0) >> 1) | + (AC12Field & 0x000F); + // The final altitude is the resulting number multiplied by 25, minus 1000. + return ((n * 25) - 1000); + } else { + // Make N a 13 bit Gillham coded altitude by inserting M=0 at bit 6 + int n = ((AC12Field & 0x0FC0) << 1) | + (AC12Field & 0x003F); + n = ModeAToModeC(decodeID13Field(n)); + if (n < -12) {n = 0;} + + return (100 * n); + } +} +// +//========================================================================= +// +// Decode the 7 bit ground movement field PWL exponential style scale +// +int decodeMovementField(int movement) { + int gspeed; + + // Note : movement codes 0,125,126,127 are all invalid, but they are + // trapped for before this function is called. + + if (movement > 123) gspeed = 199; // > 175kt + else if (movement > 108) gspeed = ((movement - 108) * 5) + 100; + else if (movement > 93) gspeed = ((movement - 93) * 2) + 70; + else if (movement > 38) gspeed = ((movement - 38) ) + 15; + else if (movement > 12) gspeed = ((movement - 11) >> 1) + 2; + else if (movement > 8) gspeed = ((movement - 6) >> 2) + 1; + else gspeed = 0; + + return (gspeed); +} +// +//========================================================================= +// +// Capability table +char *ca_str[8] = { + /* 0 */ "Level 1 (Survillance Only)", + /* 1 */ "Level 2 (DF0,4,5,11)", + /* 2 */ "Level 3 (DF0,4,5,11,20,21)", + /* 3 */ "Level 4 (DF0,4,5,11,20,21,24)", + /* 4 */ "Level 2+3+4 (DF0,4,5,11,20,21,24,code7 - is on ground)", + /* 5 */ "Level 2+3+4 (DF0,4,5,11,20,21,24,code7 - is on airborne)", + /* 6 */ "Level 2+3+4 (DF0,4,5,11,20,21,24,code7)", + /* 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 +char *fs_str[8] = { + /* 0 */ "Normal, Airborne", + /* 1 */ "Normal, On the ground", + /* 2 */ "ALERT, Airborne", + /* 3 */ "ALERT, On the ground", + /* 4 */ "ALERT & Special Position Identification. Airborne or Ground", + /* 5 */ "Special Position Identification. Airborne or Ground", + /* 6 */ "Value 6 is not assigned", + /* 7 */ "Value 7 is not assigned" +}; +// +//========================================================================= +// +char *getMEDescription(int metype, int mesub) { + char *mename = "Unknown"; + + if (metype >= 1 && metype <= 4) + mename = "Aircraft Identification and Category"; + else if (metype >= 5 && metype <= 8) + mename = "Surface Position"; + else if (metype >= 9 && metype <= 18) + mename = "Airborne Position (Baro Altitude)"; + else if (metype == 19 && mesub >=1 && mesub <= 4) + mename = "Airborne Velocity"; + else if (metype >= 20 && metype <= 22) + mename = "Airborne Position (GNSS Height)"; + else if (metype == 23 && mesub == 0) + mename = "Test Message"; + else if (metype == 24 && mesub == 1) + mename = "Surface System Status"; + else if (metype == 28 && mesub == 1) + mename = "Extended Squitter Aircraft Status (Emergency)"; + else if (metype == 28 && mesub == 2) + mename = "Extended Squitter Aircraft Status (1090ES TCAS RA)"; + else if (metype == 29 && (mesub == 0 || mesub == 1)) + mename = "Target State and Status Message"; + else if (metype == 31 && (mesub == 0 || mesub == 1)) + mename = "Aircraft Operational Status Message"; + return mename; +} +// +//========================================================================= +// +// Decode a raw Mode S message demodulated as a stream of bytes by detectModeS(), +// and split it into fields populating a modesMessage structure. +// +void decodeModesMessage(struct modesMessage *mm, unsigned char *msg) { + char *ais_charset = "?ABCDEFGHIJKLMNOPQRSTUVWXYZ????? ???????????????0123456789??????"; + + // Work on our local copy + memcpy(mm->msg, msg, MODES_LONG_MSG_BYTES); + msg = mm->msg; + + // Get the message type ASAP as other operations depend on this + mm->msgtype = msg[0] >> 3; // Downlink Format + mm->msgbits = modesMessageLenByType(mm->msgtype); + mm->crc = modesChecksum(msg, mm->msgbits); + + if ((mm->crc) && (Modes.nfix_crc) && ((mm->msgtype == 17) || (mm->msgtype == 18))) { +// if ((mm->crc) && (Modes.nfix_crc) && ((mm->msgtype == 11) || (mm->msgtype == 17))) { + // + // 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 + // less than 80. Therefore, attempting to fix DF-11 errors can result in a + // multitude of possible crc solutions, only one of which is correct. + // + // We should probably perform some sanity checks on corrected DF-11's before + // using the results. Perhaps check the ICAO against known aircraft, and check + // IID against known good IID's. That's a TODO. + // + mm->correctedbits = fixBitErrors(msg, mm->msgbits, Modes.nfix_crc, mm->corrected); + + // If we correct, validate ICAO addr to help filter birthday paradox solutions. + 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 + // single/two bit errors, otherwise we would need to recompute the fields again. + // + if (mm->msgtype == 11) { // DF 11 + mm->crcok = (mm->crc < 80); + mm->iid = mm->crc; + mm->addr = (msg[1] << 16) | (msg[2] << 8) | (msg[3]); + mm->ca = (msg[0] & 0x07); // Responder capabilities + + if (0 == mm->crc) { + // DF 11 : if crc == 0 try to populate our ICAO addresses whitelist. + 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]); + mm->ca = (msg[0] & 0x07); // Responder capabilities + + if (0 == mm->crc) { + // DF 17 : if crc == 0 try to populate our ICAO addresses whitelist. + addRecentlySeenICAOAddr(mm->addr); + } + + } else if (mm->msgtype == 18) { // DF 18 + mm->crcok = (mm->crc == 0); + 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); + } + + } 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 DF0, DF16 + if (mm->msgtype == 0 || mm->msgtype == 16) { + if (msg[0] & 0x04) { // VS Bit + mm->bFlags |= MODES_ACFLAGS_AOG_VALID | MODES_ACFLAGS_AOG; + } else { + mm->bFlags |= MODES_ACFLAGS_AOG_VALID; + } + } + + // Fields for DF11, DF17 + if (mm->msgtype == 11 || mm->msgtype == 17) { + if (mm->ca == 4) { + mm->bFlags |= MODES_ACFLAGS_AOG_VALID | MODES_ACFLAGS_AOG; + } else if (mm->ca == 5) { + mm->bFlags |= MODES_ACFLAGS_AOG_VALID; + } + } + + // Fields for DF5, DF21 = Gillham encoded Squawk + if (mm->msgtype == 5 || mm->msgtype == 21) { + int ID13Field = ((msg[2] << 8) | msg[3]) & 0x1FFF; + if (ID13Field) { + mm->bFlags |= MODES_ACFLAGS_SQUAWK_VALID; + mm->modeA = decodeID13Field(ID13Field); + } + } + + // Fields for DF0, DF4, DF16, DF20 13 bit altitude + if (mm->msgtype == 0 || mm->msgtype == 4 || + mm->msgtype == 16 || mm->msgtype == 20) { + int AC13Field = ((msg[2] << 8) | msg[3]) & 0x1FFF; + if (AC13Field) { // Only attempt to decode if a valid (non zero) altitude is present + mm->bFlags |= MODES_ACFLAGS_ALTITUDE_VALID; + mm->altitude = decodeAC13Field(AC13Field, &mm->unit); + } + } + + // Fields for DF4, DF5, DF20, DF21 + if ((mm->msgtype == 4) || (mm->msgtype == 20) || + (mm->msgtype == 5) || (mm->msgtype == 21)) { + mm->bFlags |= MODES_ACFLAGS_FS_VALID; + mm->fs = msg[0] & 7; // Flight status for DF4,5,20,21 + if (mm->fs <= 3) { + mm->bFlags |= MODES_ACFLAGS_AOG_VALID; + if (mm->fs & 1) + {mm->bFlags |= MODES_ACFLAGS_AOG;} + } + } + + // Fields for DF17, DF18_CF0, DF18_CF1, DF18_CF6 squitters + 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 mesub = mm->mesub = msg[4] & 7; // Extended squitter message subtype + + // Decode the extended squitter message + + if (metype >= 1 && metype <= 4) { // Aircraft Identification and Category + uint32_t chars; + mm->bFlags |= MODES_ACFLAGS_CALLSIGN_VALID; + + chars = (msg[5] << 16) | (msg[6] << 8) | (msg[7]); + mm->flight[3] = ais_charset[chars & 0x3F]; chars = chars >> 6; + mm->flight[2] = ais_charset[chars & 0x3F]; chars = chars >> 6; + mm->flight[1] = ais_charset[chars & 0x3F]; chars = chars >> 6; + mm->flight[0] = ais_charset[chars & 0x3F]; + + chars = (msg[8] << 16) | (msg[9] << 8) | (msg[10]); + mm->flight[7] = ais_charset[chars & 0x3F]; chars = chars >> 6; + mm->flight[6] = ais_charset[chars & 0x3F]; chars = chars >> 6; + mm->flight[5] = ais_charset[chars & 0x3F]; chars = chars >> 6; + mm->flight[4] = ais_charset[chars & 0x3F]; + + mm->flight[8] = '\0'; + + } else if (metype >= 5 && metype <= 18) { // Position Message + mm->raw_latitude = ((msg[6] & 3) << 15) | (msg[7] << 7) | (msg[8] >> 1); + mm->raw_longitude = ((msg[8] & 1) << 16) | (msg[9] << 8) | (msg[10]); + mm->bFlags |= (mm->msg[6] & 0x04) ? MODES_ACFLAGS_LLODD_VALID + : MODES_ACFLAGS_LLEVEN_VALID; + if (metype >= 9) { // Airborne + int AC12Field = ((msg[5] << 4) | (msg[6] >> 4)) & 0x0FFF; + mm->bFlags |= MODES_ACFLAGS_AOG_VALID; + if (AC12Field) {// Only attempt to decode if a valid (non zero) altitude is present + mm->bFlags |= MODES_ACFLAGS_ALTITUDE_VALID; + mm->altitude = decodeAC12Field(AC12Field, &mm->unit); + } + } else { // Ground + int movement = ((msg[4] << 4) | (msg[5] >> 4)) & 0x007F; + mm->bFlags |= MODES_ACFLAGS_AOG_VALID | MODES_ACFLAGS_AOG; + if ((movement) && (movement < 125)) { + mm->bFlags |= MODES_ACFLAGS_SPEED_VALID; + mm->velocity = decodeMovementField(movement); + } + + if (msg[5] & 0x08) { + mm->bFlags |= MODES_ACFLAGS_HEADING_VALID; + mm->heading = ((((msg[5] << 4) | (msg[6] >> 4)) & 0x007F) * 45) >> 4; + } + } + + } else if (metype == 19) { // Airborne Velocity Message + + // Presumably airborne if we get an Airborne Velocity Message + mm->bFlags |= MODES_ACFLAGS_AOG_VALID; + + if ( (mesub >= 1) && (mesub <= 4) ) { + int vert_rate = ((msg[8] & 0x07) << 6) | (msg[9] >> 2); + if (vert_rate) { + --vert_rate; + if (msg[8] & 0x08) + {vert_rate = 0 - vert_rate;} + mm->vert_rate = vert_rate * 64; + mm->bFlags |= MODES_ACFLAGS_VERTRATE_VALID; + } + } + + if ((mesub == 1) || (mesub == 2)) { + int ew_raw = ((msg[5] & 0x03) << 8) | msg[6]; + int ew_vel = ew_raw - 1; + int ns_raw = ((msg[7] & 0x7F) << 3) | (msg[8] >> 5); + int ns_vel = ns_raw - 1; + + if (mesub == 2) { // If (supersonic) unit is 4 kts + ns_vel = ns_vel << 2; + ew_vel = ew_vel << 2; + } + + if (ew_raw) { // Do East/West + mm->bFlags |= MODES_ACFLAGS_EWSPEED_VALID; + if (msg[5] & 0x04) + {ew_vel = 0 - ew_vel;} + mm->ew_velocity = ew_vel; + } + + if (ns_raw) { // Do North/South + mm->bFlags |= MODES_ACFLAGS_NSSPEED_VALID; + if (msg[7] & 0x80) + {ns_vel = 0 - ns_vel;} + mm->ns_velocity = ns_vel; + } + + if (ew_raw && ns_raw) { + // Compute velocity and angle from the two speed components + mm->bFlags |= (MODES_ACFLAGS_SPEED_VALID | MODES_ACFLAGS_HEADING_VALID | MODES_ACFLAGS_NSEWSPD_VALID); + mm->velocity = (int) sqrt((ns_vel * ns_vel) + (ew_vel * ew_vel)); + + if (mm->velocity) { + mm->heading = (int) (atan2(ew_vel, ns_vel) * 180.0 / M_PI); + // We don't want negative values but a 0-360 scale + if (mm->heading < 0) mm->heading += 360; + } + } + + } else if (mesub == 3 || mesub == 4) { + int airspeed = ((msg[7] & 0x7f) << 3) | (msg[8] >> 5); + if (airspeed) { + mm->bFlags |= MODES_ACFLAGS_SPEED_VALID; + --airspeed; + if (mesub == 4) // If (supersonic) unit is 4 kts + {airspeed = airspeed << 2;} + mm->velocity = airspeed; + } + + if (msg[5] & 0x04) { + mm->bFlags |= MODES_ACFLAGS_HEADING_VALID; + mm->heading = ((((msg[5] & 0x03) << 8) | msg[6]) * 45) >> 7; + } + } + } + } + + // Fields for DF20, DF21 Comm-B + if ((mm->msgtype == 20) || (mm->msgtype == 21)){ + + if (msg[4] == 0x20) { // Aircraft Identification + uint32_t chars; + mm->bFlags |= MODES_ACFLAGS_CALLSIGN_VALID; + + chars = (msg[5] << 16) | (msg[6] << 8) | (msg[7]); + mm->flight[3] = ais_charset[chars & 0x3F]; chars = chars >> 6; + mm->flight[2] = ais_charset[chars & 0x3F]; chars = chars >> 6; + mm->flight[1] = ais_charset[chars & 0x3F]; chars = chars >> 6; + mm->flight[0] = ais_charset[chars & 0x3F]; + + chars = (msg[8] << 16) | (msg[9] << 8) | (msg[10]); + mm->flight[7] = ais_charset[chars & 0x3F]; chars = chars >> 6; + mm->flight[6] = ais_charset[chars & 0x3F]; chars = chars >> 6; + mm->flight[5] = ais_charset[chars & 0x3F]; chars = chars >> 6; + mm->flight[4] = ais_charset[chars & 0x3F]; + + mm->flight[8] = '\0'; + } else { + } + } +} +// +//========================================================================= +// +// This function gets a decoded Mode S Message and prints it on the screen +// in a human readable format. +// +void displayModesMessage(struct modesMessage *mm) { + int j; + unsigned char * pTimeStamp; + + // Handle only addresses mode first. + if (Modes.onlyaddr) { + printf("%06x\n", mm->addr); + return; // Enough for --onlyaddr mode + } + + // Show the raw message. + if (Modes.mlat && mm->timestampMsg) { + printf("@"); + pTimeStamp = (unsigned char *) &mm->timestampMsg; + for (j=5; j>=0;j--) { + printf("%02X",pTimeStamp[j]); + } + } else + printf("*"); + + for (j = 0; j < mm->msgbits/8; j++) printf("%02x", mm->msg[j]); + printf(";\n"); + + if (Modes.raw) { + fflush(stdout); // Provide data to the reader ASAP + return; // Enough for --raw mode + } + + if (mm->msgtype < 32) + printf("CRC: %06x (%s)\n", (int)mm->crc, mm->crcok ? "ok" : "wrong"); + + if (mm->correctedbits != 0) + printf("No. of bit errors fixed: %d\n", mm->correctedbits); + + if (mm->msgtype == 0) { // DF 0 + printf("DF 0: Short Air-Air Surveillance.\n"); + printf(" Altitude : %d %s\n", mm->altitude, + (mm->unit == MODES_UNIT_METERS) ? "meters" : "feet"); + printf(" ICAO Address : %06x\n", mm->addr); + + } else if (mm->msgtype == 4 || mm->msgtype == 20) { + printf("DF %d: %s, Altitude Reply.\n", mm->msgtype, + (mm->msgtype == 4) ? "Surveillance" : "Comm-B"); + printf(" Flight Status : %s\n", fs_str[mm->fs]); + printf(" DR : %d\n", ((mm->msg[1] >> 3) & 0x1F)); + printf(" UM : %d\n", (((mm->msg[1] & 7) << 3) | (mm->msg[2] >> 5))); + printf(" Altitude : %d %s\n", mm->altitude, + (mm->unit == MODES_UNIT_METERS) ? "meters" : "feet"); + printf(" ICAO Address : %06x\n", mm->addr); + + if (mm->msgtype == 20) { + printf(" Comm-B BDS : %x\n", mm->msg[4]); + + // Decode the extended squitter message + if ( mm->msg[4] == 0x20) { // BDS 2,0 Aircraft identification + printf(" BDS 2,0 Aircraft Identification : %s\n", mm->flight); +/* + } else if ( mm->msg[4] == 0x10) { // BDS 1,0 Datalink Capability report + printf(" BDS 1,0 Datalink Capability report\n"); + + } else if ( mm->msg[4] == 0x30) { // BDS 3,0 ACAS Active Resolution Advisory + printf(" BDS 3,0 ACAS Active Resolution Advisory\n"); + + } else if ((mm->msg[4] >> 3) == 28) { // BDS 6,1 Extended Squitter Emergecy/Priority Status + printf(" BDS 6,1 Emergecy/Priority Status\n"); + + } else if ((mm->msg[4] >> 3) == 29) { // BDS 6,2 Target State and Status + printf(" BDS 6,2 Target State and Status\n"); + + } else if ((mm->msg[4] >> 3) == 31) { // BDS 6,5 Extended Squitter Aircraft Operational Status + printf(" BDS 6,5 Aircraft Operational Status\n"); +*/ + } + } + + } else if (mm->msgtype == 5 || mm->msgtype == 21) { + printf("DF %d: %s, Identity Reply.\n", mm->msgtype, + (mm->msgtype == 5) ? "Surveillance" : "Comm-B"); + printf(" Flight Status : %s\n", fs_str[mm->fs]); + printf(" DR : %d\n", ((mm->msg[1] >> 3) & 0x1F)); + printf(" UM : %d\n", (((mm->msg[1] & 7) << 3) | (mm->msg[2] >> 5))); + printf(" Squawk : %x\n", mm->modeA); + printf(" ICAO Address : %06x\n", mm->addr); + + if (mm->msgtype == 21) { + printf(" Comm-B BDS : %x\n", mm->msg[4]); + + // Decode the extended squitter message + if ( mm->msg[4] == 0x20) { // BDS 2,0 Aircraft identification + printf(" BDS 2,0 Aircraft Identification : %s\n", mm->flight); +/* + } else if ( mm->msg[4] == 0x10) { // BDS 1,0 Datalink Capability report + printf(" BDS 1,0 Datalink Capability report\n"); + + } else if ( mm->msg[4] == 0x30) { // BDS 3,0 ACAS Active Resolution Advisory + printf(" BDS 3,0 ACAS Active Resolution Advisory\n"); + + } else if ((mm->msg[4] >> 3) == 28) { // BDS 6,1 Extended Squitter Emergecy/Priority Status + printf(" BDS 6,1 Emergecy/Priority Status\n"); + + } else if ((mm->msg[4] >> 3) == 29) { // BDS 6,2 Target State and Status + printf(" BDS 6,2 Target State and Status\n"); + + } else if ((mm->msg[4] >> 3) == 31) { // BDS 6,5 Extended Squitter Aircraft Operational Status + printf(" BDS 6,5 Aircraft Operational Status\n"); +*/ + } + } + + } else if (mm->msgtype == 11) { // DF 11 + printf("DF 11: All Call Reply.\n"); + printf(" Capability : %d (%s)\n", mm->ca, ca_str[mm->ca]); + printf(" ICAO Address: %06x\n", mm->addr); + if (mm->iid > 16) + {printf(" IID : SI-%02d\n", mm->iid-16);} + else + {printf(" IID : II-%02d\n", mm->iid);} + + } else if (mm->msgtype == 16) { // DF 16 + printf("DF 16: Long Air to Air ACAS\n"); + + } else if (mm->msgtype == 17) { // DF 17 + printf("DF 17: ADS-B message.\n"); + printf(" Capability : %d (%s)\n", mm->ca, ca_str[mm->ca]); + 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 == 18) { // DF 18 + 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 + printf("DF 19: Military Extended Squitter.\n"); + + } else if (mm->msgtype == 22) { // DF 22 + printf("DF 22: Military Use.\n"); + + } else if (mm->msgtype == 24) { // DF 24 + printf("DF 24: Comm D Extended Length Message.\n"); + + } else if (mm->msgtype == 32) { // DF 32 is special code we use for Mode A/C + printf("SSR : Mode A/C Reply.\n"); + if (mm->fs & 0x0080) { + printf(" Mode A : %04x IDENT\n", mm->modeA); + } else { + printf(" Mode A : %04x\n", mm->modeA); + if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) + {printf(" Mode C : %d feet\n", mm->altitude);} + } + + } else { + printf("DF %d: Unknown DF Format.\n", mm->msgtype); + } + + printf("\n"); +} +// +//========================================================================= +// +// Turn I/Q samples pointed by Modes.data into the magnitude vector +// pointed by Modes.magnitude. +// +void computeMagnitudeVector(void) { + uint16_t *m = &Modes.magnitude[MODES_PREAMBLE_SAMPLES+MODES_LONG_MSG_SAMPLES]; + uint16_t *p = Modes.data; + uint32_t j; + + memcpy(Modes.magnitude,&Modes.magnitude[MODES_ASYNC_BUF_SAMPLES], MODES_PREAMBLE_SIZE+MODES_LONG_MSG_SIZE); + + // Compute the magnitudo vector. It's just SQRT(I^2 + Q^2), but + // we rescale to the 0-255 range to exploit the full resolution. + for (j = 0; j < MODES_ASYNC_BUF_SAMPLES; j ++) { + *m++ = Modes.maglut[*p++]; + } +} +// +//========================================================================= +// +// Return -1 if the message is out of fase left-side +// Return 1 if the message is out of fase right-size +// Return 0 if the message is not particularly out of phase. +// +// Note: this function will access pPreamble[-1], so the caller should make sure to +// call it only if we are not at the start of the current buffer +// +int detectOutOfPhase(uint16_t *pPreamble) { + if (pPreamble[ 3] > pPreamble[2]/3) return 1; + if (pPreamble[10] > pPreamble[9]/3) return 1; + if (pPreamble[ 6] > pPreamble[7]/3) return -1; + if (pPreamble[-1] > pPreamble[1]/3) return -1; + return 0; +} +// +//========================================================================= +// +// This function does not really correct the phase of the message, it just +// applies a transformation to the first sample representing a given bit: +// +// If the previous bit was one, we amplify it a bit. +// If the previous bit was zero, we decrease it a bit. +// +// This simple transformation makes the message a bit more likely to be +// correctly decoded for out of phase messages: +// +// When messages are out of phase there is more uncertainty in +// sequences of the same bit multiple times, since 11111 will be +// transmitted as continuously altering magnitude (high, low, high, low...) +// +// However because the message is out of phase some part of the high +// is mixed in the low part, so that it is hard to distinguish if it is +// a zero or a one. +// +// However when the message is out of phase passing from 0 to 1 or from +// 1 to 0 happens in a very recognizable way, for instance in the 0 -> 1 +// transition, magnitude goes low, high, high, low, and one of of the +// two middle samples the high will be *very* high as part of the previous +// or next high signal will be mixed there. +// +// Applying our simple transformation we make more likely if the current +// bit is a zero, to detect another zero. Symmetrically if it is a one +// it will be more likely to detect a one because of the transformation. +// In this way similar levels will be interpreted more likely in the +// correct way. +// +void applyPhaseCorrection(uint16_t *pPayload) { + int j; + + for (j = 0; j < MODES_LONG_MSG_SAMPLES; j += 2, pPayload += 2) { + if (pPayload[0] > pPayload[1]) { // One + pPayload[2] = (pPayload[2] * 5) / 4; + } else { // Zero + pPayload[2] = (pPayload[2] * 4) / 5; + } + } +} +// +//========================================================================= +// +// Detect a Mode S messages inside the magnitude buffer pointed by 'm' and of +// size 'mlen' bytes. Every detected Mode S message is convert it into a +// stream of bits and passed to the function to display it. +// +void detectModeS(uint16_t *m, uint32_t mlen) { + struct modesMessage mm; + unsigned char msg[MODES_LONG_MSG_BYTES], *pMsg; + uint16_t aux[MODES_LONG_MSG_SAMPLES]; + uint32_t j; + int use_correction = 0; + + memset(&mm, 0, sizeof(mm)); + + // The Mode S preamble is made of impulses of 0.5 microseconds at + // the following time offsets: + // + // 0 - 0.5 usec: first impulse. + // 1.0 - 1.5 usec: second impulse. + // 3.5 - 4 usec: third impulse. + // 4.5 - 5 usec: last impulse. + // + // Since we are sampling at 2 Mhz every sample in our magnitude vector + // is 0.5 usec, so the preamble will look like this, assuming there is + // an impulse at offset 0 in the array: + // + // 0 ----------------- + // 1 - + // 2 ------------------ + // 3 -- + // 4 - + // 5 -- + // 6 - + // 7 ------------------ + // 8 -- + // 9 ------------------- + // + for (j = 0; j < mlen; j++) { + int high, i, errors, errors56, errorsTy; + uint16_t *pPreamble, *pPayload, *pPtr; + uint8_t theByte, theErrs; + int msglen, scanlen, sigStrength; + + pPreamble = &m[j]; + 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 + { // so try to find a new preamble + + if (Modes.mode_ac) + { + int ModeA = detectModeA(pPreamble, &mm); + + if (ModeA) // We have found a valid ModeA/C in the data + { + mm.timestampMsg = Modes.timestampBlk + ((j+1) * 6); + + // Decode the received message + decodeModeAMessage(&mm, ModeA); + + // Pass data to the next layer + useModesMessage(&mm); + + j += MODEAC_MSG_SAMPLES; + Modes.stat_ModeAC++; + continue; + } + } + + // First check of relations between the first 10 samples + // representing a valid preamble. We don't even investigate further + // if this simple test is not passed + if (!(pPreamble[0] > pPreamble[1] && + pPreamble[1] < pPreamble[2] && + pPreamble[2] > pPreamble[3] && + pPreamble[3] < pPreamble[0] && + pPreamble[4] < pPreamble[0] && + pPreamble[5] < pPreamble[0] && + pPreamble[6] < pPreamble[0] && + pPreamble[7] > pPreamble[8] && + pPreamble[8] < pPreamble[9] && + pPreamble[9] > pPreamble[6])) + { + if (Modes.debug & MODES_DEBUG_NOPREAMBLE && + *pPreamble > MODES_DEBUG_NOPREAMBLE_LEVEL) + dumpRawMessage("Unexpected ratio among first 10 samples", msg, m, j); + continue; + } + + // The samples between the two spikes must be < than the average + // of the high spikes level. We don't test bits too near to + // the high levels as signals can be out of phase so part of the + // energy can be in the near samples + high = (pPreamble[0] + pPreamble[2] + pPreamble[7] + pPreamble[9]) / 6; + if (pPreamble[4] >= high || + pPreamble[5] >= high) + { + if (Modes.debug & MODES_DEBUG_NOPREAMBLE && + *pPreamble > MODES_DEBUG_NOPREAMBLE_LEVEL) + dumpRawMessage("Too high level in samples between 3 and 6", msg, m, j); + continue; + } + + // Similarly samples in the range 11-14 must be low, as it is the + // space between the preamble and real data. Again we don't test + // bits too near to high levels, see above + if (pPreamble[11] >= high || + pPreamble[12] >= high || + pPreamble[13] >= high || + pPreamble[14] >= high) + { + if (Modes.debug & MODES_DEBUG_NOPREAMBLE && + *pPreamble > MODES_DEBUG_NOPREAMBLE_LEVEL) + dumpRawMessage("Too high level in samples between 10 and 15", msg, m, j); + continue; + } + Modes.stat_valid_preamble++; + } + + else { + // If the previous attempt with this message failed, retry using + // magnitude correction + // Make a copy of the Payload, and phase correct the copy + memcpy(aux, pPayload, sizeof(aux)); + applyPhaseCorrection(aux); + Modes.stat_out_of_phase++; + pPayload = aux; + // TODO ... apply other kind of corrections + } + + // Decode all the next 112 bits, regardless of the actual message + // size. We'll check the actual message type later + pMsg = &msg[0]; + pPtr = pPayload; + theByte = 0; + theErrs = 0; errorsTy = 0; + errors = 0; errors56 = 0; + + // We should have 4 'bits' of 0/1 and 1/0 samples in the preamble, + // so include these in the signal strength + sigStrength = (pPreamble[0]-pPreamble[1]) + + (pPreamble[2]-pPreamble[3]) + + (pPreamble[7]-pPreamble[6]) + + (pPreamble[9]-pPreamble[8]); + + msglen = scanlen = MODES_LONG_MSG_BITS; + for (i = 0; i < scanlen; i++) { + uint32_t a = *pPtr++; + uint32_t b = *pPtr++; + + if (a > b) + {theByte |= 1; if (i < 56) {sigStrength += (a-b);}} + else if (a < b) + {/*theByte |= 0;*/ if (i < 56) {sigStrength += (b-a);}} + else if (i >= MODES_SHORT_MSG_BITS) //(a == b), and we're in the long part of a frame + {errors++; /*theByte |= 0;*/} + else if (i >= 5) //(a == b), and we're in the short part of a frame + {scanlen = MODES_LONG_MSG_BITS; errors56 = ++errors;/*theByte |= 0;*/} + else if (i) //(a == b), and we're in the message type part of a frame + {errorsTy = errors56 = ++errors; theErrs |= 1; /*theByte |= 0;*/} + else //(a == b), and we're in the first bit of the message type part of a frame + {errorsTy = errors56 = ++errors; theErrs |= 1; theByte |= 1;} + + if ((i & 7) == 7) + {*pMsg++ = theByte;} + else if (i == 4) { + msglen = modesMessageLenByType(theByte); + if (errors == 0) + {scanlen = msglen;} + } + + theByte = theByte << 1; + if (i < 7) + {theErrs = theErrs << 1;} + + // If we've exceeded the permissible number of encoding errors, abandon ship now + if (errors > MODES_MSG_ENCODER_ERRS) { + + if (i < MODES_SHORT_MSG_BITS) { + msglen = 0; + + } else if ((errorsTy == 1) && (theErrs == 0x80)) { + // If we only saw one error in the first bit of the byte of the frame, then it's possible + // we guessed wrongly about the value of the bit. We may be able to correct it by guessing + // the other way. + // + // We guessed a '1' at bit 7, which is the DF length bit == 112 Bits. + // Inverting bit 7 will change the message type from a long to a short. + // Invert the bit, cross your fingers and carry on. + msglen = MODES_SHORT_MSG_BITS; + msg[0] ^= theErrs; errorsTy = 0; + errors = errors56; // revert to the number of errors prior to bit 56 + Modes.stat_DF_Len_Corrected++; + + } else if (i < MODES_LONG_MSG_BITS) { + msglen = MODES_SHORT_MSG_BITS; + errors = errors56; + + } else { + msglen = MODES_LONG_MSG_BITS; + } + + break; + } + } + + // Ensure msglen is consistent with the DF type + i = modesMessageLenByType(msg[0] >> 3); + if (msglen > i) {msglen = i;} + else if (msglen < i) {msglen = 0;} + + // + // If we guessed at any of the bits in the DF type field, then look to see if our guess was sensible. + // Do this by looking to see if the original guess results in the DF type being one of the ICAO defined + // message types. If it isn't then toggle the guessed bit and see if this new value is ICAO defined. + // if the new value is ICAO defined, then update it in our message. + if ((msglen) && (errorsTy == 1) && (theErrs & 0x78)) { + // We guessed at one (and only one) of the message type bits. See if our guess is "likely" + // to be correct by comparing the DF against a list of known good DF's + int thisDF = ((theByte = msg[0]) >> 3) & 0x1f; + uint32_t validDFbits = 0x017F0831; // One bit per 32 possible DF's. Set bits 0,4,5,11,16.17.18.19,20,21,22,24 + uint32_t thisDFbit = (1 << thisDF); + if (0 == (validDFbits & thisDFbit)) { + // The current DF is not ICAO defined, so is probably an errors. + // Toggle the bit we guessed at and see if the resultant DF is more likely + theByte ^= theErrs; + thisDF = (theByte >> 3) & 0x1f; + thisDFbit = (1 << thisDF); + // if this DF any more likely? + if (validDFbits & thisDFbit) { + // Yep, more likely, so update the main message + msg[0] = theByte; + Modes.stat_DF_Type_Corrected++; + errors--; // decrease the error count so we attempt to use the modified DF. + } + } + } + + // We measured signal strength over the first 56 bits. Don't forget to add 4 + // for the preamble samples, so round up and divide by 60. + sigStrength = (sigStrength + 29) / 60; + + // When we reach this point, if error is small, and the signal strength is large enough + // we may have a Mode S message on our hands. It may still be broken and the CRC may not + // be correct, but this can be handled by the next layer. + if ( (msglen) + && (sigStrength > MODES_MSG_SQUELCH_LEVEL) + && (errors <= MODES_MSG_ENCODER_ERRS) ) { + + // Set initial mm structure details + mm.timestampMsg = Modes.timestampBlk + (j*6); + sigStrength = (sigStrength + 0x7F) >> 8; + mm.signalLevel = ((sigStrength < 255) ? sigStrength : 255); + mm.phase_corrected = use_correction; + + // Decode the received message + decodeModesMessage(&mm, msg); + + // Update statistics + if (Modes.stats) { + if (mm.crcok || use_correction || mm.correctedbits) { + + 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 (use_correction) { + 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) + && (mm.correctedbits <= MODES_MAX_BITERRORS) ) { + Modes.stat_ph_bit_fix[mm.correctedbits-1] += 1; + } + + } else { + Modes.stat_badcrc++; + Modes.stat_fixed++; + if ( (mm.correctedbits) + && (mm.correctedbits <= MODES_MAX_BITERRORS) ) { + Modes.stat_bit_fix[mm.correctedbits-1] += 1; + } + } + } + } + + // Output debug mode info if needed + if (use_correction) { + if (Modes.debug & MODES_DEBUG_DEMOD) + dumpRawMessage("Demodulated with 0 errors", msg, m, j); + else if (Modes.debug & MODES_DEBUG_BADCRC && + mm.msgtype == 17 && + (!mm.crcok || mm.correctedbits != 0)) + dumpRawMessage("Decoded with bad CRC", msg, m, j); + else if (Modes.debug & MODES_DEBUG_GOODCRC && mm.crcok && + mm.correctedbits == 0) + dumpRawMessage("Decoded with good CRC", msg, m, j); + } + + // Skip this message if we are sure it's fine + if (mm.crcok) { + j += (MODES_PREAMBLE_US+msglen)*2; + } + + // Pass data to the next layer + useModesMessage(&mm); + + } else { + if (Modes.debug & MODES_DEBUG_DEMODERR && use_correction) { + printf("The following message has %d demod errors\n", errors); + dumpRawMessage("Demodulated with errors", msg, m, j); + } + } + + // Retry with phase correction if enabled, necessary and possible. + if (Modes.phase_enhance && !mm.crcok && !mm.correctedbits && !use_correction && j && detectOutOfPhase(pPreamble)) { + use_correction = 1; j--; + } else { + use_correction = 0; + } + } + + //Send any remaining partial raw buffers now + if (Modes.rawOutUsed || Modes.beastOutUsed) + { + Modes.net_output_raw_rate_count++; + if (Modes.net_output_raw_rate_count > Modes.net_output_raw_rate) + { + if (Modes.rawOutUsed) { + modesSendAllClients(Modes.ros, Modes.rawOut, Modes.rawOutUsed); + Modes.rawOutUsed = 0; + } + if (Modes.beastOutUsed) { + modesSendAllClients(Modes.bos, Modes.beastOut, Modes.beastOutUsed); + Modes.beastOutUsed = 0; + } + Modes.net_output_raw_rate_count = 0; + } + } +} +// +//========================================================================= +// +// When a new message is available, because it was decoded from the RTL device, +// file, or received in the TCP input port, or any other way we can receive a +// decoded message, we call this function in order to use the message. +// +// Basically this function passes a raw message to the upper layers for further +// processing and visualization +// +void useModesMessage(struct modesMessage *mm) { + if ((Modes.check_crc == 0) || (mm->crcok) || (mm->correctedbits)) { // not checking, ok or fixed + + // Track aircrafts if... + if ( (Modes.interactive) // in interactive mode + || (Modes.stat_http_requests) // or if the HTTP interface is enabled + || (Modes.stat_sbs_connections) // or if sbs connections are established + || (Modes.mode_ac) ) { // or if mode A/C decoding is enabled + interactiveReceiveData(mm); + } + + // In non-interactive non-quiet mode, display messages on standard output + if (!Modes.interactive && !Modes.quiet) { + displayModesMessage(mm); + } + + // Feed output clients + if (Modes.net) {modesQueueOutput(mm);} + } +} +// +//========================================================================= +// +// Always positive MOD operation, used for CPR decoding. +// +int cprModFunction(int a, int b) { + int res = a % b; + if (res < 0) res += b; + return res; +} +// +//========================================================================= +// +// The NL function uses the precomputed table from 1090-WP-9-14 +// +int cprNLFunction(double lat) { + if (lat < 0) lat = -lat; // Table is simmetric about the equator + if (lat < 10.47047130) return 59; + if (lat < 14.82817437) return 58; + if (lat < 18.18626357) return 57; + if (lat < 21.02939493) return 56; + if (lat < 23.54504487) return 55; + if (lat < 25.82924707) return 54; + if (lat < 27.93898710) return 53; + if (lat < 29.91135686) return 52; + if (lat < 31.77209708) return 51; + if (lat < 33.53993436) return 50; + if (lat < 35.22899598) return 49; + if (lat < 36.85025108) return 48; + if (lat < 38.41241892) return 47; + if (lat < 39.92256684) return 46; + if (lat < 41.38651832) return 45; + if (lat < 42.80914012) return 44; + if (lat < 44.19454951) return 43; + if (lat < 45.54626723) return 42; + if (lat < 46.86733252) return 41; + if (lat < 48.16039128) return 40; + if (lat < 49.42776439) return 39; + if (lat < 50.67150166) return 38; + if (lat < 51.89342469) return 37; + if (lat < 53.09516153) return 36; + if (lat < 54.27817472) return 35; + if (lat < 55.44378444) return 34; + if (lat < 56.59318756) return 33; + if (lat < 57.72747354) return 32; + if (lat < 58.84763776) return 31; + if (lat < 59.95459277) return 30; + if (lat < 61.04917774) return 29; + if (lat < 62.13216659) return 28; + if (lat < 63.20427479) return 27; + if (lat < 64.26616523) return 26; + if (lat < 65.31845310) return 25; + if (lat < 66.36171008) return 24; + if (lat < 67.39646774) return 23; + if (lat < 68.42322022) return 22; + if (lat < 69.44242631) return 21; + if (lat < 70.45451075) return 20; + if (lat < 71.45986473) return 19; + if (lat < 72.45884545) return 18; + if (lat < 73.45177442) return 17; + if (lat < 74.43893416) return 16; + if (lat < 75.42056257) return 15; + if (lat < 76.39684391) return 14; + if (lat < 77.36789461) return 13; + if (lat < 78.33374083) return 12; + if (lat < 79.29428225) return 11; + if (lat < 80.24923213) return 10; + if (lat < 81.19801349) return 9; + if (lat < 82.13956981) return 8; + if (lat < 83.07199445) return 7; + if (lat < 83.99173563) return 6; + if (lat < 84.89166191) return 5; + if (lat < 85.75541621) return 4; + if (lat < 86.53536998) return 3; + if (lat < 87.00000000) return 2; + else return 1; +} +// +//========================================================================= +// +int cprNFunction(double lat, int fflag) { + int nl = cprNLFunction(lat) - (fflag ? 1 : 0); + if (nl < 1) nl = 1; + return nl; +} +// +//========================================================================= +// +double cprDlonFunction(double lat, int fflag, int surface) { + return (surface ? 90.0 : 360.0) / cprNFunction(lat, fflag); +} +// +//========================================================================= +// +// This algorithm comes from: +// http://www.lll.lu/~edward/edward/adsb/DecodingADSBposition.html. +// +// A few remarks: +// 1) 131072 is 2^17 since CPR latitude and longitude are encoded in 17 bits. +// 2) We assume that we always received the odd packet as last packet for +// simplicity. This may provide a position that is less fresh of a few +// seconds. +// +void decodeCPR(struct aircraft *a, int fflag, int surface) { + double AirDlat0 = (surface ? 90.0 : 360.0) / 60.0; + double AirDlat1 = (surface ? 90.0 : 360.0) / 59.0; + double lat0 = a->even_cprlat; + double lat1 = a->odd_cprlat; + double lon0 = a->even_cprlon; + double lon1 = a->odd_cprlon; + + // Compute the Latitude Index "j" + int j = (int) floor(((59*lat0 - 60*lat1) / 131072) + 0.5); + double rlat0 = AirDlat0 * (cprModFunction(j,60) + lat0 / 131072); + double rlat1 = AirDlat1 * (cprModFunction(j,59) + lat1 / 131072); + + if (surface) { + // If we're on the ground, make sure we have our receiver base station Lat/Lon + if (0 == (Modes.bUserFlags & MODES_USER_LATLON_VALID)) + {return;} + rlat0 += floor(Modes.fUserLat / 90.0) * 90.0; // Move from 1st quadrant to our quadrant + rlat1 += floor(Modes.fUserLat / 90.0) * 90.0; + } else { + if (rlat0 >= 270) rlat0 -= 360; + if (rlat1 >= 270) rlat1 -= 360; + } + + // Check that both are in the same latitude zone, or abort. + if (cprNLFunction(rlat0) != cprNLFunction(rlat1)) return; + + // Compute ni and the Longitude Index "m" + if (fflag) { // Use odd packet. + int ni = cprNFunction(rlat1,1); + int m = (int) floor((((lon0 * (cprNLFunction(rlat1)-1)) - + (lon1 * cprNLFunction(rlat1))) / 131072.0) + 0.5); + a->lon = cprDlonFunction(rlat1, 1, surface) * (cprModFunction(m, ni)+lon1/131072); + a->lat = rlat1; + } else { // Use even packet. + int ni = cprNFunction(rlat0,0); + int m = (int) floor((((lon0 * (cprNLFunction(rlat0)-1)) - + (lon1 * cprNLFunction(rlat0))) / 131072) + 0.5); + a->lon = cprDlonFunction(rlat0, 0, surface) * (cprModFunction(m, ni)+lon0/131072); + a->lat = rlat0; + } + + if (surface) { + a->lon += floor(Modes.fUserLon / 90.0) * 90.0; // Move from 1st quadrant to our quadrant + } else if (a->lon > 180) { + a->lon -= 360; + } + + a->seenLatLon = a->seen; + a->timestampLatLon = a->timestamp; + a->bFlags |= (MODES_ACFLAGS_LATLON_VALID | MODES_ACFLAGS_LATLON_REL_OK); +} +// +//========================================================================= +// +// This algorithm comes from: +// 1090-WP29-07-Draft_CPR101 (which also defines decodeCPR() ) +// +// There is an error in this document related to CPR relative decode. +// Should use trunc() rather than the floor() function in Eq 38 and related for deltaZI. +// floor() returns integer less than argument +// trunc() returns integer closer to zero than argument. +// Note: text of document describes trunc() functionality for deltaZI calculation +// but the formulae use floor(). +// +int decodeCPRrelative(struct aircraft *a, int fflag, int surface) { + double AirDlat; + double AirDlon; + double lat; + double lon; + double lonr, latr; + double rlon, rlat; + int j,m; + + if (a->bFlags & MODES_ACFLAGS_LATLON_REL_OK) { // Ok to try aircraft relative first + latr = a->lat; + lonr = a->lon; + } else if (Modes.bUserFlags & MODES_USER_LATLON_VALID) { // Try ground station relative next + latr = Modes.fUserLat; + lonr = Modes.fUserLon; + } else { + return (-1); // Exit with error - can't do relative if we don't have ref. + } + + if (fflag) { // odd + AirDlat = (surface ? 90.0 : 360.0) / 59.0; + lat = a->odd_cprlat; + lon = a->odd_cprlon; + } else { // even + AirDlat = (surface ? 90.0 : 360.0) / 60.0; + lat = a->even_cprlat; + lon = a->even_cprlon; + } + + // Compute the Latitude Index "j" + j = (int) (floor(latr/AirDlat) + + trunc(0.5 + cprModFunction((int)latr, (int)AirDlat)/AirDlat - lat/131072)); + rlat = AirDlat * (j + lat/131072); + if (rlat >= 270) rlat -= 360; + + // Check to see that answer is reasonable - ie no more than 1/2 cell away + if (fabs(rlat - a->lat) > (AirDlat/2)) { + a->bFlags &= ~MODES_ACFLAGS_LATLON_REL_OK; // This will cause a quick exit next time if no global has been done + return (-1); // Time to give up - Latitude error + } + + // Compute the Longitude Index "m" + AirDlon = cprDlonFunction(rlat, fflag, surface); + m = (int) (floor(lonr/AirDlon) + + trunc(0.5 + cprModFunction((int)lonr, (int)AirDlon)/AirDlon - lon/131072)); + rlon = AirDlon * (m + lon/131072); + if (rlon > 180) rlon -= 360; + + // Check to see that answer is reasonable - ie no more than 1/2 cell away + if (fabs(rlon - a->lon) > (AirDlon/2)) { + a->bFlags &= ~MODES_ACFLAGS_LATLON_REL_OK; // This will cause a quick exit next time if no global has been done + return (-1); // Time to give up - Longitude error + } + + a->lat = rlat; + a->lon = rlon; + + a->seenLatLon = a->seen; + a->timestampLatLon = a->timestamp; + a->bFlags |= (MODES_ACFLAGS_LATLON_VALID | MODES_ACFLAGS_LATLON_REL_OK); + return (0); +} +// +// ===================== Mode S detection and decoding =================== +// \ No newline at end of file diff --git a/net_io.c b/net_io.c new file mode 100644 index 0000000..538c92b --- /dev/null +++ b/net_io.c @@ -0,0 +1,870 @@ +// dump1090, a Mode S messages decoder for RTLSDR devices. +// +// Copyright (C) 2012 by Salvatore Sanfilippo +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include "dump1090.h" +// +// ============================= Networking ============================= +// +// Note: here we disregard any kind of good coding practice in favor of +// extreme simplicity, that is: +// +// 1) We only rely on the kernel buffers for our I/O without any kind of +// user space buffering. +// 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 +// 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. +// +//========================================================================= +// +// Networking "stack" initialization +// +void modesInitNet(void) { + struct { + char *descr; + int *socket; + int port; + } services[6] = { + {"Raw TCP output", &Modes.ros, Modes.net_output_raw_port}, + {"Raw TCP input", &Modes.ris, Modes.net_input_raw_port}, + {"Beast TCP output", &Modes.bos, Modes.net_output_beast_port}, + {"Beast TCP input", &Modes.bis, Modes.net_input_beast_port}, + {"HTTP server", &Modes.https, Modes.net_http_port}, + {"Basestation TCP output", &Modes.sbsos, Modes.net_output_sbs_port} + }; + int j; + + memset(Modes.clients,0,sizeof(Modes.clients)); + Modes.maxfd = -1; + + for (j = 0; j < 6; j++) { + int s = anetTcpServer(Modes.aneterr, services[j].port, NULL); + if (s == -1) { + fprintf(stderr, "Error opening the listening port %d (%s): %s\n", + services[j].port, services[j].descr, strerror(errno)); + exit(1); + } + anetNonBlock(Modes.aneterr, s); + *services[j].socket = s; + } + + signal(SIGPIPE, SIG_IGN); +} +// +//========================================================================= +// +// This function gets called from time to time when the decoding thread is +// awakened by new data arriving. This usually happens a few times every second +// +void modesAcceptClients(void) { + int fd, port; + unsigned int j; + struct client *c; + int services[6]; + + services[0] = Modes.ros; + services[1] = Modes.ris; + services[2] = Modes.bos; + services[3] = Modes.bis; + services[4] = Modes.https; + services[5] = Modes.sbsos; + + for (j = 0; j < sizeof(services)/sizeof(int); j++) { + fd = anetTcpAccept(Modes.aneterr, services[j], NULL, &port); + if (fd == -1) continue; + + if (fd >= MODES_NET_MAX_FD) { + close(fd); + return; // Max number of clients reached + } + + anetNonBlock(Modes.aneterr, fd); + c = (struct client *) malloc(sizeof(*c)); + c->service = services[j]; + c->fd = fd; + c->buflen = 0; + Modes.clients[fd] = c; + anetSetSendBuffer(Modes.aneterr,fd,MODES_NET_SNDBUF_SIZE); + + if (Modes.maxfd < fd) Modes.maxfd = fd; + if (services[j] == Modes.sbsos) Modes.stat_sbs_connections++; + if (services[j] == Modes.ros) Modes.stat_raw_connections++; + if (services[j] == Modes.bos) Modes.stat_beast_connections++; + + j--; // Try again with the same listening port + + if (Modes.debug & MODES_DEBUG_NET) + printf("Created new client %d\n", fd); + } +} +// +//========================================================================= +// +// On error free the client, collect the structure, adjust maxfd if needed. +// +void modesFreeClient(int fd) { + close(fd); + if (Modes.clients[fd]->service == Modes.sbsos) { + if (Modes.stat_sbs_connections) Modes.stat_sbs_connections--; + } + else if (Modes.clients[fd]->service == Modes.ros) { + if (Modes.stat_raw_connections) Modes.stat_raw_connections--; + } + else if (Modes.clients[fd]->service == Modes.bos) { + if (Modes.stat_beast_connections) Modes.stat_beast_connections--; + } + free(Modes.clients[fd]); + Modes.clients[fd] = NULL; + + if (Modes.debug & MODES_DEBUG_NET) + printf("Closing client %d\n", fd); + + // If this was our maxfd, rescan the full clients array to check what's + // the new max. + if (Modes.maxfd == fd) { + int j; + + Modes.maxfd = -1; + for (j = 0; j < MODES_NET_MAX_FD; j++) { + if (Modes.clients[j]) Modes.maxfd = j; + } + } +} +// +//========================================================================= +// +// Send the specified message to all clients listening for a given service +// +void modesSendAllClients(int service, void *msg, int len) { + int j; + struct client *c; + + for (j = 0; j <= Modes.maxfd; j++) { + c = Modes.clients[j]; + if (c && c->service == service) { + int nwritten = write(j, msg, len); + if (nwritten != len) { + modesFreeClient(j); + } + } + } +} +// +//========================================================================= +// +// Write raw output in Beast Binary format with Timestamp to TCP clients +// +void modesSendBeastOutput(struct modesMessage *mm) { + char *p = &Modes.beastOut[Modes.beastOutUsed]; + int msgLen = mm->msgbits / 8; + char * pTimeStamp; + int j; + + *p++ = 0x1a; + if (msgLen == MODES_SHORT_MSG_BYTES) + {*p++ = '2';} + else if (msgLen == MODES_LONG_MSG_BYTES) + {*p++ = '3';} + else if (msgLen == MODEAC_MSG_BYTES) + {*p++ = '1';} + else + {return;} + + pTimeStamp = (char *) &mm->timestampMsg; + for (j = 5; j >= 0; j--) { + *p++ = pTimeStamp[j]; + } + + *p++ = mm->signalLevel; + + memcpy(p, mm->msg, msgLen); + + Modes.beastOutUsed += (msgLen + 9); + if (Modes.beastOutUsed >= Modes.net_output_raw_size) + { + modesSendAllClients(Modes.bos, Modes.beastOut, Modes.beastOutUsed); + Modes.beastOutUsed = 0; + Modes.net_output_raw_rate_count = 0; + } +} +// +//========================================================================= +// +// Write raw output to TCP clients +// +void modesSendRawOutput(struct modesMessage *mm) { + char *p = &Modes.rawOut[Modes.rawOutUsed]; + int msgLen = mm->msgbits / 8; + int j; + unsigned char * pTimeStamp; + + if (Modes.mlat && mm->timestampMsg) { + *p++ = '@'; + pTimeStamp = (unsigned char *) &mm->timestampMsg; + for (j = 5; j >= 0; j--) { + sprintf(p, "%02X", pTimeStamp[j]); + p += 2; + } + Modes.rawOutUsed += 12; // additional 12 characters for timestamp + } else + *p++ = '*'; + + for (j = 0; j < msgLen; j++) { + sprintf(p, "%02X", mm->msg[j]); + p += 2; + } + + *p++ = ';'; + *p++ = '\n'; + + Modes.rawOutUsed += ((msgLen*2) + 3); + if (Modes.rawOutUsed >= Modes.net_output_raw_size) + { + modesSendAllClients(Modes.ros, Modes.rawOut, Modes.rawOutUsed); + Modes.rawOutUsed = 0; + Modes.net_output_raw_rate_count = 0; + } +} +// +//========================================================================= +// +// Write SBS output to TCP clients +// The message structure mm->bFlags tells us what has been updated by this message +// +void modesSendSBSOutput(struct modesMessage *mm) { + char msg[256], *p = msg; + uint32_t offset; + struct timeb epocTime; + struct tm stTime; + int msgType; + + // + // SBS BS style output checked against the following reference + // http://www.homepages.mcb.net/bones/SBS/Article/Barebones42_Socket_Data.htm - seems comprehensive + // + + // Decide on the basic SBS Message Type + if ((mm->msgtype == 4) || (mm->msgtype == 20)) { + msgType = 5; + } else if ((mm->msgtype == 5) || (mm->msgtype == 21)) { + msgType = 6; + } else if ((mm->msgtype == 0) || (mm->msgtype == 16)) { + msgType = 7; + } else if (mm->msgtype == 11) { + msgType = 8; + } else if ((mm->msgtype != 17) && (mm->msgtype != 18)) { + return; + } else if ((mm->metype >= 1) && (mm->metype <= 4)) { + msgType = 1; + } else if ((mm->metype >= 5) && (mm->metype <= 8)) { + if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID) + {msgType = 2;} + else + {msgType = 7;} + } else if ((mm->metype >= 9) && (mm->metype <= 18)) { + if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID) + {msgType = 3;} + else + {msgType = 7;} + } else if (mm->metype != 19) { + return; + } else if ((mm->mesub == 1) || (mm->mesub == 2)) { + msgType = 4; + } else { + return; + } + + // Fields 1 to 6 : SBS message type and ICAO address of the aircraft and some other stuff + p += sprintf(p, "MSG,%d,111,11111,%06X,111111,", msgType, mm->addr); + + // Fields 7 & 8 are the current time and date + 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 + offset = (int) (mm->timestampMsg - Modes.timestampBlk); // This is the time (in 12Mhz ticks) into the Block + offset = offset / 12000; // convert to milliseconds + epocTime.millitm += offset; // add on the offset time to the Block start time + if (epocTime.millitm > 999) // if we've caused an overflow into the next second... + {epocTime.millitm -= 1000; epocTime.time ++;} // ..correct the overflow + stTime = *localtime(&epocTime.time); // convert the time to year, month day, hours, min, sec + p += sprintf(p, "%04d/%02d/%02d,", (stTime.tm_year+1900),(stTime.tm_mon+1), stTime.tm_mday); + p += sprintf(p, "%02d:%02d:%02d.%03d,", stTime.tm_hour, stTime.tm_min, stTime.tm_sec, epocTime.millitm); + } else { + p += sprintf(p, ",,"); + } + + // Fields 9 & 10 are the current time and date + ftime(&epocTime); // get the current system time & date + stTime = *localtime(&epocTime.time); // convert the time to year, month day, hours, min, sec + p += sprintf(p, "%04d/%02d/%02d,", (stTime.tm_year+1900),(stTime.tm_mon+1), stTime.tm_mday); + p += sprintf(p, "%02d:%02d:%02d.%03d", stTime.tm_hour, stTime.tm_min, stTime.tm_sec, epocTime.millitm); + + // Field 11 is the callsign (if we have it) + if (mm->bFlags & MODES_ACFLAGS_CALLSIGN_VALID) {p += sprintf(p, ",%s", mm->flight);} + else {p += sprintf(p, ",");} + + // Field 12 is the altitude (if we have it) - force to zero if we're on the ground + if ((mm->bFlags & MODES_ACFLAGS_AOG_GROUND) == MODES_ACFLAGS_AOG_GROUND) { + p += sprintf(p, ",0"); + } else if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) { + p += sprintf(p, ",%d", mm->altitude); + } else { + p += sprintf(p, ","); + } + + // Field 13 and 14 are the ground Speed and Heading (if we have them) + if (mm->bFlags & MODES_ACFLAGS_NSEWSPD_VALID) {p += sprintf(p, ",%d,%d", mm->velocity, mm->heading);} + else {p += sprintf(p, ",,");} + + // Fields 15 and 16 are the Lat/Lon (if we have it) + if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID) {p += sprintf(p, ",%1.5f,%1.5f", mm->fLat, mm->fLon);} + else {p += sprintf(p, ",,");} + + // Field 17 is the VerticalRate (if we have it) + if (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) {p += sprintf(p, ",%d", mm->vert_rate);} + else {p += sprintf(p, ",");} + + // Field 18 is the Squawk (if we have it) + if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) {p += sprintf(p, ",%x", mm->modeA);} + else {p += sprintf(p, ",");} + + // Field 19 is the Squawk Changing Alert flag (if we have it) + if (mm->bFlags & MODES_ACFLAGS_FS_VALID) { + if ((mm->fs >= 2) && (mm->fs <= 4)) { + p += sprintf(p, ",-1"); + } else { + p += sprintf(p, ",0"); + } + } else { + p += sprintf(p, ","); + } + + // Field 20 is the Squawk Emergency flag (if we have it) + if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) { + if ((mm->modeA == 0x7500) || (mm->modeA == 0x7600) || (mm->modeA == 0x7700)) { + p += sprintf(p, ",-1"); + } else { + p += sprintf(p, ",0"); + } + } else { + p += sprintf(p, ","); + } + + // Field 21 is the Squawk Ident flag (if we have it) + if (mm->bFlags & MODES_ACFLAGS_FS_VALID) { + if ((mm->fs >= 4) && (mm->fs <= 5)) { + p += sprintf(p, ",-1"); + } else { + p += sprintf(p, ",0"); + } + } else { + p += sprintf(p, ","); + } + + // Field 22 is the OnTheGround flag (if we have it) + if (mm->bFlags & MODES_ACFLAGS_AOG_VALID) { + if (mm->bFlags & MODES_ACFLAGS_AOG) { + p += sprintf(p, ",-1"); + } else { + p += sprintf(p, ",0"); + } + } else { + p += sprintf(p, ","); + } + + p += sprintf(p, "\r\n"); + modesSendAllClients(Modes.sbsos, msg, p-msg); +} +// +//========================================================================= +// +void modesQueueOutput(struct modesMessage *mm) { + if (Modes.stat_sbs_connections) {modesSendSBSOutput(mm);} + if (Modes.stat_beast_connections) {modesSendBeastOutput(mm);} + if (Modes.stat_raw_connections) {modesSendRawOutput(mm);} +} +// +//========================================================================= +// +// This function decodes a Beast binary format message +// +// The message is passed to the higher level layers, so it feeds +// the selected screen output, the network output and so forth. +// +// If the message looks invalid it is silently discarded. +// +// The function always returns 0 (success) to the caller as there is no +// case where we want broken messages here to close the client connection. +// +int decodeBinMessage(struct client *c, char *p) { + int msgLen = 0; + unsigned char msg[MODES_LONG_MSG_BYTES]; + struct modesMessage mm; + MODES_NOTUSED(c); + memset(&mm, 0, sizeof(mm)); + + if ((*p == '1') && (Modes.mode_ac)) { // skip ModeA/C unless user enables --modes-ac + msgLen = MODEAC_MSG_BYTES; + } else if (*p == '2') { + msgLen = MODES_SHORT_MSG_BYTES; + } else if (*p == '3') { + msgLen = MODES_LONG_MSG_BYTES; + } + + if (msgLen) { + // Mark messages received over the internet as remote so that we don't try to + // pass them off as being received by this instance when forwarding them + mm.remote = 1; + p += 7; // Skip the timestamp + mm.signalLevel = *p++; // Grab the signal level + memcpy(msg, p, msgLen); // and the data + + if (msgLen == MODEAC_MSG_BYTES) { // ModeA or ModeC + decodeModeAMessage(&mm, ((msg[0] << 8) | msg[1])); + } else { + decodeModesMessage(&mm, msg); + } + + useModesMessage(&mm); + } + return (0); +} +// +//========================================================================= +// +// Turn an hex digit into its 4 bit decimal value. +// Returns -1 if the digit is not in the 0-F range. +// +int hexDigitVal(int c) { + c = tolower(c); + if (c >= '0' && c <= '9') return c-'0'; + else if (c >= 'a' && c <= 'f') return c-'a'+10; + else return -1; +} +// +//========================================================================= +// +// This function decodes a string representing message in raw hex format +// like: *8D4B969699155600E87406F5B69F; The string is null-terminated. +// +// The message is passed to the higher level layers, so it feeds +// the selected screen output, the network output and so forth. +// +// If the message looks invalid it is silently discarded. +// +// The function always returns 0 (success) to the caller as there is no +// case where we want broken messages here to close the client connection. +// +int decodeHexMessage(struct client *c, char *hex) { + int l = strlen(hex), j; + unsigned char msg[MODES_LONG_MSG_BYTES]; + struct modesMessage mm; + MODES_NOTUSED(c); + memset(&mm, 0, sizeof(mm)); + + // Mark messages received over the internet as remote so that we don't try to + // pass them off as being received by this instance when forwarding them + mm.remote = 1; + mm.signalLevel = 0xFF; + + // Remove spaces on the left and on the right + while(l && isspace(hex[l-1])) { + hex[l-1] = '\0'; l--; + } + while(isspace(*hex)) { + hex++; l--; + } + + // Turn the message into binary. + // Accept *-AVR raw @-AVR/BEAST timeS+raw %-AVR timeS+raw (CRC good) <-BEAST timeS+sigL+raw + // and some AVR records that we can understand + if (hex[l-1] != ';') {return (0);} // not complete - abort + + switch(hex[0]) { + case '<': { + mm.signalLevel = (hexDigitVal(hex[13])<<4) | hexDigitVal(hex[14]); + hex += 15; l -= 16; // Skip <, timestamp and siglevel, and ; + break;} + + case '@': // No CRC check + case '%': { // CRC is OK + hex += 13; l -= 14; // Skip @,%, and timestamp, and ; + break;} + + case '*': + case ':': { + hex++; l-=2; // Skip * and ; + break;} + + default: { + return (0); // We don't know what this is, so abort + break;} + } + + if ( (l != (MODEAC_MSG_BYTES * 2)) + && (l != (MODES_SHORT_MSG_BYTES * 2)) + && (l != (MODES_LONG_MSG_BYTES * 2)) ) + {return (0);} // Too short or long message... broken + + if ( (0 == Modes.mode_ac) + && (l == (MODEAC_MSG_BYTES * 2)) ) + {return (0);} // Right length for ModeA/C, but not enabled + + for (j = 0; j < l; j += 2) { + int high = hexDigitVal(hex[j]); + int low = hexDigitVal(hex[j+1]); + + if (high == -1 || low == -1) return 0; + msg[j/2] = (high << 4) | low; + } + + if (l == (MODEAC_MSG_BYTES * 2)) { // ModeA or ModeC + decodeModeAMessage(&mm, ((msg[0] << 8) | msg[1])); + } else { // Assume ModeS + decodeModesMessage(&mm, msg); + } + + useModesMessage(&mm); + return (0); +} +// +//========================================================================= +// +// Return a description of planes in json. No metric conversion +// +char *aircraftsToJson(int *len) { + time_t now = time(NULL); + struct aircraft *a = Modes.aircrafts; + int buflen = 1024; // The initial buffer is incremented as needed + char *buf = (char *) malloc(buflen), *p = buf; + int l; + + l = snprintf(p,buflen,"[\n"); + p += l; buflen -= l; + while(a) { + int position = 0; + int track = 0; + + if (a->modeACflags & MODEAC_MSG_FLAG) { // skip any fudged ICAO records Mode A/C + a = a->next; + continue; + } + + if (a->bFlags & MODES_ACFLAGS_LATLON_VALID) { + position = 1; + } + + if (a->bFlags & MODES_ACFLAGS_HEADING_VALID) { + track = 1; + } + + // No metric conversion + l = snprintf(p,buflen, + "{\"hex\":\"%06x\", \"squawk\":\"%04x\", \"flight\":\"%s\", \"lat\":%f, " + "\"lon\":%f, \"validposition\":%d, \"altitude\":%d, \"track\":%d, \"validtrack\":%d," + "\"speed\":%d, \"messages\":%ld, \"seen\":%d},\n", + a->addr, a->modeA, a->flight, a->lat, a->lon, position, a->altitude, a->track, track, + a->speed, a->messages, (int)(now - a->seen)); + p += l; buflen -= l; + + //Resize if needed + if (buflen < 256) { + int used = p-buf; + buflen += 1024; // Our increment. + buf = (char *) realloc(buf,used+buflen); + p = buf+used; + } + + a = a->next; + } + + //Remove the final comma if any, and closes the json array. + if (*(p-2) == ',') { + *(p-2) = '\n'; + p--; + buflen++; + } + + l = snprintf(p,buflen,"]\n"); + p += l; buflen -= l; + + *len = p-buf; + return buf; +} +// +//========================================================================= +// +#define MODES_CONTENT_TYPE_HTML "text/html;charset=utf-8" +#define MODES_CONTENT_TYPE_CSS "text/css;charset=utf-8" +#define MODES_CONTENT_TYPE_JSON "application/json;charset=utf-8" +#define MODES_CONTENT_TYPE_JS "application/javascript;charset=utf-8" +// +// Get an HTTP request header and write the response to the client. +// gain here we assume that the socket buffer is enough without doing +// any kind of userspace buffering. +// +// Returns 1 on error to signal the caller the client connection should +// be closed. +// +int handleHTTPRequest(struct client *c, char *p) { + char hdr[512]; + int clen, hdrlen; + int httpver, keepalive; + char *url, *content; + char ctype[48]; + char getFile[1024]; + char *ext; + + if (Modes.debug & MODES_DEBUG_NET) + printf("\nHTTP request: %s\n", c->buf); + + // Minimally parse the request. + httpver = (strstr(p, "HTTP/1.1") != NULL) ? 11 : 10; + if (httpver == 10) { + // HTTP 1.0 defaults to close, unless otherwise specified. + keepalive = strstr(p, "Connection: keep-alive") != NULL; + } else if (httpver == 11) { + // HTTP 1.1 defaults to keep-alive, unless close is specified. + keepalive = strstr(p, "Connection: close") == NULL; + } + + // Identify he URL. + p = strchr(p,' '); + if (!p) return 1; // There should be the method and a space + url = ++p; // Now this should point to the requested URL + p = strchr(p, ' '); + if (!p) return 1; // There should be a space before HTTP/ + *p = '\0'; + + if (Modes.debug & MODES_DEBUG_NET) { + printf("\nHTTP keep alive: %d\n", keepalive); + printf("HTTP requested URL: %s\n\n", url); + } + + if (strlen(url) < 2) { + snprintf(getFile, sizeof getFile, "%s/gmap.html", HTMLPATH); // Default file + } else { + snprintf(getFile, sizeof getFile, "%s/%s", HTMLPATH, url); + } + + // Select the content to send, we have just two so far: + // "/" -> Our google map application. + // "/data.json" -> Our ajax request to update planes. + if (strstr(url, "/data.json")) { + content = aircraftsToJson(&clen); + //snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JSON); + } else { + struct stat sbuf; + int fd = -1; + + if (stat(getFile, &sbuf) != -1 && (fd = open(getFile, O_RDONLY)) != -1) { + content = (char *) malloc(sbuf.st_size); + if (read(fd, content, sbuf.st_size) == -1) { + snprintf(content, sbuf.st_size, "Error reading from file: %s", strerror(errno)); + } + clen = sbuf.st_size; + } else { + char buf[128]; + clen = snprintf(buf,sizeof(buf),"Error opening HTML file: %s", strerror(errno)); + content = strdup(buf); + } + + if (fd != -1) { + close(fd); + } + } + + // Get file extension and content type + snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_HTML); // Default content type + ext = strrchr(getFile, '.'); + + if (strlen(ext) > 0) { + if (strstr(ext, ".json")) { + snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JSON); + } else if (strstr(ext, ".css")) { + snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_CSS); + } else if (strstr(ext, ".js")) { + snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JS); + } + } + + // Create the header and send the reply + hdrlen = snprintf(hdr, sizeof(hdr), + "HTTP/1.1 200 OK\r\n" + "Server: Dump1090\r\n" + "Content-Type: %s\r\n" + "Connection: %s\r\n" + "Content-Length: %d\r\n" + "\r\n", + ctype, + keepalive ? "keep-alive" : "close", + clen); + + if (Modes.debug & MODES_DEBUG_NET) { + printf("HTTP Reply header:\n%s", hdr); + } + + // Send header and content. + if (write(c->fd, hdr, hdrlen) == -1 || write(c->fd, content, clen) == -1) { + free(content); + return 1; + } + free(content); + Modes.stat_http_requests++; + return !keepalive; +} +// +//========================================================================= +// +// This function polls the clients using read() in order to receive new +// messages from the net. +// +// The message is supposed to be separated from the next message by the +// separator 'sep', which is a null-terminated C string. +// +// Every full message received is decoded and passed to the higher layers +// calling the function's 'handler'. +// +// The handler returns 0 on success, or 1 to signal this function we should +// close the connection with the client in case of non-recoverable errors. +// +void modesReadFromClient(struct client *c, char *sep, + int(*handler)(struct client *, char *)) { + int left; + int nread; + int fullmsg; + char *s, *e; + + while(1) { + + fullmsg = 0; + left = MODES_CLIENT_BUF_SIZE - c->buflen; + // If our buffer is full discard it, this is some badly formatted shit + if (left == 0) { + c->buflen = 0; + left = MODES_CLIENT_BUF_SIZE; + // If there is garbage, read more to discard it ASAP + } + nread = read(c->fd, c->buf+c->buflen, left); + + if (nread <= 0) { + if (nread == 0 || errno != EAGAIN) { // Error, or end of file + modesFreeClient(c->fd); + } + break; // Serve next client + } + c->buflen += nread; + + // Always null-term so we are free to use strstr() (it won't affect binary case) + c->buf[c->buflen] = '\0'; + + e = s = c->buf; // Start with the start of buffer, first message + + if (c->service == Modes.bis) { + // This is the Bease Binary scanning case. + // If there is a complete message still in the buffer, there must be the separator 'sep' + // in the buffer, note that we full-scan the buffer at every read for simplicity. + + left = c->buflen; // Length of valid search for memchr() + while (left && ((s = memchr(e, (char) 0x1a, left)) != NULL)) { // In reality the first byte of buffer 'should' be 0x1a + s++; // skip the 0x1a + if (*s == '1') { + e = s + MODEAC_MSG_BYTES + 8; // point past remainder of message + } else if (*s == '2') { + e = s + MODES_SHORT_MSG_BYTES + 8; + } else if (*s == '3') { + e = s + MODES_LONG_MSG_BYTES + 8; + } else { + e = s; // Not a valid beast message, skip + left = &(c->buf[c->buflen]) - e; + continue; + } + left = &(c->buf[c->buflen]) - e; + if (left < 0) { // Incomplete message in buffer + e = s - 1; // point back at last found 0x1a. + break; + } + // Have a 0x1a followed by 1, 2 or 3 - pass message less 0x1a to handler. + if (handler(c, s)) { + modesFreeClient(c->fd); + return; + } + fullmsg = 1; + } + s = e; // For the buffer remainder below + + } else { + // This is the ASCII scanning case, AVR RAW or HTTP at present + // If there is a complete message still in the buffer, there must be the separator 'sep' + // in the buffer, note that we full-scan the buffer at every read for simplicity. + + while ((e = strstr(s, sep)) != NULL) { // end of first message if found + *e = '\0'; // The handler expects null terminated strings + if (handler(c, s)) { // Pass message to handler. + modesFreeClient(c->fd); // Handler returns 1 on error to signal we . + return; // should close the client connection + } + s = e + strlen(sep); // Move to start of next message + fullmsg = 1; + } + } + + if (fullmsg) { // We processed something - so + c->buflen = &(c->buf[c->buflen]) - s; // The unprocessed buffer length + memmove(c->buf, s, c->buflen); // move what's remaining to the start of the buffer + } else { // If no message was decoded process the next client + break; + } + } +} +// +//========================================================================= +// +// Read data from clients. This function actually delegates a lower-level +// function that depends on the kind of service (raw, http, ...). +// +void modesReadFromClients(void) { + int j; + struct client *c; + + modesAcceptClients(); + + for (j = 0; j <= Modes.maxfd; j++) { + if ((c = Modes.clients[j]) == NULL) continue; + if (c->service == Modes.ris) + modesReadFromClient(c,"\n",decodeHexMessage); + else if (c->service == Modes.bis) + modesReadFromClient(c,"",decodeBinMessage); + else if (c->service == Modes.https) + modesReadFromClient(c,"\r\n\r\n",handleHTTPRequest); + } +} +// +// =============================== Network IO =========================== +//