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.
This commit is contained in:
parent
704d8aaffb
commit
899c51ce85
4
Makefile
4
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:
|
||||
|
|
|
@ -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) {
|
||||
|
|
50
dump1090.h
50
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);
|
||||
|
|
473
interactive.c
473
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
|
|
20
mode_s.c
20
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);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
|
517
track.c
Normal file
517
track.c
Normal file
|
@ -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 <oliver@mutability.co.uk>
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// This file incorporates work covered by the following copyright and
|
||||
// permission notice:
|
||||
//
|
||||
// Copyright (C) 2012 by Salvatore Sanfilippo <antirez@gmail.com>
|
||||
//
|
||||
// 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();
|
||||
}
|
||||
}
|
103
track.h
Normal file
103
track.h
Normal file
|
@ -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 <oliver@mutability.co.uk>
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// This file incorporates work covered by the following copyright and
|
||||
// permission notice:
|
||||
//
|
||||
// Copyright (C) 2012 by Salvatore Sanfilippo <antirez@gmail.com>
|
||||
//
|
||||
// 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
|
64
util.c
Normal file
64
util.c
Normal file
|
@ -0,0 +1,64 @@
|
|||
// Part of dump1090, a Mode S message decoder for RTLSDR devices.
|
||||
//
|
||||
// util.c: misc utilities
|
||||
//
|
||||
// Copyright (c) 2015 Oliver Jowett <oliver@mutability.co.uk>
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// This file incorporates work covered by the following copyright and
|
||||
// permission notice:
|
||||
//
|
||||
// Copyright (C) 2012 by Salvatore Sanfilippo <antirez@gmail.com>
|
||||
//
|
||||
// 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 <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
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;
|
||||
}
|
27
util.h
Normal file
27
util.h
Normal file
|
@ -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 <oliver@mutability.co.uk>
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef DUMP1090_UTIL_H
|
||||
#define DUMP1090_UTIL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
uint64_t mstime(void);
|
||||
|
||||
#endif
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue