Initial HTTP support with planes animated using google map.
This commit is contained in:
parent
4203b9caf6
commit
a583615b66
16
README.md
16
README.md
|
@ -16,7 +16,10 @@ The main features are:
|
||||||
* Interactive mode where aircrafts currently detected are shown
|
* Interactive mode where aircrafts currently detected are shown
|
||||||
as a list refreshing as more data arrives.
|
as a list refreshing as more data arrives.
|
||||||
* CPR coordinates decoding and track calculation from velocity.
|
* CPR coordinates decoding and track calculation from velocity.
|
||||||
* TCP server streaming and recceiving raw data to/from connected clients (using --net).
|
* TCP server streaming and recceiving raw data to/from connected clients
|
||||||
|
(using --net).
|
||||||
|
* Embedded HTTP server that displays the currently detected aircrafts on
|
||||||
|
Google Map.
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
---
|
---
|
||||||
|
@ -39,6 +42,11 @@ To run the program in interactive mode:
|
||||||
|
|
||||||
./dump1090 --interactive
|
./dump1090 --interactive
|
||||||
|
|
||||||
|
To run the program in interactive mode, with networking support, and connect
|
||||||
|
with your browser to http://localhost:8080 to see live traffic:
|
||||||
|
|
||||||
|
./dump1090 --interactive --net
|
||||||
|
|
||||||
In iteractive mode it is possible to have a less information dense but more
|
In iteractive mode it is possible to have a less information dense but more
|
||||||
"arcade style" output, where the screen is refreshed every second displaying
|
"arcade style" output, where the screen is refreshed every second displaying
|
||||||
all the recently seen aircrafts with some additional information such as
|
all the recently seen aircrafts with some additional information such as
|
||||||
|
@ -136,7 +144,11 @@ normal traffic from RTL devices or from file when --ifile is used.
|
||||||
It is possible to use Dump1090 just as an hub using --ifile with /dev/zero
|
It is possible to use Dump1090 just as an hub using --ifile with /dev/zero
|
||||||
as argument as in the following example:
|
as argument as in the following example:
|
||||||
|
|
||||||
./dump1090 --ifile /dev/zero --net --interactive
|
./dump1090 --net-only
|
||||||
|
|
||||||
|
Or alternatively to see what's happening on the screen:
|
||||||
|
|
||||||
|
./dump1090 --net-only --interactive
|
||||||
|
|
||||||
Then you can feed it from different data sources from the internet.
|
Then you can feed it from different data sources from the internet.
|
||||||
|
|
||||||
|
|
302
dump1090.c
302
dump1090.c
|
@ -40,6 +40,7 @@
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include "rtl-sdr.h"
|
#include "rtl-sdr.h"
|
||||||
#include "anet.h"
|
#include "anet.h"
|
||||||
|
|
||||||
|
@ -81,7 +82,8 @@
|
||||||
#define MODES_NET_MAX_FD 1024
|
#define MODES_NET_MAX_FD 1024
|
||||||
#define MODES_NET_OUTPUT_RAW_PORT 30002
|
#define MODES_NET_OUTPUT_RAW_PORT 30002
|
||||||
#define MODES_NET_INPUT_RAW_PORT 30001
|
#define MODES_NET_INPUT_RAW_PORT 30001
|
||||||
#define MODES_CLIENT_BUF_SIZE 256
|
#define MODES_NET_HTTP_PORT 8080
|
||||||
|
#define MODES_CLIENT_BUF_SIZE 1024
|
||||||
|
|
||||||
#define MODES_NOTUSED(V) ((void) V)
|
#define MODES_NOTUSED(V) ((void) V)
|
||||||
|
|
||||||
|
@ -142,6 +144,7 @@ struct {
|
||||||
int maxfd; /* Greatest fd currently active. */
|
int maxfd; /* Greatest fd currently active. */
|
||||||
int ros; /* Raw output listening socket. */
|
int ros; /* Raw output listening socket. */
|
||||||
int ris; /* Raw input listening socket. */
|
int ris; /* Raw input listening socket. */
|
||||||
|
int https; /* HTTP listening socket. */
|
||||||
|
|
||||||
/* Configuration */
|
/* Configuration */
|
||||||
char *filename; /* Input form file, --ifile option. */
|
char *filename; /* Input form file, --ifile option. */
|
||||||
|
@ -150,8 +153,10 @@ struct {
|
||||||
int raw; /* Raw output format. */
|
int raw; /* Raw output format. */
|
||||||
int debug; /* Debugging mode. */
|
int debug; /* Debugging mode. */
|
||||||
int net; /* Enable networking. */
|
int net; /* Enable networking. */
|
||||||
|
int net_only; /* Enable just networking. */
|
||||||
int net_output_raw_port; /* Raw output TCP port. */
|
int net_output_raw_port; /* Raw output TCP port. */
|
||||||
int net_input_raw_port; /* Raw input TCP port. */
|
int net_input_raw_port; /* Raw input TCP port. */
|
||||||
|
int net_http_port; /* HTTP port. */
|
||||||
int interactive; /* Interactive mode */
|
int interactive; /* Interactive mode */
|
||||||
int interactive_rows; /* Interactive mode: max number of rows. */
|
int interactive_rows; /* Interactive mode: max number of rows. */
|
||||||
int interactive_ttl; /* Interactive mode: TTL before deletion. */
|
int interactive_ttl; /* Interactive mode: TTL before deletion. */
|
||||||
|
@ -169,6 +174,7 @@ struct {
|
||||||
long long stat_goodcrc;
|
long long stat_goodcrc;
|
||||||
long long stat_badcrc;
|
long long stat_badcrc;
|
||||||
long long stat_fixed;
|
long long stat_fixed;
|
||||||
|
long long stat_http_requests;
|
||||||
} Modes;
|
} Modes;
|
||||||
|
|
||||||
/* The struct we use to store information about a decoded message. */
|
/* The struct we use to store information about a decoded message. */
|
||||||
|
@ -244,8 +250,10 @@ void modesInitConfig(void) {
|
||||||
Modes.check_crc = 1;
|
Modes.check_crc = 1;
|
||||||
Modes.raw = 0;
|
Modes.raw = 0;
|
||||||
Modes.net = 0;
|
Modes.net = 0;
|
||||||
|
Modes.net_only = 0;
|
||||||
Modes.net_output_raw_port = MODES_NET_OUTPUT_RAW_PORT;
|
Modes.net_output_raw_port = MODES_NET_OUTPUT_RAW_PORT;
|
||||||
Modes.net_input_raw_port = MODES_NET_INPUT_RAW_PORT;
|
Modes.net_input_raw_port = MODES_NET_INPUT_RAW_PORT;
|
||||||
|
Modes.net_http_port = MODES_NET_HTTP_PORT;
|
||||||
Modes.onlyaddr = 0;
|
Modes.onlyaddr = 0;
|
||||||
Modes.debug = 0;
|
Modes.debug = 0;
|
||||||
Modes.interactive = 0;
|
Modes.interactive = 0;
|
||||||
|
@ -292,6 +300,7 @@ void modesInit(void) {
|
||||||
Modes.stat_goodcrc = 0;
|
Modes.stat_goodcrc = 0;
|
||||||
Modes.stat_badcrc = 0;
|
Modes.stat_badcrc = 0;
|
||||||
Modes.stat_fixed = 0;
|
Modes.stat_fixed = 0;
|
||||||
|
Modes.stat_http_requests = 0;
|
||||||
Modes.exit = 0;
|
Modes.exit = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1257,9 +1266,13 @@ void detectModeS(uint16_t *m, uint32_t mlen) {
|
||||||
* further processing and visualization. */
|
* further processing and visualization. */
|
||||||
void useModesMessage(struct modesMessage *mm) {
|
void useModesMessage(struct modesMessage *mm) {
|
||||||
if (!Modes.stats && (Modes.check_crc == 0 || mm->crcok)) {
|
if (!Modes.stats && (Modes.check_crc == 0 || mm->crcok)) {
|
||||||
if (Modes.interactive) {
|
/* Track aircrafts in interactive mode or if the HTTP
|
||||||
|
* interface is enabled. */
|
||||||
|
if (Modes.interactive || Modes.stat_http_requests > 0) {
|
||||||
interactiveReceiveData(mm);
|
interactiveReceiveData(mm);
|
||||||
} else {
|
}
|
||||||
|
/* In non-interactive way, display messages on standard output. */
|
||||||
|
if (!Modes.interactive) {
|
||||||
displayModesMessage(mm);
|
displayModesMessage(mm);
|
||||||
if (!Modes.raw && !Modes.onlyaddr) printf("\n");
|
if (!Modes.raw && !Modes.onlyaddr) printf("\n");
|
||||||
}
|
}
|
||||||
|
@ -1596,27 +1609,31 @@ void snipMode(int level) {
|
||||||
|
|
||||||
/* Networking "stack" initialization. */
|
/* Networking "stack" initialization. */
|
||||||
void modesInitNet(void) {
|
void modesInitNet(void) {
|
||||||
|
struct {
|
||||||
|
char *descr;
|
||||||
|
int *socket;
|
||||||
|
int port;
|
||||||
|
} services[3] = {
|
||||||
|
{"Raw TCP output", &Modes.ros, Modes.net_output_raw_port},
|
||||||
|
{"Raw TCP input", &Modes.ris, Modes.net_input_raw_port},
|
||||||
|
{"HTTP server", &Modes.https, Modes.net_http_port}
|
||||||
|
};
|
||||||
|
int j;
|
||||||
|
|
||||||
memset(Modes.clients,0,sizeof(Modes.clients));
|
memset(Modes.clients,0,sizeof(Modes.clients));
|
||||||
Modes.maxfd = -1;
|
Modes.maxfd = -1;
|
||||||
|
|
||||||
/* Raw output port */
|
for (j = 0; j < 3; j++) {
|
||||||
Modes.ros = anetTcpServer(Modes.aneterr, Modes.net_output_raw_port, NULL);
|
int s = anetTcpServer(Modes.aneterr, services[j].port, NULL);
|
||||||
if (Modes.ros == -1) {
|
if (s == -1) {
|
||||||
fprintf(stderr, "Error opening raw TCP output port %d: %s\n",
|
fprintf(stderr, "Error opening the listening port %d (%s): %s\n",
|
||||||
Modes.net_output_raw_port, Modes.aneterr);
|
services[j].port, services[j].descr, strerror(errno));
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
anetNonBlock(Modes.aneterr, s);
|
||||||
/* Raw input port */
|
*services[j].socket = s;
|
||||||
Modes.ris = anetTcpServer(Modes.aneterr, Modes.net_input_raw_port, NULL);
|
|
||||||
if (Modes.ris == -1) {
|
|
||||||
fprintf(stderr, "Error opening raw TCP input port %d: %s\n",
|
|
||||||
Modes.net_input_raw_port, Modes.aneterr);
|
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
anetNonBlock(Modes.aneterr, Modes.ros);
|
|
||||||
anetNonBlock(Modes.aneterr, Modes.ris);
|
|
||||||
signal(SIGPIPE, SIG_IGN);
|
signal(SIGPIPE, SIG_IGN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1627,10 +1644,11 @@ void modesAcceptClients(void) {
|
||||||
int fd, port;
|
int fd, port;
|
||||||
unsigned int j;
|
unsigned int j;
|
||||||
struct client *c;
|
struct client *c;
|
||||||
int services[2];
|
int services[3];
|
||||||
|
|
||||||
services[0] = Modes.ros;
|
services[0] = Modes.ros;
|
||||||
services[1] = Modes.ris;
|
services[1] = Modes.ris;
|
||||||
|
services[2] = Modes.https;
|
||||||
|
|
||||||
for (j = 0; j < sizeof(services)/sizeof(int); j++) {
|
for (j = 0; j < sizeof(services)/sizeof(int); j++) {
|
||||||
fd = anetTcpAccept(Modes.aneterr, services[j], NULL, &port);
|
fd = anetTcpAccept(Modes.aneterr, services[j], NULL, &port);
|
||||||
|
@ -1712,16 +1730,30 @@ int hexDigitVal(int c) {
|
||||||
|
|
||||||
/* This function decodes a string representing a Mode S message in
|
/* This function decodes a string representing a Mode S message in
|
||||||
* raw hex format like: *8D4B969699155600E87406F5B69F;
|
* raw hex format like: *8D4B969699155600E87406F5B69F;
|
||||||
|
* The string is supposed to be at the start of the client buffer
|
||||||
|
* and null-terminated.
|
||||||
*
|
*
|
||||||
* The message is passed to the higher level layers, so it feeds
|
* The message is passed to the higher level layers, so it feeds
|
||||||
* the selected screen output, the network output and so forth.
|
* the selected screen output, the network output and so forth.
|
||||||
*
|
*
|
||||||
* If the message looks invalid is silently discarded. */
|
* If the message looks invalid is silently discarded. */
|
||||||
void decodeHexMessage(char *hex) {
|
void decodeHexMessage(struct client *c) {
|
||||||
|
char *hex = c->buf;
|
||||||
int l = strlen(hex), j;
|
int l = strlen(hex), j;
|
||||||
unsigned char msg[MODES_LONG_MSG_BYTES];
|
unsigned char msg[MODES_LONG_MSG_BYTES];
|
||||||
struct modesMessage mm;
|
struct modesMessage mm;
|
||||||
|
|
||||||
|
/* Remove spaces on the left and on the right. */
|
||||||
|
while(l && isspace(hex[l-1])) {
|
||||||
|
hex[l-1] = '\0';
|
||||||
|
l--;
|
||||||
|
}
|
||||||
|
while(isspace(*hex)) {
|
||||||
|
hex++;
|
||||||
|
l--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Turn the message into binary. */
|
||||||
if (l < 2 || hex[0] != '*' || hex[l-1] != ';') return;
|
if (l < 2 || hex[0] != '*' || hex[l-1] != ';') return;
|
||||||
hex++; l-=2; /* Skip * and ; */
|
hex++; l-=2; /* Skip * and ; */
|
||||||
if (l > MODES_LONG_MSG_BYTES*2) return; /* Too long message... broken. */
|
if (l > MODES_LONG_MSG_BYTES*2) return; /* Too long message... broken. */
|
||||||
|
@ -1736,52 +1768,164 @@ void decodeHexMessage(char *hex) {
|
||||||
useModesMessage(&mm);
|
useModesMessage(&mm);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This function polls all the clients using read() in order to receive new
|
/* Return a description of planes in json. */
|
||||||
|
char *aircraftsToJson(int *len) {
|
||||||
|
struct aircraft *a = Modes.aircrafts;
|
||||||
|
int buflen = 1024; /* The initial buffer is incremented as needed. */
|
||||||
|
char *buf = malloc(buflen), *p = buf;
|
||||||
|
int l;
|
||||||
|
|
||||||
|
l = snprintf(p,buflen,"[\n");
|
||||||
|
p += l; buflen -= l;
|
||||||
|
while(a) {
|
||||||
|
int altitude = a->altitude, speed = a->speed;
|
||||||
|
|
||||||
|
/* Convert units to metric if --metric was specified. */
|
||||||
|
if (Modes.metric) {
|
||||||
|
altitude /= 3.2828;
|
||||||
|
speed *= 1.852;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a->lat != 0 && a->lon != 0) {
|
||||||
|
l = snprintf(p,buflen,
|
||||||
|
"{\"hex\":\"%s\", \"lat\":%f, \"lon\":%f, \"track\":%d},\n",
|
||||||
|
a->hexaddr, a->lat, a->lon, a->track);
|
||||||
|
p += l; buflen -= l;
|
||||||
|
/* Resize if needed. */
|
||||||
|
if (buflen < 256) {
|
||||||
|
int used = p-buf;
|
||||||
|
buflen += 1024; /* Our increment. */
|
||||||
|
buf = realloc(buf,used+buflen);
|
||||||
|
p = buf+used;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a = a->next;
|
||||||
|
}
|
||||||
|
/* Remove the final comma if any, and closes the json array. */
|
||||||
|
if (*(p-2) == ',') {
|
||||||
|
*(p-2) = '\n';
|
||||||
|
p--;
|
||||||
|
buflen++;
|
||||||
|
}
|
||||||
|
l = snprintf(p,buflen,"]\n");
|
||||||
|
p += l; buflen -= l;
|
||||||
|
|
||||||
|
*len = p-buf;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MODES_CONTENT_TYPE_HTML "text/html;charset=utf-8"
|
||||||
|
#define MODES_CONTENT_TYPE_JSON "application/json;charset=utf-8"
|
||||||
|
|
||||||
|
/* Get an HTTP request header and write the response to the client.
|
||||||
|
* Again here we assume that the socket buffer is enough without doing
|
||||||
|
* any kind of userspace buffering. */
|
||||||
|
void handleHTTPRequest(struct client *c) {
|
||||||
|
char hdr[512];
|
||||||
|
int clen, hdrlen;
|
||||||
|
int keepalive;
|
||||||
|
char *p, *url, *content;
|
||||||
|
char *ctype;
|
||||||
|
|
||||||
|
/* printf("HTTP request: %s\n", c->buf); */
|
||||||
|
|
||||||
|
/* Minimally parse the request. */
|
||||||
|
keepalive = strstr(c->buf, "keep-alive") != NULL;
|
||||||
|
p = strchr(c->buf,' ');
|
||||||
|
if (!p) return; /* There should be the method and a space... */
|
||||||
|
url = ++p; /* Now this should point to the requested URL. */
|
||||||
|
p = strchr(p, ' ');
|
||||||
|
if (!p) return; /* There should be a space before HTTP/... */
|
||||||
|
*p = '\0';
|
||||||
|
/* printf("URL: %s\n", url); */
|
||||||
|
|
||||||
|
/* Select the content to send, we have just two so far:
|
||||||
|
* "/" -> Our google map application.
|
||||||
|
* "/data.json" -> Our ajax request to update planes. */
|
||||||
|
if (strstr(url, "/data.json")) {
|
||||||
|
content = aircraftsToJson(&clen);
|
||||||
|
ctype = MODES_CONTENT_TYPE_JSON;
|
||||||
|
} else {
|
||||||
|
struct stat sbuf;
|
||||||
|
int fd = -1;
|
||||||
|
|
||||||
|
if (stat("gmap.html",&sbuf) != -1 &&
|
||||||
|
(fd = open("gmap.html",O_RDONLY)) != -1)
|
||||||
|
{
|
||||||
|
content = malloc(sbuf.st_size);
|
||||||
|
read(fd,content,sbuf.st_size);
|
||||||
|
} else {
|
||||||
|
char buf[128];
|
||||||
|
|
||||||
|
clen = snprintf(buf,sizeof(buf),"Error opening HTML file: %s",
|
||||||
|
strerror(errno));
|
||||||
|
content = strdup(buf);
|
||||||
|
}
|
||||||
|
if (fd != -1) close(fd);
|
||||||
|
ctype = MODES_CONTENT_TYPE_HTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create the header and send the reply. */
|
||||||
|
hdrlen = snprintf(hdr, sizeof(hdr),
|
||||||
|
"HTTP/1.1 200 OK\r\n"
|
||||||
|
"Server: Dump1090\r\n"
|
||||||
|
"Content-Type: %s\r\n"
|
||||||
|
"Connection: %s\r\n"
|
||||||
|
"Content-Length: %d\r\n"
|
||||||
|
"\r\n",
|
||||||
|
ctype,
|
||||||
|
keepalive ? "keep-alive" : "close",
|
||||||
|
clen);
|
||||||
|
write(c->fd, hdr, hdrlen);
|
||||||
|
|
||||||
|
/* Send the actual content. */
|
||||||
|
write(c->fd, content, clen);
|
||||||
|
free(content);
|
||||||
|
Modes.stat_http_requests++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function polls the clients using read() in order to receive new
|
||||||
* messages from the net.
|
* messages from the net.
|
||||||
*
|
*
|
||||||
* Every full message received is decoded and passed to the higher layers. */
|
* The message is supposed to be separated by the next message by the
|
||||||
void modesReceiveRawInput(void) {
|
* separator 'sep', that is a null-terminated C string.
|
||||||
int j;
|
*
|
||||||
struct client *c;
|
* Every full message received is decoded and passed to the higher layers
|
||||||
|
* calling the function 'handler'. */
|
||||||
for (j = 0; j <= Modes.maxfd; j++) {
|
void modesReadFromClient(struct client *c, char *sep,
|
||||||
c = Modes.clients[j];
|
void(*handler)(struct client *))
|
||||||
if (c && c->service == Modes.ris) {
|
{
|
||||||
while(1) {
|
while(1) {
|
||||||
int left = sizeof(c->buf) - c->buflen;
|
int left = sizeof(c->buf) - c->buflen;
|
||||||
int nread = read(j, c->buf+c->buflen, left);
|
int nread = read(c->fd, c->buf+c->buflen, left);
|
||||||
int decoded = 0;
|
int fullmsg = 0;
|
||||||
int oldpos = c->buflen;
|
|
||||||
int i;
|
int i;
|
||||||
|
char *p;
|
||||||
|
|
||||||
if (nread < 0) {
|
if (nread < 0) {
|
||||||
if (nread == 0 || errno != EAGAIN) {
|
if (nread == 0 || errno != EAGAIN) {
|
||||||
/* Error, or end of file. */
|
/* Error, or end of file. */
|
||||||
modesFreeClient(j);
|
modesFreeClient(c->fd);
|
||||||
}
|
}
|
||||||
break; /* Serve next client. */
|
break; /* Serve next client. */
|
||||||
}
|
}
|
||||||
c->buflen += nread;
|
c->buflen += nread;
|
||||||
|
|
||||||
/* If there is a complete message there must be a newline
|
/* If there is a complete message there must be the separator 'sep'
|
||||||
* in the buffer. The iteration starts from 'oldpos' as
|
* in the buffer, note that we full-scan the buffer at every read
|
||||||
* we need to check only the chars we read in this interaction
|
* for simplicity. */
|
||||||
* as we are sure there is no newline in the pre-existing
|
if ((p = strstr(c->buf, sep)) != NULL) {
|
||||||
* buffer. */
|
i = p - c->buf; /* Turn it as an index inside the buffer. */
|
||||||
for (i = oldpos; i < c->buflen; i++) {
|
c->buf[i] = '\0'; /* Te handler expects null terminated strings. */
|
||||||
if (c->buf[i] == '\n') {
|
handler(c); /* Call the function to process the message. */
|
||||||
c->buf[i] = '\0';
|
|
||||||
if (i && c->buf[i-1] == '\r') c->buf[i-1] = '\0';
|
|
||||||
decodeHexMessage(c->buf);
|
|
||||||
/* Move what's left at the start of the buffer. */
|
/* Move what's left at the start of the buffer. */
|
||||||
i++;
|
i += strlen(sep); /* The separator is part of the previous msg. */
|
||||||
memmove(c->buf,c->buf+i,c->buflen-i);
|
memmove(c->buf,c->buf+i,c->buflen-i);
|
||||||
c->buflen -= i;
|
c->buflen -= i;
|
||||||
/* Maybe there are more messages inside the buffer.
|
/* Maybe there are more messages inside the buffer.
|
||||||
* Start looping from the start again. */
|
* Start looping from the start again. */
|
||||||
i = -1;
|
i = -1;
|
||||||
decoded = 1;
|
fullmsg = 1;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/* If our buffer is full discard it, this is some badly
|
/* If our buffer is full discard it, this is some badly
|
||||||
* formatted shit. */
|
* formatted shit. */
|
||||||
|
@ -1792,9 +1936,22 @@ void modesReceiveRawInput(void) {
|
||||||
}
|
}
|
||||||
/* If no message was decoded process the next client, otherwise
|
/* If no message was decoded process the next client, otherwise
|
||||||
* read more data from the same client. */
|
* read more data from the same client. */
|
||||||
if (!decoded) break;
|
if (!fullmsg) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Read data from clients. This function actually delegates a lower-level
|
||||||
|
* function that depends on the kind of service (raw, http, ...). */
|
||||||
|
void modesReadFromClients(void) {
|
||||||
|
int j;
|
||||||
|
struct client *c;
|
||||||
|
|
||||||
|
for (j = 0; j <= Modes.maxfd; j++) {
|
||||||
|
if ((c = Modes.clients[j]) == NULL) continue;
|
||||||
|
if (c->service == Modes.ris)
|
||||||
|
modesReadFromClient(c,"\n",decodeHexMessage);
|
||||||
|
else if (c->service == Modes.https)
|
||||||
|
modesReadFromClient(c,"\r\n\r\n",handleHTTPRequest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1812,8 +1969,10 @@ void showHelp(void) {
|
||||||
"--interactive-ttl <sec> Remove from list if idle for <sec> (default: 60).\n"
|
"--interactive-ttl <sec> Remove from list if idle for <sec> (default: 60).\n"
|
||||||
"--raw Show only messages hex values.\n"
|
"--raw Show only messages hex values.\n"
|
||||||
"--net Enable networking.\n"
|
"--net Enable networking.\n"
|
||||||
|
"--net-only Enable just networking, no RTL device or file used.\n"
|
||||||
"--net-ro-port <port> TCP listening port for raw output (default: 30002).\n"
|
"--net-ro-port <port> TCP listening port for raw output (default: 30002).\n"
|
||||||
"--net-ri-port <port> TCP listening port for raw input (default: 30001).\n"
|
"--net-ri-port <port> TCP listening port for raw input (default: 30001).\n"
|
||||||
|
"--net-http-port <port> HTTP server port (default: 8080).\n"
|
||||||
"--no-fix Disable single-bits error correction using CRC.\n"
|
"--no-fix Disable single-bits error correction using CRC.\n"
|
||||||
"--no-crc-check Disable messages with broken CRC (discouraged).\n"
|
"--no-crc-check Disable messages with broken CRC (discouraged).\n"
|
||||||
"--stats With --ifile print stats at exit. No other output.\n"
|
"--stats With --ifile print stats at exit. No other output.\n"
|
||||||
|
@ -1825,6 +1984,26 @@ void showHelp(void) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This function is called a few times every second by main in order to
|
||||||
|
* perform tasks we need to do continuously, like accepting new clients
|
||||||
|
* from the net, refreshing the screen in interactive mode, and so forth. */
|
||||||
|
void backgroundTasks(void) {
|
||||||
|
if (Modes.net) {
|
||||||
|
modesAcceptClients();
|
||||||
|
modesReadFromClients();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Refresh screen when in interactive mode. */
|
||||||
|
if (Modes.interactive &&
|
||||||
|
(mstime() - Modes.interactive_last_update) >
|
||||||
|
MODES_INTERACTIVE_REFRESH_TIME)
|
||||||
|
{
|
||||||
|
interactiveRemoveStaleAircrafts();
|
||||||
|
interactiveShowData();
|
||||||
|
Modes.interactive_last_update = mstime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
|
@ -1853,10 +2032,15 @@ int main(int argc, char **argv) {
|
||||||
Modes.raw = 1;
|
Modes.raw = 1;
|
||||||
} else if (!strcmp(argv[j],"--net")) {
|
} else if (!strcmp(argv[j],"--net")) {
|
||||||
Modes.net = 1;
|
Modes.net = 1;
|
||||||
|
} else if (!strcmp(argv[j],"--net-only")) {
|
||||||
|
Modes.net = 1;
|
||||||
|
Modes.net_only = 1;
|
||||||
} else if (!strcmp(argv[j],"--net-ro-port") && more) {
|
} else if (!strcmp(argv[j],"--net-ro-port") && more) {
|
||||||
Modes.net_output_raw_port = atoi(argv[++j]);
|
Modes.net_output_raw_port = atoi(argv[++j]);
|
||||||
} else if (!strcmp(argv[j],"--net-ri-port") && more) {
|
} else if (!strcmp(argv[j],"--net-ri-port") && more) {
|
||||||
Modes.net_input_raw_port = atoi(argv[++j]);
|
Modes.net_input_raw_port = atoi(argv[++j]);
|
||||||
|
} else if (!strcmp(argv[j],"--net-http-port") && more) {
|
||||||
|
Modes.net_http_port = atoi(argv[++j]);
|
||||||
} else if (!strcmp(argv[j],"--onlyaddr")) {
|
} else if (!strcmp(argv[j],"--onlyaddr")) {
|
||||||
Modes.onlyaddr = 1;
|
Modes.onlyaddr = 1;
|
||||||
} else if (!strcmp(argv[j],"--metric")) {
|
} else if (!strcmp(argv[j],"--metric")) {
|
||||||
|
@ -1888,7 +2072,9 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
/* Initialization */
|
/* Initialization */
|
||||||
modesInit();
|
modesInit();
|
||||||
if (Modes.filename == NULL) {
|
if (Modes.net_only) {
|
||||||
|
fprintf(stderr,"Net-only mode, no RTL device or file open.\n");
|
||||||
|
} else if (Modes.filename == NULL) {
|
||||||
modesInitRTLSDR();
|
modesInitRTLSDR();
|
||||||
} else {
|
} else {
|
||||||
if (Modes.filename[0] == '-' && Modes.filename[1] == '\0') {
|
if (Modes.filename[0] == '-' && Modes.filename[1] == '\0') {
|
||||||
|
@ -1900,6 +2086,13 @@ int main(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
if (Modes.net) modesInitNet();
|
if (Modes.net) modesInitNet();
|
||||||
|
|
||||||
|
/* If the user specifies --net-only, just run in order to serve network
|
||||||
|
* clients without reading data from the RTL device. */
|
||||||
|
while (Modes.net_only) {
|
||||||
|
backgroundTasks();
|
||||||
|
usleep(100000);
|
||||||
|
}
|
||||||
|
|
||||||
/* Create the thread that will read the data from the device. */
|
/* Create the thread that will read the data from the device. */
|
||||||
pthread_create(&Modes.reader_thread, NULL, readerThreadEntryPoint, NULL);
|
pthread_create(&Modes.reader_thread, NULL, readerThreadEntryPoint, NULL);
|
||||||
|
|
||||||
|
@ -1922,20 +2115,7 @@ int main(int argc, char **argv) {
|
||||||
* slow processors). */
|
* slow processors). */
|
||||||
pthread_mutex_unlock(&Modes.data_mutex);
|
pthread_mutex_unlock(&Modes.data_mutex);
|
||||||
detectModeS(Modes.magnitude, Modes.data_len/2);
|
detectModeS(Modes.magnitude, Modes.data_len/2);
|
||||||
if (Modes.net) {
|
backgroundTasks();
|
||||||
modesAcceptClients();
|
|
||||||
modesReceiveRawInput();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Refresh screen when in interactive mode. */
|
|
||||||
if (Modes.interactive &&
|
|
||||||
(mstime() - Modes.interactive_last_update) >
|
|
||||||
MODES_INTERACTIVE_REFRESH_TIME)
|
|
||||||
{
|
|
||||||
interactiveRemoveStaleAircrafts();
|
|
||||||
interactiveShowData();
|
|
||||||
Modes.interactive_last_update = mstime();
|
|
||||||
}
|
|
||||||
pthread_mutex_lock(&Modes.data_mutex);
|
pthread_mutex_lock(&Modes.data_mutex);
|
||||||
if (Modes.exit) break;
|
if (Modes.exit) break;
|
||||||
}
|
}
|
||||||
|
|
88
gmap.html
Normal file
88
gmap.html
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
|
||||||
|
<style type="text/css">
|
||||||
|
html { height: 100% }
|
||||||
|
body { height: 100%; margin: 0; padding: 0 }
|
||||||
|
#map_canvas { height: 100% }
|
||||||
|
</style>
|
||||||
|
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js">
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript"
|
||||||
|
src="https://maps.googleapis.com/maps/api/js?sensor=true">
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
Map=null;
|
||||||
|
CenterLat=50.0;
|
||||||
|
CenterLon=9.0;
|
||||||
|
Planes={};
|
||||||
|
|
||||||
|
function getIconForPlane(plane) {
|
||||||
|
return {
|
||||||
|
strokeWeight: 2,
|
||||||
|
path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW,
|
||||||
|
scale: 5,
|
||||||
|
fillColor: 'yellow',
|
||||||
|
fillOpacity: 0.8,
|
||||||
|
rotation: plane.track
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchData() {
|
||||||
|
$.getJSON('/data.json', function(data) {
|
||||||
|
var stillhere = {}
|
||||||
|
for (var j=0; j < data.length; j++) {
|
||||||
|
var plane = data[j];
|
||||||
|
stillhere[plane.hex] = true;
|
||||||
|
|
||||||
|
if (Planes[plane.hex]) {
|
||||||
|
var myplane = Planes[plane.hex];
|
||||||
|
var marker = myplane.marker;
|
||||||
|
var icon = marker.getIcon();
|
||||||
|
var newpos = new google.maps.LatLng(plane.lat, plane.lon);
|
||||||
|
marker.setPosition(newpos);
|
||||||
|
marker.setIcon(getIconForPlane(plane));
|
||||||
|
} else {
|
||||||
|
var marker = new google.maps.Marker({
|
||||||
|
position: new google.maps.LatLng(plane.lat, plane.lon),
|
||||||
|
map: Map,
|
||||||
|
title: plane.hex,
|
||||||
|
icon: getIconForPlane(plane)
|
||||||
|
});
|
||||||
|
plane.marker = marker;
|
||||||
|
Planes[plane.hex] = plane;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove idle planes. */
|
||||||
|
for (var p in Planes) {
|
||||||
|
if (!stillhere[p]) {
|
||||||
|
Planes[p].marker.setMap(null);
|
||||||
|
delete Planes[p];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function initialize() {
|
||||||
|
var mapOptions = {
|
||||||
|
center: new google.maps.LatLng(CenterLat, CenterLon),
|
||||||
|
zoom: 8,
|
||||||
|
mapTypeId: google.maps.MapTypeId.ROADMAP
|
||||||
|
};
|
||||||
|
Map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
|
||||||
|
|
||||||
|
/* Setup our timer to poll from the server. */
|
||||||
|
window.setInterval(function() {
|
||||||
|
fetchData();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body onload="initialize()">
|
||||||
|
<div id="map_canvas" style="width:100%; height:100%"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in a new issue