diff --git a/Makefile b/Makefile index 73a01d1..9314ec0 100644 --- a/Makefile +++ b/Makefile @@ -33,11 +33,11 @@ dump1090: dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o crc.o demo view1090: view1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o crc.o stats.o cpr.o icao_filter.o track.o util.o $(CC) -g -o $@ $^ $(LIBS) $(LDFLAGS) -faup1090: faup1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o crc.o stats.o cpr.o icao_filter.o track.o util.o +faup1090: faup1090.o anet.o mode_ac.o mode_s.o net_io.o crc.o stats.o cpr.o icao_filter.o track.o util.o $(CC) -g -o $@ $^ $(LIBS) $(LDFLAGS) clean: - rm -f *.o dump1090 view1090 cprtests crctests + rm -f *.o dump1090 view1090 faup1090 cprtests crctests test: cprtests ./cprtests diff --git a/demod_2000.c b/demod_2000.c index 5ffb092..493052e 100644 --- a/demod_2000.c +++ b/demod_2000.c @@ -51,6 +51,288 @@ // Mode S 2.0MHz demodulator +// +// ===================== 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);} + + mm->signalLevel = (fSig + fNoise) * (fSig + fNoise) / MAX_POWER; + + return ModeABits; + } + // ============================== Debugging ================================= // // Helper function for dumpMagnitudeVector(). diff --git a/mode_ac.c b/mode_ac.c index e9a78f5..c2e3a8f 100644 --- a/mode_ac.c +++ b/mode_ac.c @@ -30,287 +30,6 @@ #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);} - - mm->signalLevel = (fSig + fNoise) * (fSig + fNoise) / MAX_POWER; - - return ModeABits; - } -// //========================================================================= // // Input format is : 00:A4:A2:A1:00:B4:B2:B1:00:C4:C2:C1:00:D4:D2:D1 diff --git a/stats.c b/stats.c index 92f0dfa..fe5e5be 100644 --- a/stats.c +++ b/stats.c @@ -66,8 +66,6 @@ void display_stats(struct stats *st) { char tb_start[30], tb_end[30]; printf("\n\n"); - if (Modes.interactive) - interactiveShowData(); tt_start = st->start/1000; localtime_r(&tt_start, &tm_start);