From 899c51ce85cb986a81c1157b4133aaa8f41878fa Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sun, 8 Feb 2015 14:27:03 +0000 Subject: [PATCH] Only emit network messages once we have seen two of them (except in --net-verbatim mode, where we emit them all) Move aircraft tracking into track.[ch]. Clean up references to "interactive mode" when tracking aircraft - we always track aircraft, even in non-interactive mode. --- Makefile | 4 +- dump1090.c | 6 +- dump1090.h | 50 +---- interactive.c | 473 +-------------------------------------------- mode_s.c | 20 +- track.c | 517 ++++++++++++++++++++++++++++++++++++++++++++++++++ track.h | 103 ++++++++++ util.c | 64 +++++++ util.h | 27 +++ view1090.c | 3 +- 10 files changed, 738 insertions(+), 529 deletions(-) create mode 100644 track.c create mode 100644 track.h create mode 100644 util.c create mode 100644 util.h diff --git a/Makefile b/Makefile index 83bf0d1..86c3a1d 100644 --- a/Makefile +++ b/Makefile @@ -25,10 +25,10 @@ all: dump1090 view1090 %.o: %.c dump1090.h $(CC) $(CPPFLAGS) $(CFLAGS) $(EXTRACFLAGS) -c $< -o $@ -dump1090: dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o crc.o demod_2000.o demod_2400.o stats.o cpr.o icao_filter.o +dump1090: dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o crc.o demod_2000.o demod_2400.o stats.o cpr.o icao_filter.o track.o util.o $(CC) -g -o $@ $^ $(LIBS) $(LIBS_RTL) $(LDFLAGS) -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 +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) clean: diff --git a/dump1090.c b/dump1090.c index f13a9ee..cf8c139 100644 --- a/dump1090.c +++ b/dump1090.c @@ -117,7 +117,6 @@ void modesInitConfig(void) { Modes.net_http_port = MODES_NET_HTTP_PORT; Modes.net_fatsv_port = MODES_NET_OUTPUT_FA_TSV_PORT; Modes.interactive_rows = getTermRows(); - Modes.interactive_delete_ttl = MODES_INTERACTIVE_DELETE_TTL; Modes.interactive_display_ttl = MODES_INTERACTIVE_DISPLAY_TTL; Modes.json_interval = 1; Modes.json_location_accuracy = 1; @@ -645,15 +644,12 @@ void backgroundTasks(void) { time_t now = time(NULL); icaoFilterExpire(); + trackPeriodicUpdate(); if (Modes.net) { modesNetPeriodicWork(); } - // If Modes.aircrafts is not NULL, remove any stale aircraft - if (Modes.aircrafts) { - interactiveRemoveStaleAircrafts(); - } // Refresh screen when in interactive mode if (Modes.interactive) { diff --git a/dump1090.h b/dump1090.h index ebe3959..5395e8f 100644 --- a/dump1090.h +++ b/dump1090.h @@ -179,7 +179,6 @@ #define MODES_INTERACTIVE_REFRESH_TIME 250 // Milliseconds #define MODES_INTERACTIVE_ROWS 22 // Rows on screen -#define MODES_INTERACTIVE_DELETE_TTL 300 // Delete from the list after 300 seconds #define MODES_INTERACTIVE_DISPLAY_TTL 60 // Delete from display after 60 seconds #define MODES_NET_HEARTBEAT_INTERVAL 60 // seconds @@ -212,6 +211,7 @@ // Include subheaders after all the #defines are in place +#include "util.h" #include "anet.h" #include "crc.h" #include "demod_2000.h" @@ -219,7 +219,7 @@ #include "stats.h" #include "cpr.h" #include "icao_filter.h" - +#include "track.h" //======================== structure declarations ========================= @@ -232,42 +232,6 @@ struct client { char buf[MODES_CLIENT_BUF_SIZE+1]; // Read buffer }; -// Structure used to describe an aircraft in iteractive mode -struct aircraft { - uint32_t addr; // ICAO address - char flight[16]; // Flight number - double signalLevel[8]; // Last 8 Signal Amplitudes - int altitude; // Altitude - int speed; // Velocity - int track; // Angle of flight - int vert_rate; // Vertical rate. - time_t seen; // Time at which the last packet was received - time_t seenLatLon; // Time at which the last lat long was calculated - uint64_t timestamp; // Timestamp at which the last packet was received - uint64_t timestampLatLon;// Timestamp at which the last lat long was calculated - long messages; // Number of Mode S messages received - int modeA; // Squawk - 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 - - int fatsv_emitted_altitude; // last FA emitted altitude - int fatsv_emitted_track; // last FA emitted angle of flight - time_t fatsv_last_emitted; // time aircraft was last FA emitted - - // Encoded latitude and longitude as extracted by odd and even CPR encoded messages - int odd_cprlat; - int odd_cprlon; - int even_cprlat; - int even_cprlon; - uint64_t odd_cprtime; - uint64_t even_cprtime; - double lat, lon; // Coordinated obtained from CPR encoded data - int bFlags; // Flags related to valid fields in this structure - struct aircraft *next; // Next aircraft in our linked list -}; - // Common writer state for all output sockets of one type struct net_writer { int socket; // listening socket FD, used to identify the owning service @@ -377,10 +341,11 @@ struct { // Internal state int bUserFlags; // Flags relating to the user details double maxRange; // Absolute maximum decoding range, in *metres* - // Interactive mode + // State tracking struct aircraft *aircrafts; + + // Interactive mode uint64_t interactive_last_update; // Last screen update in milliseconds - time_t last_cleanup_time; // Last cleanup time in seconds // Statistics struct stats stats_current; @@ -466,11 +431,7 @@ void computeMagnitudeVector(uint16_t *pData); // // Functions exported from interactive.c // -struct aircraft* interactiveReceiveData(struct modesMessage *mm); void interactiveShowData(void); -void interactiveRemoveStaleAircrafts(void); -int decodeBinMessage (struct client *c, char *p); -struct aircraft *interactiveFindAircraft(uint32_t addr); // // Functions exported from net_io.c @@ -479,6 +440,7 @@ void modesInitNet (void); void modesQueueOutput (struct modesMessage *mm); void modesReadFromClient(struct client *c, char *sep, int(*handler)(struct client *, char *)); void modesNetPeriodicWork (void); +int decodeBinMessage (struct client *c, char *p); void writeJsonToFile(const char *file, char * (*generator) (const char*,int*)); char *generateAircraftJson(const char *url_path, int *len); diff --git a/interactive.c b/interactive.c index ebaa005..07dfcff 100644 --- a/interactive.c +++ b/interactive.c @@ -48,448 +48,11 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "dump1090.h" -// -// ============================= Utility functions ========================== -// -static uint64_t mstime(void) { - struct timeval tv; - uint64_t mst; - - gettimeofday(&tv, NULL); - mst = ((uint64_t)tv.tv_sec)*1000; - mst += tv.tv_usec/1000; - return mst; -} // //========================= Interactive mode =============================== -// -// Return a new aircraft structure for the interactive mode linked list -// of aircraft -// -struct aircraft *interactiveCreateAircraft(struct modesMessage *mm) { - static struct aircraft zeroAircraft; - struct aircraft *a = (struct aircraft *) malloc(sizeof(*a)); - int i; - // Default everything to zero/NULL - *a = zeroAircraft; - // Now initialise things that should not be 0/NULL to their defaults - a->addr = mm->addr; - for (i = 0; i < 8; ++i) - a->signalLevel[i] = mm->signalLevel; // First time, initialise everything - // to the first signal strength - - // 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) { - int modeC = ModeAToModeC(mm->modeA | mm->fs); - a->modeACflags = MODEAC_MSG_FLAG; - if (modeC < -12) { - a->modeACflags |= MODEAC_MSG_MODEA_ONLY; - } else { - mm->altitude = modeC * 100; - mm->bFlags |= MODES_ACFLAGS_ALTITUDE_VALID; - } - } - return (a); -} -// -//========================================================================= -// -// Return the aircraft with the specified address, or NULL if no aircraft -// exists with this address. -// -struct aircraft *interactiveFindAircraft(uint32_t addr) { - struct aircraft *a = Modes.aircrafts; - - while(a) { - if (a->addr == addr) return (a); - a = a->next; - } - return (NULL); -} -// -//========================================================================= -// -// We have received a Mode A or C response. -// -// 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. -// -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 both (a) and (b) have valid squawks... - if ((a->bFlags & b->bFlags) & MODES_ACFLAGS_SQUAWK_VALID) { - // ...check for Mode-A == Mode-S Squawk matches - if (a->modeA == b->modeA) { // 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 ((a->bFlags & b->bFlags) & MODES_ACFLAGS_ALTITUDE_VALID) { - // ... check for Mode-C == Mode-S Altitude matches - if ( (a->modeC == b->modeC ) // If a 'real' Mode-S ICAO exists at this Mode-C Altitude - || (a->modeC == b->modeC + 1) // or this Mode-C - 100 ft - || (a->modeC + 1 == b->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; - } -} -// -//========================================================================= -// -void interactiveUpdateAircraftModeS() { - 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); - - interactiveUpdateAircraftModeA(a); // and attempt to match them with Mode-S - } - a = a->next; - } -} -// -//========================================================================= -// -// Receive new messages and populate the interactive mode with more info -// - -// Distance between points on a spherical earth. -// This has up to 0.5% error because the earth isn't actually spherical -// (but we don't use it in situations where that matters) -static double greatcircle(double lat0, double lon0, double lat1, double lon1) -{ - lat0 = lat0 * M_PI / 180.0; - lon0 = lon0 * M_PI / 180.0; - lat1 = lat1 * M_PI / 180.0; - lon1 = lon1 * M_PI / 180.0; - return 6371e3 * acos(sin(lat0) * sin(lat1) + cos(lat0) * cos(lat1) * cos(fabs(lon0 - lon1))); -} - -static int doGlobalCPR(struct aircraft *a, int fflag, int surface) -{ - int result; - double lat=0, lon=0; - - if (surface) { - // surface global CPR - // find reference location - double reflat, reflon; - - if (a->bFlags & MODES_ACFLAGS_LATLON_REL_OK) { // Ok to try aircraft relative first - reflat = a->lat; - reflon = a->lon; - } else if (Modes.bUserFlags & MODES_USER_LATLON_VALID) { - reflat = Modes.fUserLat; - reflon = Modes.fUserLon; - } else { - // No local reference, give up - return (-1); - } - - result = decodeCPRsurface(reflat, reflon, - a->even_cprlat, a->even_cprlon, - a->odd_cprlat, a->odd_cprlon, - fflag, - &lat, &lon); - } else { - // airborne global CPR - result = decodeCPRairborne(a->even_cprlat, a->even_cprlon, - a->odd_cprlat, a->odd_cprlon, - fflag, - &lat, &lon); - } - - if (result < 0) - return result; - - // check max range - if (Modes.maxRange > 0 && (Modes.bUserFlags & MODES_USER_LATLON_VALID)) { - double range = greatcircle(Modes.fUserLat, Modes.fUserLon, lat, lon); - if (range > Modes.maxRange) - return (-2); // we consider an out-of-range value to be bad data - } - - a->lat = lat; - a->lon = lon; - return 0; -} - -static int doLocalCPR(struct aircraft *a, int fflag, int surface, time_t now) -{ - // relative CPR - // find reference location - double reflat, reflon, lat=0, lon=0; - double range_limit = 0; - int result; - - if (a->bFlags & MODES_ACFLAGS_LATLON_REL_OK) { - int elapsed = (int)(now - a->seenLatLon); - if (elapsed < 0) elapsed = 0; - - reflat = a->lat; - reflon = a->lon; - - // impose a range limit based on 2000km/h speed - range_limit = 5e3 + (2000e3 * elapsed / 3600); // 5km + 2000km/h - } else if (!surface && (Modes.bUserFlags & MODES_USER_LATLON_VALID)) { - reflat = Modes.fUserLat; - reflon = Modes.fUserLon; - - // The cell size is at least 360NM, giving a nominal - // max range of 180NM (half a cell). - // - // If the receiver range is more than half a cell - // then we must limit this range further to avoid - // ambiguity. (e.g. if we receive a position report - // at 200NM distance, this may resolve to a position - // at (200-360) = 160NM in the wrong direction) - if (Modes.maxRange > 1852*180) - range_limit = (1852*360) - Modes.maxRange; - } else { - // No local reference, give up - return (-1); - } - - result = decodeCPRrelative(reflat, reflon, - fflag ? a->odd_cprlat : a->even_cprlat, - fflag ? a->odd_cprlon : a->even_cprlon, - fflag, surface, - &lat, &lon); - if (result < 0) - return result; - // check range limit - if (range_limit > 0) { - double range = greatcircle(reflat, reflon, lat, lon); - if (range > range_limit) - return (-1); - } - - // check max range - if (Modes.maxRange > 0 && (Modes.bUserFlags & MODES_USER_LATLON_VALID)) { - double range = greatcircle(Modes.fUserLat, Modes.fUserLon, lat, lon); - if (range > Modes.maxRange) - return (-2); // we consider an out-of-range value to be bad data - } - - a->lat = lat; - a->lon = lon; - return 0; -} - -static void updatePosition(struct aircraft *a, struct modesMessage *mm, time_t now) -{ - int location_result = -1; - - if (mm->bFlags & MODES_ACFLAGS_LLODD_VALID) { - a->odd_cprlat = mm->raw_latitude; - a->odd_cprlon = mm->raw_longitude; - a->odd_cprtime = mstime(); - } else { - a->even_cprlat = mm->raw_latitude; - a->even_cprlon = mm->raw_longitude; - a->even_cprtime = mstime(); - } - - // If we have enough recent data, try global CPR - if (((mm->bFlags | a->bFlags) & MODES_ACFLAGS_LLEITHER_VALID) == MODES_ACFLAGS_LLBOTH_VALID && abs((int)(a->even_cprtime - a->odd_cprtime)) <= 10000) { - location_result = doGlobalCPR(a, (mm->bFlags & MODES_ACFLAGS_LLODD_VALID), (mm->bFlags & MODES_ACFLAGS_AOG)); - if (location_result == -2) { - // Global CPR failed because an airborne position produced implausible results. - // This is bad data. Discard both odd and even messages and wait for a fresh pair. - // Also disable aircraft-relative positions until we have a new good position (but don't discard the - // recorded position itself) - Modes.stats_current.cpr_global_bad++; - mm->bFlags &= ~(MODES_ACFLAGS_LATLON_VALID | MODES_ACFLAGS_LLODD_VALID | MODES_ACFLAGS_LLEVEN_VALID); - a->bFlags &= ~(MODES_ACFLAGS_LATLON_REL_OK | MODES_ACFLAGS_LLODD_VALID | MODES_ACFLAGS_LLEVEN_VALID); - return; - } else if (location_result == -1) { - // No local reference for surface position available, or the two messages crossed a zone. - // Nonfatal, try again later. - Modes.stats_current.cpr_global_skipped++; - } else { - Modes.stats_current.cpr_global_ok++; - } - } - - // Otherwise try relative CPR. - if (location_result == -1) { - location_result = doLocalCPR(a, (mm->bFlags & MODES_ACFLAGS_LLODD_VALID), (mm->bFlags & MODES_ACFLAGS_AOG), now); - if (location_result == -1) { - Modes.stats_current.cpr_local_skipped++; - } else { - Modes.stats_current.cpr_local_ok++; - mm->bFlags |= MODES_ACFLAGS_REL_CPR_USED; - } - } - - if (location_result == 0) { - // If we sucessfully decoded, back copy the results to mm so that we can print them in list output - mm->bFlags |= MODES_ACFLAGS_LATLON_VALID; - mm->fLat = a->lat; - mm->fLon = a->lon; - - // Update aircraft state - a->bFlags |= (MODES_ACFLAGS_LATLON_VALID | MODES_ACFLAGS_LATLON_REL_OK); - a->seenLatLon = a->seen; - a->timestampLatLon = a->timestamp; - } -} - -struct aircraft *interactiveReceiveData(struct modesMessage *mm) { - struct aircraft *a, *aux; - time_t now = time(NULL); - - // Lookup our aircraft or create a new one - a = interactiveFindAircraft(mm->addr); - if (!a) { // If it's a currently unknown aircraft.... - a = interactiveCreateAircraft(mm); // ., 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 - * so we keep aircrafts ordered by received message time. - * - * However move it on head only if at least one second elapsed - * since the aircraft that is currently on head sent a message, - * othewise with multiple aircrafts at the same time we have an - * useless shuffle of positions on the screen. */ - if (0 && Modes.aircrafts != a && (now - a->seen) >= 1) { - aux = Modes.aircrafts; - while(aux->next != a) aux = aux->next; - /* Now we are a node before the aircraft to remove. */ - aux->next = aux->next->next; /* removed. */ - /* Add on head */ - a->next = Modes.aircrafts; - Modes.aircrafts = a; - } - } - - a->signalLevel[a->messages & 7] = mm->signalLevel;// replace the 8th oldest signal strength - a->seen = now; - a->timestamp = mm->timestampMsg; - a->messages++; - - // If a (new) CALLSIGN has been received, copy it to the aircraft structure - if (mm->bFlags & MODES_ACFLAGS_CALLSIGN_VALID) { - memcpy(a->flight, mm->flight, sizeof(a->flight)); - } - - // If a (new) ALTITUDE has been received, copy it to the aircraft structure - if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) { - if ( (a->modeCcount) // if we've a modeCcount already - && (a->altitude != mm->altitude ) ) // and Altitude has changed -// && (a->modeC != mm->modeC + 1) // and Altitude not changed by +100 feet -// && (a->modeC + 1 != mm->modeC ) ) // and Altitude not changes by -100 feet - { - a->modeCcount = 0; //....zero the hit count - a->modeACflags &= ~MODEAC_MSG_MODEC_HIT; - } - a->altitude = mm->altitude; - a->modeC = (mm->altitude + 49) / 100; - } - - // If a (new) SQUAWK has been received, copy it to the aircraft structure - if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) { - if (a->modeA != mm->modeA) { - a->modeAcount = 0; // Squawk has changed, so zero the hit count - a->modeACflags &= ~MODEAC_MSG_MODEA_HIT; - } - a->modeA = mm->modeA; - } - - // If a (new) HEADING has been received, copy it to the aircraft structure - if (mm->bFlags & MODES_ACFLAGS_HEADING_VALID) { - a->track = mm->heading; - } - - // If a (new) SPEED has been received, copy it to the aircraft structure - if (mm->bFlags & MODES_ACFLAGS_SPEED_VALID) { - a->speed = mm->velocity; - } - - // If a (new) Vertical Descent rate has been received, copy it to the aircraft structure - if (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) { - a->vert_rate = mm->vert_rate; - } - - // if the Aircraft has landed or taken off since the last message, clear the even/odd CPR flags - if ((mm->bFlags & MODES_ACFLAGS_AOG_VALID) && ((a->bFlags ^ mm->bFlags) & MODES_ACFLAGS_AOG)) { - a->bFlags &= ~(MODES_ACFLAGS_LLBOTH_VALID | MODES_ACFLAGS_AOG); - } - - // If we've got a new cprlat or cprlon - if (mm->bFlags & MODES_ACFLAGS_LLEITHER_VALID) { - updatePosition(a, mm, now); - } - - // Update the aircrafts a->bFlags to reflect the newly received mm->bFlags; - a->bFlags |= mm->bFlags; - - 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); -} // //========================================================================= // @@ -508,11 +71,6 @@ void interactiveShowData(void) { Modes.interactive_last_update = mstime(); - // Attempt to reconsile any ModeA/C with known Mode-S - // We can't condition on Modes.modeac because ModeA/C could be comming - // in from a raw input port which we can't turn off. - interactiveUpdateAircraftModeS(); - progress = spinner[time(NULL)%4]; #ifndef _WIN32 @@ -612,36 +170,7 @@ void interactiveShowData(void) { a = a->next; } } -// -//========================================================================= -// -// When in interactive mode If we don't receive new nessages within -// MODES_INTERACTIVE_DELETE_TTL seconds we remove the aircraft from the list. -// -void interactiveRemoveStaleAircrafts(void) { - struct aircraft *a = Modes.aircrafts; - struct aircraft *prev = NULL; - time_t now = time(NULL); - - // Only do cleanup once per second - if (Modes.last_cleanup_time != now) { - Modes.last_cleanup_time = now; - - while(a) { - if ((now - a->seen) > Modes.interactive_delete_ttl) { - // Remove the element from the linked list, with care - // if we are removing the first element - if (!prev) { - Modes.aircrafts = a->next; free(a); a = Modes.aircrafts; - } else { - prev->next = a->next; free(a); a = prev->next; - } - } else { - prev = a; a = a->next; - } - } - } -} + // //========================================================================= // diff --git a/mode_s.c b/mode_s.c index fe68720..1a5db10 100644 --- a/mode_s.c +++ b/mode_s.c @@ -1191,18 +1191,30 @@ void computeMagnitudeVector(uint16_t *p) { // processing and visualization // void useModesMessage(struct modesMessage *mm) { + struct aircraft *a; + ++Modes.stats_current.messages_total; - // If we are decoding, track aircraft - interactiveReceiveData(mm); + // Track aircraft state + a = trackUpdateFromMessage(mm); // In non-interactive non-quiet mode, display messages on standard output if (!Modes.interactive && !Modes.quiet) { displayModesMessage(mm); } - // Feed output clients - if (Modes.net) {modesQueueOutput(mm);} + // Feed output clients. + // If in --net-verbatim mode, do this for all messages. + // Otherwise, apply a sanity-check filter and only + // forward messages when we have seen two of them. + + // TODO: buffer the original message and forward it when we + // see a second message? + + if (Modes.net) { + if (Modes.net_verbatim || a->messages > 1) + modesQueueOutput(mm); + } } // diff --git a/track.c b/track.c new file mode 100644 index 0000000..962ebef --- /dev/null +++ b/track.c @@ -0,0 +1,517 @@ +// Part of dump1090, a Mode S message decoder for RTLSDR devices. +// +// track.c: aircraft state tracking +// +// 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" + +// +// Return a new aircraft structure for the linked list of tracked +// aircraft +// +struct aircraft *trackCreateAircraft(struct modesMessage *mm) { + static struct aircraft zeroAircraft; + struct aircraft *a = (struct aircraft *) malloc(sizeof(*a)); + int i; + + // Default everything to zero/NULL + *a = zeroAircraft; + + // Now initialise things that should not be 0/NULL to their defaults + a->addr = mm->addr; + for (i = 0; i < 8; ++i) + a->signalLevel[i] = mm->signalLevel; // First time, initialise everything + // to the first signal strength + + // 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) { + int modeC = ModeAToModeC(mm->modeA | mm->fs); + a->modeACflags = MODEAC_MSG_FLAG; + if (modeC < -12) { + a->modeACflags |= MODEAC_MSG_MODEA_ONLY; + } else { + mm->altitude = modeC * 100; + mm->bFlags |= MODES_ACFLAGS_ALTITUDE_VALID; + } + } + return (a); +} + +// +//========================================================================= +// +// Return the aircraft with the specified address, or NULL if no aircraft +// exists with this address. +// +struct aircraft *trackFindAircraft(uint32_t addr) { + struct aircraft *a = Modes.aircrafts; + + while(a) { + if (a->addr == addr) return (a); + a = a->next; + } + return (NULL); +} + +// +// CPR position updating +// + +// Distance between points on a spherical earth. +// This has up to 0.5% error because the earth isn't actually spherical +// (but we don't use it in situations where that matters) +static double greatcircle(double lat0, double lon0, double lat1, double lon1) +{ + lat0 = lat0 * M_PI / 180.0; + lon0 = lon0 * M_PI / 180.0; + lat1 = lat1 * M_PI / 180.0; + lon1 = lon1 * M_PI / 180.0; + return 6371e3 * acos(sin(lat0) * sin(lat1) + cos(lat0) * cos(lat1) * cos(fabs(lon0 - lon1))); +} + +static int doGlobalCPR(struct aircraft *a, int fflag, int surface) +{ + int result; + double lat=0, lon=0; + + if (surface) { + // surface global CPR + // find reference location + double reflat, reflon; + + if (a->bFlags & MODES_ACFLAGS_LATLON_REL_OK) { // Ok to try aircraft relative first + reflat = a->lat; + reflon = a->lon; + } else if (Modes.bUserFlags & MODES_USER_LATLON_VALID) { + reflat = Modes.fUserLat; + reflon = Modes.fUserLon; + } else { + // No local reference, give up + return (-1); + } + + result = decodeCPRsurface(reflat, reflon, + a->even_cprlat, a->even_cprlon, + a->odd_cprlat, a->odd_cprlon, + fflag, + &lat, &lon); + } else { + // airborne global CPR + result = decodeCPRairborne(a->even_cprlat, a->even_cprlon, + a->odd_cprlat, a->odd_cprlon, + fflag, + &lat, &lon); + } + + if (result < 0) + return result; + + // check max range + if (Modes.maxRange > 0 && (Modes.bUserFlags & MODES_USER_LATLON_VALID)) { + double range = greatcircle(Modes.fUserLat, Modes.fUserLon, lat, lon); + if (range > Modes.maxRange) + return (-2); // we consider an out-of-range value to be bad data + } + + a->lat = lat; + a->lon = lon; + return 0; +} + +static int doLocalCPR(struct aircraft *a, int fflag, int surface, time_t now) +{ + // relative CPR + // find reference location + double reflat, reflon, lat=0, lon=0; + double range_limit = 0; + int result; + + if (a->bFlags & MODES_ACFLAGS_LATLON_REL_OK) { + int elapsed = (int)(now - a->seenLatLon); + if (elapsed < 0) elapsed = 0; + + reflat = a->lat; + reflon = a->lon; + + // impose a range limit based on 2000km/h speed + range_limit = 5e3 + (2000e3 * elapsed / 3600); // 5km + 2000km/h + } else if (!surface && (Modes.bUserFlags & MODES_USER_LATLON_VALID)) { + reflat = Modes.fUserLat; + reflon = Modes.fUserLon; + + // The cell size is at least 360NM, giving a nominal + // max range of 180NM (half a cell). + // + // If the receiver range is more than half a cell + // then we must limit this range further to avoid + // ambiguity. (e.g. if we receive a position report + // at 200NM distance, this may resolve to a position + // at (200-360) = 160NM in the wrong direction) + if (Modes.maxRange > 1852*180) + range_limit = (1852*360) - Modes.maxRange; + } else { + // No local reference, give up + return (-1); + } + + result = decodeCPRrelative(reflat, reflon, + fflag ? a->odd_cprlat : a->even_cprlat, + fflag ? a->odd_cprlon : a->even_cprlon, + fflag, surface, + &lat, &lon); + if (result < 0) + return result; + // check range limit + if (range_limit > 0) { + double range = greatcircle(reflat, reflon, lat, lon); + if (range > range_limit) + return (-1); + } + + // check max range + if (Modes.maxRange > 0 && (Modes.bUserFlags & MODES_USER_LATLON_VALID)) { + double range = greatcircle(Modes.fUserLat, Modes.fUserLon, lat, lon); + if (range > Modes.maxRange) + return (-2); // we consider an out-of-range value to be bad data + } + + a->lat = lat; + a->lon = lon; + return 0; +} + +static void updatePosition(struct aircraft *a, struct modesMessage *mm, time_t now) +{ + int location_result = -1; + + if (mm->bFlags & MODES_ACFLAGS_LLODD_VALID) { + a->odd_cprlat = mm->raw_latitude; + a->odd_cprlon = mm->raw_longitude; + a->odd_cprtime = mstime(); + } else { + a->even_cprlat = mm->raw_latitude; + a->even_cprlon = mm->raw_longitude; + a->even_cprtime = mstime(); + } + + // If we have enough recent data, try global CPR + if (((mm->bFlags | a->bFlags) & MODES_ACFLAGS_LLEITHER_VALID) == MODES_ACFLAGS_LLBOTH_VALID && abs((int)(a->even_cprtime - a->odd_cprtime)) <= 10000) { + location_result = doGlobalCPR(a, (mm->bFlags & MODES_ACFLAGS_LLODD_VALID), (mm->bFlags & MODES_ACFLAGS_AOG)); + if (location_result == -2) { + // Global CPR failed because an airborne position produced implausible results. + // This is bad data. Discard both odd and even messages and wait for a fresh pair. + // Also disable aircraft-relative positions until we have a new good position (but don't discard the + // recorded position itself) + Modes.stats_current.cpr_global_bad++; + mm->bFlags &= ~(MODES_ACFLAGS_LATLON_VALID | MODES_ACFLAGS_LLODD_VALID | MODES_ACFLAGS_LLEVEN_VALID); + a->bFlags &= ~(MODES_ACFLAGS_LATLON_REL_OK | MODES_ACFLAGS_LLODD_VALID | MODES_ACFLAGS_LLEVEN_VALID); + return; + } else if (location_result == -1) { + // No local reference for surface position available, or the two messages crossed a zone. + // Nonfatal, try again later. + Modes.stats_current.cpr_global_skipped++; + } else { + Modes.stats_current.cpr_global_ok++; + } + } + + // Otherwise try relative CPR. + if (location_result == -1) { + location_result = doLocalCPR(a, (mm->bFlags & MODES_ACFLAGS_LLODD_VALID), (mm->bFlags & MODES_ACFLAGS_AOG), now); + if (location_result == -1) { + Modes.stats_current.cpr_local_skipped++; + } else { + Modes.stats_current.cpr_local_ok++; + mm->bFlags |= MODES_ACFLAGS_REL_CPR_USED; + } + } + + if (location_result == 0) { + // If we sucessfully decoded, back copy the results to mm so that we can print them in list output + mm->bFlags |= MODES_ACFLAGS_LATLON_VALID; + mm->fLat = a->lat; + mm->fLon = a->lon; + + // Update aircraft state + a->bFlags |= (MODES_ACFLAGS_LATLON_VALID | MODES_ACFLAGS_LATLON_REL_OK); + a->seenLatLon = a->seen; + a->timestampLatLon = a->timestamp; + } +} + +// +//========================================================================= +// +// Receive new messages and update tracked aircraft state +// + +struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) +{ + struct aircraft *a; + time_t now = time(NULL); + + // Lookup our aircraft or create a new one + a = trackFindAircraft(mm->addr); + if (!a) { // If it's a currently unknown aircraft.... + a = trackCreateAircraft(mm); // ., create a new record for it, + a->next = Modes.aircrafts; // .. and put it at the head of the list + Modes.aircrafts = a; + } + + a->signalLevel[a->messages & 7] = mm->signalLevel;// replace the 8th oldest signal strength + a->seen = now; + a->timestamp = mm->timestampMsg; + a->messages++; + + // If a (new) CALLSIGN has been received, copy it to the aircraft structure + if (mm->bFlags & MODES_ACFLAGS_CALLSIGN_VALID) { + memcpy(a->flight, mm->flight, sizeof(a->flight)); + } + + // If a (new) ALTITUDE has been received, copy it to the aircraft structure + if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) { + if ( (a->modeCcount) // if we've a modeCcount already + && (a->altitude != mm->altitude ) ) // and Altitude has changed +// && (a->modeC != mm->modeC + 1) // and Altitude not changed by +100 feet +// && (a->modeC + 1 != mm->modeC ) ) // and Altitude not changes by -100 feet + { + a->modeCcount = 0; //....zero the hit count + a->modeACflags &= ~MODEAC_MSG_MODEC_HIT; + } + a->altitude = mm->altitude; + a->modeC = (mm->altitude + 49) / 100; + } + + // If a (new) SQUAWK has been received, copy it to the aircraft structure + if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) { + if (a->modeA != mm->modeA) { + a->modeAcount = 0; // Squawk has changed, so zero the hit count + a->modeACflags &= ~MODEAC_MSG_MODEA_HIT; + } + a->modeA = mm->modeA; + } + + // If a (new) HEADING has been received, copy it to the aircraft structure + if (mm->bFlags & MODES_ACFLAGS_HEADING_VALID) { + a->track = mm->heading; + } + + // If a (new) SPEED has been received, copy it to the aircraft structure + if (mm->bFlags & MODES_ACFLAGS_SPEED_VALID) { + a->speed = mm->velocity; + } + + // If a (new) Vertical Descent rate has been received, copy it to the aircraft structure + if (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) { + a->vert_rate = mm->vert_rate; + } + + // if the Aircraft has landed or taken off since the last message, clear the even/odd CPR flags + if ((mm->bFlags & MODES_ACFLAGS_AOG_VALID) && ((a->bFlags ^ mm->bFlags) & MODES_ACFLAGS_AOG)) { + a->bFlags &= ~(MODES_ACFLAGS_LLBOTH_VALID | MODES_ACFLAGS_AOG); + } + + // If we've got a new cprlat or cprlon + if (mm->bFlags & MODES_ACFLAGS_LLEITHER_VALID) { + updatePosition(a, mm, now); + } + + // Update the aircrafts a->bFlags to reflect the newly received mm->bFlags; + a->bFlags |= mm->bFlags; + + 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); +} + +// +// 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) +{ + 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 ((a->bFlags & b->bFlags) & MODES_ACFLAGS_SQUAWK_VALID) { + // ...check for Mode-A == Mode-S Squawk matches + if (a->modeA == b->modeA) { // 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 ((a->bFlags & b->bFlags) & MODES_ACFLAGS_ALTITUDE_VALID) { + // ... check for Mode-C == Mode-S Altitude matches + if ( (a->modeC == b->modeC ) // If a 'real' Mode-S ICAO exists at this Mode-C Altitude + || (a->modeC == b->modeC + 1) // or this Mode-C - 100 ft + || (a->modeC + 1 == b->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; + } +} +// +//========================================================================= +// +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 + } + a = a->next; + } +} + +// +//========================================================================= +// +// If we don't receive new nessages within TRACK_AIRCRAFT_TTL +// we remove the aircraft from the list. +// +static void trackRemoveStaleAircraft(time_t now) +{ + struct aircraft *a = Modes.aircrafts; + struct aircraft *prev = NULL; + + while(a) { + if ((now - a->seen) > TRACK_AIRCRAFT_TTL) { + // Remove the element from the linked list, with care + // if we are removing the first element + if (!prev) { + Modes.aircrafts = a->next; free(a); a = Modes.aircrafts; + } else { + prev->next = a->next; free(a); a = prev->next; + } + } else { + prev = a; a = a->next; + } + } +} + + +// +// Entry point for periodic updates +// + +void trackPeriodicUpdate() +{ + static time_t next_update; + time_t now = time(NULL); + + // Only do updates once per second + if (now >= next_update) { + next_update = now; + trackRemoveStaleAircraft(now); + trackUpdateAircraftModeS(); + } +} diff --git a/track.h b/track.h new file mode 100644 index 0000000..e9f627e --- /dev/null +++ b/track.h @@ -0,0 +1,103 @@ +// Part of dump1090, a Mode S message decoder for RTLSDR devices. +// +// track.h: aircraft state tracking prototypes +// +// 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. + +#ifndef DUMP1090_TRACK_H +#define DUMP1090_TRACK_H + +/* Maximum age of tracked aircraft in seconds */ +#define TRACK_AIRCRAFT_TTL 300 + +/* Structure used to describe the state of one tracked aircraft */ +struct aircraft { + uint32_t addr; // ICAO address + char flight[16]; // Flight number + double signalLevel[8]; // Last 8 Signal Amplitudes + int altitude; // Altitude + int speed; // Velocity + int track; // Angle of flight + int vert_rate; // Vertical rate. + time_t seen; // Time at which the last packet was received + time_t seenLatLon; // Time at which the last lat long was calculated + uint64_t timestamp; // Timestamp at which the last packet was received + uint64_t timestampLatLon;// Timestamp at which the last lat long was calculated + long messages; // Number of Mode S messages received + int modeA; // Squawk + 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 + + int fatsv_emitted_altitude; // last FA emitted altitude + int fatsv_emitted_track; // last FA emitted angle of flight + time_t fatsv_last_emitted; // time aircraft was last FA emitted + + // Encoded latitude and longitude as extracted by odd and even CPR encoded messages + int odd_cprlat; + int odd_cprlon; + int even_cprlat; + int even_cprlon; + uint64_t odd_cprtime; + uint64_t even_cprtime; + double lat, lon; // Coordinated obtained from CPR encoded data + int bFlags; // Flags related to valid fields in this structure + struct aircraft *next; // Next aircraft in our linked list +}; + + + +/* Update aircraft state from data in the provided mesage. + * Return the tracked aircraft. + */ +struct modesMessage; +struct aircraft *trackUpdateFromMessage(struct modesMessage *mm); + +/* Call periodically */ +void trackPeriodicUpdate(); + +#endif diff --git a/util.c b/util.c new file mode 100644 index 0000000..b7e1887 --- /dev/null +++ b/util.c @@ -0,0 +1,64 @@ +// Part of dump1090, a Mode S message decoder for RTLSDR devices. +// +// util.c: misc utilities +// +// Copyright (c) 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 "util.h" + +#include +#include + +uint64_t mstime(void) +{ + struct timeval tv; + uint64_t mst; + + gettimeofday(&tv, NULL); + mst = ((uint64_t)tv.tv_sec)*1000; + mst += tv.tv_usec/1000; + return mst; +} diff --git a/util.h b/util.h new file mode 100644 index 0000000..6526b7b --- /dev/null +++ b/util.h @@ -0,0 +1,27 @@ +// Part of dump1090, a Mode S message decoder for RTLSDR devices. +// +// track.h: aircraft state tracking prototypes +// +// Copyright (c) 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 . + +#ifndef DUMP1090_UTIL_H +#define DUMP1090_UTIL_H + +#include + +uint64_t mstime(void); + +#endif diff --git a/view1090.c b/view1090.c index 65d210e..2ba21aa 100644 --- a/view1090.c +++ b/view1090.c @@ -70,7 +70,6 @@ void view1090InitConfig(void) { strcpy(View1090.net_input_beast_ipaddr,VIEW1090_NET_OUTPUT_IP_ADDRESS); Modes.net_input_beast_port = MODES_NET_OUTPUT_BEAST_PORT; Modes.interactive_rows = getTermRows(); - Modes.interactive_delete_ttl = MODES_INTERACTIVE_DELETE_TTL; Modes.interactive_display_ttl = MODES_INTERACTIVE_DISPLAY_TTL; Modes.interactive = 1; @@ -286,7 +285,7 @@ int main(int argc, char **argv) { // Keep going till the user does something that stops us while (!Modes.exit) { icaoFilterExpire(); - interactiveRemoveStaleAircrafts(); + trackPeriodicUpdate(); interactiveShowData(); if ((fd == ANET_ERR) || (recv(c->fd, pk_buf, sizeof(pk_buf), MSG_PEEK | MSG_DONTWAIT) == 0)) { free(c);