Implement Mode A/C decoding
First attempt at decoding legacy SSR Modes A and C. If the command line switch --modeac is used, the program will now attempt to recover Mode A/C signals contained in the raw I/Q data stream. The current recovery mechanism is quite strict and does not cope well with overlapping and corrupt SSR replies. I estimate that less than 20% of possible returns are decoded correctly. Hopefully over the next few iterations this can be improved. If outputting raw data it is recommended to use the --net-ro-size and --net-ro-rate command line options to reduce the number of very small ethernet packets that will be generated by mode A/C replies.
This commit is contained in:
parent
39cb96f24b
commit
9fff65ba58
530
dump1090.c
530
dump1090.c
|
@ -56,7 +56,7 @@
|
|||
// MinorVer changes when additional features are added, but not for bug fixes (range 00-99)
|
||||
// DayDate & Year changes for all changes, including for bug fixes. It represent the release date of the update
|
||||
//
|
||||
#define MODES_DUMP1090_VERSION "1.01.2004.13"
|
||||
#define MODES_DUMP1090_VERSION "1.02.2204.13"
|
||||
|
||||
#define MODES_DEFAULT_RATE 2000000
|
||||
#define MODES_DEFAULT_FREQ 1090000000
|
||||
|
@ -70,7 +70,11 @@
|
|||
#define MODES_MSG_SQUELCH_LEVEL 0x02FF /* Average signal strength limit */
|
||||
#define MODES_MSG_ENCODER_ERRS 3 /* Maximum number of encoding errors */
|
||||
|
||||
#define MODEA_MSG_BYTES 2
|
||||
#define MODEAC_MSG_SAMPLES (25 * 2) /* include up to the SPI bit */
|
||||
#define MODEAC_MSG_BYTES 2
|
||||
#define MODEAC_MSG_SQUELCH_LEVEL 0x07FF /* Average signal strength limit */
|
||||
#define MODEAC_MSG_FLAG (1<<0)
|
||||
#define MODEAC_MSG_MODES_HIT (1<<1)
|
||||
|
||||
#define MODES_PREAMBLE_US 8 /* microseconds = bits */
|
||||
#define MODES_PREAMBLE_SAMPLES (MODES_PREAMBLE_US * 2)
|
||||
|
@ -139,6 +143,10 @@ struct aircraft {
|
|||
int track; /* Angle of flight. */
|
||||
time_t seen; /* Time at which the last packet was received. */
|
||||
long messages; /* Number of Mode S messages received. */
|
||||
int modeC; /* Altitude */
|
||||
long modeAcount; /* Mode A Squawk hit Count */
|
||||
long modeCcount; /* Mode C Altitude hit Count */
|
||||
int modeACflags; /* Flags for mode A/C recognition */
|
||||
/* Encoded latitude and longitude as extracted by odd and even
|
||||
* CPR encoded messages. */
|
||||
int odd_cprlat;
|
||||
|
@ -194,6 +202,7 @@ struct {
|
|||
int check_crc; /* Only display messages with good CRC. */
|
||||
int raw; /* Raw output format. */
|
||||
int beast; /* Beast binary format output. */
|
||||
int mode_ac; /* Enable decoding of SSR Modes A & C. */
|
||||
int debug; /* Debugging mode. */
|
||||
int net; /* Enable networking. */
|
||||
int net_only; /* Enable just networking. */
|
||||
|
@ -231,6 +240,7 @@ struct {
|
|||
unsigned int stat_sbs_connections;
|
||||
unsigned int stat_out_of_phase;
|
||||
unsigned int stat_DF_Corrected;
|
||||
unsigned int stat_ModeAC;
|
||||
} Modes;
|
||||
|
||||
/* The struct we use to store information about a decoded message. */
|
||||
|
@ -278,7 +288,7 @@ struct modesMessage {
|
|||
int identity; /* 13 bits identity (Squawk). */
|
||||
|
||||
/* Fields used by multiple message types. */
|
||||
int altitude, unit;
|
||||
int altitude, unit, modeC;
|
||||
};
|
||||
|
||||
void interactiveShowData(void);
|
||||
|
@ -317,6 +327,7 @@ void modesInitConfig(void) {
|
|||
Modes.check_crc = 1;
|
||||
Modes.raw = 0;
|
||||
Modes.beast = 0;
|
||||
Modes.mode_ac = 0;
|
||||
Modes.net = 0;
|
||||
Modes.net_only = 0;
|
||||
Modes.net_output_sbs_port = MODES_NET_OUTPUT_SBS_PORT;
|
||||
|
@ -448,6 +459,7 @@ void modesInit(void) {
|
|||
Modes.stat_sbs_connections = 0;
|
||||
Modes.stat_out_of_phase = 0;
|
||||
Modes.stat_DF_Corrected = 0;
|
||||
Modes.stat_ModeAC = 0;
|
||||
Modes.exit = 0;
|
||||
}
|
||||
|
||||
|
@ -541,7 +553,7 @@ void readDataFromFile(void) {
|
|||
/* 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(5000);
|
||||
usleep(64000);
|
||||
pthread_mutex_lock(&Modes.data_mutex);
|
||||
}
|
||||
|
||||
|
@ -708,6 +720,365 @@ void dumpRawMessage(char *descr, unsigned char *msg,
|
|||
printf("---\n\n");
|
||||
}
|
||||
|
||||
/* ===================== 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
|
||||
|| ((ModeA & 0xFFFFFB0F) == 0) ) // Whilst legal, indicates an altitude less than -200 feet
|
||||
{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(unsigned int ModeA, struct modesMessage *mm)
|
||||
{
|
||||
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
|
||||
mm->aa1 = 0xFF; // Use an upper address byte of FF, since this is ICAO unallocated
|
||||
mm->aa2 = (ModeA >> 8);
|
||||
mm->aa3 = (ModeA & 0x7F); // remove the Ident bit
|
||||
|
||||
// Set the Identity field to decimal ModeA
|
||||
mm->identity = (ModeA & 7)
|
||||
+ (((ModeA >> 4) & 7) * 10)
|
||||
+ (((ModeA >> 8) & 7) * 100)
|
||||
+ (((ModeA >> 12) & 7) * 1000);
|
||||
|
||||
// Flag ident in flight status
|
||||
mm->fs = ModeA & 0x0080;
|
||||
|
||||
// Convert ModeA to ModeC and use as an altitude
|
||||
mm->modeC = ModeAToModeC(ModeA);
|
||||
mm->altitude = 0;
|
||||
|
||||
// Limit the altitude to sensible values
|
||||
if ( (mm->modeC < 460) && (mm->modeC >= 0))
|
||||
{mm->altitude = mm->modeC * 100;}
|
||||
|
||||
// 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->errorbit = -1;
|
||||
}
|
||||
|
||||
/* ===================== Mode S detection and decoding ===================== */
|
||||
|
||||
/* Parity table for MODE S Messages.
|
||||
|
@ -1173,6 +1544,7 @@ void decodeModesMessage(struct modesMessage *mm, unsigned char *msg) {
|
|||
if (mm->msgtype == 0 || mm->msgtype == 4 ||
|
||||
mm->msgtype == 16 || mm->msgtype == 20) {
|
||||
mm->altitude = decodeAC13Field(msg, &mm->unit);
|
||||
mm->modeC = (mm->altitude + 49) / 100;
|
||||
}
|
||||
|
||||
/* Decode extended squitter specific stuff. */
|
||||
|
@ -1196,6 +1568,7 @@ void decodeModesMessage(struct modesMessage *mm, unsigned char *msg) {
|
|||
mm->fflag = msg[6] & (1<<2);
|
||||
mm->tflag = msg[6] & (1<<3);
|
||||
mm->altitude = decodeAC12Field(msg,&mm->unit);
|
||||
mm->modeC = (mm->altitude + 49) / 100;
|
||||
mm->raw_latitude = ((msg[6] & 3) << 15) |
|
||||
(msg[7] << 7) |
|
||||
(msg[8] >> 1);
|
||||
|
@ -1256,7 +1629,7 @@ void displayModesMessage(struct modesMessage *mm) {
|
|||
|
||||
/* Show the raw message. */
|
||||
if (Modes.mlat) {
|
||||
printf("@"); //&&&
|
||||
printf("@");
|
||||
pTimeStamp = (char *) &mm->timestampMsg;
|
||||
for (j=5; j>=0;j--) {
|
||||
printf("%02X",pTimeStamp[j]);
|
||||
|
@ -1272,7 +1645,9 @@ void displayModesMessage(struct modesMessage *mm) {
|
|||
return; /* Enough for --raw mode */
|
||||
}
|
||||
|
||||
if (mm->msgtype < 32)
|
||||
printf("CRC: %06x (%s)\n", (int)mm->crc, mm->crcok ? "ok" : "wrong");
|
||||
|
||||
if (mm->errorbit != -1)
|
||||
printf("Single bit error fixed, bit %d\n", mm->errorbit);
|
||||
|
||||
|
@ -1362,6 +1737,17 @@ void displayModesMessage(struct modesMessage *mm) {
|
|||
printf(" Unrecognized ME type: %d subtype: %d\n",
|
||||
mm->metype, mm->mesub);
|
||||
}
|
||||
} 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 : %04d IDENT\n", mm->identity);
|
||||
} else {
|
||||
printf(" Mode A : %04d\n", mm->identity);
|
||||
if (mm->altitude >= -1300)
|
||||
{printf(" Mode C : %d feet\n",mm->altitude);}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (Modes.check_crc)
|
||||
printf("DF %d with good CRC received "
|
||||
|
@ -1485,6 +1871,26 @@ void detectModeS(uint16_t *m, uint32_t mlen) {
|
|||
if (!use_correction) // This is not a re-try with phase correction
|
||||
{ // so try to find a new preamble
|
||||
|
||||
if (Modes.mode_ac)
|
||||
{
|
||||
struct modesMessage mm;
|
||||
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 and update statistics
|
||||
decodeModeAMessage(ModeA, &mm);
|
||||
|
||||
// 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. */
|
||||
|
@ -1712,19 +2118,23 @@ void detectModeS(uint16_t *m, uint32_t mlen) {
|
|||
* further processing and visualization. */
|
||||
void useModesMessage(struct modesMessage *mm) {
|
||||
if (!Modes.stats && (Modes.check_crc == 0 || mm->crcok)) {
|
||||
/* Track aircrafts in interactive mode or if the HTTP
|
||||
* interface is enabled. */
|
||||
if (Modes.interactive || Modes.stat_http_requests > 0 || Modes.stat_sbs_connections > 0) {
|
||||
// Track aircrafts if...
|
||||
if ( (Modes.interactive) // in interactive mode
|
||||
|| (Modes.stat_http_requests > 0) // or if the HTTP interface is enabled
|
||||
|| (Modes.stat_sbs_connections > 0) // or if sbs connections are established
|
||||
|| (Modes.mode_ac) ) { // or if mode A/C decoding is enabled
|
||||
struct aircraft *a = interactiveReceiveData(mm);
|
||||
if (a && Modes.stat_sbs_connections > 0) modesSendSBSOutput(mm, a); /* Feed SBS output clients. */
|
||||
if (a && Modes.stat_sbs_connections > 0) modesSendSBSOutput(mm, a); // Feed SBS output clients
|
||||
}
|
||||
/* In non-interactive way, display messages on standard output. */
|
||||
|
||||
// In non-interactive mode, display messages on standard output
|
||||
if (!Modes.interactive && !Modes.quiet) {
|
||||
displayModesMessage(mm);
|
||||
if (!Modes.raw && !Modes.onlyaddr) printf("\n");
|
||||
}
|
||||
/* Send data to connected clients. */
|
||||
if (Modes.net) { /* Feed raw output clients. */
|
||||
|
||||
// Send data to connected network clients
|
||||
if (Modes.net) {
|
||||
if (Modes.beast)
|
||||
modesSendBeastOutput(mm);
|
||||
else
|
||||
|
@ -1758,6 +2168,10 @@ struct aircraft *interactiveCreateAircraft(uint32_t addr) {
|
|||
a->seen = time(NULL);
|
||||
a->messages = 0;
|
||||
a->squawk = 0;
|
||||
a->modeACflags = 0;
|
||||
a->modeAcount = 0;
|
||||
a->modeCcount = 0;
|
||||
a->modeC = 0;
|
||||
a->next = NULL;
|
||||
return a;
|
||||
}
|
||||
|
@ -1773,6 +2187,48 @@ struct aircraft *interactiveFindAircraft(uint32_t addr) {
|
|||
}
|
||||
return NULL;
|
||||
}
|
||||
//
|
||||
// We have received a Mode A or C response.
|
||||
//
|
||||
// Search through the list of known aircraft and tag them if this Mode A/C matches any
|
||||
// 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
|
||||
//
|
||||
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 (a->squawk == b->squawk) { // If a 'real' Mode S ICAO exists using this Squawk
|
||||
b->modeAcount++;
|
||||
if ((b->modeAcount > 0) && (b->modeCcount > 1))
|
||||
{a->modeACflags |= MODEAC_MSG_MODES_HIT;} // flag that this ModeA/C probably belongs to a known Mode S
|
||||
|
||||
} else if ( (a->altitude) // If this ModeC altitude is valid and...
|
||||
&& (a->modeC == b->modeC) ) // ...a 'real' Mode S ICAO exists at this Altitude
|
||||
{
|
||||
b->modeCcount++;
|
||||
if ((b->modeAcount > 0) && (b->modeCcount > 1))
|
||||
{a->modeACflags |= MODEAC_MSG_MODES_HIT;} // flag that this ModeA/C probably belongs to a known Mode S
|
||||
}
|
||||
}
|
||||
b = b->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Always positive MOD operation, used for CPR decoding. */
|
||||
int cprModFunction(int a, int b) {
|
||||
|
@ -1912,11 +2368,11 @@ struct aircraft *interactiveReceiveData(struct modesMessage *mm) {
|
|||
if (Modes.check_crc && mm->crcok == 0) return NULL;
|
||||
addr = (mm->aa1 << 16) | (mm->aa2 << 8) | mm->aa3;
|
||||
|
||||
/* Loookup our aircraft or create a new one. */
|
||||
// Loookup our aircraft or create a new one
|
||||
a = interactiveFindAircraft(addr);
|
||||
if (!a) {
|
||||
a = interactiveCreateAircraft(addr);
|
||||
a->next = Modes.aircrafts;
|
||||
if (!a) { // If it's a currently unknown aircraft....
|
||||
a = interactiveCreateAircraft(addr); // ., 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
|
||||
|
@ -1942,13 +2398,21 @@ struct aircraft *interactiveReceiveData(struct modesMessage *mm) {
|
|||
|
||||
if (mm->msgtype == 0 || mm->msgtype == 4 || mm->msgtype == 20) {
|
||||
a->altitude = mm->altitude;
|
||||
a->modeC = mm->modeC;
|
||||
} else if(mm->msgtype == 5 || mm->msgtype == 21) {
|
||||
if (a->squawk != mm->identity) {
|
||||
a->modeAcount = 0; // Squawk has changed, so zero the hit count
|
||||
}
|
||||
a->squawk = mm->identity;
|
||||
} else if (mm->msgtype == 17) {
|
||||
if (mm->metype >= 1 && mm->metype <= 4) {
|
||||
memcpy(a->flight, mm->flight, sizeof(a->flight));
|
||||
} else if (mm->metype >= 9 && mm->metype <= 18) {
|
||||
if (a->modeC != mm->modeC) {
|
||||
a->modeCcount = 0; // Altitude has changed, so zero the hit count
|
||||
}
|
||||
a->altitude = mm->altitude;
|
||||
a->modeC = mm->modeC;
|
||||
if (mm->fflag) {
|
||||
a->odd_cprlat = mm->raw_latitude;
|
||||
a->odd_cprlon = mm->raw_longitude;
|
||||
|
@ -1969,6 +2433,14 @@ struct aircraft *interactiveReceiveData(struct modesMessage *mm) {
|
|||
a->track = mm->heading;
|
||||
}
|
||||
}
|
||||
} else if(mm->msgtype == 32) {
|
||||
a->modeACflags = MODEAC_MSG_FLAG;
|
||||
a->squawk = mm->identity;
|
||||
a->modeC = mm->modeC;
|
||||
if (mm->altitude > -1300) {
|
||||
a->altitude = mm->altitude;
|
||||
}
|
||||
interactiveUpdateAircraftModeA(a);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
@ -2003,6 +2475,10 @@ void interactiveShowData(void) {
|
|||
char gs[5] = " ";
|
||||
char spacer = '\0';
|
||||
|
||||
if ( ((a->modeACflags & MODEAC_MSG_FLAG) == 0)
|
||||
|| (((a->modeACflags & MODEAC_MSG_MODES_HIT) == 0) && (altitude == 0) && (msgs > 4))
|
||||
|| (((a->modeACflags & MODEAC_MSG_MODES_HIT) == 0) && (msgs > 127)) ) {
|
||||
|
||||
/* Convert units to metric if --metric was specified. */
|
||||
if (Modes.metric) {
|
||||
altitude = (int) (altitude / 3.2828);
|
||||
|
@ -2019,7 +2495,7 @@ void interactiveShowData(void) {
|
|||
sprintf(squawk, "%04d", a->squawk);
|
||||
}
|
||||
|
||||
if (a->messages > 99999) {
|
||||
if (msgs > 99999) {
|
||||
msgs = 99999;
|
||||
}
|
||||
|
||||
|
@ -2045,9 +2521,10 @@ void interactiveShowData(void) {
|
|||
a->hexaddr, squawk, a->flight, altitude, speed,
|
||||
a->lat, a->lon, a->track, msgs, (int)(now - a->seen), spacer);
|
||||
}
|
||||
a = a->next;
|
||||
count++;
|
||||
}
|
||||
a = a->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* When in interactive mode If we don't receive new nessages within
|
||||
|
@ -2228,7 +2705,7 @@ void modesSendBeastOutput(struct modesMessage *mm) {
|
|||
{*p++ = '2';}
|
||||
else if (msgLen == MODES_LONG_MSG_BYTES)
|
||||
{*p++ = '3';}
|
||||
else if (msgLen == MODEA_MSG_BYTES)
|
||||
else if (msgLen == MODEAC_MSG_BYTES)
|
||||
{*p++ = '1';}
|
||||
else
|
||||
{return;}
|
||||
|
@ -2682,6 +3159,7 @@ void showHelp(void) {
|
|||
"--interactive-rtl1090 Display flight table in RTL1090 format\n"
|
||||
"--raw Show only messages hex values\n"
|
||||
"--net Enable networking\n"
|
||||
"--modeac Enable decoding of SSR Modes 3/A & 3/C\n"
|
||||
"--net-beast TCP raw output in Beast binary format\n"
|
||||
"--net-only Enable just networking, no RTL device or file used\n"
|
||||
"--net-ro-size <size> TCP raw output minimum size (default: 0)\n"
|
||||
|
@ -2720,17 +3198,14 @@ void backgroundTasks(void) {
|
|||
if (Modes.net) {
|
||||
modesAcceptClients();
|
||||
modesReadFromClients();
|
||||
interactiveRemoveStaleAircrafts();
|
||||
}
|
||||
|
||||
/* Refresh screen when in interactive mode. */
|
||||
if (Modes.interactive &&
|
||||
(mstime() - Modes.interactive_last_update) >
|
||||
MODES_INTERACTIVE_REFRESH_TIME)
|
||||
{
|
||||
if ( (Modes.aircrafts) && ((mstime() - Modes.interactive_last_update) > MODES_INTERACTIVE_REFRESH_TIME)) {
|
||||
interactiveRemoveStaleAircrafts();
|
||||
interactiveShowData();
|
||||
Modes.interactive_last_update = mstime();
|
||||
// Refresh screen when in interactive mode
|
||||
if (Modes.interactive)
|
||||
{interactiveShowData();}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2762,6 +3237,8 @@ int main(int argc, char **argv) {
|
|||
Modes.raw = 1;
|
||||
} else if (!strcmp(argv[j],"--net")) {
|
||||
Modes.net = 1;
|
||||
} else if (!strcmp(argv[j],"--modeac")) {
|
||||
Modes.mode_ac = 1;
|
||||
} else if (!strcmp(argv[j],"--net-beast")) {
|
||||
Modes.beast = 1;
|
||||
} else if (!strcmp(argv[j],"--net-only")) {
|
||||
|
@ -2889,6 +3366,7 @@ int main(int argc, char **argv) {
|
|||
|
||||
/* If --ifile and --stats were given, print statistics. */
|
||||
if (Modes.stats && Modes.filename) {
|
||||
printf("%d ModeA/C detected\n", Modes.stat_ModeAC);
|
||||
printf("%d valid preambles\n", Modes.stat_valid_preamble);
|
||||
printf("%d DF-?? fields corrected for length\n", Modes.stat_DF_Corrected);
|
||||
printf("%d demodulated again after phase correction\n", Modes.stat_out_of_phase);
|
||||
|
|
Loading…
Reference in a new issue