diff --git a/Makefile b/Makefile index 6dbd941..5888ee3 100644 --- a/Makefile +++ b/Makefile @@ -11,5 +11,5 @@ clean: %.o: %.c *.h $(CC) -c $(CFLAGS) $< -o $@ -adsbus: adsbus.o backend.o airspy_adsb.o common.o - $(CC) $(LDFLAGS) -o adsbus adsbus.o backend.o airspy_adsb.o common.o $(LIBS) +adsbus: adsbus.o backend.o client.o airspy_adsb.o json.o common.o + $(CC) $(LDFLAGS) -o adsbus adsbus.o backend.o client.o airspy_adsb.o json.o common.o $(LIBS) diff --git a/adsbus.c b/adsbus.c index 08aa1fa..9c7867f 100644 --- a/adsbus.c +++ b/adsbus.c @@ -5,31 +5,37 @@ #include #include "common.h" +#include "backend.h" +#include "client.h" #include "airspy_adsb.h" +#include "json.h" -struct opts { - bool dump; -}; +static bool add_dump(char *format) { + struct serializer *serializer = client_get_serializer(format); + if (!serializer) { + fprintf(stderr, "Unknown dump format: %s\n", format); + return false; + } + client_add(1, serializer); + return true; +} -struct client { - int placeholder; -}; - - -static int parse_opts(int argc, char *argv[], struct opts *opts) { +static bool parse_opts(int argc, char *argv[]) { int opt; - while ((opt = getopt(argc, argv, "d")) != -1) { + while ((opt = getopt(argc, argv, "d:")) != -1) { switch (opt) { case 'd': - opts->dump = true; + if (!add_dump(optarg)) { + return false; + } break; default: - return -1; + return false; } } - return 0; + return true; } static int loop(int epoll_fd) { @@ -62,16 +68,7 @@ static int loop(int epoll_fd) { int main(int argc, char *argv[]) { hex_init(); airspy_adsb_init(); - - struct opts opts = { - .dump = false, - }; - if (parse_opts(argc, argv, &opts) || - argc - optind < 2 || - (argc - optind) % 2 != 0) { - fprintf(stderr, "Usage: %s -d localhost 30006 [ remotehost 30002 ... ]\n", argv[0]); - return EXIT_FAILURE; - } + json_init(); int epoll_fd = epoll_create1(0); if (epoll_fd == -1) { @@ -79,6 +76,13 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } + if (!parse_opts(argc, argv) || + argc - optind < 2 || + (argc - optind) % 2 != 0) { + fprintf(stderr, "Usage: %s [ -d format ] localhost 30006 [ remotehost 30002 ... ]\n", argv[0]); + return EXIT_FAILURE; + } + int nbackends = (argc - optind) / 2; struct backend backends[nbackends]; for (int i = 0, j = optind; i < nbackends && j < argc; i++, j += 2) { diff --git a/backend.c b/backend.c index 3b6f9e0..f9f8e4b 100644 --- a/backend.c +++ b/backend.c @@ -5,9 +5,9 @@ #include #include #include -#include #include "airspy_adsb.h" +#include "client.h" #include "backend.h" @@ -31,11 +31,7 @@ void backend_init(struct backend *backend) { bool backend_connect(char *node, char *service, struct backend *backend, int epoll_fd) { assert(backend->type == PEER_BACKEND); - { - uuid_t uuid; - uuid_generate(uuid); - uuid_unparse(uuid, backend->id); - } + uuid_gen(backend->id); struct addrinfo *addrs; @@ -106,6 +102,7 @@ bool backend_read(struct backend *backend) { struct packet packet; while (backend->parser(backend, &packet)) { + client_write(&packet); } if (backend->buf.length == BUF_LEN_MAX) { diff --git a/backend.h b/backend.h index 8bad0a7..68e3ac3 100644 --- a/backend.h +++ b/backend.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "common.h" diff --git a/client.c b/client.c new file mode 100644 index 0000000..91186d9 --- /dev/null +++ b/client.c @@ -0,0 +1,98 @@ +#include +#include +#include +#include +#include + +#include "json.h" +#include "client.h" + +struct client { + char id[UUID_LEN]; + int fd; + struct client *next; +}; + +typedef size_t (*serializer)(struct packet *, char *); +struct serializer { + char *name; + serializer serialize; + struct client *client_head; +} serializers[] = { + { + .name = "json", + .serialize = json_serialize, + .client_head = NULL, + }, +}; +#define NUM_SERIALIZERS (sizeof(serializers) / sizeof(*serializers)) + + +struct serializer *client_get_serializer(char *name) { + for (int i = 0; i < NUM_SERIALIZERS; i++) { + if (strcasecmp(serializers[i].name, name) == 0) { + return &serializers[i]; + } + } + return NULL; +} + +static bool client_hello(int fd, struct serializer *serializer) { + char buf[SERIALIZE_LEN]; + size_t len = serializer->serialize(NULL, buf); + if (len == 0) { + return true; + } + if (write(fd, buf, len) != len) { + fprintf(stderr, "Failed to write hello to client\n"); + return false; + } + return true; +} + +void client_add(int fd, struct serializer *serializer) { + if (!client_hello(fd, serializer)) { + return; + } + + struct client *client = malloc(sizeof(*client)); + assert(client); + + uuid_gen(client->id); + client->fd = fd; + client->next = serializer->client_head; + serializer->client_head = client; + + fprintf(stderr, "%s (%s): New client\n", client->id, serializer->name); +} + +void client_write(struct packet *packet) { + for (int i = 0; i < NUM_SERIALIZERS; i++) { + struct serializer *serializer = &serializers[i]; + if (serializer->client_head == NULL) { + continue; + } + char buf[SERIALIZE_LEN]; + size_t len = serializer->serialize(packet, buf); + if (len == 0) { + continue; + } + struct client *client = serializer->client_head, *prev_client = NULL; + while (client) { + if (write(client->fd, buf, len) == len) { + prev_client = client; + client = client->next; + } else { + fprintf(stderr, "%s (%s): Client disconnected\n", client->id, serializer->name); + if (prev_client) { + prev_client->next = client->next; + } else { + serializer->client_head = client->next; + } + struct client *del = client; + client = client->next; + free(del); + } + } + } +} diff --git a/client.h b/client.h new file mode 100644 index 0000000..8aae8d3 --- /dev/null +++ b/client.h @@ -0,0 +1,10 @@ +#pragma once + +#include "common.h" + +#define SERIALIZE_LEN 256 + + +struct serializer *client_get_serializer(char *); +void client_add(int, struct serializer *); +void client_write(struct packet *); diff --git a/common.c b/common.c index 2989fef..798e8b0 100644 --- a/common.c +++ b/common.c @@ -3,14 +3,11 @@ #include #include #include +#include #include "common.h" -#define MLAT_MHZ 120 -#define RSSI_MAX UINT32_MAX - - void buf_init(struct buf *buf) { buf->start = 0; buf->length = 0; @@ -69,6 +66,7 @@ uint32_t rssi_scale_in(uint32_t value, uint32_t max) { static uint8_t hex_table[256] = {0}; +static char hex_char_table[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', }; void hex_init() { for (int i = '0'; i <= '9'; i++) { @@ -99,3 +97,18 @@ uint64_t hex_to_int(char *in, size_t bytes) { } return ret; } + +void hex_from_bin(char *out, char *in, size_t bytes) { + uint8_t *in2 = (uint8_t *) in; + for (size_t i = 0, j = 0; i < bytes; i++, j += 2) { + out[j] = hex_char_table[in2[i] >> 4]; + out[j + 1] = hex_char_table[in2[i] & 0xf]; + } +} + + +void uuid_gen(char *out) { + uuid_t uuid; + uuid_generate(uuid); + uuid_unparse(uuid, out); +} diff --git a/common.h b/common.h index ec7c7be..e000f6a 100644 --- a/common.h +++ b/common.h @@ -49,6 +49,10 @@ struct packet { //////// mlat +#define MLAT_MHZ 120 +#define MLAT_MAX UINT64_MAX +#define RSSI_MAX UINT32_MAX + struct mlat_state { uint64_t timestamp_last; uint64_t timestamp_generation; @@ -67,8 +71,10 @@ uint32_t rssi_scale_in(uint32_t, uint32_t); void hex_init(); void hex_to_bin(char *, char *, size_t); uint64_t hex_to_int(char *, size_t); +void hex_from_bin(char *, char *, size_t); -///////// misc +///////// uuid #define UUID_LEN 37 +void uuid_gen(char *); diff --git a/json.c b/json.c new file mode 100644 index 0000000..e5718c2 --- /dev/null +++ b/json.c @@ -0,0 +1,64 @@ +#include +#include + +#include "client.h" +#include "json.h" + + +// Hobo JSON to avoid overhead. Assumes that we can't get quotes in the data. + +void json_init() { +} + +static size_t json_hello(char *buf) { + int len = snprintf(buf, SERIALIZE_LEN, + "{\"mlat_timestamp_mhz\":%ju,\"mlat_timestamp_max\":%ju,\"rssi_max\":%ju}\n", + (uintmax_t) MLAT_MHZ, + (uintmax_t) MLAT_MAX, + (uintmax_t) RSSI_MAX); + assert(len < SERIALIZE_LEN); + return len; +} + +static size_t json_serialize_mode_s_short(struct packet *packet, char *buf) { + char hexbuf[14]; + hex_from_bin(hexbuf, packet->data, 7); + int len = snprintf(buf, SERIALIZE_LEN, + "{\"payload\":\"%.14s\",\"mlat_timestamp\":%ju,\"rssi\":%ju}\n", + hexbuf, + (uintmax_t) packet->mlat_timestamp, + (uintmax_t) packet->rssi); + assert(len < SERIALIZE_LEN); + return len; +} + +static size_t json_serialize_mode_s_long(struct packet *packet, char *buf) { + char hexbuf[28]; + hex_from_bin(hexbuf, packet->data, 14); + int len = snprintf(buf, SERIALIZE_LEN, + "{\"payload\":\"%.28s\",\"mlat_timestamp\":%ju,\"rssi\":%ju}\n", + hexbuf, + (uintmax_t) packet->mlat_timestamp, + (uintmax_t) packet->rssi); + assert(len < SERIALIZE_LEN); + return len; +} + +size_t json_serialize(struct packet *packet, char *buf) { + if (!packet) { + return json_hello(buf); + } + + switch (packet->type) { + case MODE_AC: + return 0; + + case MODE_S_SHORT: + return json_serialize_mode_s_short(packet, buf); + + case MODE_S_LONG: + return json_serialize_mode_s_long(packet, buf); + } + + return 0; +} diff --git a/json.h b/json.h new file mode 100644 index 0000000..813f447 --- /dev/null +++ b/json.h @@ -0,0 +1,7 @@ +#pragma once + +#include "common.h" + + +void json_init(); +size_t json_serialize(struct packet *, char *);