stratux/dump978/uat2json.c

373 wiersze
9.1 KiB
C

//
// Copyright 2015, Oliver Jowett <oliver@mutability.co.uk>
//
// This file is free software: you may copy, redistribute and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation, either version 2 of the License, or (at your
// option) any later version.
//
// This file is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <time.h>
#include <sys/select.h>
#include <errno.h>
#include "uat.h"
#include "uat_decode.h"
#include "reader.h"
#define NON_ICAO_ADDRESS 0x1000000U
struct aircraft {
struct aircraft *next;
uint32_t address;
uint32_t messages;
time_t last_seen;
time_t last_seen_pos;
int position_valid : 1;
int altitude_valid : 1;
int track_valid : 1;
int speed_valid : 1;
int vert_rate_valid : 1;
airground_state_t airground_state;
char callsign[9];
char squawk[9];
// if position_valid:
double lat;
double lon;
// if altitude_valid:
int32_t altitude; // in feet
// if track_valid:
uint16_t track;
// if speed_valid:
uint16_t speed; // in kts
// if vert_rate_valid:
int16_t vert_rate; // in ft/min
};
static struct aircraft *aircraft_list;
static time_t NOW;
static const char *json_dir;
static struct aircraft *find_aircraft(uint32_t address)
{
struct aircraft *a;
for (a = aircraft_list; a; a = a->next)
if (a->address == address)
return a;
return NULL;
}
static struct aircraft *find_or_create_aircraft(uint32_t address)
{
struct aircraft *a = find_aircraft(address);
if (a)
return a;
a = calloc(1, sizeof(*a));
a->address = address;
a->airground_state = AG_RESERVED;
a->next = aircraft_list;
aircraft_list = a;
return a;
}
static void expire_old_aircraft()
{
struct aircraft *a, **last;
for (last = &aircraft_list, a = *last; a; a = *last) {
if ((NOW - a->last_seen) > 300) {
*last = a->next;
free(a);
} else {
last = &a->next;
}
}
}
static uint32_t message_count;
static void process_mdb(struct uat_adsb_mdb *mdb)
{
struct aircraft *a;
uint32_t addr;
++message_count;
switch (mdb->address_qualifier) {
case AQ_ADSB_ICAO:
case AQ_TISB_ICAO:
addr = mdb->address;
break;
default:
addr = mdb->address | NON_ICAO_ADDRESS;
break;
}
a = find_or_create_aircraft(addr);
a->last_seen = NOW;
++a->messages;
// copy state into aircraft
if (mdb->airground_state != AG_RESERVED)
a->airground_state = mdb->airground_state;
if (mdb->position_valid) {
a->position_valid = 1;
a->lat = mdb->lat;
a->lon = mdb->lon;
a->last_seen_pos = NOW;
}
if (mdb->altitude_type != ALT_INVALID) {
a->altitude_valid = 1;
a->altitude = mdb->altitude;
}
if (mdb->track_type != TT_INVALID) {
a->track_valid = 1;
a->track = mdb->track;
}
if (mdb->speed_valid) {
a->speed_valid = 1;
a->speed = mdb->speed;
}
if (mdb->vert_rate_source != ALT_INVALID) {
a->vert_rate_valid = 1;
a->vert_rate = mdb->vert_rate;
}
if (mdb->callsign_type == CS_CALLSIGN)
strcpy(a->callsign, mdb->callsign);
else if (mdb->callsign_type == CS_SQUAWK)
strcpy(a->squawk, mdb->callsign);
if (mdb->sec_altitude_type != ALT_INVALID) {
// only use secondary if no primary is available
if (!a->altitude_valid || mdb->altitude_type == ALT_INVALID) {
a->altitude_valid = 1;
a->altitude = mdb->sec_altitude;
}
}
}
static int write_receiver_json(const char *dir)
{
char path[PATH_MAX];
FILE *f;
if (snprintf(path, PATH_MAX, "%s/receiver.json.new", dir) >= PATH_MAX) {
fprintf(stderr, "write_receiver_json: path too long\n");
return 0;
}
if (!(f = fopen(path, "w"))) {
fprintf(stderr, "fopen(%s): %m\n", path);
return 0;
}
fprintf(f,
"{\n"
" \"version\" : \"dump978-uat2json\",\n"
" \"refresh\" : 1000,\n"
" \"history\" : 0\n"
"}\n");
fclose(f);
return 1;
}
static int write_aircraft_json(const char *dir)
{
char path[PATH_MAX];
char path_new[PATH_MAX];
FILE *f;
struct aircraft *a;
if (snprintf(path, PATH_MAX, "%s/aircraft.json", dir) >= PATH_MAX || snprintf(path_new, PATH_MAX, "%s/aircraft.json.new", dir) >= PATH_MAX) {
fprintf(stderr, "write_aircraft_json: path too long\n");
return 0;
}
if (!(f = fopen(path_new, "w"))) {
fprintf(stderr, "fopen(%s): %m\n", path_new);
return 0;
}
fprintf(f,
"{\n"
" \"now\" : %u,\n"
" \"messages\" : %u,\n"
" \"aircraft\" : [\n",
(unsigned)NOW,
message_count);
for (a = aircraft_list; a; a = a->next) {
if (a != aircraft_list)
fprintf(f, ",\n");
fprintf(f,
" {\"hex\":\"%s%06x\"",
(a->address & NON_ICAO_ADDRESS) ? "~" : "",
a->address & 0xFFFFFF);
if (a->squawk[0])
fprintf(f, ",\"squawk\":\"%s\"", a->squawk);
if (a->callsign[0])
fprintf(f, ",\"flight\":\"%s\"", a->callsign);
if (a->position_valid)
fprintf(f, ",\"lat\":%.6f,\"lon\":%.6f,\"seen_pos\":%u", a->lat, a->lon, (unsigned) (NOW - a->last_seen_pos));
if (a->altitude_valid)
fprintf(f, ",\"altitude\":%d", a->altitude);
if (a->vert_rate_valid)
fprintf(f, ",\"vert_rate\":%d", a->vert_rate);
if (a->track_valid)
fprintf(f, ",\"track\":%u", a->track);
if (a->speed_valid)
fprintf(f, ",\"speed\":%u", a->speed);
fprintf(f, ",\"messages\":%u,\"seen\":%u,\"rssi\":0}",
a->messages, (unsigned) (NOW - a->last_seen));
}
fprintf(f,
"\n ]\n"
"}\n");
fclose(f);
if (rename(path_new, path) < 0) {
fprintf(stderr, "rename(%s,%s): %m\n", path_new, path);
return 0;
}
return 1;
}
static void periodic_work()
{
static time_t next_write;
if (NOW >= next_write) {
expire_old_aircraft();
write_aircraft_json(json_dir);
next_write = NOW + 1;
}
}
static void handle_frame(frame_type_t type, uint8_t *frame, int len, void *extra)
{
struct uat_adsb_mdb mdb;
if (type != UAT_DOWNLINK)
return;
if (len == SHORT_FRAME_DATA_BYTES) {
if ((frame[0] >> 3) != 0) {
fprintf(stderr, "short frame with non-zero type\n");
return;
}
} else if (len == LONG_FRAME_DATA_BYTES) {
if ((frame[0] >> 3) == 0) {
fprintf(stderr, "long frame with zero type\n");
return;
}
} else {
fprintf(stderr, "odd frame size: %d\n", len);
return;
}
uat_decode_adsb_mdb(frame, &mdb);
//uat_display_adsb_mdb(&mdb, stdout);
process_mdb(&mdb);
}
static void read_loop()
{
struct dump978_reader *reader;
reader = dump978_reader_new(0, 1);
if (!reader) {
perror("dump978_reader_new");
return;
}
for (;;) {
fd_set readset, writeset, excset;
struct timeval timeout;
int framecount;
FD_ZERO(&readset);
FD_ZERO(&writeset);
FD_ZERO(&excset);
FD_SET(0, &readset);
FD_SET(0, &excset);
timeout.tv_sec = 0;
timeout.tv_usec = 500000;
select(1, &readset, &writeset, &excset, &timeout);
NOW = time(NULL);
framecount = dump978_read_frames(reader, handle_frame, NULL);
if (framecount == 0)
break;
if (framecount < 0 && errno != EAGAIN && errno != EINTR && errno != EWOULDBLOCK) {
perror("dump978_read_frames");
break;
}
periodic_work();
}
dump978_reader_free(reader);
}
int main(int argc, char **argv)
{
if (argc < 2) {
fprintf(stderr,
"Syntax: %s <dir>\n"
"\n"
"Reads UAT messages on stdin.\n"
"Periodically writes aircraft state to <dir>/aircraft.json\n"
"Also writes <dir>/receiver.json once on startup\n",
argv[0]);
return 1;
}
json_dir = argv[1];
if (!write_receiver_json(json_dir)) {
fprintf(stderr, "Failed to write receiver.json - check permissions?\n");
return 1;
}
read_loop();
write_aircraft_json(json_dir);
return 0;
}