Add direct support for FATSV-format output.

This is adapted from the FlightAware fork, with some cleanup and
modifications needed to work with the net-cleanup changes.

Inclusion of "verbatim" TSV data read from an AVR-format input
connection is not supported.
This commit is contained in:
Oliver Jowett 2014-12-08 19:56:45 +00:00
parent 66849e1096
commit 2dcc8e3524
6 changed files with 173 additions and 3 deletions

2
debian/changelog vendored
View file

@ -1,6 +1,8 @@
dump1090-mutability (1.08.2302.14+1mu-2) UNRELEASED; urgency=medium
* Fix a memory leak from use of realpath() in HTTP request processing.
* Add direct support for FATSV-format output, adapted from the FlightAware
fork.
-- Oliver Jowett <oliver@mutability.co.uk> Sun, 07 Dec 2014 14:14:25 +0000

View file

@ -81,6 +81,9 @@ DUMP1090_USER="dump1090"
# Port to listen on for Beast-format output connections. 0 disables.
#BEAST_OUTPUT_PORT=30005
# Port to listen on for FATSV-format output connections. 0 disables.
#FATSV_OUTPUT_PORT=10001
# TCP heartbeat interval in seconds. 0 disables.
#NET_HEARTBEAT=60

View file

@ -44,6 +44,7 @@ RAW_OUTPUT_PORT=30002
SBS_OUTPUT_PORT=30003
BEAST_INPUT_PORT=30004
BEAST_OUTPUT_PORT=30005
FATSV_OUTPUT_PORT=10001
NET_HEARTBEAT=60
NET_OUTPUT_SIZE=500
NET_OUTPUT_INTERVAL=1
@ -87,6 +88,7 @@ if [ "x$RAW_OUTPUT_PORT" != "x30002" ]; then ARGS="$ARGS --net-ro-port $RAW_OUTP
if [ "x$SBS_OUTPUT_PORT" != "x30003" ]; then ARGS="$ARGS --net-sbs-port $SBS_OUTPUT_PORT"; fi
if [ "x$BEAST_INPUT_PORT" != "x30004" ]; then ARGS="$ARGS --net-bi-port $BEAST_INPUT_PORT"; fi
if [ "x$BEAST_OUTPUT_PORT" != "x30005" ]; then ARGS="$ARGS --net-bo-port $BEAST_OUTPUT_PORT"; fi
if [ "x$FATSV_OUTPUT_PORT" != "x10001" ]; then ARGS="$ARGS --net-fatsv-port $FATSV_OUTPUT_PORT"; fi
if [ "x$NET_HEARTBEAT" != "x60" ]; then ARGS="$ARGS --net-heartbeat $NET_HEARTBEAT"; fi
if [ "x$NET_OUTPUT_SIZE" != "x0" ]; then ARGS="$ARGS --net-ro-size $NET_OUTPUT_SIZE"; fi
if [ "x$NET_OUTPUT_INTERVAL" != "x0" ]; then ARGS="$ARGS --net-ro-interval $NET_OUTPUT_INTERVAL"; fi

View file

