From 25ea6d398b646d04b3f6d3a7e356c36f9de22eeb Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 11 Oct 2016 18:00:11 +0100 Subject: [PATCH] Overhaul mode A/C matching to be much cheaper. The old matching process which tracked mode A values as pseudo-aircraft got very, very expensive with a large number of mode A/C messages (and with lots of single-bit errors, which seems common with a Beast doing the reception) Instead just count A/C messages directly into a 4096-entry array (which is very fast) and periodically scan the mode S aircraft list to see if we can match anything up (which is fixed overhead + cost proportional to the number of mode S aircraft) --- dump1090.h | 6 -- interactive.c | 47 ++++++++++---- mode_s.c | 2 +- net_io.c | 8 +-- track.c | 170 ++++++++++++++++++-------------------------------- track.h | 18 ++++-- 6 files changed, 111 insertions(+), 140 deletions(-) diff --git a/dump1090.h b/dump1090.h index aac2d1e..704284a 100644 --- a/dump1090.h +++ b/dump1090.h @@ -105,12 +105,6 @@ typedef struct rtlsdr_dev rtlsdr_dev_t; #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 MODEAC_MSG_MODEA_HIT (1<<2) -#define MODEAC_MSG_MODEC_HIT (1<<3) -#define MODEAC_MSG_MODEA_ONLY (1<<4) -#define MODEAC_MSG_MODEC_OLD (1<<5) #define MODES_PREAMBLE_US 8 // microseconds = bits #define MODES_PREAMBLE_SAMPLES (MODES_PREAMBLE_US * 2) diff --git a/interactive.c b/interactive.c index 02d3152..6dfca6d 100644 --- a/interactive.c +++ b/interactive.c @@ -111,12 +111,8 @@ void interactiveShowData(void) { if ((now - a->seen) < Modes.interactive_display_ttl) { int msgs = a->messages; - int flags = a->modeACflags; - if ( (((flags & (MODEAC_MSG_FLAG )) == 0 ) && (msgs > 1 ) ) - || (((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) ) - ) { + if (msgs > 1) { char strSquawk[5] = " "; char strFl[7] = " "; char strTt[5] = " "; @@ -153,13 +149,9 @@ void interactiveShowData(void) { double signalAverage = (pSig[0] + pSig[1] + pSig[2] + pSig[3] + pSig[4] + pSig[5] + pSig[6] + pSig[7]) / 8.0; - 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';} + strMode[0] = 'S'; + if (a->modeA_hit) {strMode[2] = 'a';} + if (a->modeC_hit) {strMode[3] = 'c';} if (trackDataValid(&a->position_valid)) { snprintf(strLat, 8,"%7.03f", a->lat); @@ -184,6 +176,37 @@ void interactiveShowData(void) { } a = a->next; } + + if (!Modes.interactive_rtl1090 && Modes.mode_ac) { + for (unsigned i = 1; i < 4096 && count < Modes.interactive_rows; ++i) { + if (modeAC_match[i] || modeAC_count[i] < 100) + continue; + + char strMode[5] = " A "; + char strFl[7] = " "; + unsigned modeA = indexToModeA(i); + int modeC = modeAToModeC(modeA); + if (modeC != INVALID_ALTITUDE) { + strMode[3] = 'C'; + snprintf(strFl, 7, "%5d ", convert_altitude(modeC * 100)); + } + + printf("%7s %-4s %04x %-8s %6s %3s %3s %7s %8s %5s %5d %2s\n", + "", /* address */ + strMode, /* mode */ + modeA, /* squawk */ + "", /* callsign */ + strFl, /* altitude */ + "", /* gs */ + "", /* heading */ + "", /* lat */ + "", /* lon */ + "", /* signal */ + modeAC_count[i], /* messages */ + ""); /* seen */ + count++; + } + } } // diff --git a/mode_s.c b/mode_s.c index 0ea822c..d8dcae1 100644 --- a/mode_s.c +++ b/mode_s.c @@ -1806,7 +1806,7 @@ void useModesMessage(struct modesMessage *mm) { // If this is the second message, and we // squelched the first message, then re-emit the // first message now. - if (!Modes.net_verbatim && a->messages == 2) { + if (!Modes.net_verbatim && a && a->messages == 2) { modesQueueOutput(&a->first_message, a); } modesQueueOutput(mm, a); diff --git a/net_io.c b/net_io.c index e2f5ea1..e4ce985 100644 --- a/net_io.c +++ b/net_io.c @@ -710,7 +710,7 @@ static void send_sbs_heartbeat(struct net_service *service) void modesQueueOutput(struct modesMessage *mm, struct aircraft *a) { int is_mlat = (mm->source == SOURCE_MLAT); - if (!is_mlat && mm->correctedbits < 2) { + if (a && !is_mlat && mm->correctedbits < 2) { // Don't ever forward 2-bit-corrected messages via SBS output. // Don't ever forward mlat messages via SBS output. modesSendSBSOutput(mm, a); @@ -728,7 +728,7 @@ void modesQueueOutput(struct modesMessage *mm, struct aircraft *a) { modesSendBeastOutput(mm); } - if (!is_mlat) { + if (a && !is_mlat) { writeFATSVEvent(mm, a); } } @@ -1027,10 +1027,6 @@ char *generateAircraftJson(const char *url_path, int *len) { Modes.stats_current.messages_total + Modes.stats_alltime.messages_total); for (a = Modes.aircrafts; a; a = a->next) { - if (a->modeACflags & MODEAC_MSG_FLAG) { // skip any fudged ICAO records Mode A/C - continue; - } - if (a->messages < 2) { // basic filter for bad decodes continue; } diff --git a/track.c b/track.c index 27172f8..12e1c61 100644 --- a/track.c +++ b/track.c @@ -52,6 +52,10 @@ /* #define DEBUG_CPR_CHECKS */ +uint32_t modeAC_count[4096]; +uint32_t modeAC_lastcount[4096]; +uint32_t modeAC_match[4096]; + // // Return a new aircraft structure for the linked list of tracked // aircraft @@ -76,16 +80,6 @@ struct aircraft *trackCreateAircraft(struct modesMessage *mm) { a->fatsv_emitted_bds_30[0] = 0x30; a->fatsv_emitted_es_acas_ra[0] = 0xE2; - // 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) { - a->modeACflags = MODEAC_MSG_FLAG; - if (!mm->altitude_valid) { - a->modeACflags |= MODEAC_MSG_MODEA_ONLY; - } - } - // Copy the first message so we can emit it later when a second message arrives. a->first_message = *mm; @@ -520,6 +514,13 @@ static void updatePosition(struct aircraft *a, struct modesMessage *mm, uint64_t struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) { struct aircraft *a; + + if (mm->msgtype == 32) { + // Mode A/C, just count it (we ignore SPI) + modeAC_count[modeAToIndex(mm->squawk)]++; + return NULL; + } + uint64_t now = mstime(); // Lookup our aircraft or create a new one @@ -542,20 +543,20 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) a->addrtype = mm->addrtype; if (mm->altitude_valid && mm->altitude_source == ALTITUDE_BARO && accept_data(&a->altitude_valid, mm->source, now)) { - unsigned modeC = (a->altitude + 49) / 100; - if (modeC != a->altitude_modeC) { - a->modeCcount = 0; //....zero the hit count - a->modeACflags &= ~MODEAC_MSG_MODEC_HIT; + if (a->modeC_hit) { + int new_modeC = (a->altitude + 49) / 100; + int old_modeC = (mm->altitude + 49) / 100; + if (new_modeC != old_modeC) { + a->modeC_hit = 0; + } } a->altitude = mm->altitude; - a->altitude_modeC = modeC; } if (mm->squawk_valid && accept_data(&a->squawk_valid, mm->source, now)) { if (mm->squawk != a->squawk) { - a->modeAcount = 0; //....zero the hit count - a->modeACflags &= ~MODEAC_MSG_MODEA_HIT; + a->modeA_hit = 0; } a->squawk = mm->squawk; } @@ -636,24 +637,6 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) updatePosition(a, mm, now); } - 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); } @@ -661,85 +644,50 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) // Periodic updates of tracking state // -// -//========================================================================= -// -// Periodically 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. -// -static void trackUpdateAircraftModeA(struct aircraft *a) +// Periodically match up mode A/C results with mode S results +static void trackMatchAC(uint64_t now) { - 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 (trackDataValid(&a->squawk_valid) && trackDataValid(&b->squawk_valid)) { - // ...check for Mode-A == Mode-S Squawk matches - if (a->squawk == b->squawk) { // 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 (trackDataValid(&a->altitude_valid) && trackDataValid(&b->altitude_valid)) { - // ... check for Mode-C == Mode-S Altitude matches - if ( (a->altitude_modeC == b->altitude_modeC ) // If a 'real' Mode-S ICAO exists at this Mode-C Altitude - || (a->altitude_modeC == b->altitude_modeC + 1) // or this Mode-C - 100 ft - || (a->altitude_modeC + 1 == b->altitude_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; + // clear match flags + for (unsigned i = 0; i < 4096; ++i) { + modeAC_match[i] = 0; } -} -// -//========================================================================= -// -static void trackUpdateAircraftModeS() -{ - 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); - - trackUpdateAircraftModeA(a); // and attempt to match them with Mode-S + // scan aircraft list, look for matches + for (struct aircraft *a = Modes.aircrafts; a; a = a->next) { + if ((now - a->seen) > 5000) { + continue; + } + + // match on Mode A + if (trackDataValid(&a->squawk_valid)) { + unsigned i = modeAToIndex(a->squawk); + if ((modeAC_count[i] - modeAC_lastcount[i]) > TRACK_MODEAC_MIN_MESSAGES) { + a->modeA_hit = 1; + modeAC_match[i] = (modeAC_match[i] ? 0xFFFFFFFF : a->addr); + } + } + + // match on Mode C + if (trackDataValid(&a->altitude_valid)) { + int modeC = (a->altitude + 49) / 100; + unsigned modeA = modeCToModeA(modeC); + if (modeA) { + unsigned i = modeAToIndex(modeA); + if ((modeAC_count[i] - modeAC_lastcount[i]) > TRACK_MODEAC_MIN_MESSAGES) { + a->modeC_hit = 1; + modeAC_match[i] = (modeAC_match[i] ? 0xFFFFFFFF : a->addr); + } + } + } + } + + // reset counts for next time + for (unsigned i = 0; i < 4096; ++i) { + if ((modeAC_count[i] - modeAC_lastcount[i]) <= TRACK_MODEAC_MIN_MESSAGES) { + modeAC_lastcount[i] = modeAC_count[i] = 0; + } else { + modeAC_lastcount[i] = modeAC_count[i]; } - a = a->next; } } @@ -808,6 +756,6 @@ void trackPeriodicUpdate() if (now >= next_update) { next_update = now + 1000; trackRemoveStaleAircraft(now); - trackUpdateAircraftModeS(); + trackMatchAC(now); } } diff --git a/track.h b/track.h index 5761621..1ce3ae4 100644 --- a/track.h +++ b/track.h @@ -59,6 +59,11 @@ /* Maximum validity of an aircraft position */ #define TRACK_AIRCRAFT_POSITION_TTL 60000 +/* Minimum number of repeated Mode A/C replies with a particular Mode A code needed in a + * 1 second period before accepting that code. + */ +#define TRACK_MODEAC_MIN_MESSAGES 3 + typedef struct { datasource_t source; /* where the data came from */ uint64_t updated; /* when it arrived */ @@ -82,7 +87,6 @@ struct aircraft { data_validity altitude_valid; int altitude; // Altitude (Baro) - unsigned altitude_modeC; // (as a Mode C value) data_validity altitude_gnss_valid; int altitude_gnss; // Altitude (GNSS) @@ -134,9 +138,8 @@ struct aircraft { double lat, lon; // Coordinated obtained from CPR encoded data unsigned pos_nuc; // NUCp of last computed position - long modeAcount; // Mode A Squawk hit Count - long modeCcount; // Mode C Altitude hit Count - int modeACflags; // Flags for mode A/C recognition + int modeA_hit; // did our squawk match a possible mode A reply in the last check period? + int modeC_hit; // did our altitude match a possible mode C reply in the last check period? int fatsv_emitted_altitude; // last FA emitted altitude int fatsv_emitted_altitude_gnss; // -"- GNSS altitude @@ -159,6 +162,13 @@ struct aircraft { struct modesMessage first_message; // A copy of the first message we received for this aircraft. }; +/* Mode A/C tracking is done separately, not via the aircraft list, + * and via a flat array rather than a list since there are only 4k possible values + * (nb: we ignore the ident/SPI bit when tracking) + */ +extern uint32_t modeAC_count[4096]; +extern uint32_t modeAC_match[4096]; + /* is this bit of data valid? */ static inline int trackDataValid(const data_validity *v) {