From b6f007a104be2015e2cd075483e17c3e9ca655c1 Mon Sep 17 00:00:00 2001 From: Determinant Date: Mon, 23 Mar 2020 18:53:24 -0400 Subject: [PATCH 01/14] WIP: support stratux protocol --- dump1090.c | 5 ++ dump1090.h | 4 +- net_io.c | 227 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 235 insertions(+), 1 deletion(-) diff --git a/dump1090.c b/dump1090.c index 2506e8c..356c1f5 100644 --- a/dump1090.c +++ b/dump1090.c @@ -116,6 +116,7 @@ void modesInitConfig(void) { Modes.net_output_sbs_ports = strdup("30003"); Modes.net_input_beast_ports = strdup("30004,30104"); Modes.net_output_beast_ports = strdup("30005"); + Modes.net_output_stratux_ports= strdup("30006"); Modes.interactive_display_ttl = MODES_INTERACTIVE_DISPLAY_TTL; Modes.json_interval = 1000; Modes.json_location_accuracy = 1; @@ -299,6 +300,7 @@ void showHelp(void) { "--net-sbs-port TCP BaseStation output listen ports (default: 30003)\n" "--net-bi-port TCP Beast input listen ports (default: 30004,30104)\n" "--net-bo-port TCP Beast output listen ports (default: 30005)\n" +"--net-stratux-port TCP Stratux output listen ports (default: 30006)\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" @@ -532,6 +534,9 @@ int main(int argc, char **argv) { } else if (!strcmp(argv[j],"--net-sbs-port") && more) { free(Modes.net_output_sbs_ports); Modes.net_output_sbs_ports = strdup(argv[++j]); + } else if (!strcmp(argv[j],"--net-stratux-port") && more) { + free(Modes.net_output_stratux_ports); + Modes.net_output_stratux_ports = strdup(argv[++j]); } else if (!strcmp(argv[j],"--net-buffer") && more) { Modes.net_sndbuf_size = atoi(argv[++j]); } else if (!strcmp(argv[j],"--net-verbatim")) { diff --git a/dump1090.h b/dump1090.h index 5c7cf73..ec45d8e 100644 --- a/dump1090.h +++ b/dump1090.h @@ -52,7 +52,7 @@ // Default version number, if not overriden by the Makefile #ifndef MODES_DUMP1090_VERSION -# define MODES_DUMP1090_VERSION "v1.13-custom" +# define MODES_DUMP1090_VERSION "v1.13-stratux" #endif #ifndef MODES_DUMP1090_VARIANT @@ -332,6 +332,7 @@ struct { // Internal state struct net_writer beast_verbatim_out; // Beast-format output, verbatim mode struct net_writer beast_cooked_out; // Beast-format output, "cooked" mode struct net_writer sbs_out; // SBS-format output + struct net_writer stratux_out; // Stratux-format output struct net_writer fatsv_out; // FATSV-format output #ifdef _WIN32 @@ -354,6 +355,7 @@ struct { // Internal state char *net_output_raw_ports; // List of raw output TCP ports char *net_input_raw_ports; // List of raw input TCP ports char *net_output_sbs_ports; // List of SBS output TCP ports + char *net_output_stratux_ports; // List of Stratux output TCP ports char *net_input_beast_ports; // List of Beast input TCP ports char *net_output_beast_ports; // List of Beast output TCP ports char *net_bind_address; // Bind address diff --git a/net_io.c b/net_io.c index 6375f01..6c72595 100644 --- a/net_io.c +++ b/net_io.c @@ -77,6 +77,7 @@ static void moveNetClient(struct client *c, struct net_service *new_service); static void send_raw_heartbeat(struct net_service *service); static void send_beast_heartbeat(struct net_service *service); static void send_sbs_heartbeat(struct net_service *service); +static void send_stratux_heartbeat(struct net_service *service); static void writeBeastMessage(struct net_writer *writer, uint64_t timestamp, double signalLevel, unsigned char *msg, int msgLen); @@ -268,6 +269,9 @@ void modesInitNet(void) { s = serviceInit("Basestation TCP output", &Modes.sbs_out, send_sbs_heartbeat, READ_MODE_IGNORE, NULL, NULL); serviceListen(s, Modes.net_bind_address, Modes.net_output_sbs_ports); + s = serviceInit("Stratux TCP output", &Modes.stratux_out, send_stratux_heartbeat, READ_MODE_IGNORE, NULL, NULL); + serviceListen(s, Modes.net_bind_address, Modes.net_output_stratux_ports); + s = serviceInit("Raw TCP input", NULL, NULL, READ_MODE_ASCII, "\n", decodeHexMessage); serviceListen(s, Modes.net_bind_address, Modes.net_input_raw_ports); @@ -776,6 +780,228 @@ static void send_sbs_heartbeat(struct net_service *service) completeWrite(service->writer, data + len); } +////////////////////////////////////////// Start Stratux block + +// +//========================================================================= +// +// Write Stratux output to TCP clients +// The message structure mm->bFlags tells us what has been updated by this message +// +// Output format is a JSON representation of a subset of the fields used in the +// Stratux traffic structure. +// +static void modesSendStratuxOutput(struct modesMessage *mm, struct aircraft *a) { + char *p; + struct timespec now; + struct tm stTime_receive, stTime_now; + int msgType; + + // Send all addresses, since we want to see TIS-B data. + //if (mm->addr & MODES_NON_ICAO_ADDRESS) + // return; + + p = prepareWrite(&Modes.stratux_out, 1000); // larger buffer size needed vs SBS + if (!p) + return; + + + // 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->cpr_decoded) + {msgType = 2;} + else + {msgType = 7;} + } else if ((mm->metype >= 9) && (mm->metype <= 18)) { + if (mm->cpr_decoded) + {msgType = 3;} + else + {msgType = 7;} + } else if ((mm->metype == 29) || (mm->metype == 31)) { + msgType = 9; // new message type for target state and operational status messages + } else if (mm->metype != 19) { + return; + } else if ((mm->mesub == 1) || (mm->mesub == 2)) { + msgType = 4; + } else { + return; + } + + // Begin populating the traffic.go fields. + // ICAO address, Mode S message types, and signal level + + int cacf = 0; // overload the JSON "CA" field to report CA (DF11 or DF17), CF (DF18), or zero (all other DF types) + if ((mm->msgtype == 11) || (mm->msgtype == 17)) { + cacf = mm->CA; + } else if (mm->msgtype == 18) { + cacf = mm->CF; + } + + p += sprintf(p, "{\"Icao_addr\":%d,\"DF\":%d,\"CA\":%d,\"TypeCode\":%d,\"SubtypeCode\":%d,\"SBS_MsgType\":%d,\"SignalLevel\":%f,",mm->addr, mm->msgtype, cacf, mm->metype, mm->mesub, msgType, mm->signalLevel); // what precision and range is needed for RSSI? + + // Callsign + if (mm->callsign_valid) { + p += sprintf(p, "\"Tail\":\"%s\",", mm->callsign); + } else { + p += sprintf(p, "\"Tail\":null,"); + } + + //Squawk + if (mm->squawk_valid) { + p += sprintf(p, "\"Squawk\":%x,", mm->squawk); + } + else { + p += sprintf(p, "\"Squawk\":null,"); + } + + // Emitter type + int emitter = 0; + int setEmitter = 0; + if ((mm->msgtype == 17) || (mm->msgtype = 18)) { + switch (mm->metype) { + case 1: + emitter = ((mm->mesub) | 0x18); + setEmitter = 1; + break; + case 2: + emitter = ((mm->mesub) | 0x10); + setEmitter = 1; + break; + case 3: + emitter = ((mm->mesub) | 0x08); + setEmitter = 1; + break; + case 4: + emitter = (mm->mesub); + setEmitter = 1; + break; + } + } + + if (setEmitter) { + p += sprintf(p, "\"Emitter_category\":%d,", emitter); + } else { + p += sprintf(p, "\"Emitter_category\":null,"); + } + + // OnGround + if (mm->airground == AG_GROUND) { + p += sprintf(p, "\"OnGround\":true,"); + } else if (mm->airground == AG_AIRBORNE) { + p += sprintf(p, "\"OnGround\":false,"); + } else { + p += sprintf(p, "\"OnGround\":null,"); + } + + // Position and position valid flag + if (mm->cpr_decoded) { + p += sprintf(p, "\"Lat\":%.6f,\"Lng\":%.6f,\"Position_valid\":true,", mm->decoded_lat, mm->decoded_lon); + } else { + p += sprintf(p, "\"Lat\":null,\"Lng\":null,\"Position_valid\":false,"); + } + + // Navigation Accuracy Category - Position + if (mm->accuracy.nac_p_valid) { + p += sprintf(p, "\"NACp\":%d,", mm->accuracy.nac_p); + } else { + p += sprintf(p, "\"NACp\":null,"); + } + + bool alt_is_geom = false; + // Altitude + if (Modes.use_gnss && mm->altitude_geom_valid) { + p += sprintf(p, "\"Alt\":%d,",mm->altitude_geom); + alt_is_geom = true; + } else if (mm->altitude_baro_valid) { + p += sprintf(p, "\"Alt\":%d,",mm->altitude_baro); + } else { + p += sprintf(p, "\"Alt\":null,"); + } + + // Altitude Source. True if altitude source is GNSS, false if pressure altitude. + if (alt_is_geom) { + p += sprintf(p, "\"AltIsGNSS\":true,"); + } else { + p += sprintf(p, "\"AltIsGNSS\":false,"); + } + // GNSS Alt Diff From Baro Alt + if (trackDataValid(&a->geom_delta_valid)) { + p += sprintf(p, "\"GnssDiffFromBaroAlt\":%d,",a->geom_delta); // Verify that this is part of a and not mm + } else { + p += sprintf(p, "\"GnssDiffFromBaroAlt\":null,"); + } + + //Vertical velocity + if (alt_is_geom) { + if (mm->geom_rate_valid) + p += sprintf(p, "\"Vvel\":%d,", mm->geom_rate); + else + p += sprintf(p, "\"Vvel\":null,"); + } else { + if (mm->baro_rate_valid) + p += sprintf(p, "\"Vvel\":%d,", mm->baro_rate); + else + p += sprintf(p, "\"Vvel\":null,"); + } + + // Ground speed and track + if (mm->gs_valid) { + p += sprintf(p, "\"Speed_valid\":true,\"Speed\":%.0f,", mm->gs.selected); + } else { + p += sprintf(p, "\"Speed_valid\":false,\"Speed\":null,"); + } + + if (mm->heading_valid && mm->heading_type == HEADING_GROUND_TRACK) { + p += sprintf(p, "\"Track\":%.0f,", mm->heading); + } else { + p += sprintf(p, "\"Track\":null,"); + } + + // Find current system time + clock_gettime(CLOCK_REALTIME, &now); + gmtime_r(&now.tv_sec, &stTime_now); // we expect UTC + + // Find message reception time + time_t received = (time_t) (mm->sysTimestampMsg / 1000); + localtime_r(&received, &stTime_receive); + + //Time message received (based on system clock). Format is 2016-02-20T06:35:43.155Z + p += sprintf(p, "\"Timestamp\":\"%04d-%02d-%02dT%02d:%02d:%02d.%03dZ\"", (stTime_receive.tm_year+1900),(stTime_receive.tm_mon+1), stTime_receive.tm_mday, stTime_receive.tm_hour, stTime_receive.tm_min, stTime_receive.tm_sec, (unsigned) (mm->sysTimestampMsg % 1000)); + + p += sprintf(p, "}\r\n"); + + completeWrite(&Modes.stratux_out, p); +} + +static void send_stratux_heartbeat(struct net_service *service) +{ + static char *heartbeat_message = "{\"Icao_addr\":134217727}\r\n"; // 0x07FFFFFF. Overflows 24-bit ICAO to signal invalic #, need to validate that this won't cause problems with traffic.go + char *data; + int len = strlen(heartbeat_message); + + if (!service->writer) + return; + + data = prepareWrite(service->writer, len); + if (!data) + return; + + memcpy(data, heartbeat_message, len); + completeWrite(service->writer, data + len); +} + // //========================================================================= // @@ -783,6 +1009,7 @@ void modesQueueOutput(struct modesMessage *mm, struct aircraft *a) { // Delegate to the format-specific outputs, each of which makes its own decision about filtering messages modesSendSBSOutput(mm, a); + modesSendStratuxOutput(mm, a); modesSendRawOutput(mm, a); modesSendBeastVerbatimOutput(mm, a); modesSendBeastCookedOutput(mm, a); From 52338202d1d0783a23cba9d5313cd8c077d1d430 Mon Sep 17 00:00:00 2001 From: Determinant Date: Tue, 24 Mar 2020 14:08:44 -0400 Subject: [PATCH 02/14] ... --- net_io.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/net_io.c b/net_io.c index 6c72595..73ecff8 100644 --- a/net_io.c +++ b/net_io.c @@ -797,9 +797,21 @@ static void modesSendStratuxOutput(struct modesMessage *mm, struct aircraft *a) struct tm stTime_receive, stTime_now; int msgType; - // Send all addresses, since we want to see TIS-B data. - //if (mm->addr & MODES_NON_ICAO_ADDRESS) - // return; + // We require a tracked aircraft for SBS output + if (!a) + return; + + // Don't ever forward 2-bit-corrected messages via SBS output. + if (mm->correctedbits >= 2) + return; + + // Don't ever forward mlat messages via SBS output. + if (mm->source == SOURCE_MLAT) + return; + + // Don't ever send unreliable messages via SBS output + if (!mm->reliable && !a->reliable) + return; p = prepareWrite(&Modes.stratux_out, 1000); // larger buffer size needed vs SBS if (!p) @@ -869,7 +881,7 @@ static void modesSendStratuxOutput(struct modesMessage *mm, struct aircraft *a) // Emitter type int emitter = 0; int setEmitter = 0; - if ((mm->msgtype == 17) || (mm->msgtype = 18)) { + if ((mm->msgtype == 17) || (mm->msgtype == 18)) { switch (mm->metype) { case 1: emitter = ((mm->mesub) | 0x18); From 2f9c3fe0e180ddd2286c224331d939e790d9895a Mon Sep 17 00:00:00 2001 From: Determinant Date: Sat, 28 Mar 2020 15:23:30 -0400 Subject: [PATCH 03/14] clean up --- net_io.c | 283 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 150 insertions(+), 133 deletions(-) diff --git a/net_io.c b/net_io.c index 73ecff8..d4ca0ff 100644 --- a/net_io.c +++ b/net_io.c @@ -780,16 +780,10 @@ static void send_sbs_heartbeat(struct net_service *service) completeWrite(service->writer, data + len); } -////////////////////////////////////////// Start Stratux block - // //========================================================================= // // Write Stratux output to TCP clients -// The message structure mm->bFlags tells us what has been updated by this message -// -// Output format is a JSON representation of a subset of the fields used in the -// Stratux traffic structure. // static void modesSendStratuxOutput(struct modesMessage *mm, struct aircraft *a) { char *p; @@ -813,11 +807,17 @@ static void modesSendStratuxOutput(struct modesMessage *mm, struct aircraft *a) if (!mm->reliable && !a->reliable) return; + //// For now, suppress non-ICAO addresses + //if (mm->addr & MODES_NON_ICAO_ADDRESS) + // return; + p = prepareWrite(&Modes.stratux_out, 1000); // larger buffer size needed vs SBS if (!p) return; - + // NOTE: not sure about message types, so keep the original code from + // stratux/dump1090 as-is. + // // Decide on the basic SBS Message Type if ((mm->msgtype == 4) || (mm->msgtype == 20)) { msgType = 5; @@ -842,7 +842,7 @@ static void modesSendStratuxOutput(struct modesMessage *mm, struct aircraft *a) else {msgType = 7;} } else if ((mm->metype == 29) || (mm->metype == 31)) { - msgType = 9; // new message type for target state and operational status messages + msgType = 9; // new message type for target state and operational status messages } else if (mm->metype != 19) { return; } else if ((mm->mesub == 1) || (mm->mesub == 2)) { @@ -852,146 +852,163 @@ static void modesSendStratuxOutput(struct modesMessage *mm, struct aircraft *a) } // Begin populating the traffic.go fields. - // ICAO address, Mode S message types, and signal level - + // ICAO address, Mode S message types, and signal level + int cacf = 0; // overload the JSON "CA" field to report CA (DF11 or DF17), CF (DF18), or zero (all other DF types) if ((mm->msgtype == 11) || (mm->msgtype == 17)) { cacf = mm->CA; } else if (mm->msgtype == 18) { cacf = mm->CF; } - - p += sprintf(p, "{\"Icao_addr\":%d,\"DF\":%d,\"CA\":%d,\"TypeCode\":%d,\"SubtypeCode\":%d,\"SBS_MsgType\":%d,\"SignalLevel\":%f,",mm->addr, mm->msgtype, cacf, mm->metype, mm->mesub, msgType, mm->signalLevel); // what precision and range is needed for RSSI? - - // Callsign - if (mm->callsign_valid) { - p += sprintf(p, "\"Tail\":\"%s\",", mm->callsign); - } else { - p += sprintf(p, "\"Tail\":null,"); - } - - //Squawk - if (mm->squawk_valid) { - p += sprintf(p, "\"Squawk\":%x,", mm->squawk); - } - else { - p += sprintf(p, "\"Squawk\":null,"); - } - - // Emitter type - int emitter = 0; - int setEmitter = 0; - if ((mm->msgtype == 17) || (mm->msgtype == 18)) { - switch (mm->metype) { - case 1: - emitter = ((mm->mesub) | 0x18); - setEmitter = 1; - break; - case 2: - emitter = ((mm->mesub) | 0x10); - setEmitter = 1; - break; - case 3: - emitter = ((mm->mesub) | 0x08); - setEmitter = 1; - break; - case 4: - emitter = (mm->mesub); - setEmitter = 1; - break; - } - } - - if (setEmitter) { - p += sprintf(p, "\"Emitter_category\":%d,", emitter); - } else { - p += sprintf(p, "\"Emitter_category\":null,"); - } - - // OnGround - if (mm->airground == AG_GROUND) { - p += sprintf(p, "\"OnGround\":true,"); - } else if (mm->airground == AG_AIRBORNE) { - p += sprintf(p, "\"OnGround\":false,"); - } else { - p += sprintf(p, "\"OnGround\":null,"); - } - - // Position and position valid flag - if (mm->cpr_decoded) { - p += sprintf(p, "\"Lat\":%.6f,\"Lng\":%.6f,\"Position_valid\":true,", mm->decoded_lat, mm->decoded_lon); - } else { - p += sprintf(p, "\"Lat\":null,\"Lng\":null,\"Position_valid\":false,"); - } - - // Navigation Accuracy Category - Position - if (mm->accuracy.nac_p_valid) { - p += sprintf(p, "\"NACp\":%d,", mm->accuracy.nac_p); - } else { - p += sprintf(p, "\"NACp\":null,"); - } - - bool alt_is_geom = false; - // Altitude - if (Modes.use_gnss && mm->altitude_geom_valid) { - p += sprintf(p, "\"Alt\":%d,",mm->altitude_geom); - alt_is_geom = true; - } else if (mm->altitude_baro_valid) { - p += sprintf(p, "\"Alt\":%d,",mm->altitude_baro); - } else { - p += sprintf(p, "\"Alt\":null,"); - } - - // Altitude Source. True if altitude source is GNSS, false if pressure altitude. - if (alt_is_geom) { - p += sprintf(p, "\"AltIsGNSS\":true,"); - } else { - p += sprintf(p, "\"AltIsGNSS\":false,"); - } - // GNSS Alt Diff From Baro Alt - if (trackDataValid(&a->geom_delta_valid)) { - p += sprintf(p, "\"GnssDiffFromBaroAlt\":%d,",a->geom_delta); // Verify that this is part of a and not mm - } else { - p += sprintf(p, "\"GnssDiffFromBaroAlt\":null,"); - } - - //Vertical velocity - if (alt_is_geom) { - if (mm->geom_rate_valid) - p += sprintf(p, "\"Vvel\":%d,", mm->geom_rate); - else - p += sprintf(p, "\"Vvel\":null,"); - } else { - if (mm->baro_rate_valid) - p += sprintf(p, "\"Vvel\":%d,", mm->baro_rate); - else - p += sprintf(p, "\"Vvel\":null,"); - } - // Ground speed and track - if (mm->gs_valid) { - p += sprintf(p, "\"Speed_valid\":true,\"Speed\":%.0f,", mm->gs.selected); - } else { - p += sprintf(p, "\"Speed_valid\":false,\"Speed\":null,"); - } - - if (mm->heading_valid && mm->heading_type == HEADING_GROUND_TRACK) { - p += sprintf(p, "\"Track\":%.0f,", mm->heading); - } else { - p += sprintf(p, "\"Track\":null,"); - } + p += sprintf(p, + "{\"Icao_addr\":%d," + "\"DF\":%d,\"CA\":%d," + "\"TypeCode\":%d," + "\"SubtypeCode\":%d," + "\"SBS_MsgType\":%d," + "\"SignalLevel\":%f,", + mm->addr, + mm->msgtype, cacf, + mm->metype, + mm->mesub, + msgType, + mm->signalLevel); // what precision and range is needed for RSSI? // Find current system time clock_gettime(CLOCK_REALTIME, &now); - gmtime_r(&now.tv_sec, &stTime_now); // we expect UTC + gmtime_r(&now.tv_sec, &stTime_now); // we expect UTC // Find message reception time time_t received = (time_t) (mm->sysTimestampMsg / 1000); localtime_r(&received, &stTime_receive); - //Time message received (based on system clock). Format is 2016-02-20T06:35:43.155Z - p += sprintf(p, "\"Timestamp\":\"%04d-%02d-%02dT%02d:%02d:%02d.%03dZ\"", (stTime_receive.tm_year+1900),(stTime_receive.tm_mon+1), stTime_receive.tm_mday, stTime_receive.tm_hour, stTime_receive.tm_min, stTime_receive.tm_sec, (unsigned) (mm->sysTimestampMsg % 1000)); - + // Time message received (based on system clock). Format is 2016-02-20T06:35:43.155Z + p += sprintf(p, "\"Timestamp\":\"%04d-%02d-%02dT%02d:%02d:%02d.%03dZ\"", (stTime_receive.tm_year+1900),(stTime_receive.tm_mon+1), stTime_receive.tm_mday, stTime_receive.tm_hour, stTime_receive.tm_min, stTime_receive.tm_sec, (unsigned) (mm->sysTimestampMsg % 1000)); + + //// callsign + if (mm->callsign_valid) + p += sprintf(p, "\"Tail\":\"%s\",", mm->callsign); + else + p += sprintf(p, "\"Tail\":null,"); + + //// altitude & gnss + bool alt_is_geom = false; + if (Modes.use_gnss && mm->altitude_geom_valid) { + p += sprintf(p, "\"Alt\":%d,",mm->altitude_geom); + alt_is_geom = true; + } else if (mm->altitude_baro_valid) + p += sprintf(p, "\"Alt\":%d,",mm->altitude_baro); + else + p += sprintf(p, "\"Alt\":null,"); + + // altitude source + if (alt_is_geom) + p += sprintf(p, "\"AltIsGNSS\":true,"); + else + p += sprintf(p, "\"AltIsGNSS\":false,"); + + // GNSS alt. delta From baro alt. + if (trackDataValid(&a->geom_delta_valid)) + p += sprintf(p, "\"GnssDiffFromBaroAlt\":%d,",a->geom_delta); + else + p += sprintf(p, "\"GnssDiffFromBaroAlt\":null,"); + //// + + //// ground speed and track + if (mm->gs_valid) + p += sprintf(p, "\"Speed_valid\":true,\"Speed\":%.0f,", mm->gs.selected); + else + p += sprintf(p, "\"Speed_valid\":false,\"Speed\":null,"); + + //// ground heading + if (mm->heading_valid && mm->heading_type == HEADING_GROUND_TRACK) + p += sprintf(p, "\"Track\":%.0f,", mm->heading); + else + p += sprintf(p, "\"Track\":null,"); + + //// position + if (mm->cpr_decoded) + p += sprintf(p, "\"Lat\":%.6f,\"Lng\":%.6f,\"Position_valid\":true,", + mm->decoded_lat, mm->decoded_lon); + else + p += sprintf(p, "\"Lat\":null,\"Lng\":null,\"Position_valid\":false,"); + + //// vrate + if (Modes.use_gnss) { + if (mm->geom_rate_valid) + p += sprintf(p, "\"Vvel\":%d,", mm->geom_rate); + else if (mm->baro_rate_valid) + p += sprintf(p, "\"Vvel\":%d,", mm->baro_rate); + else + p += sprintf(p, "\"Vvel\":null,"); + } else { + if (mm->baro_rate_valid) + p += sprintf(p, "\"Vvel\":%d,", mm->baro_rate); + else if (mm->geom_rate_valid) + p += sprintf(p, "\"Vvel\":%d,", mm->geom_rate); + else + p += sprintf(p, "\"Vvel\":null,"); + } + + //// squawk + if (mm->squawk_valid) + p += sprintf(p, "\"Squawk\":%x,", mm->squawk); + else + p += sprintf(p, "\"Squawk\":null,"); + + // TODO: squawk changing alert support in stratux? + // TODO: squawk emergency flag? + // TODO: squawk ident flag? + + // airground + switch (mm->airground) { + case AG_GROUND: + p += sprintf(p, "\"OnGround\":true,"); + break; + case AG_AIRBORNE: + p += sprintf(p, "\"OnGround\":false,"); + break; + default: + p += sprintf(p, "\"OnGround\":null,"); + } + + // navigation accuracy category - position + if (mm->accuracy.nac_p_valid) { + p += sprintf(p, "\"NACp\":%d,", mm->accuracy.nac_p); + } else { + p += sprintf(p, "\"NACp\":null,"); + } + + // emitter type + int emitter = 0; + int setEmitter = 0; + if ((mm->msgtype == 17) || (mm->msgtype == 18)) { + switch (mm->metype) { + case 1: + emitter = ((mm->mesub) | 0x18); + setEmitter = 1; + break; + case 2: + emitter = ((mm->mesub) | 0x10); + setEmitter = 1; + break; + case 3: + emitter = ((mm->mesub) | 0x08); + setEmitter = 1; + break; + case 4: + emitter = (mm->mesub); + setEmitter = 1; + break; + } + } + + if (setEmitter) + p += sprintf(p, "\"Emitter_category\":%d,", emitter); + else + p += sprintf(p, "\"Emitter_category\":null,"); + p += sprintf(p, "}\r\n"); completeWrite(&Modes.stratux_out, p); From eb1d42e47a6d4814e7d1fd0ca0d69ff009dc67a1 Mon Sep 17 00:00:00 2001 From: Determinant Date: Sat, 28 Mar 2020 15:27:22 -0400 Subject: [PATCH 04/14] ... --- dump1090.h | 2 +- net_io.c | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/dump1090.h b/dump1090.h index ec45d8e..e5137ba 100644 --- a/dump1090.h +++ b/dump1090.h @@ -52,7 +52,7 @@ // Default version number, if not overriden by the Makefile #ifndef MODES_DUMP1090_VERSION -# define MODES_DUMP1090_VERSION "v1.13-stratux" +# define MODES_DUMP1090_VERSION "v1.13-custom" #endif #ifndef MODES_DUMP1090_VARIANT diff --git a/net_io.c b/net_io.c index d4ca0ff..d4b3738 100644 --- a/net_io.c +++ b/net_io.c @@ -884,7 +884,11 @@ static void modesSendStratuxOutput(struct modesMessage *mm, struct aircraft *a) localtime_r(&received, &stTime_receive); // Time message received (based on system clock). Format is 2016-02-20T06:35:43.155Z - p += sprintf(p, "\"Timestamp\":\"%04d-%02d-%02dT%02d:%02d:%02d.%03dZ\"", (stTime_receive.tm_year+1900),(stTime_receive.tm_mon+1), stTime_receive.tm_mday, stTime_receive.tm_hour, stTime_receive.tm_min, stTime_receive.tm_sec, (unsigned) (mm->sysTimestampMsg % 1000)); + p += sprintf(p, "\"Timestamp\":\"%04d-%02d-%02dT%02d:%02d:%02d.%03dZ\"", + (stTime_receive.tm_year+1900),(stTime_receive.tm_mon+1), + stTime_receive.tm_mday, stTime_receive.tm_hour, + stTime_receive.tm_min, stTime_receive.tm_sec, + (unsigned)(mm->sysTimestampMsg % 1000)); //// callsign if (mm->callsign_valid) From 8ba24e8c3bb137e38f2ce945e3f60a18d1c895a4 Mon Sep 17 00:00:00 2001 From: Determinant Date: Sat, 28 Mar 2020 22:51:31 -0400 Subject: [PATCH 05/14] put the timestamp at last --- net_io.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/net_io.c b/net_io.c index d4b3738..3c5f2ca 100644 --- a/net_io.c +++ b/net_io.c @@ -883,13 +883,6 @@ static void modesSendStratuxOutput(struct modesMessage *mm, struct aircraft *a) time_t received = (time_t) (mm->sysTimestampMsg / 1000); localtime_r(&received, &stTime_receive); - // Time message received (based on system clock). Format is 2016-02-20T06:35:43.155Z - p += sprintf(p, "\"Timestamp\":\"%04d-%02d-%02dT%02d:%02d:%02d.%03dZ\"", - (stTime_receive.tm_year+1900),(stTime_receive.tm_mon+1), - stTime_receive.tm_mday, stTime_receive.tm_hour, - stTime_receive.tm_min, stTime_receive.tm_sec, - (unsigned)(mm->sysTimestampMsg % 1000)); - //// callsign if (mm->callsign_valid) p += sprintf(p, "\"Tail\":\"%s\",", mm->callsign); @@ -1013,6 +1006,13 @@ static void modesSendStratuxOutput(struct modesMessage *mm, struct aircraft *a) else p += sprintf(p, "\"Emitter_category\":null,"); + // Time message received (based on system clock). Format is 2016-02-20T06:35:43.155Z + p += sprintf(p, "\"Timestamp\":\"%04d-%02d-%02dT%02d:%02d:%02d.%03dZ\"", + (stTime_receive.tm_year+1900),(stTime_receive.tm_mon+1), + stTime_receive.tm_mday, stTime_receive.tm_hour, + stTime_receive.tm_min, stTime_receive.tm_sec, + (unsigned)(mm->sysTimestampMsg % 1000)); + p += sprintf(p, "}\r\n"); completeWrite(&Modes.stratux_out, p); From 258e3f9d6542d65b6411ee923e1631852a7c09a6 Mon Sep 17 00:00:00 2001 From: Determinant Date: Mon, 30 Mar 2020 12:34:11 -0400 Subject: [PATCH 06/14] turn off stratux support by default --- dump1090.c | 5 +++-- net_io.c | 9 ++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/dump1090.c b/dump1090.c index 356c1f5..467676b 100644 --- a/dump1090.c +++ b/dump1090.c @@ -116,7 +116,7 @@ void modesInitConfig(void) { Modes.net_output_sbs_ports = strdup("30003"); Modes.net_input_beast_ports = strdup("30004,30104"); Modes.net_output_beast_ports = strdup("30005"); - Modes.net_output_stratux_ports= strdup("30006"); + Modes.net_output_stratux_ports= NULL; Modes.interactive_display_ttl = MODES_INTERACTIVE_DISPLAY_TTL; Modes.json_interval = 1000; Modes.json_location_accuracy = 1; @@ -535,7 +535,8 @@ int main(int argc, char **argv) { free(Modes.net_output_sbs_ports); Modes.net_output_sbs_ports = strdup(argv[++j]); } else if (!strcmp(argv[j],"--net-stratux-port") && more) { - free(Modes.net_output_stratux_ports); + if (Modes.net_output_stratux_ports) + free(Modes.net_output_stratux_ports); Modes.net_output_stratux_ports = strdup(argv[++j]); } else if (!strcmp(argv[j],"--net-buffer") && more) { Modes.net_sndbuf_size = atoi(argv[++j]); diff --git a/net_io.c b/net_io.c index 3c5f2ca..408b6b6 100644 --- a/net_io.c +++ b/net_io.c @@ -269,8 +269,10 @@ void modesInitNet(void) { s = serviceInit("Basestation TCP output", &Modes.sbs_out, send_sbs_heartbeat, READ_MODE_IGNORE, NULL, NULL); serviceListen(s, Modes.net_bind_address, Modes.net_output_sbs_ports); - s = serviceInit("Stratux TCP output", &Modes.stratux_out, send_stratux_heartbeat, READ_MODE_IGNORE, NULL, NULL); - serviceListen(s, Modes.net_bind_address, Modes.net_output_stratux_ports); + if (Modes.net_output_stratux_ports) { + s = serviceInit("Stratux TCP output", &Modes.stratux_out, send_stratux_heartbeat, READ_MODE_IGNORE, NULL, NULL); + serviceListen(s, Modes.net_bind_address, Modes.net_output_stratux_ports); + } s = serviceInit("Raw TCP input", NULL, NULL, READ_MODE_ASCII, "\n", decodeHexMessage); serviceListen(s, Modes.net_bind_address, Modes.net_input_raw_ports); @@ -1042,7 +1044,8 @@ void modesQueueOutput(struct modesMessage *mm, struct aircraft *a) { // Delegate to the format-specific outputs, each of which makes its own decision about filtering messages modesSendSBSOutput(mm, a); - modesSendStratuxOutput(mm, a); + if (Modes.net_output_stratux_ports) + modesSendStratuxOutput(mm, a); modesSendRawOutput(mm, a); modesSendBeastVerbatimOutput(mm, a); modesSendBeastCookedOutput(mm, a); From d25103361a3c1a6f9bafc089e3bcd51d4637ca6a Mon Sep 17 00:00:00 2001 From: Determinant Date: Sun, 30 Aug 2020 21:21:49 -0400 Subject: [PATCH 07/14] make change according to the suggestions --- dump1090.c | 2 +- net_io.c | 83 +++++++++++++----------------------------------------- 2 files changed, 20 insertions(+), 65 deletions(-) diff --git a/dump1090.c b/dump1090.c index 467676b..f6c0dc6 100644 --- a/dump1090.c +++ b/dump1090.c @@ -300,7 +300,7 @@ void showHelp(void) { "--net-sbs-port TCP BaseStation output listen ports (default: 30003)\n" "--net-bi-port TCP Beast input listen ports (default: 30004,30104)\n" "--net-bo-port TCP Beast output listen ports (default: 30005)\n" -"--net-stratux-port TCP Stratux output listen ports (default: 30006)\n" +"--net-stratux-port TCP Stratux output listen ports (default: disabled)\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" diff --git a/net_io.c b/net_io.c index 408b6b6..25a21de 100644 --- a/net_io.c +++ b/net_io.c @@ -791,21 +791,20 @@ static void modesSendStratuxOutput(struct modesMessage *mm, struct aircraft *a) char *p; struct timespec now; struct tm stTime_receive, stTime_now; - int msgType; - // We require a tracked aircraft for SBS output + // We require a tracked aircraft for Stratux output if (!a) return; - // Don't ever forward 2-bit-corrected messages via SBS output. + // Don't ever forward 2-bit-corrected messages via Stratux output. if (mm->correctedbits >= 2) return; - // Don't ever forward mlat messages via SBS output. + // Don't ever forward mlat messages via Stratux output. if (mm->source == SOURCE_MLAT) return; - // Don't ever send unreliable messages via SBS output + // Don't ever send unreliable messages via Stratux output if (!mm->reliable && !a->reliable) return; @@ -817,42 +816,6 @@ static void modesSendStratuxOutput(struct modesMessage *mm, struct aircraft *a) if (!p) return; - // NOTE: not sure about message types, so keep the original code from - // stratux/dump1090 as-is. - // - // 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->cpr_decoded) - {msgType = 2;} - else - {msgType = 7;} - } else if ((mm->metype >= 9) && (mm->metype <= 18)) { - if (mm->cpr_decoded) - {msgType = 3;} - else - {msgType = 7;} - } else if ((mm->metype == 29) || (mm->metype == 31)) { - msgType = 9; // new message type for target state and operational status messages - } else if (mm->metype != 19) { - return; - } else if ((mm->mesub == 1) || (mm->mesub == 2)) { - msgType = 4; - } else { - return; - } - // Begin populating the traffic.go fields. // ICAO address, Mode S message types, and signal level @@ -868,13 +831,11 @@ static void modesSendStratuxOutput(struct modesMessage *mm, struct aircraft *a) "\"DF\":%d,\"CA\":%d," "\"TypeCode\":%d," "\"SubtypeCode\":%d," - "\"SBS_MsgType\":%d," "\"SignalLevel\":%f,", mm->addr, mm->msgtype, cacf, mm->metype, mm->mesub, - msgType, mm->signalLevel); // what precision and range is needed for RSSI? // Find current system time @@ -892,14 +853,17 @@ static void modesSendStratuxOutput(struct modesMessage *mm, struct aircraft *a) p += sprintf(p, "\"Tail\":null,"); //// altitude & gnss - bool alt_is_geom = false; - if (Modes.use_gnss && mm->altitude_geom_valid) { + bool alt_is_geom; + if (mm->altitude_baro_valid) { + p += sprintf(p, "\"Alt\":%d,",mm->altitude_baro); + alt_is_geom = false; + } else if (mm->altitude_geom_valid) { p += sprintf(p, "\"Alt\":%d,",mm->altitude_geom); alt_is_geom = true; - } else if (mm->altitude_baro_valid) - p += sprintf(p, "\"Alt\":%d,",mm->altitude_baro); - else + } else { p += sprintf(p, "\"Alt\":null,"); + alt_is_geom = false; + } // altitude source if (alt_is_geom) @@ -933,22 +897,13 @@ static void modesSendStratuxOutput(struct modesMessage *mm, struct aircraft *a) else p += sprintf(p, "\"Lat\":null,\"Lng\":null,\"Position_valid\":false,"); - //// vrate - if (Modes.use_gnss) { - if (mm->geom_rate_valid) - p += sprintf(p, "\"Vvel\":%d,", mm->geom_rate); - else if (mm->baro_rate_valid) - p += sprintf(p, "\"Vvel\":%d,", mm->baro_rate); - else - p += sprintf(p, "\"Vvel\":null,"); - } else { - if (mm->baro_rate_valid) - p += sprintf(p, "\"Vvel\":%d,", mm->baro_rate); - else if (mm->geom_rate_valid) - p += sprintf(p, "\"Vvel\":%d,", mm->geom_rate); - else - p += sprintf(p, "\"Vvel\":null,"); - } + //// vrate (use barometric if possible) + if (mm->baro_rate_valid) + p += sprintf(p, "\"Vvel\":%d,", mm->baro_rate); + else if (mm->geom_rate_valid) + p += sprintf(p, "\"Vvel\":%d,", mm->geom_rate); + else + p += sprintf(p, "\"Vvel\":null,"); //// squawk if (mm->squawk_valid) From c89930b4b94834d549b9b5ddc00fefcf603c859c Mon Sep 17 00:00:00 2001 From: Determinant Date: Mon, 7 Sep 2020 15:36:34 -0400 Subject: [PATCH 08/14] forward mlat messages to Stratux --- net_io.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/net_io.c b/net_io.c index 25a21de..225dac6 100644 --- a/net_io.c +++ b/net_io.c @@ -800,9 +800,7 @@ static void modesSendStratuxOutput(struct modesMessage *mm, struct aircraft *a) if (mm->correctedbits >= 2) return; - // Don't ever forward mlat messages via Stratux output. - if (mm->source == SOURCE_MLAT) - return; + // NOTE: mlat messages are forwarded via Stratux output. // Don't ever send unreliable messages via Stratux output if (!mm->reliable && !a->reliable) From bc9336e0be00f30568774ae025833b2f23c1245e Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 8 Sep 2020 23:10:18 +0800 Subject: [PATCH 09/14] Revert "forward mlat messages to Stratux" This reverts commit c89930b4b94834d549b9b5ddc00fefcf603c859c. --- net_io.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net_io.c b/net_io.c index 7b7876b..c65a43c 100644 --- a/net_io.c +++ b/net_io.c @@ -800,7 +800,9 @@ static void modesSendStratuxOutput(struct modesMessage *mm, struct aircraft *a) if (mm->correctedbits >= 2) return; - // NOTE: mlat messages are forwarded via Stratux output. + // Don't ever forward mlat messages via Stratux output. + if (mm->source == SOURCE_MLAT) + return; // Don't ever send unreliable messages via Stratux output if (!mm->reliable && !a->reliable) From ec90edcd8db077ead144be3a39455735dd4d9229 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 8 Sep 2020 23:21:34 +0800 Subject: [PATCH 10/14] Simpify stratux service init --- net_io.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net_io.c b/net_io.c index c65a43c..14bceda 100644 --- a/net_io.c +++ b/net_io.c @@ -269,10 +269,8 @@ void modesInitNet(void) { s = serviceInit("Basestation TCP output", &Modes.sbs_out, send_sbs_heartbeat, READ_MODE_IGNORE, NULL, NULL); serviceListen(s, Modes.net_bind_address, Modes.net_output_sbs_ports); - if (Modes.net_output_stratux_ports) { - s = serviceInit("Stratux TCP output", &Modes.stratux_out, send_stratux_heartbeat, READ_MODE_IGNORE, NULL, NULL); - serviceListen(s, Modes.net_bind_address, Modes.net_output_stratux_ports); - } + s = serviceInit("Stratux TCP output", &Modes.stratux_out, send_stratux_heartbeat, READ_MODE_IGNORE, NULL, NULL); + serviceListen(s, Modes.net_bind_address, Modes.net_output_stratux_ports); s = serviceInit("Raw TCP input", NULL, NULL, READ_MODE_ASCII, "\n", decodeHexMessage); serviceListen(s, Modes.net_bind_address, Modes.net_input_raw_ports); From da3e9766dec303bb604b6d092932266719c82cd0 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 8 Sep 2020 23:22:53 +0800 Subject: [PATCH 11/14] Harden the stratux output a bit: use safe_snprintf, jsonEscapeString --- net_io.c | 89 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 43 deletions(-) diff --git a/net_io.c b/net_io.c index 14bceda..3740301 100644 --- a/net_io.c +++ b/net_io.c @@ -86,6 +86,11 @@ static void writeFATSVPositionUpdate(float lat, float lon, float alt); static void autoset_modeac(); +__attribute__ ((format (printf,3,0))) static char *safe_vsnprintf(char *p, char *end, const char *format, va_list ap); +__attribute__ ((format (printf,3,4))) static char *safe_snprintf(char *p, char *end, const char *format, ...); + +static const char *jsonEscapeString(const char *str); + // //========================================================================= // @@ -785,6 +790,8 @@ static void send_sbs_heartbeat(struct net_service *service) // // Write Stratux output to TCP clients // + +#define STRATUX_MAX_PACKET_SIZE 1000 static void modesSendStratuxOutput(struct modesMessage *mm, struct aircraft *a) { char *p; struct timespec now; @@ -806,14 +813,12 @@ static void modesSendStratuxOutput(struct modesMessage *mm, struct aircraft *a) if (!mm->reliable && !a->reliable) return; - //// For now, suppress non-ICAO addresses - //if (mm->addr & MODES_NON_ICAO_ADDRESS) - // return; - - p = prepareWrite(&Modes.stratux_out, 1000); // larger buffer size needed vs SBS + p = prepareWrite(&Modes.stratux_out, STRATUX_MAX_PACKET_SIZE); // larger buffer size needed vs SBS if (!p) return; + char *end = p + STRATUX_MAX_PACKET_SIZE; + // Begin populating the traffic.go fields. // ICAO address, Mode S message types, and signal level @@ -824,7 +829,7 @@ static void modesSendStratuxOutput(struct modesMessage *mm, struct aircraft *a) cacf = mm->CF; } - p += sprintf(p, + p = safe_snprintf(p, end, "{\"Icao_addr\":%d," "\"DF\":%d,\"CA\":%d," "\"TypeCode\":%d," @@ -846,68 +851,68 @@ static void modesSendStratuxOutput(struct modesMessage *mm, struct aircraft *a) //// callsign if (mm->callsign_valid) - p += sprintf(p, "\"Tail\":\"%s\",", mm->callsign); + p = safe_snprintf(p, end, "\"Tail\":\"%s\",", jsonEscapeString(mm->callsign)); else - p += sprintf(p, "\"Tail\":null,"); + p = safe_snprintf(p, end, "\"Tail\":null,"); //// altitude & gnss bool alt_is_geom; if (mm->altitude_baro_valid) { - p += sprintf(p, "\"Alt\":%d,",mm->altitude_baro); + p = safe_snprintf(p, end, "\"Alt\":%d,",mm->altitude_baro); alt_is_geom = false; } else if (mm->altitude_geom_valid) { - p += sprintf(p, "\"Alt\":%d,",mm->altitude_geom); + p = safe_snprintf(p, end, "\"Alt\":%d,",mm->altitude_geom); alt_is_geom = true; } else { - p += sprintf(p, "\"Alt\":null,"); + p = safe_snprintf(p, end, "\"Alt\":null,"); alt_is_geom = false; } // altitude source if (alt_is_geom) - p += sprintf(p, "\"AltIsGNSS\":true,"); + p = safe_snprintf(p, end, "\"AltIsGNSS\":true,"); else - p += sprintf(p, "\"AltIsGNSS\":false,"); + p = safe_snprintf(p, end, "\"AltIsGNSS\":false,"); // GNSS alt. delta From baro alt. if (trackDataValid(&a->geom_delta_valid)) - p += sprintf(p, "\"GnssDiffFromBaroAlt\":%d,",a->geom_delta); + p = safe_snprintf(p, end, "\"GnssDiffFromBaroAlt\":%d,",a->geom_delta); else - p += sprintf(p, "\"GnssDiffFromBaroAlt\":null,"); + p = safe_snprintf(p, end, "\"GnssDiffFromBaroAlt\":null,"); //// //// ground speed and track if (mm->gs_valid) - p += sprintf(p, "\"Speed_valid\":true,\"Speed\":%.0f,", mm->gs.selected); + p = safe_snprintf(p, end, "\"Speed_valid\":true,\"Speed\":%.0f,", mm->gs.selected); else - p += sprintf(p, "\"Speed_valid\":false,\"Speed\":null,"); + p = safe_snprintf(p, end, "\"Speed_valid\":false,\"Speed\":null,"); //// ground heading if (mm->heading_valid && mm->heading_type == HEADING_GROUND_TRACK) - p += sprintf(p, "\"Track\":%.0f,", mm->heading); + p = safe_snprintf(p, end, "\"Track\":%.0f,", mm->heading); else - p += sprintf(p, "\"Track\":null,"); + p = safe_snprintf(p, end, "\"Track\":null,"); //// position if (mm->cpr_decoded) - p += sprintf(p, "\"Lat\":%.6f,\"Lng\":%.6f,\"Position_valid\":true,", + p = safe_snprintf(p, end, "\"Lat\":%.6f,\"Lng\":%.6f,\"Position_valid\":true,", mm->decoded_lat, mm->decoded_lon); else - p += sprintf(p, "\"Lat\":null,\"Lng\":null,\"Position_valid\":false,"); + p = safe_snprintf(p, end, "\"Lat\":null,\"Lng\":null,\"Position_valid\":false,"); //// vrate (use barometric if possible) if (mm->baro_rate_valid) - p += sprintf(p, "\"Vvel\":%d,", mm->baro_rate); + p = safe_snprintf(p, end, "\"Vvel\":%d,", mm->baro_rate); else if (mm->geom_rate_valid) - p += sprintf(p, "\"Vvel\":%d,", mm->geom_rate); + p = safe_snprintf(p, end, "\"Vvel\":%d,", mm->geom_rate); else - p += sprintf(p, "\"Vvel\":null,"); + p = safe_snprintf(p, end, "\"Vvel\":null,"); //// squawk if (mm->squawk_valid) - p += sprintf(p, "\"Squawk\":%x,", mm->squawk); + p = safe_snprintf(p, end, "\"Squawk\":%x,", mm->squawk); else - p += sprintf(p, "\"Squawk\":null,"); + p = safe_snprintf(p, end, "\"Squawk\":null,"); // TODO: squawk changing alert support in stratux? // TODO: squawk emergency flag? @@ -916,61 +921,59 @@ static void modesSendStratuxOutput(struct modesMessage *mm, struct aircraft *a) // airground switch (mm->airground) { case AG_GROUND: - p += sprintf(p, "\"OnGround\":true,"); + p = safe_snprintf(p, end, "\"OnGround\":true,"); break; case AG_AIRBORNE: - p += sprintf(p, "\"OnGround\":false,"); + p = safe_snprintf(p, end, "\"OnGround\":false,"); break; default: - p += sprintf(p, "\"OnGround\":null,"); + p = safe_snprintf(p, end, "\"OnGround\":null,"); } // navigation accuracy category - position if (mm->accuracy.nac_p_valid) { - p += sprintf(p, "\"NACp\":%d,", mm->accuracy.nac_p); + p = safe_snprintf(p, end, "\"NACp\":%d,", mm->accuracy.nac_p); } else { - p += sprintf(p, "\"NACp\":null,"); + p = safe_snprintf(p, end, "\"NACp\":null,"); } // emitter type - int emitter = 0; - int setEmitter = 0; + int emitter = -1; if ((mm->msgtype == 17) || (mm->msgtype == 18)) { switch (mm->metype) { case 1: emitter = ((mm->mesub) | 0x18); - setEmitter = 1; break; case 2: emitter = ((mm->mesub) | 0x10); - setEmitter = 1; break; case 3: emitter = ((mm->mesub) | 0x08); - setEmitter = 1; break; case 4: emitter = (mm->mesub); - setEmitter = 1; break; } } - if (setEmitter) - p += sprintf(p, "\"Emitter_category\":%d,", emitter); + if (emitter >= 0) + p = safe_snprintf(p, end, "\"Emitter_category\":%d,", emitter); else - p += sprintf(p, "\"Emitter_category\":null,"); + p = safe_snprintf(p, end, "\"Emitter_category\":null,"); // Time message received (based on system clock). Format is 2016-02-20T06:35:43.155Z - p += sprintf(p, "\"Timestamp\":\"%04d-%02d-%02dT%02d:%02d:%02d.%03dZ\"", + p = safe_snprintf(p, end, "\"Timestamp\":\"%04d-%02d-%02dT%02d:%02d:%02d.%03dZ\"", (stTime_receive.tm_year+1900),(stTime_receive.tm_mon+1), stTime_receive.tm_mday, stTime_receive.tm_hour, stTime_receive.tm_min, stTime_receive.tm_sec, (unsigned)(mm->sysTimestampMsg % 1000)); - p += sprintf(p, "}\r\n"); + p = safe_snprintf(p, end, "}\r\n"); - completeWrite(&Modes.stratux_out, p); + if (p < end) + completeWrite(&Modes.stratux_out, p); + else + fprintf(stderr, "stratux: output too large (max %d, overran by %d)\n", STRATUX_MAX_PACKET_SIZE, (int) (p - end)); } static void send_stratux_heartbeat(struct net_service *service) From 9b06099971a6d7acd0b1270a0d5ac6372b4777c4 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 8 Sep 2020 23:25:01 +0800 Subject: [PATCH 12/14] Enable net mode on bare stratux port option --- dump1090.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dump1090.c b/dump1090.c index c8304a2..aa23b73 100644 --- a/dump1090.c +++ b/dump1090.c @@ -550,6 +550,7 @@ int main(int argc, char **argv) { free(Modes.net_output_sbs_ports); Modes.net_output_sbs_ports = strdup(argv[++j]); } else if (!strcmp(argv[j],"--net-stratux-port") && more) { + Modes.net = 1; free(Modes.net_output_stratux_ports); Modes.net_output_stratux_ports = strdup(argv[++j]); } else if (!strcmp(argv[j],"--net-buffer") && more) { From 7356676c5b2ef2db2b147d223ee4db5dafe414e4 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 8 Sep 2020 23:30:24 +0800 Subject: [PATCH 13/14] Don't need conditional output on the stratux port, that's done in prepareWrite() --- net_io.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net_io.c b/net_io.c index 3740301..64436b7 100644 --- a/net_io.c +++ b/net_io.c @@ -1000,8 +1000,7 @@ void modesQueueOutput(struct modesMessage *mm, struct aircraft *a) { // Delegate to the format-specific outputs, each of which makes its own decision about filtering messages modesSendSBSOutput(mm, a); - if (Modes.net_output_stratux_ports) - modesSendStratuxOutput(mm, a); + modesSendStratuxOutput(mm, a); modesSendRawOutput(mm, a); modesSendBeastVerbatimOutput(mm, a); modesSendBeastCookedOutput(mm, a); From 303dcc991575bfadf85e0e2858cc64b1c0271a5d Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 8 Sep 2020 23:35:17 +0800 Subject: [PATCH 14/14] Fix stratux output timestamps to really be UTC as they claim --- net_io.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/net_io.c b/net_io.c index 64436b7..f0754c4 100644 --- a/net_io.c +++ b/net_io.c @@ -794,8 +794,6 @@ static void send_sbs_heartbeat(struct net_service *service) #define STRATUX_MAX_PACKET_SIZE 1000 static void modesSendStratuxOutput(struct modesMessage *mm, struct aircraft *a) { char *p; - struct timespec now; - struct tm stTime_receive, stTime_now; // We require a tracked aircraft for Stratux output if (!a) @@ -841,14 +839,6 @@ static void modesSendStratuxOutput(struct modesMessage *mm, struct aircraft *a) mm->mesub, mm->signalLevel); // what precision and range is needed for RSSI? - // Find current system time - clock_gettime(CLOCK_REALTIME, &now); - gmtime_r(&now.tv_sec, &stTime_now); // we expect UTC - - // Find message reception time - time_t received = (time_t) (mm->sysTimestampMsg / 1000); - localtime_r(&received, &stTime_receive); - //// callsign if (mm->callsign_valid) p = safe_snprintf(p, end, "\"Tail\":\"%s\",", jsonEscapeString(mm->callsign)); @@ -962,6 +952,9 @@ static void modesSendStratuxOutput(struct modesMessage *mm, struct aircraft *a) p = safe_snprintf(p, end, "\"Emitter_category\":null,"); // Time message received (based on system clock). Format is 2016-02-20T06:35:43.155Z + struct tm stTime_receive; + time_t received = (time_t) (mm->sysTimestampMsg / 1000); + gmtime_r(&received, &stTime_receive); p = safe_snprintf(p, end, "\"Timestamp\":\"%04d-%02d-%02dT%02d:%02d:%02d.%03dZ\"", (stTime_receive.tm_year+1900),(stTime_receive.tm_mon+1), stTime_receive.tm_mday, stTime_receive.tm_hour,