diff --git a/debian/changelog b/debian/changelog index 0ceed80..e8ecf27 100644 --- a/debian/changelog +++ b/debian/changelog @@ -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 Sun, 07 Dec 2014 14:14:25 +0000 diff --git a/debian/dump1090-mutability.default b/debian/dump1090-mutability.default index 1017adb..d14386b 100644 --- a/debian/dump1090-mutability.default +++ b/debian/dump1090-mutability.default @@ -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 diff --git a/debian/dump1090-mutability.init b/debian/dump1090-mutability.init index e6e8e94..2afeb55 100644 --- a/debian/dump1090-mutability.init +++ b/debian/dump1090-mutability.init @@ -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 diff --git a/dump1090.c b/dump1090.c index d868f6d..7c06e0e 100644 --- a/dump1090.c +++ b/dump1090.c @@ -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 TCP BaseStation output listen port (default: 30003)\n" "--net-bi-port TCP Beast input listen port (default: 30004)\n" "--net-bo-port TCP Beast output listen port (default: 30005)\n" +"--net-fatsv-port FlightAware TSV output port (default: 10001)\n" "--net-ro-size TCP output minimum size (default: 0)\n" "--net-ro-interval TCP output memory flush rate in seconds (default: 0)\n" "--net-heartbeat 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) { diff --git a/dump1090.h b/dump1090.h index 2dfd491..f7ee3ec 100644 --- a/dump1090.h +++ b/dump1090.h @@ -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 diff --git a/net_io.c b/net_io.c index b27db28..1d4df73 100644 --- a/net_io.c +++ b/net_io.c @@ -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) {