// Part of dump1090, a Mode S message decoder for RTLSDR devices. // // demod_2000.c: 2MHz Mode S demodulator. // // Copyright (c) 2014,2015 Oliver Jowett // // This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the // Free Software Foundation, either version 2 of the License, or (at your // option) any later version. // // This file is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // This file incorporates work covered by the following copyright and // permission notice: // // 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 2.0MHz demodulator // ============================== 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 // static 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 0x%04X\n", index, buf, magnitude); else printf("[%.2d] |%-66s 0x%04X\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. // static 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. // static 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. // static 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"); } // //========================================================================= // // 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 // static 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; } static uint16_t clamped_scale(uint16_t v, uint16_t scale) { uint32_t scaled = (uint32_t)v * scale / 16384; if (scaled > 65535) return 65535; return (uint16_t) scaled; } // This function decides whether we are sampling early or late, // and by approximately how much, by looking at the energy in // preamble bits before and after the expected pulse locations. // // It then deals with one sample pair at a time, comparing samples // to make a decision about the bit value. Based on this decision it // modifies the sample value of the *adjacent* sample which will // contain some of the energy from the bit we just inspected. // // pPayload[0] should be the start of the preamble, // pPayload[-1 .. MODES_PREAMBLE_SAMPLES + MODES_LONG_MSG_SAMPLES - 1] should be accessible. // pPayload[MODES_PREAMBLE_SAMPLES .. MODES_PREAMBLE_SAMPLES + MODES_LONG_MSG_SAMPLES - 1] will be updated. static void applyPhaseCorrection(uint16_t *pPayload) { int j; // we expect 1 bits at 0, 2, 7, 9 // and 0 bits at -1, 1, 3, 4, 5, 6, 8, 10, 11, 12, 13, 14 // use bits -1,6 for early detection (bit 0/7 arrived a little early, our sample period starts after the bit phase so we include some of the next bit) // use bits 3,10 for late detection (bit 2/9 arrived a little late, our sample period starts before the bit phase so we include some of the last bit) uint32_t onTime = (pPayload[0] + pPayload[2] + pPayload[7] + pPayload[9]); uint32_t early = (pPayload[-1] + pPayload[6]) << 1; uint32_t late = (pPayload[3] + pPayload[10]) << 1; if (onTime == 0 && early == 0 && late == 0) { // Blah, can't do anything with this, avoid a divide-by-zero return; } if (early > late) { // Our sample period starts late and so includes some of the next bit. uint16_t scaleUp = 16384 + 16384 * early / (early + onTime); // 1 + early / (early+onTime) uint16_t scaleDown = 16384 - 16384 * early / (early + onTime); // 1 - early / (early+onTime) // trailing bits are 0; final data sample will be a bit low. pPayload[MODES_PREAMBLE_SAMPLES + MODES_LONG_MSG_SAMPLES - 1] = clamped_scale(pPayload[MODES_PREAMBLE_SAMPLES + MODES_LONG_MSG_SAMPLES - 1], scaleUp); for (j = MODES_PREAMBLE_SAMPLES + MODES_LONG_MSG_SAMPLES - 2; j > MODES_PREAMBLE_SAMPLES; j -= 2) { if (pPayload[j] > pPayload[j+1]) { // x [1 0] y // x overlapped with the "1" bit and is slightly high pPayload[j-1] = clamped_scale(pPayload[j-1], scaleDown); } else { // x [0 1] y // x overlapped with the "0" bit and is slightly low pPayload[j-1] = clamped_scale(pPayload[j-1], scaleUp); } } } else { // Our sample period starts early and so includes some of the previous bit. uint16_t scaleUp = 16384 + 16384 * late / (late + onTime); // 1 + late / (late+onTime) uint16_t scaleDown = 16384 - 16384 * late / (late + onTime); // 1 - late / (late+onTime) // leading bits are 0; first data sample will be a bit low. pPayload[MODES_PREAMBLE_SAMPLES] = clamped_scale(pPayload[MODES_PREAMBLE_SAMPLES], scaleUp); for (j = MODES_PREAMBLE_SAMPLES; j < MODES_PREAMBLE_SAMPLES + MODES_LONG_MSG_SAMPLES - 2; j += 2) { if (pPayload[j] > pPayload[j+1]) { // x [1 0] y // y overlapped with the "0" bit and is slightly low pPayload[j+2] = clamped_scale(pPayload[j+2], scaleUp); } else { // x [0 1] y // y overlapped with the "1" bit and is slightly high pPayload[j+2] = clamped_scale(pPayload[j+2], scaleDown); } } } } // //========================================================================= // // 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 demodulate2000(uint16_t *m, uint32_t mlen) { struct modesMessage mm; unsigned char msg[MODES_LONG_MSG_BYTES], *pMsg; uint16_t aux[MODES_PREAMBLE_SAMPLES+MODES_LONG_MSG_SAMPLES+1]; 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; uint32_t sigLevel, noiseLevel; uint16_t snr; 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.stats_current.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.stats_current.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, &pPreamble[-1], sizeof(aux)); applyPhaseCorrection(&aux[1]); Modes.stats_current.out_of_phase++; pPayload = &aux[1 + MODES_PREAMBLE_SAMPLES]; // 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 sigLevel = pPreamble[0] + pPreamble[2] + pPreamble[7] + pPreamble[9]; noiseLevel = pPreamble[1] + pPreamble[3] + pPreamble[4] + pPreamble[6] + 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) { sigLevel += a; noiseLevel += b; }} else if (a < b) {/*theByte |= 0;*/ if (i < 56) { sigLevel += b; noiseLevel += a; }} else { if (i < 56) { sigLevel += a; noiseLevel += a; } 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.stats_current.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 if (msglen > 0) { 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.stats_current.DF_Type_Corrected++; errors--; // decrease the error count so we attempt to use the modified DF. } } } // snr = 5 * 20log10(sigLevel / noiseLevel) (in units of 0.2dB) // = 100log10(sigLevel) - 100log10(noiseLevel) while (sigLevel > 65535 || noiseLevel > 65535) { sigLevel >>= 1; noiseLevel >>= 1; } snr = Modes.log10lut[sigLevel] - Modes.log10lut[noiseLevel]; // 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) && ((2 * snr) > (int) (MODES_MSG_SQUELCH_DB * 10)) && (errors <= MODES_MSG_ENCODER_ERRS) ) { // Set initial mm structure details mm.timestampMsg = Modes.timestampBlk + (j*6); mm.signalLevel = (snr > 255 ? 255 : (uint8_t)snr); mm.phase_corrected = use_correction; // Decode the received message decodeModesMessage(&mm, msg); // Update statistics if (Modes.stats) { struct demod_stats *dstats = (use_correction ? &Modes.stats_current.demod_phasecorrected : &Modes.stats_current.demod); switch (errors) { case 0: dstats->demodulated0++; break; case 1: dstats->demodulated1++; break; case 2: dstats->demodulated2++; break; default: dstats->demodulated3++; break; } if (mm.crcok) { dstats->goodcrc++; dstats->goodcrc_byphase[0]++; } else if (mm.correctedbits > 0) { dstats->badcrc++; dstats->fixed++; if (mm.correctedbits <= MODES_MAX_BITERRORS) dstats->bit_fix[mm.correctedbits-1] += 1; } else { dstats->badcrc++; } } // 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 || mm.correctedbits) { j += (MODES_PREAMBLE_US+msglen)*2 - 1; } // 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; } } }