@ -77,6 +77,7 @@ void modesInitConfig(void) {
Modes.net_output_beast_port = MODES_NET_OUTPUT_BEAST_PORT;
Modes.net_input_beast_port = MODES_NET_INPUT_BEAST_PORT;
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;
@ -424,6 +425,7 @@ void showHelp(void) {
"--net-sbs-port <port> TCP BaseStation output listen port (default: 30003)\n"
"--net-bi-port <port> TCP Beast input listen port (default: 30004)\n"
"--net-bo-port <port> TCP Beast output listen port (default: 30005)\n"
"--net-fatsv-port <port> FlightAware TSV output port (default: 10001)\n"
"--net-ro-size <size> TCP output minimum size (default: 0)\n"
"--net-ro-interval <rate> TCP output memory flush rate in seconds (default: 0)\n"
"--net-heartbeat <rate> TCP heartbeat rate in seconds (default: 60 sec; 0 to disable)\n"
@ -767,6 +769,8 @@ int main(int argc, char **argv) {
Modes.net_bind_address = strdup(argv[++j]);
} else if (!strcmp(argv[j],"--net-http-port") && more) {
Modes.net_http_port = atoi(argv[++j]);
} else if (!strcmp(argv[j],"--net-fatsv-port") && more) {
Modes.net_fatsv_port = atoi(argv[++j]);
} else if (!strcmp(argv[j],"--net-sbs-port") && more) {
Modes.net_output_sbs_port = atoi(argv[++j]);
} else if (!strcmp(argv[j],"--net-buffer") && more) {

View file

@ -179,13 +179,14 @@
#define MODES_NET_HEARTBEAT_INTERVAL 60 // seconds
#define MODES_NET_SERVICES_NUM 6
#define MODES_NET_SERVICES_NUM 7
#define MODES_NET_INPUT_RAW_PORT 30001
#define MODES_NET_OUTPUT_RAW_PORT 30002
#define MODES_NET_OUTPUT_SBS_PORT 30003
#define MODES_NET_INPUT_BEAST_PORT 30004
#define MODES_NET_OUTPUT_BEAST_PORT 30005
#define MODES_NET_HTTP_PORT 8080
#define MODES_NET_OUTPUT_FA_TSV_PORT 10001
#define MODES_CLIENT_BUF_SIZE 1024
#define MODES_NET_SNDBUF_SIZE (1024*64)
#define MODES_NET_SNDBUF_MAX (7)
@ -227,6 +228,10 @@ struct aircraft {
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;
@ -317,6 +322,7 @@ struct { // Internal state
struct net_writer raw_out; // Raw output
struct net_writer beast_out; // Beast-format output
struct net_writer sbs_out; // SBS-format output
struct net_writer fatsv_out; // FATSV-format output
#ifdef _WIN32
WSADATA wsaData; // Windows socket initialisation
@ -344,6 +350,7 @@ struct { // Internal state
int net_input_beast_port; // Beast input TCP port
char *net_bind_address; // Bind address
int net_http_port; // HTTP port
int net_fatsv_port; // FlightAware TSV port
int net_sndbuf_size; // TCP output buffer size (64Kb * 2^n)
int quiet; // Suppress stdout
int interactive; // Interactive mode

156
net_io.c
View file

@ -65,7 +65,8 @@ void modesInitNet(void) {
{"Beast TCP output", &Modes.beast_out.socket, &Modes.beast_out, Modes.net_output_beast_port, 1},
{"Beast TCP input", &Modes.bis, NULL, Modes.net_input_beast_port, 1},
{"HTTP server", &Modes.https, NULL, Modes.net_http_port, 1},
{"Basestation TCP output", &Modes.sbs_out.socket, &Modes.sbs_out, Modes.net_output_sbs_port, 1}
{"Basestation TCP output", &Modes.sbs_out.socket, &Modes.sbs_out, Modes.net_output_sbs_port, 1},
{"FlightAware TSV output", &Modes.fatsv_out.socket, &Modes.fatsv_out, Modes.net_fatsv_port, 1}
};
memcpy(&services, &svc, sizeof(svc));//services = svc;
@ -217,7 +218,7 @@ static void *prepareWrite(struct net_writer *writer, int len) {
!writer->data)
return NULL;
if (len >= MODES_OUT_BUF_SIZE)
if (len > MODES_OUT_BUF_SIZE)
return NULL;
if (writer->dataUsed + len >= MODES_OUT_BUF_SIZE) {
@ -1053,6 +1054,154 @@ void modesReadFromClient(struct client *c, char *sep,
}
}
#define TSV_MAX_PACKET_SIZE 160
static void writeFATSV() {
struct aircraft *a;
time_t now;
static time_t lastTime = 0;
if (!Modes.fatsv_out.connections) {
return; // no active connections
}
now = time(NULL);
if (now <= lastTime) {
// scan once a second at most
return;
}
lastTime = now;
for (a = Modes.aircrafts; a; a = a->next) {
int altValid = 0;
int alt = 0;
int groundValid = 0;
int ground = 0;
int latlonValid = 0;
int useful = 0;
int emittedSecondsAgo;
char *p;
// don't emit if it hasn't updated since last time
if (a->seen < a->fatsv_last_emitted) {
continue;
}
emittedSecondsAgo = (int)(now - a->fatsv_last_emitted);
// don't emit more than once every five seconds
if (emittedSecondsAgo < 5) {
continue;
}
if (a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) {
altValid = 1;
alt = a->altitude;
}
if (a->bFlags & MODES_ACFLAGS_AOG_VALID) {
groundValid = 1;
if (a->bFlags & MODES_ACFLAGS_AOG) {
alt = 0;
ground = 1;
}
}
if (a->bFlags & MODES_ACFLAGS_LATLON_VALID) {
latlonValid = 1;
}
// if it's over 10,000 feet, don't emit more than once every 10 seconds
if (alt > 10000 && emittedSecondsAgo < 10) {
continue;
}
// disable if you want only ads-b
// also don't send mode S very often
if (!latlonValid) {
if (emittedSecondsAgo < 30) {
continue;
}
} else {
// if it hasn't changed altitude very much and it hasn't changed
// heading very much, don't update real often
if (abs(a->track - a->fatsv_emitted_track) < 2 && abs(alt - a->fatsv_emitted_altitude) < 50) {
if (alt < 10000) {
// it hasn't changed much but we're below 10,000 feet
// so update more frequently
if (emittedSecondsAgo < 10) {
continue;
}
} else {
// above 10,000 feet, don't update so often when it
// hasn't changed much
if (emittedSecondsAgo < 30) {
continue;
}
}
}
}
p = prepareWrite(&Modes.fatsv_out, MODES_OUT_BUF_SIZE);
if (!p)
return;
p += sprintf(p, "clock\t%ld\thexid\t%06X", a->seen, a->addr);
if (*a->flight != '\0') {
p += sprintf(p, "\tident\t%s", a->flight);
}
if (a->bFlags & MODES_ACFLAGS_SQUAWK_VALID) {
p += sprintf(p, "\tsquawk\t%04x", a->modeA);
}
if (altValid) {
p += sprintf(p, "\talt\t%d", alt);
useful = 1;
}
if (a->bFlags & MODES_ACFLAGS_SPEED_VALID) {
p += sprintf(p, "\tspeed\t%d", a->speed);
useful = 1;
}
if (groundValid) {
if (ground) {
p += sprintf(p, "\tairGround\tG");
} else {
p += sprintf(p, "\tairGround\tA");
}
}
if (latlonValid) {
p += sprintf(p, "\tlat\t%.5f\tlon\t%.5f", a->lat, a->lon);
useful = 1;
}
if (a->bFlags & MODES_ACFLAGS_HEADING_VALID) {
p += sprintf(p, "\theading\t%d", a->track);
useful = 1;
}
// if we didn't get at least an alt or a speed or a latlon or
// a heading, bail out. We don't need to do anything special
// to unwind prepareWrite().
if (!useful) {
continue;
}
p += sprintf(p, "\n");
completeWrite(&Modes.fatsv_out, p);
a->fatsv_last_emitted = now;
a->fatsv_emitted_altitude = alt;
a->fatsv_emitted_track = a->track;
}
}
//
// Perform periodic network work
//
@ -1076,6 +1225,9 @@ void modesNetPeriodicWork(void) {
}
}
// Generate FATSV output
writeFATSV();
// If we have generated no messages for a while, generate
// a dummy heartbeat message.
if (Modes.net_heartbeat_interval) {