7ea2e8fdef
The modesReadFromClient() funtion is called from modesReadFromClients(), which in turn is called from backgroundTasks(). backgroundTasks() is called from within the main processing loop. However, modesReadFromClient() can and does block. It attempts to read characters from the input stream, and loops whilst there was no error. This stalls the main RTL processing loop until an error occurs. In order to support simultaneous local reception (via our RTL dongle) and remote forwarding (data received from the interweb) we cannot allow this internet read to stall. To fix this, in modesReadFromClient() attempt to read a buffer of data (currently 0x400 bytes). If we get a full buffer of bytes, then process them, and attempt to read another full buffer. Keep doing thios untill we read only a partial buffer (less than 0x400 bytes). Process the partial buffer bytes and return. This allows us to occasionally process data that is arriving from the internet (which is buffered anyway in the TCP stack), without blocking local RTL dongle decoding.
505 lines
20 KiB
C
505 lines
20 KiB
C
// dump1090, a Mode S messages decoder for RTLSDR devices.
|
|
//
|
|
// 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"
|
|
//
|
|
// ============================= 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;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
// Standard error macro for reporting API errors
|
|
#define PERR(bSuccess, api){if(!(bSuccess)) printf("%s:Error %d from %s on line %d\n", __FILE__, GetLastError(), api, __LINE__);}
|
|
|
|
void cls( HANDLE hConsole ) {
|
|
COORD coordScreen = { 0, 0 }; // here's where we'll home the cursor
|
|
BOOL bSuccess;
|
|
DWORD cCharsWritten;
|
|
CONSOLE_SCREEN_BUFFER_INFO csbi; // to get buffer info
|
|
DWORD dwConSize; // number of character cells in the current buffer
|
|
|
|
// get the number of character cells in the current buffer
|
|
bSuccess = GetConsoleScreenBufferInfo( hConsole, &csbi );
|
|
PERR( bSuccess, "GetConsoleScreenBufferInfo" );
|
|
dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
|
|
|
|
// fill the entire screen with blanks
|
|
bSuccess = FillConsoleOutputCharacter( hConsole, (TCHAR) ' ', dwConSize, coordScreen, &cCharsWritten );
|
|
PERR( bSuccess, "FillConsoleOutputCharacter" );
|
|
|
|
// get the current text attribute
|
|
bSuccess = GetConsoleScreenBufferInfo( hConsole, &csbi );
|
|
PERR( bSuccess, "ConsoleScreenBufferInfo" );
|
|
|
|
// now set the buffer's attributes accordingly
|
|
bSuccess = FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten );
|
|
PERR( bSuccess, "FillConsoleOutputAttribute" );
|
|
|
|
// put the cursor at (0, 0)
|
|
bSuccess = SetConsoleCursorPosition( hConsole, coordScreen );
|
|
PERR( bSuccess, "SetConsoleCursorPosition" );
|
|
}
|
|
#endif
|
|
//
|
|
//========================= Interactive mode ===============================
|
|
//
|
|
// Return a new aircraft structure for the interactive mode linked list
|
|
// of aircraft
|
|
//
|
|
struct aircraft *interactiveCreateAircraft(struct modesMessage *mm) {
|
|
struct aircraft *a = (struct aircraft *) malloc(sizeof(*a));
|
|
|
|
// Default everything to zero/NULL
|
|
memset(a, 0, sizeof(*a));
|
|
|
|
// Now initialise things that should not be 0/NULL to their defaults
|
|
a->addr = mm->addr;
|
|
a->lat = a->lon = 0.0;
|
|
memset(a->signalLevel, mm->signalLevel, 8); // 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
|
|
//
|
|
struct aircraft *interactiveReceiveData(struct modesMessage *mm) {
|
|
struct aircraft *a, *aux;
|
|
|
|
// Return if (checking crc) AND (not crcok) AND (not fixed)
|
|
if (Modes.check_crc && (mm->crcok == 0) && (mm->correctedbits == 0))
|
|
return 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 && (time(NULL) - 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 = time(NULL);
|
|
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);
|
|
|
|
} else if ( (mm->bFlags & MODES_ACFLAGS_LLEITHER_VALID)
|
|
&& (((mm->bFlags | a->bFlags) & MODES_ACFLAGS_LLEITHER_VALID) == MODES_ACFLAGS_LLBOTH_VALID) ) {
|
|
// If it's a new even/odd raw lat/lon, and we now have both even and odd,decode the CPR
|
|
int fflag;
|
|
|
|
if (mm->bFlags & MODES_ACFLAGS_LLODD_VALID) {
|
|
fflag = 1;
|
|
a->odd_cprlat = mm->raw_latitude;
|
|
a->odd_cprlon = mm->raw_longitude;
|
|
a->odd_cprtime = mstime();
|
|
} else {
|
|
fflag = 0;
|
|
a->even_cprlat = mm->raw_latitude;
|
|
a->even_cprlon = mm->raw_longitude;
|
|
a->even_cprtime = mstime();
|
|
}
|
|
// Try relative CPR first
|
|
if (decodeCPRrelative(a, fflag, (mm->bFlags & MODES_ACFLAGS_AOG))) {
|
|
// If it fails then try global if the two data are less than 10 seconds apart
|
|
if (abs((int)(a->even_cprtime - a->odd_cprtime)) <= 10000) {
|
|
decodeCPR(a, fflag, (mm->bFlags & MODES_ACFLAGS_AOG));
|
|
}
|
|
}
|
|
|
|
//If we sucessfully decoded, back copy the results to mm so that we can print them in list output
|
|
if (a->bFlags & MODES_ACFLAGS_LATLON_VALID) {
|
|
mm->bFlags |= MODES_ACFLAGS_LATLON_VALID;
|
|
mm->fLat = a->lat;
|
|
mm->fLon = a->lon;
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
//
|
|
//=========================================================================
|
|
//
|
|
// Show the currently captured interactive data on screen.
|
|
//
|
|
void interactiveShowData(void) {
|
|
struct aircraft *a = Modes.aircrafts;
|
|
time_t now = time(NULL);
|
|
int count = 0;
|
|
char progress;
|
|
char spinner[4] = "|/-\\";
|
|
|
|
// Refresh screen every (MODES_INTERACTIVE_REFRESH_TIME) miliseconde
|
|
if ((mstime() - Modes.interactive_last_update) < MODES_INTERACTIVE_REFRESH_TIME)
|
|
{return;}
|
|
|
|
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
|
|
printf("\x1b[H\x1b[2J"); // Clear the screen
|
|
#else
|
|
cls(GetStdHandle(STD_OUTPUT_HANDLE));
|
|
#endif
|
|
|
|
if (Modes.interactive_rtl1090 == 0) {
|
|
printf (
|
|
"Hex Mode Sqwk Flight Alt Spd Hdg Lat Long Sig Msgs Ti%c\n", progress);
|
|
} else {
|
|
printf (
|
|
"Hex Flight Alt V/S GS TT SSR G*456^ Msgs Seen %c\n", progress);
|
|
}
|
|
printf(
|
|
"-------------------------------------------------------------------------------\n");
|
|
|
|
while(a && (count < Modes.interactive_rows)) {
|
|
|
|
if ((now - a->seen) < Modes.interactive_display_ttl)
|
|
{
|
|
int msgs = a->messages;
|
|
int flags = a->modeACflags;
|
|
|
|
if ( (((flags & (MODEAC_MSG_FLAG )) == 0 ) )
|
|
|| (((flags & (MODEAC_MSG_MODES_HIT | MODEAC_MSG_MODEA_ONLY)) == MODEAC_MSG_MODEA_ONLY) && (msgs > 4 ) )
|
|
|| (((flags & (MODEAC_MSG_MODES_HIT | MODEAC_MSG_MODEC_OLD )) == 0 ) && (msgs > 127) )
|
|
) {
|
|
int altitude = a->altitude, speed = a->speed;
|
|
char strSquawk[5] = " ";
|
|
char strFl[6] = " ";
|
|
char strTt[5] = " ";
|
|
char strGs[5] = " ";
|
|
|
|
// Convert units to metric if --metric was specified
|
|
if (Modes.metric) {
|
|
altitude = (int) (altitude / 3.2828);
|
|
speed = (int) (speed * 1.852);
|
|
}
|
|
|
|
if (a->bFlags & MODES_ACFLAGS_SQUAWK_VALID) {
|
|
snprintf(strSquawk,5,"%04x", a->modeA);}
|
|
|
|
if (a->bFlags & MODES_ACFLAGS_SPEED_VALID) {
|
|
snprintf (strGs, 5,"%3d", speed);}
|
|
|
|
if (a->bFlags & MODES_ACFLAGS_HEADING_VALID) {
|
|
snprintf (strTt, 5,"%03d", a->track);}
|
|
|
|
if (msgs > 99999) {
|
|
msgs = 99999;}
|
|
|
|
if (Modes.interactive_rtl1090) { // RTL1090 display mode
|
|
|
|
if (a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) {
|
|
snprintf(strFl,6,"F%03d",(altitude/100));
|
|
}
|
|
printf("%06x %-8s %-4s %-3s %-3s %4s %-6d %-2d\n",
|
|
a->addr, a->flight, strFl, strGs, strTt, strSquawk, msgs, (int)(now - a->seen));
|
|
|
|
} else { // Dump1090 display mode
|
|
char strMode[5] = " ";
|
|
char strLat[8] = " ";
|
|
char strLon[9] = " ";
|
|
unsigned char * pSig = a->signalLevel;
|
|
unsigned int signalAverage = (pSig[0] + pSig[1] + pSig[2] + pSig[3] +
|
|
pSig[4] + pSig[5] + pSig[6] + pSig[7] + 3) >> 3;
|
|
|
|
if ((flags & MODEAC_MSG_FLAG) == 0) {
|
|
strMode[0] = 'S';
|
|
} else if (flags & MODEAC_MSG_MODEA_ONLY) {
|
|
strMode[0] = 'A';
|
|
}
|
|
if (flags & MODEAC_MSG_MODEA_HIT) {strMode[2] = 'a';}
|
|
if (flags & MODEAC_MSG_MODEC_HIT) {strMode[3] = 'c';}
|
|
|
|
if (a->bFlags & MODES_ACFLAGS_LATLON_VALID) {
|
|
snprintf(strLat, 8,"%7.03f", a->lat);
|
|
snprintf(strLon, 9,"%8.03f", a->lon);
|
|
}
|
|
|
|
if (a->bFlags & MODES_ACFLAGS_AOG) {
|
|
snprintf(strFl, 6," grnd");
|
|
} else if (a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) {
|
|
snprintf(strFl, 6, "%5d", altitude);
|
|
}
|
|
|
|
printf("%06x %-4s %-4s %-8s %5s %3s %3s %7s %8s %3d %5d %2d\n",
|
|
a->addr, strMode, strSquawk, a->flight, strFl, strGs, strTt,
|
|
strLat, strLon, signalAverage, msgs, (int)(now - a->seen));
|
|
}
|
|
count++;
|
|
}
|
|
}
|
|
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);
|
|
|
|
while(a) {
|
|
if ((now - a->seen) > Modes.interactive_delete_ttl) {
|
|
struct aircraft *next = a->next;
|
|
// Remove the element from the linked list, with care
|
|
// if we are removing the first element
|
|
|
|
if (!prev)
|
|
Modes.aircrafts = next;
|
|
else
|
|
prev->next = next;
|
|
|
|
free(a);
|
|
a = next;
|
|
} else {
|
|
prev = a;
|
|
a = a->next;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
//=========================================================================
|
|
//
|