Split into separate module files
Ok - this is likely to upset some people. Up until now, the vast majority of the code has been in just one file - dump1090.c. This file has grown so that it was approaching of 5000 lines long, and it was becoming unmanagable. So I've split the file into several modules, hopefully along fairly logical boundaries. The files are : 1) dump1090.c : Basically just the main() entry function, the help function, the RTL dongle hardware interface, and a few orphan functions that don't really fit anywhere else. 2) mode_s.c : This contains all the mode S / ADSB decoding functions. 3) mode_ac.c : This contains all the mode A & C decoding functions 4) interactive.c : This contains all the functions to maintain an internal list of aircraft seen over the last period, and functions to print them out to the local console. 5) net_io.c : This contains all the network input/output functions allowing data to be passed in/out to/from other receivers, in formats such as SBS-1/3, Beast, AVR and JavaScript. Hopefully this should provide an easier way forward if/when more functions are added.
This commit is contained in:
parent
1ebdd6d7a7
commit
6997715fed
4
Makefile
4
Makefile
|
@ -20,8 +20,8 @@ all: dump1090
|
|||
%.o: %.c
|
||||
$(CC) $(CFLAGS) $(EXTRACFLAGS) -c $<
|
||||
|
||||
dump1090: dump1090.o anet.o
|
||||
$(CC) -g -o dump1090 dump1090.o anet.o $(LIBS)
|
||||
dump1090: dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o
|
||||
$(CC) -g -o dump1090 dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o $(LIBS)
|
||||
|
||||
clean:
|
||||
rm -f *.o dump1090
|
||||
|
|
3642
dump1090.c
3642
dump1090.c
File diff suppressed because it is too large
Load diff
29
dump1090.h
29
dump1090.h
|
@ -388,29 +388,30 @@ int detectModeA (uint16_t *m, struct modesMessage *mm);
|
|||
void decodeModeAMessage(struct modesMessage *mm, int ModeA);
|
||||
int ModeAToModeC (unsigned int ModeA);
|
||||
|
||||
//
|
||||
// Functions exported from mode_s.c
|
||||
//
|
||||
void detectModeS (uint16_t *m, uint32_t mlen);
|
||||
void decodeModesMessage (struct modesMessage *mm, unsigned char *msg);
|
||||
void displayModesMessage(struct modesMessage *mm);
|
||||
void useModesMessage (struct modesMessage *mm);
|
||||
void computeMagnitudeVector();
|
||||
void decodeCPR (struct aircraft *a, int fflag, int surface);
|
||||
int decodeCPRrelative (struct aircraft *a, int fflag, int surface);
|
||||
void modesInitErrorInfo ();
|
||||
//
|
||||
// Functions exported from interactive.c
|
||||
//
|
||||
struct aircraft* interactiveReceiveData(struct modesMessage *mm);
|
||||
void interactiveShowData(void);
|
||||
void interactiveRemoveStaleAircrafts(void);
|
||||
|
||||
//
|
||||
// Functions exported from dump1090.c
|
||||
// Functions exported from net_io.c
|
||||
//
|
||||
void modesInitNet (void);
|
||||
void modesReadFromClients (void);
|
||||
void modesSendAllClients (int service, void *msg, int len);
|
||||
void modesSendRawOutput (struct modesMessage *mm);
|
||||
void modesSendBeastOutput (struct modesMessage *mm);
|
||||
void modesSendSBSOutput (struct modesMessage *mm);
|
||||
void useModesMessage (struct modesMessage *mm);
|
||||
|
||||
void decodeCPR (struct aircraft *a, int fflag, int surface);
|
||||
int decodeCPRrelative(struct aircraft *a, int fflag, int surface);
|
||||
|
||||
int fixBitErrors (unsigned char *msg, int bits, int maxfix, char *fixedbits);
|
||||
|
||||
void modesInitErrorInfo ();
|
||||
int modesMessageLenByType(int type);
|
||||
void modesQueueOutput (struct modesMessage *mm);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
480
interactive.c
Normal file
480
interactive.c
Normal file
|
@ -0,0 +1,480 @@
|
|||
// 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;
|
||||
}
|
||||
//
|
||||
//========================= 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
|
||||
system("cls");
|
||||
#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) {
|
||||
if ( (0x3300 != (a->modeA & 0x7700)) // London Mil South 3300 - 3377
|
||||
&& (0x6400 != (a->modeA & 0x7700)) // London Mil Daventry 6400 - 6477
|
||||
&& (0x7400 != (a->modeA & 0x7740)) // Yeovilton 7400 - 7437
|
||||
&& (0x4300 != (a->modeA & 0x7700)) // Yeovilton Lars 4300 - 4377
|
||||
&& (0x2600 != (a->modeA & 0x7700)) // Boscombe 2600 - 2677
|
||||
&& (0x7400 != (a->modeA & 0x7477)) ) // Emergency 7400,7500,7600,7700
|
||||
msgs = 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
386
mode_ac.c
Normal file
386
mode_ac.c
Normal file
|
@ -0,0 +1,386 @@
|
|||
// 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"
|
||||
//
|
||||
// ===================== Mode A/C detection and decoding ===================
|
||||
//
|
||||
//
|
||||
// This table is used to build the Mode A/C variable called ModeABits.Each
|
||||
// bit period is inspected, and if it's value exceeds the threshold limit,
|
||||
// then the value in this table is or-ed into ModeABits.
|
||||
//
|
||||
// At the end of message processing, ModeABits will be the decoded ModeA value.
|
||||
//
|
||||
// We can also flag noise in bits that should be zeros - the xx bits. Noise in
|
||||
// these bits cause bits (31-16) in ModeABits to be set. Then at the end of message
|
||||
// processing we can test for errors by looking at these bits.
|
||||
//
|
||||
uint32_t ModeABitTable[24] = {
|
||||
0x00000000, // F1 = 1
|
||||
0x00000010, // C1
|
||||
0x00001000, // A1
|
||||
0x00000020, // C2
|
||||
0x00002000, // A2
|
||||
0x00000040, // C4
|
||||
0x00004000, // A4
|
||||
0x40000000, // xx = 0 Set bit 30 if we see this high
|
||||
0x00000100, // B1
|
||||
0x00000001, // D1
|
||||
0x00000200, // B2
|
||||
0x00000002, // D2
|
||||
0x00000400, // B4
|
||||
0x00000004, // D4
|
||||
0x00000000, // F2 = 1
|
||||
0x08000000, // xx = 0 Set bit 27 if we see this high
|
||||
0x04000000, // xx = 0 Set bit 26 if we see this high
|
||||
0x00000080, // SPI
|
||||
0x02000000, // xx = 0 Set bit 25 if we see this high
|
||||
0x01000000, // xx = 0 Set bit 24 if we see this high
|
||||
0x00800000, // xx = 0 Set bit 23 if we see this high
|
||||
0x00400000, // xx = 0 Set bit 22 if we see this high
|
||||
0x00200000, // xx = 0 Set bit 21 if we see this high
|
||||
0x00100000, // xx = 0 Set bit 20 if we see this high
|
||||
};
|
||||
//
|
||||
// This table is used to produce an error variable called ModeAErrs.Each
|
||||
// inter-bit period is inspected, and if it's value falls outside of the
|
||||
// expected range, then the value in this table is or-ed into ModeAErrs.
|
||||
//
|
||||
// At the end of message processing, ModeAErrs will indicate if we saw
|
||||
// any inter-bit anomolies, and the bits that are set will show which
|
||||
// bits had them.
|
||||
//
|
||||
uint32_t ModeAMidTable[24] = {
|
||||
0x80000000, // F1 = 1 Set bit 31 if we see F1_C1 error
|
||||
0x00000010, // C1 Set bit 4 if we see C1_A1 error
|
||||
0x00001000, // A1 Set bit 12 if we see A1_C2 error
|
||||
0x00000020, // C2 Set bit 5 if we see C2_A2 error
|
||||
0x00002000, // A2 Set bit 13 if we see A2_C4 error
|
||||
0x00000040, // C4 Set bit 6 if we see C3_A4 error
|
||||
0x00004000, // A4 Set bit 14 if we see A4_xx error
|
||||
0x40000000, // xx = 0 Set bit 30 if we see xx_B1 error
|
||||
0x00000100, // B1 Set bit 8 if we see B1_D1 error
|
||||
0x00000001, // D1 Set bit 0 if we see D1_B2 error
|
||||
0x00000200, // B2 Set bit 9 if we see B2_D2 error
|
||||
0x00000002, // D2 Set bit 1 if we see D2_B4 error
|
||||
0x00000400, // B4 Set bit 10 if we see B4_D4 error
|
||||
0x00000004, // D4 Set bit 2 if we see D4_F2 error
|
||||
0x20000000, // F2 = 1 Set bit 29 if we see F2_xx error
|
||||
0x08000000, // xx = 0 Set bit 27 if we see xx_xx error
|
||||
0x04000000, // xx = 0 Set bit 26 if we see xx_SPI error
|
||||
0x00000080, // SPI Set bit 15 if we see SPI_xx error
|
||||
0x02000000, // xx = 0 Set bit 25 if we see xx_xx error
|
||||
0x01000000, // xx = 0 Set bit 24 if we see xx_xx error
|
||||
0x00800000, // xx = 0 Set bit 23 if we see xx_xx error
|
||||
0x00400000, // xx = 0 Set bit 22 if we see xx_xx error
|
||||
0x00200000, // xx = 0 Set bit 21 if we see xx_xx error
|
||||
0x00100000, // xx = 0 Set bit 20 if we see xx_xx error
|
||||
};
|
||||
//
|
||||
// The "off air" format is,,
|
||||
// _F1_C1_A1_C2_A2_C4_A4_xx_B1_D1_B2_D2_B4_D4_F2_xx_xx_SPI_
|
||||
//
|
||||
// Bit spacing is 1.45uS, with 0.45uS high, and 1.00us low. This is a problem
|
||||
// because we ase sampling at 2Mhz (500nS) so we are below Nyquist.
|
||||
//
|
||||
// The bit spacings are..
|
||||
// F1 : 0.00,
|
||||
// 1.45, 2.90, 4.35, 5.80, 7.25, 8.70,
|
||||
// X : 10.15,
|
||||
// : 11.60, 13.05, 14.50, 15.95, 17.40, 18.85,
|
||||
// F2 : 20.30,
|
||||
// X : 21.75, 23.20, 24.65
|
||||
//
|
||||
// This equates to the following sample point centers at 2Mhz.
|
||||
// [ 0.0],
|
||||
// [ 2.9], [ 5.8], [ 8.7], [11.6], [14.5], [17.4],
|
||||
// [20.3],
|
||||
// [23.2], [26.1], [29.0], [31.9], [34.8], [37.7]
|
||||
// [40.6]
|
||||
// [43.5], [46.4], [49.3]
|
||||
//
|
||||
// We know that this is a supposed to be a binary stream, so the signal
|
||||
// should either be a 1 or a 0. Therefore, any energy above the noise level
|
||||
// in two adjacent samples must be from the same pulse, so we can simply
|
||||
// add the values together..
|
||||
//
|
||||
int detectModeA(uint16_t *m, struct modesMessage *mm)
|
||||
{
|
||||
int j, lastBitWasOne;
|
||||
int ModeABits = 0;
|
||||
int ModeAErrs = 0;
|
||||
int byte, bit;
|
||||
int thisSample, lastBit, lastSpace = 0;
|
||||
int m0, m1, m2, m3, mPhase;
|
||||
int n0, n1, n2 ,n3;
|
||||
int F1_sig, F1_noise;
|
||||
int F2_sig, F2_noise;
|
||||
int fSig, fNoise, fLevel, fLoLo;
|
||||
|
||||
// m[0] contains the energy from 0 -> 499 nS
|
||||
// m[1] contains the energy from 500 -> 999 nS
|
||||
// m[2] contains the energy from 1000 -> 1499 nS
|
||||
// m[3] contains the energy from 1500 -> 1999 nS
|
||||
//
|
||||
// We are looking for a Frame bit (F1) whose width is 450nS, followed by
|
||||
// 1000nS of quiet.
|
||||
//
|
||||
// The width of the frame bit is 450nS, which is 90% of our sample rate.
|
||||
// Therefore, in an ideal world, all the energy for the frame bit will be
|
||||
// in a single sample, preceeded by (at least) one zero, and followed by
|
||||
// two zeros, Best case we can look for ...
|
||||
//
|
||||
// 0 - 1 - 0 - 0
|
||||
//
|
||||
// However, our samples are not phase aligned, so some of the energy from
|
||||
// each bit could be spread over two consecutive samples. Worst case is
|
||||
// that we sample half in one bit, and half in the next. In that case,
|
||||
// we're looking for
|
||||
//
|
||||
// 0 - 0.5 - 0.5 - 0.
|
||||
|
||||
m0 = m[0]; m1 = m[1];
|
||||
|
||||
if (m0 >= m1) // m1 *must* be bigger than m0 for this to be F1
|
||||
{return (0);}
|
||||
|
||||
m2 = m[2]; m3 = m[3];
|
||||
|
||||
//
|
||||
// if (m2 <= m0), then assume the sample bob on (Phase == 0), so don't look at m3
|
||||
if ((m2 <= m0) || (m2 < m3))
|
||||
{m3 = m2; m2 = m0;}
|
||||
|
||||
if ( (m3 >= m1) // m1 must be bigger than m3
|
||||
|| (m0 > m2) // m2 can be equal to m0 if ( 0,1,0,0 )
|
||||
|| (m3 > m2) ) // m2 can be equal to m3 if ( 0,1,0,0 )
|
||||
{return (0);}
|
||||
|
||||
// m0 = noise
|
||||
// m1 = noise + (signal * X))
|
||||
// m2 = noise + (signal * (1-X))
|
||||
// m3 = noise
|
||||
//
|
||||
// Hence, assuming all 4 samples have similar amounts of noise in them
|
||||
// signal = (m1 + m2) - ((m0 + m3) * 2)
|
||||
// noise = (m0 + m3) / 2
|
||||
//
|
||||
F1_sig = (m1 + m2) - ((m0 + m3) << 1);
|
||||
F1_noise = (m0 + m3) >> 1;
|
||||
|
||||
if ( (F1_sig < MODEAC_MSG_SQUELCH_LEVEL) // minimum required F1 signal amplitude
|
||||
|| (F1_sig < (F1_noise << 2)) ) // minimum allowable Sig/Noise ratio 4:1
|
||||
{return (0);}
|
||||
|
||||
// If we get here then we have a potential F1, so look for an equally valid F2 20.3uS later
|
||||
//
|
||||
// Our F1 is centered somewhere between samples m[1] and m[2]. We can guestimate where F2 is
|
||||
// by comparing the ratio of m1 and m2, and adding on 20.3 uS (40.6 samples)
|
||||
//
|
||||
mPhase = ((m2 * 20) / (m1 + m2));
|
||||
byte = (mPhase + 812) / 20;
|
||||
n0 = m[byte++]; n1 = m[byte++];
|
||||
|
||||
if (n0 >= n1) // n1 *must* be bigger than n0 for this to be F2
|
||||
{return (0);}
|
||||
|
||||
n2 = m[byte++];
|
||||
//
|
||||
// if the sample bob on (Phase == 0), don't look at n3
|
||||
//
|
||||
if ((mPhase + 812) % 20)
|
||||
{n3 = m[byte++];}
|
||||
else
|
||||
{n3 = n2; n2 = n0;}
|
||||
|
||||
if ( (n3 >= n1) // n1 must be bigger than n3
|
||||
|| (n0 > n2) // n2 can be equal to n0 ( 0,1,0,0 )
|
||||
|| (n3 > n2) ) // n2 can be equal to n3 ( 0,1,0,0 )
|
||||
{return (0);}
|
||||
|
||||
F2_sig = (n1 + n2) - ((n0 + n3) << 1);
|
||||
F2_noise = (n0 + n3) >> 1;
|
||||
|
||||
if ( (F2_sig < MODEAC_MSG_SQUELCH_LEVEL) // minimum required F2 signal amplitude
|
||||
|| (F2_sig < (F2_noise << 2)) ) // maximum allowable Sig/Noise ratio 4:1
|
||||
{return (0);}
|
||||
|
||||
fSig = (F1_sig + F2_sig) >> 1;
|
||||
fNoise = (F1_noise + F2_noise) >> 1;
|
||||
fLoLo = fNoise + (fSig >> 2); // 1/2
|
||||
fLevel = fNoise + (fSig >> 1);
|
||||
lastBitWasOne = 1;
|
||||
lastBit = F1_sig;
|
||||
//
|
||||
// Now step by a half ModeA bit, 0.725nS, which is 1.45 samples, which is 29/20
|
||||
// No need to do bit 0 because we've already selected it as a valid F1
|
||||
// Do several bits past the SPI to increase error rejection
|
||||
//
|
||||
for (j = 1, mPhase += 29; j < 48; mPhase += 29, j ++)
|
||||
{
|
||||
byte = 1 + (mPhase / 20);
|
||||
|
||||
thisSample = m[byte] - fNoise;
|
||||
if (mPhase % 20) // If the bit is split over two samples...
|
||||
{thisSample += (m[byte+1] - fNoise);} // add in the second sample's energy
|
||||
|
||||
// If we're calculating a space value
|
||||
if (j & 1)
|
||||
{lastSpace = thisSample;}
|
||||
|
||||
else
|
||||
{// We're calculating a new bit value
|
||||
bit = j >> 1;
|
||||
if (thisSample >= fLevel)
|
||||
{// We're calculating a new bit value, and its a one
|
||||
ModeABits |= ModeABitTable[bit--]; // or in the correct bit
|
||||
|
||||
if (lastBitWasOne)
|
||||
{ // This bit is one, last bit was one, so check the last space is somewhere less than one
|
||||
if ( (lastSpace >= (thisSample>>1)) || (lastSpace >= lastBit) )
|
||||
{ModeAErrs |= ModeAMidTable[bit];}
|
||||
}
|
||||
|
||||
else
|
||||
{// This bit,is one, last bit was zero, so check the last space is somewhere less than one
|
||||
if (lastSpace >= (thisSample >> 1))
|
||||
{ModeAErrs |= ModeAMidTable[bit];}
|
||||
}
|
||||
|
||||
lastBitWasOne = 1;
|
||||
}
|
||||
|
||||
|
||||
else
|
||||
{// We're calculating a new bit value, and its a zero
|
||||
if (lastBitWasOne)
|
||||
{ // This bit is zero, last bit was one, so check the last space is somewhere in between
|
||||
if (lastSpace >= lastBit)
|
||||
{ModeAErrs |= ModeAMidTable[bit];}
|
||||
}
|
||||
|
||||
else
|
||||
{// This bit,is zero, last bit was zero, so check the last space is zero too
|
||||
if (lastSpace >= fLoLo)
|
||||
{ModeAErrs |= ModeAMidTable[bit];}
|
||||
}
|
||||
|
||||
lastBitWasOne = 0;
|
||||
}
|
||||
|
||||
lastBit = (thisSample >> 1);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Output format is : 00:A4:A2:A1:00:B4:B2:B1:00:C4:C2:C1:00:D4:D2:D1
|
||||
//
|
||||
if ((ModeABits < 3) || (ModeABits & 0xFFFF8808) || (ModeAErrs) )
|
||||
{return (ModeABits = 0);}
|
||||
|
||||
fSig = (fSig + 0x7F) >> 8;
|
||||
mm->signalLevel = ((fSig < 255) ? fSig : 255);
|
||||
|
||||
return ModeABits;
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// Input format is : 00:A4:A2:A1:00:B4:B2:B1:00:C4:C2:C1:00:D4:D2:D1
|
||||
//
|
||||
int ModeAToModeC(unsigned int ModeA)
|
||||
{
|
||||
unsigned int FiveHundreds = 0;
|
||||
unsigned int OneHundreds = 0;
|
||||
|
||||
if ( (ModeA & 0xFFFF888B) // D1 set is illegal. D2 set is > 62700ft which is unlikely
|
||||
|| ((ModeA & 0x000000F0) == 0) ) // C1,,C4 cannot be Zero
|
||||
{return -9999;}
|
||||
|
||||
if (ModeA & 0x0010) {OneHundreds ^= 0x007;} // C1
|
||||
if (ModeA & 0x0020) {OneHundreds ^= 0x003;} // C2
|
||||
if (ModeA & 0x0040) {OneHundreds ^= 0x001;} // C4
|
||||
|
||||
// Remove 7s from OneHundreds (Make 7->5, snd 5->7).
|
||||
if ((OneHundreds & 5) == 5) {OneHundreds ^= 2;}
|
||||
|
||||
// Check for invalid codes, only 1 to 5 are valid
|
||||
if (OneHundreds > 5)
|
||||
{return -9999;}
|
||||
|
||||
//if (ModeA & 0x0001) {FiveHundreds ^= 0x1FF;} // D1 never used for altitude
|
||||
if (ModeA & 0x0002) {FiveHundreds ^= 0x0FF;} // D2
|
||||
if (ModeA & 0x0004) {FiveHundreds ^= 0x07F;} // D4
|
||||
|
||||
if (ModeA & 0x1000) {FiveHundreds ^= 0x03F;} // A1
|
||||
if (ModeA & 0x2000) {FiveHundreds ^= 0x01F;} // A2
|
||||
if (ModeA & 0x4000) {FiveHundreds ^= 0x00F;} // A4
|
||||
|
||||
if (ModeA & 0x0100) {FiveHundreds ^= 0x007;} // B1
|
||||
if (ModeA & 0x0200) {FiveHundreds ^= 0x003;} // B2
|
||||
if (ModeA & 0x0400) {FiveHundreds ^= 0x001;} // B4
|
||||
|
||||
// Correct order of OneHundreds.
|
||||
if (FiveHundreds & 1) {OneHundreds = 6 - OneHundreds;}
|
||||
|
||||
return ((FiveHundreds * 5) + OneHundreds - 13);
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
void decodeModeAMessage(struct modesMessage *mm, int ModeA)
|
||||
{
|
||||
mm->msgtype = 32; // Valid Mode S DF's are DF-00 to DF-31.
|
||||
// so use 32 to indicate Mode A/C
|
||||
|
||||
mm->msgbits = 16; // Fudge up a Mode S style data stream
|
||||
mm->msg[0] = (ModeA >> 8);
|
||||
mm->msg[1] = (ModeA);
|
||||
|
||||
// Fudge an ICAO address based on Mode A (remove the Ident bit)
|
||||
// Use an upper address byte of FF, since this is ICAO unallocated
|
||||
mm->addr = 0x00FF0000 | (ModeA & 0x0000FF7F);
|
||||
|
||||
// Set the Identity field to ModeA
|
||||
mm->modeA = ModeA & 0x7777;
|
||||
mm->bFlags |= MODES_ACFLAGS_SQUAWK_VALID;
|
||||
|
||||
// Flag ident in flight status
|
||||
mm->fs = ModeA & 0x0080;
|
||||
|
||||
// Not much else we can tell from a Mode A/C reply.
|
||||
// Just fudge up a few bits to keep other code happy
|
||||
mm->crcok = 1;
|
||||
mm->correctedbits = 0;
|
||||
}
|
||||
//
|
||||
// ===================== Mode A/C detection and decoding ===================
|
||||
//
|
870
net_io.c
Normal file
870
net_io.c
Normal file
|
@ -0,0 +1,870 @@
|
|||
// 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"
|
||||
//
|
||||
// ============================= Networking =============================
|
||||
//
|
||||
// Note: here we disregard any kind of good coding practice in favor of
|
||||
// extreme simplicity, that is:
|
||||
//
|
||||
// 1) We only rely on the kernel buffers for our I/O without any kind of
|
||||
// user space buffering.
|
||||
// 2) We don't register any kind of event handler, from time to time a
|
||||
// function gets called and we accept new connections. All the rest is
|
||||
// handled via non-blocking I/O and manually polling clients to see if
|
||||
// they have something new to share with us when reading is needed.
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// Networking "stack" initialization
|
||||
//
|
||||
void modesInitNet(void) {
|
||||
struct {
|
||||
char *descr;
|
||||
int *socket;
|
||||
int port;
|
||||
} services[6] = {
|
||||
{"Raw TCP output", &Modes.ros, Modes.net_output_raw_port},
|
||||
{"Raw TCP input", &Modes.ris, Modes.net_input_raw_port},
|
||||
{"Beast TCP output", &Modes.bos, Modes.net_output_beast_port},
|
||||
{"Beast TCP input", &Modes.bis, Modes.net_input_beast_port},
|
||||
{"HTTP server", &Modes.https, Modes.net_http_port},
|
||||
{"Basestation TCP output", &Modes.sbsos, Modes.net_output_sbs_port}
|
||||
};
|
||||
int j;
|
||||
|
||||
memset(Modes.clients,0,sizeof(Modes.clients));
|
||||
Modes.maxfd = -1;
|
||||
|
||||
for (j = 0; j < 6; j++) {
|
||||
int s = anetTcpServer(Modes.aneterr, services[j].port, NULL);
|
||||
if (s == -1) {
|
||||
fprintf(stderr, "Error opening the listening port %d (%s): %s\n",
|
||||
services[j].port, services[j].descr, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
anetNonBlock(Modes.aneterr, s);
|
||||
*services[j].socket = s;
|
||||
}
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// This function gets called from time to time when the decoding thread is
|
||||
// awakened by new data arriving. This usually happens a few times every second
|
||||
//
|
||||
void modesAcceptClients(void) {
|
||||
int fd, port;
|
||||
unsigned int j;
|
||||
struct client *c;
|
||||
int services[6];
|
||||
|
||||
services[0] = Modes.ros;
|
||||
services[1] = Modes.ris;
|
||||
services[2] = Modes.bos;
|
||||
services[3] = Modes.bis;
|
||||
services[4] = Modes.https;
|
||||
services[5] = Modes.sbsos;
|
||||
|
||||
for (j = 0; j < sizeof(services)/sizeof(int); j++) {
|
||||
fd = anetTcpAccept(Modes.aneterr, services[j], NULL, &port);
|
||||
if (fd == -1) continue;
|
||||
|
||||
if (fd >= MODES_NET_MAX_FD) {
|
||||
close(fd);
|
||||
return; // Max number of clients reached
|
||||
}
|
||||
|
||||
anetNonBlock(Modes.aneterr, fd);
|
||||
c = (struct client *) malloc(sizeof(*c));
|
||||
c->service = services[j];
|
||||
c->fd = fd;
|
||||
c->buflen = 0;
|
||||
Modes.clients[fd] = c;
|
||||
anetSetSendBuffer(Modes.aneterr,fd,MODES_NET_SNDBUF_SIZE);
|
||||
|
||||
if (Modes.maxfd < fd) Modes.maxfd = fd;
|
||||
if (services[j] == Modes.sbsos) Modes.stat_sbs_connections++;
|
||||
if (services[j] == Modes.ros) Modes.stat_raw_connections++;
|
||||
if (services[j] == Modes.bos) Modes.stat_beast_connections++;
|
||||
|
||||
j--; // Try again with the same listening port
|
||||
|
||||
if (Modes.debug & MODES_DEBUG_NET)
|
||||
printf("Created new client %d\n", fd);
|
||||
}
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// On error free the client, collect the structure, adjust maxfd if needed.
|
||||
//
|
||||
void modesFreeClient(int fd) {
|
||||
close(fd);
|
||||
if (Modes.clients[fd]->service == Modes.sbsos) {
|
||||
if (Modes.stat_sbs_connections) Modes.stat_sbs_connections--;
|
||||
}
|
||||
else if (Modes.clients[fd]->service == Modes.ros) {
|
||||
if (Modes.stat_raw_connections) Modes.stat_raw_connections--;
|
||||
}
|
||||
else if (Modes.clients[fd]->service == Modes.bos) {
|
||||
if (Modes.stat_beast_connections) Modes.stat_beast_connections--;
|
||||
}
|
||||
free(Modes.clients[fd]);
|
||||
Modes.clients[fd] = NULL;
|
||||
|
||||
if (Modes.debug & MODES_DEBUG_NET)
|
||||
printf("Closing client %d\n", fd);
|
||||
|
||||
// If this was our maxfd, rescan the full clients array to check what's
|
||||
// the new max.
|
||||
if (Modes.maxfd == fd) {
|
||||
int j;
|
||||
|
||||
Modes.maxfd = -1;
|
||||
for (j = 0; j < MODES_NET_MAX_FD; j++) {
|
||||
if (Modes.clients[j]) Modes.maxfd = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// Send the specified message to all clients listening for a given service
|
||||
//
|
||||
void modesSendAllClients(int service, void *msg, int len) {
|
||||
int j;
|
||||
struct client *c;
|
||||
|
||||
for (j = 0; j <= Modes.maxfd; j++) {
|
||||
c = Modes.clients[j];
|
||||
if (c && c->service == service) {
|
||||
int nwritten = write(j, msg, len);
|
||||
if (nwritten != len) {
|
||||
modesFreeClient(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// Write raw output in Beast Binary format with Timestamp to TCP clients
|
||||
//
|
||||
void modesSendBeastOutput(struct modesMessage *mm) {
|
||||
char *p = &Modes.beastOut[Modes.beastOutUsed];
|
||||
int msgLen = mm->msgbits / 8;
|
||||
char * pTimeStamp;
|
||||
int j;
|
||||
|
||||
*p++ = 0x1a;
|
||||
if (msgLen == MODES_SHORT_MSG_BYTES)
|
||||
{*p++ = '2';}
|
||||
else if (msgLen == MODES_LONG_MSG_BYTES)
|
||||
{*p++ = '3';}
|
||||
else if (msgLen == MODEAC_MSG_BYTES)
|
||||
{*p++ = '1';}
|
||||
else
|
||||
{return;}
|
||||
|
||||
pTimeStamp = (char *) &mm->timestampMsg;
|
||||
for (j = 5; j >= 0; j--) {
|
||||
*p++ = pTimeStamp[j];
|
||||
}
|
||||
|
||||
*p++ = mm->signalLevel;
|
||||
|
||||
memcpy(p, mm->msg, msgLen);
|
||||
|
||||
Modes.beastOutUsed += (msgLen + 9);
|
||||
if (Modes.beastOutUsed >= Modes.net_output_raw_size)
|
||||
{
|
||||
modesSendAllClients(Modes.bos, Modes.beastOut, Modes.beastOutUsed);
|
||||
Modes.beastOutUsed = 0;
|
||||
Modes.net_output_raw_rate_count = 0;
|
||||
}
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// Write raw output to TCP clients
|
||||
//
|
||||
void modesSendRawOutput(struct modesMessage *mm) {
|
||||
char *p = &Modes.rawOut[Modes.rawOutUsed];
|
||||
int msgLen = mm->msgbits / 8;
|
||||
int j;
|
||||
unsigned char * pTimeStamp;
|
||||
|
||||
if (Modes.mlat && mm->timestampMsg) {
|
||||
*p++ = '@';
|
||||
pTimeStamp = (unsigned char *) &mm->timestampMsg;
|
||||
for (j = 5; j >= 0; j--) {
|
||||
sprintf(p, "%02X", pTimeStamp[j]);
|
||||
p += 2;
|
||||
}
|
||||
Modes.rawOutUsed += 12; // additional 12 characters for timestamp
|
||||
} else
|
||||
*p++ = '*';
|
||||
|
||||
for (j = 0; j < msgLen; j++) {
|
||||
sprintf(p, "%02X", mm->msg[j]);
|
||||
p += 2;
|
||||
}
|
||||
|
||||
*p++ = ';';
|
||||
*p++ = '\n';
|
||||
|
||||
Modes.rawOutUsed += ((msgLen*2) + 3);
|
||||
if (Modes.rawOutUsed >= Modes.net_output_raw_size)
|
||||
{
|
||||
modesSendAllClients(Modes.ros, Modes.rawOut, Modes.rawOutUsed);
|
||||
Modes.rawOutUsed = 0;
|
||||
Modes.net_output_raw_rate_count = 0;
|
||||
}
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// Write SBS output to TCP clients
|
||||
// The message structure mm->bFlags tells us what has been updated by this message
|
||||
//
|
||||
void modesSendSBSOutput(struct modesMessage *mm) {
|
||||
char msg[256], *p = msg;
|
||||
uint32_t offset;
|
||||
struct timeb epocTime;
|
||||
struct tm stTime;
|
||||
int msgType;
|
||||
|
||||
//
|
||||
// SBS BS style output checked against the following reference
|
||||
// http://www.homepages.mcb.net/bones/SBS/Article/Barebones42_Socket_Data.htm - seems comprehensive
|
||||
//
|
||||
|
||||
// Decide on the basic SBS Message Type
|
||||
if ((mm->msgtype == 4) || (mm->msgtype == 20)) {
|
||||
msgType = 5;
|
||||
} else if ((mm->msgtype == 5) || (mm->msgtype == 21)) {
|
||||
msgType = 6;
|
||||
} else if ((mm->msgtype == 0) || (mm->msgtype == 16)) {
|
||||
msgType = 7;
|
||||
} else if (mm->msgtype == 11) {
|
||||
msgType = 8;
|
||||
} else if ((mm->msgtype != 17) && (mm->msgtype != 18)) {
|
||||
return;
|
||||
} else if ((mm->metype >= 1) && (mm->metype <= 4)) {
|
||||
msgType = 1;
|
||||
} else if ((mm->metype >= 5) && (mm->metype <= 8)) {
|
||||
if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID)
|
||||
{msgType = 2;}
|
||||
else
|
||||
{msgType = 7;}
|
||||
} else if ((mm->metype >= 9) && (mm->metype <= 18)) {
|
||||
if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID)
|
||||
{msgType = 3;}
|
||||
else
|
||||
{msgType = 7;}
|
||||
} else if (mm->metype != 19) {
|
||||
return;
|
||||
} else if ((mm->mesub == 1) || (mm->mesub == 2)) {
|
||||
msgType = 4;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fields 1 to 6 : SBS message type and ICAO address of the aircraft and some other stuff
|
||||
p += sprintf(p, "MSG,%d,111,11111,%06X,111111,", msgType, mm->addr);
|
||||
|
||||
// Fields 7 & 8 are the current time and date
|
||||
if (mm->timestampMsg) { // Make sure the records' timestamp is valid before outputing it
|
||||
epocTime = Modes.stSystemTimeBlk; // This is the time of the start of the Block we're processing
|
||||
offset = (int) (mm->timestampMsg - Modes.timestampBlk); // This is the time (in 12Mhz ticks) into the Block
|
||||
offset = offset / 12000; // convert to milliseconds
|
||||
epocTime.millitm += offset; // add on the offset time to the Block start time
|
||||
if (epocTime.millitm > 999) // if we've caused an overflow into the next second...
|
||||
{epocTime.millitm -= 1000; epocTime.time ++;} // ..correct the overflow
|
||||
stTime = *localtime(&epocTime.time); // convert the time to year, month day, hours, min, sec
|
||||
p += sprintf(p, "%04d/%02d/%02d,", (stTime.tm_year+1900),(stTime.tm_mon+1), stTime.tm_mday);
|
||||
p += sprintf(p, "%02d:%02d:%02d.%03d,", stTime.tm_hour, stTime.tm_min, stTime.tm_sec, epocTime.millitm);
|
||||
} else {
|
||||
p += sprintf(p, ",,");
|
||||
}
|
||||
|
||||
// Fields 9 & 10 are the current time and date
|
||||
ftime(&epocTime); // get the current system time & date
|
||||
stTime = *localtime(&epocTime.time); // convert the time to year, month day, hours, min, sec
|
||||
p += sprintf(p, "%04d/%02d/%02d,", (stTime.tm_year+1900),(stTime.tm_mon+1), stTime.tm_mday);
|
||||
p += sprintf(p, "%02d:%02d:%02d.%03d", stTime.tm_hour, stTime.tm_min, stTime.tm_sec, epocTime.millitm);
|
||||
|
||||
// Field 11 is the callsign (if we have it)
|
||||
if (mm->bFlags & MODES_ACFLAGS_CALLSIGN_VALID) {p += sprintf(p, ",%s", mm->flight);}
|
||||
else {p += sprintf(p, ",");}
|
||||
|
||||
// Field 12 is the altitude (if we have it) - force to zero if we're on the ground
|
||||
if ((mm->bFlags & MODES_ACFLAGS_AOG_GROUND) == MODES_ACFLAGS_AOG_GROUND) {
|
||||
p += sprintf(p, ",0");
|
||||
} else if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) {
|
||||
p += sprintf(p, ",%d", mm->altitude);
|
||||
} else {
|
||||
p += sprintf(p, ",");
|
||||
}
|
||||
|
||||
// Field 13 and 14 are the ground Speed and Heading (if we have them)
|
||||
if (mm->bFlags & MODES_ACFLAGS_NSEWSPD_VALID) {p += sprintf(p, ",%d,%d", mm->velocity, mm->heading);}
|
||||
else {p += sprintf(p, ",,");}
|
||||
|
||||
// Fields 15 and 16 are the Lat/Lon (if we have it)
|
||||
if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID) {p += sprintf(p, ",%1.5f,%1.5f", mm->fLat, mm->fLon);}
|
||||
else {p += sprintf(p, ",,");}
|
||||
|
||||
// Field 17 is the VerticalRate (if we have it)
|
||||
if (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) {p += sprintf(p, ",%d", mm->vert_rate);}
|
||||
else {p += sprintf(p, ",");}
|
||||
|
||||
// Field 18 is the Squawk (if we have it)
|
||||
if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) {p += sprintf(p, ",%x", mm->modeA);}
|
||||
else {p += sprintf(p, ",");}
|
||||
|
||||
// Field 19 is the Squawk Changing Alert flag (if we have it)
|
||||
if (mm->bFlags & MODES_ACFLAGS_FS_VALID) {
|
||||
if ((mm->fs >= 2) && (mm->fs <= 4)) {
|
||||
p += sprintf(p, ",-1");
|
||||
} else {
|
||||
p += sprintf(p, ",0");
|
||||
}
|
||||
} else {
|
||||
p += sprintf(p, ",");
|
||||
}
|
||||
|
||||
// Field 20 is the Squawk Emergency flag (if we have it)
|
||||
if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) {
|
||||
if ((mm->modeA == 0x7500) || (mm->modeA == 0x7600) || (mm->modeA == 0x7700)) {
|
||||
p += sprintf(p, ",-1");
|
||||
} else {
|
||||
p += sprintf(p, ",0");
|
||||
}
|
||||
} else {
|
||||
p += sprintf(p, ",");
|
||||
}
|
||||
|
||||
// Field 21 is the Squawk Ident flag (if we have it)
|
||||
if (mm->bFlags & MODES_ACFLAGS_FS_VALID) {
|
||||
if ((mm->fs >= 4) && (mm->fs <= 5)) {
|
||||
p += sprintf(p, ",-1");
|
||||
} else {
|
||||
p += sprintf(p, ",0");
|
||||
}
|
||||
} else {
|
||||
p += sprintf(p, ",");
|
||||
}
|
||||
|
||||
// Field 22 is the OnTheGround flag (if we have it)
|
||||
if (mm->bFlags & MODES_ACFLAGS_AOG_VALID) {
|
||||
if (mm->bFlags & MODES_ACFLAGS_AOG) {
|
||||
p += sprintf(p, ",-1");
|
||||
} else {
|
||||
p += sprintf(p, ",0");
|
||||
}
|
||||
} else {
|
||||
p += sprintf(p, ",");
|
||||
}
|
||||
|
||||
p += sprintf(p, "\r\n");
|
||||
modesSendAllClients(Modes.sbsos, msg, p-msg);
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
void modesQueueOutput(struct modesMessage *mm) {
|
||||
if (Modes.stat_sbs_connections) {modesSendSBSOutput(mm);}
|
||||
if (Modes.stat_beast_connections) {modesSendBeastOutput(mm);}
|
||||
if (Modes.stat_raw_connections) {modesSendRawOutput(mm);}
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// This function decodes a Beast binary format message
|
||||
//
|
||||
// The message is passed to the higher level layers, so it feeds
|
||||
// the selected screen output, the network output and so forth.
|
||||
//
|
||||
// If the message looks invalid it is silently discarded.
|
||||
//
|
||||
// The function always returns 0 (success) to the caller as there is no
|
||||
// case where we want broken messages here to close the client connection.
|
||||
//
|
||||
int decodeBinMessage(struct client *c, char *p) {
|
||||
int msgLen = 0;
|
||||
unsigned char msg[MODES_LONG_MSG_BYTES];
|
||||
struct modesMessage mm;
|
||||
MODES_NOTUSED(c);
|
||||
memset(&mm, 0, sizeof(mm));
|
||||
|
||||
if ((*p == '1') && (Modes.mode_ac)) { // skip ModeA/C unless user enables --modes-ac
|
||||
msgLen = MODEAC_MSG_BYTES;
|
||||
} else if (*p == '2') {
|
||||
msgLen = MODES_SHORT_MSG_BYTES;
|
||||
} else if (*p == '3') {
|
||||
msgLen = MODES_LONG_MSG_BYTES;
|
||||
}
|
||||
|
||||
if (msgLen) {
|
||||
// Mark messages received over the internet as remote so that we don't try to
|
||||
// pass them off as being received by this instance when forwarding them
|
||||
mm.remote = 1;
|
||||
p += 7; // Skip the timestamp
|
||||
mm.signalLevel = *p++; // Grab the signal level
|
||||
memcpy(msg, p, msgLen); // and the data
|
||||
|
||||
if (msgLen == MODEAC_MSG_BYTES) { // ModeA or ModeC
|
||||
decodeModeAMessage(&mm, ((msg[0] << 8) | msg[1]));
|
||||
} else {
|
||||
decodeModesMessage(&mm, msg);
|
||||
}
|
||||
|
||||
useModesMessage(&mm);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// Turn an hex digit into its 4 bit decimal value.
|
||||
// Returns -1 if the digit is not in the 0-F range.
|
||||
//
|
||||
int hexDigitVal(int c) {
|
||||
c = tolower(c);
|
||||
if (c >= '0' && c <= '9') return c-'0';
|
||||
else if (c >= 'a' && c <= 'f') return c-'a'+10;
|
||||
else return -1;
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// This function decodes a string representing message in raw hex format
|
||||
// like: *8D4B969699155600E87406F5B69F; The string is null-terminated.
|
||||
//
|
||||
// The message is passed to the higher level layers, so it feeds
|
||||
// the selected screen output, the network output and so forth.
|
||||
//
|
||||
// If the message looks invalid it is silently discarded.
|
||||
//
|
||||
// The function always returns 0 (success) to the caller as there is no
|
||||
// case where we want broken messages here to close the client connection.
|
||||
//
|
||||
int decodeHexMessage(struct client *c, char *hex) {
|
||||
int l = strlen(hex), j;
|
||||
unsigned char msg[MODES_LONG_MSG_BYTES];
|
||||
struct modesMessage mm;
|
||||
MODES_NOTUSED(c);
|
||||
memset(&mm, 0, sizeof(mm));
|
||||
|
||||
// Mark messages received over the internet as remote so that we don't try to
|
||||
// pass them off as being received by this instance when forwarding them
|
||||
mm.remote = 1;
|
||||
mm.signalLevel = 0xFF;
|
||||
|
||||
// Remove spaces on the left and on the right
|
||||
while(l && isspace(hex[l-1])) {
|
||||
hex[l-1] = '\0'; l--;
|
||||
}
|
||||
while(isspace(*hex)) {
|
||||
hex++; l--;
|
||||
}
|
||||
|
||||
// Turn the message into binary.
|
||||
// Accept *-AVR raw @-AVR/BEAST timeS+raw %-AVR timeS+raw (CRC good) <-BEAST timeS+sigL+raw
|
||||
// and some AVR records that we can understand
|
||||
if (hex[l-1] != ';') {return (0);} // not complete - abort
|
||||
|
||||
switch(hex[0]) {
|
||||
case '<': {
|
||||
mm.signalLevel = (hexDigitVal(hex[13])<<4) | hexDigitVal(hex[14]);
|
||||
hex += 15; l -= 16; // Skip <, timestamp and siglevel, and ;
|
||||
break;}
|
||||
|
||||
case '@': // No CRC check
|
||||
case '%': { // CRC is OK
|
||||
hex += 13; l -= 14; // Skip @,%, and timestamp, and ;
|
||||
break;}
|
||||
|
||||
case '*':
|
||||
case ':': {
|
||||
hex++; l-=2; // Skip * and ;
|
||||
break;}
|
||||
|
||||
default: {
|
||||
return (0); // We don't know what this is, so abort
|
||||
break;}
|
||||
}
|
||||
|
||||
if ( (l != (MODEAC_MSG_BYTES * 2))
|
||||
&& (l != (MODES_SHORT_MSG_BYTES * 2))
|
||||
&& (l != (MODES_LONG_MSG_BYTES * 2)) )
|
||||
{return (0);} // Too short or long message... broken
|
||||
|
||||
if ( (0 == Modes.mode_ac)
|
||||
&& (l == (MODEAC_MSG_BYTES * 2)) )
|
||||
{return (0);} // Right length for ModeA/C, but not enabled
|
||||
|
||||
for (j = 0; j < l; j += 2) {
|
||||
int high = hexDigitVal(hex[j]);
|
||||
int low = hexDigitVal(hex[j+1]);
|
||||
|
||||
if (high == -1 || low == -1) return 0;
|
||||
msg[j/2] = (high << 4) | low;
|
||||
}
|
||||
|
||||
if (l == (MODEAC_MSG_BYTES * 2)) { // ModeA or ModeC
|
||||
decodeModeAMessage(&mm, ((msg[0] << 8) | msg[1]));
|
||||
} else { // Assume ModeS
|
||||
decodeModesMessage(&mm, msg);
|
||||
}
|
||||
|
||||
useModesMessage(&mm);
|
||||
return (0);
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// Return a description of planes in json. No metric conversion
|
||||
//
|
||||
char *aircraftsToJson(int *len) {
|
||||
time_t now = time(NULL);
|
||||
struct aircraft *a = Modes.aircrafts;
|
||||
int buflen = 1024; // The initial buffer is incremented as needed
|
||||
char *buf = (char *) malloc(buflen), *p = buf;
|
||||
int l;
|
||||
|
||||
l = snprintf(p,buflen,"[\n");
|
||||
p += l; buflen -= l;
|
||||
while(a) {
|
||||
int position = 0;
|
||||
int track = 0;
|
||||
|
||||
if (a->modeACflags & MODEAC_MSG_FLAG) { // skip any fudged ICAO records Mode A/C
|
||||
a = a->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (a->bFlags & MODES_ACFLAGS_LATLON_VALID) {
|
||||
position = 1;
|
||||
}
|
||||
|
||||
if (a->bFlags & MODES_ACFLAGS_HEADING_VALID) {
|
||||
track = 1;
|
||||
}
|
||||
|
||||
// No metric conversion
|
||||
l = snprintf(p,buflen,
|
||||
"{\"hex\":\"%06x\", \"squawk\":\"%04x\", \"flight\":\"%s\", \"lat\":%f, "
|
||||
"\"lon\":%f, \"validposition\":%d, \"altitude\":%d, \"track\":%d, \"validtrack\":%d,"
|
||||
"\"speed\":%d, \"messages\":%ld, \"seen\":%d},\n",
|
||||
a->addr, a->modeA, a->flight, a->lat, a->lon, position, a->altitude, a->track, track,
|
||||
a->speed, a->messages, (int)(now - a->seen));
|
||||
p += l; buflen -= l;
|
||||
|
||||
//Resize if needed
|
||||
if (buflen < 256) {
|
||||
int used = p-buf;
|
||||
buflen += 1024; // Our increment.
|
||||
buf = (char *) realloc(buf,used+buflen);
|
||||
p = buf+used;
|
||||
}
|
||||
|
||||
a = a->next;
|
||||
}
|
||||
|
||||
//Remove the final comma if any, and closes the json array.
|
||||
if (*(p-2) == ',') {
|
||||
*(p-2) = '\n';
|
||||
p--;
|
||||
buflen++;
|
||||
}
|
||||
|
||||
l = snprintf(p,buflen,"]\n");
|
||||
p += l; buflen -= l;
|
||||
|
||||
*len = p-buf;
|
||||
return buf;
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
#define MODES_CONTENT_TYPE_HTML "text/html;charset=utf-8"
|
||||
#define MODES_CONTENT_TYPE_CSS "text/css;charset=utf-8"
|
||||
#define MODES_CONTENT_TYPE_JSON "application/json;charset=utf-8"
|
||||
#define MODES_CONTENT_TYPE_JS "application/javascript;charset=utf-8"
|
||||
//
|
||||
// Get an HTTP request header and write the response to the client.
|
||||
// gain here we assume that the socket buffer is enough without doing
|
||||
// any kind of userspace buffering.
|
||||
//
|
||||
// Returns 1 on error to signal the caller the client connection should
|
||||
// be closed.
|
||||
//
|
||||
int handleHTTPRequest(struct client *c, char *p) {
|
||||
char hdr[512];
|
||||
int clen, hdrlen;
|
||||
int httpver, keepalive;
|
||||
char *url, *content;
|
||||
char ctype[48];
|
||||
char getFile[1024];
|
||||
char *ext;
|
||||
|
||||
if (Modes.debug & MODES_DEBUG_NET)
|
||||
printf("\nHTTP request: %s\n", c->buf);
|
||||
|
||||
// Minimally parse the request.
|
||||
httpver = (strstr(p, "HTTP/1.1") != NULL) ? 11 : 10;
|
||||
if (httpver == 10) {
|
||||
// HTTP 1.0 defaults to close, unless otherwise specified.
|
||||
keepalive = strstr(p, "Connection: keep-alive") != NULL;
|
||||
} else if (httpver == 11) {
|
||||
// HTTP 1.1 defaults to keep-alive, unless close is specified.
|
||||
keepalive = strstr(p, "Connection: close") == NULL;
|
||||
}
|
||||
|
||||
// Identify he URL.
|
||||
p = strchr(p,' ');
|
||||
if (!p) return 1; // There should be the method and a space
|
||||
url = ++p; // Now this should point to the requested URL
|
||||
p = strchr(p, ' ');
|
||||
if (!p) return 1; // There should be a space before HTTP/
|
||||
*p = '\0';
|
||||
|
||||
if (Modes.debug & MODES_DEBUG_NET) {
|
||||
printf("\nHTTP keep alive: %d\n", keepalive);
|
||||
printf("HTTP requested URL: %s\n\n", url);
|
||||
}
|
||||
|
||||
if (strlen(url) < 2) {
|
||||
snprintf(getFile, sizeof getFile, "%s/gmap.html", HTMLPATH); // Default file
|
||||
} else {
|
||||
snprintf(getFile, sizeof getFile, "%s/%s", HTMLPATH, url);
|
||||
}
|
||||
|
||||
// Select the content to send, we have just two so far:
|
||||
// "/" -> Our google map application.
|
||||
// "/data.json" -> Our ajax request to update planes.
|
||||
if (strstr(url, "/data.json")) {
|
||||
content = aircraftsToJson(&clen);
|
||||
//snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JSON);
|
||||
} else {
|
||||
struct stat sbuf;
|
||||
int fd = -1;
|
||||
|
||||
if (stat(getFile, &sbuf) != -1 && (fd = open(getFile, O_RDONLY)) != -1) {
|
||||
content = (char *) malloc(sbuf.st_size);
|
||||
if (read(fd, content, sbuf.st_size) == -1) {
|
||||
snprintf(content, sbuf.st_size, "Error reading from file: %s", strerror(errno));
|
||||
}
|
||||
clen = sbuf.st_size;
|
||||
} else {
|
||||
char buf[128];
|
||||
clen = snprintf(buf,sizeof(buf),"Error opening HTML file: %s", strerror(errno));
|
||||
content = strdup(buf);
|
||||
}
|
||||
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
// Get file extension and content type
|
||||
snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_HTML); // Default content type
|
||||
ext = strrchr(getFile, '.');
|
||||
|
||||
if (strlen(ext) > 0) {
|
||||
if (strstr(ext, ".json")) {
|
||||
snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JSON);
|
||||
} else if (strstr(ext, ".css")) {
|
||||
snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_CSS);
|
||||
} else if (strstr(ext, ".js")) {
|
||||
snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JS);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the header and send the reply
|
||||
hdrlen = snprintf(hdr, sizeof(hdr),
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Server: Dump1090\r\n"
|
||||
"Content-Type: %s\r\n"
|
||||
"Connection: %s\r\n"
|
||||
"Content-Length: %d\r\n"
|
||||
"\r\n",
|
||||
ctype,
|
||||
keepalive ? "keep-alive" : "close",
|
||||
clen);
|
||||
|
||||
if (Modes.debug & MODES_DEBUG_NET) {
|
||||
printf("HTTP Reply header:\n%s", hdr);
|
||||
}
|
||||
|
||||
// Send header and content.
|
||||
if (write(c->fd, hdr, hdrlen) == -1 || write(c->fd, content, clen) == -1) {
|
||||
free(content);
|
||||
return 1;
|
||||
}
|
||||
free(content);
|
||||
Modes.stat_http_requests++;
|
||||
return !keepalive;
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// This function polls the clients using read() in order to receive new
|
||||
// messages from the net.
|
||||
//
|
||||
// The message is supposed to be separated from the next message by the
|
||||
// separator 'sep', which is a null-terminated C string.
|
||||
//
|
||||
// Every full message received is decoded and passed to the higher layers
|
||||
// calling the function's 'handler'.
|
||||
//
|
||||
// The handler returns 0 on success, or 1 to signal this function we should
|
||||
// close the connection with the client in case of non-recoverable errors.
|
||||
//
|
||||
void modesReadFromClient(struct client *c, char *sep,
|
||||
int(*handler)(struct client *, char *)) {
|
||||
int left;
|
||||
int nread;
|
||||
int fullmsg;
|
||||
char *s, *e;
|
||||
|
||||
while(1) {
|
||||
|
||||
fullmsg = 0;
|
||||
left = MODES_CLIENT_BUF_SIZE - c->buflen;
|
||||
// If our buffer is full discard it, this is some badly formatted shit
|
||||
if (left == 0) {
|
||||
c->buflen = 0;
|
||||
left = MODES_CLIENT_BUF_SIZE;
|
||||
// If there is garbage, read more to discard it ASAP
|
||||
}
|
||||
nread = read(c->fd, c->buf+c->buflen, left);
|
||||
|
||||
if (nread <= 0) {
|
||||
if (nread == 0 || errno != EAGAIN) { // Error, or end of file
|
||||
modesFreeClient(c->fd);
|
||||
}
|
||||
break; // Serve next client
|
||||
}
|
||||
c->buflen += nread;
|
||||
|
||||
// Always null-term so we are free to use strstr() (it won't affect binary case)
|
||||
c->buf[c->buflen] = '\0';
|
||||
|
||||
e = s = c->buf; // Start with the start of buffer, first message
|
||||
|
||||
if (c->service == Modes.bis) {
|
||||
// This is the Bease Binary scanning case.
|
||||
// If there is a complete message still in the buffer, there must be the separator 'sep'
|
||||
// in the buffer, note that we full-scan the buffer at every read for simplicity.
|
||||
|
||||
left = c->buflen; // Length of valid search for memchr()
|
||||
while (left && ((s = memchr(e, (char) 0x1a, left)) != NULL)) { // In reality the first byte of buffer 'should' be 0x1a
|
||||
s++; // skip the 0x1a
|
||||
if (*s == '1') {
|
||||
e = s + MODEAC_MSG_BYTES + 8; // point past remainder of message
|
||||
} else if (*s == '2') {
|
||||
e = s + MODES_SHORT_MSG_BYTES + 8;
|
||||
} else if (*s == '3') {
|
||||
e = s + MODES_LONG_MSG_BYTES + 8;
|
||||
} else {
|
||||
e = s; // Not a valid beast message, skip
|
||||
left = &(c->buf[c->buflen]) - e;
|
||||
continue;
|
||||
}
|
||||
left = &(c->buf[c->buflen]) - e;
|
||||
if (left < 0) { // Incomplete message in buffer
|
||||
e = s - 1; // point back at last found 0x1a.
|
||||
break;
|
||||
}
|
||||
// Have a 0x1a followed by 1, 2 or 3 - pass message less 0x1a to handler.
|
||||
if (handler(c, s)) {
|
||||
modesFreeClient(c->fd);
|
||||
return;
|
||||
}
|
||||
fullmsg = 1;
|
||||
}
|
||||
s = e; // For the buffer remainder below
|
||||
|
||||
} else {
|
||||
// This is the ASCII scanning case, AVR RAW or HTTP at present
|
||||
// If there is a complete message still in the buffer, there must be the separator 'sep'
|
||||
// in the buffer, note that we full-scan the buffer at every read for simplicity.
|
||||
|
||||
while ((e = strstr(s, sep)) != NULL) { // end of first message if found
|
||||
*e = '\0'; // The handler expects null terminated strings
|
||||
if (handler(c, s)) { // Pass message to handler.
|
||||
modesFreeClient(c->fd); // Handler returns 1 on error to signal we .
|
||||
return; // should close the client connection
|
||||
}
|
||||
s = e + strlen(sep); // Move to start of next message
|
||||
fullmsg = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (fullmsg) { // We processed something - so
|
||||
c->buflen = &(c->buf[c->buflen]) - s; // The unprocessed buffer length
|
||||
memmove(c->buf, s, c->buflen); // move what's remaining to the start of the buffer
|
||||
} else { // If no message was decoded process the next client
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// Read data from clients. This function actually delegates a lower-level
|
||||
// function that depends on the kind of service (raw, http, ...).
|
||||
//
|
||||
void modesReadFromClients(void) {
|
||||
int j;
|
||||
struct client *c;
|
||||
|
||||
modesAcceptClients();
|
||||
|
||||
for (j = 0; j <= Modes.maxfd; j++) {
|
||||
if ((c = Modes.clients[j]) == NULL) continue;
|
||||
if (c->service == Modes.ris)
|
||||
modesReadFromClient(c,"\n",decodeHexMessage);
|
||||
else if (c->service == Modes.bis)
|
||||
modesReadFromClient(c,"",decodeBinMessage);
|
||||
else if (c->service == Modes.https)
|
||||
modesReadFromClient(c,"\r\n\r\n",handleHTTPRequest);
|
||||
}
|
||||
}
|
||||
//
|
||||
// =============================== Network IO ===========================
|
||||
//
|
Loading…
Reference in a new issue