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)
This commit is contained in:
Oliver Jowett 2016-10-11 18:00:11 +01:00
parent 60f1f3bcb6
commit 25ea6d398b
6 changed files with 111 additions and 140 deletions

170
track.c
View file

@ -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);
}
}