WIP: support stratux protocol

This commit is contained in:
Determinant 2020-03-23 18:53:24 -04:00
parent d6b8065c3b
commit b6f007a104
3 changed files with 235 additions and 1 deletions

View File

@ -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 <ports> TCP BaseStation output listen ports (default: 30003)\n"
"--net-bi-port <ports> TCP Beast input listen ports (default: 30004,30104)\n"
"--net-bo-port <ports> TCP Beast output listen ports (default: 30005)\n"
"--net-stratux-port <ports> TCP Stratux output listen ports (default: 30006)\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"
@ -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")) {

View File

@ -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

227
net_io.c
View File

@ -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);