diff --git a/README.md b/README.md index 3fdd396ed..1d4b0bed7 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,16 @@ In iteractive mode it is possible to have a less information dense but more all the recently seen aircrafts with some additional information such as altitude and flight number, extracted from the received Mode S packets. +When using network server mode, you can quickly set your "home" location +using Google Maps' address resolution by passing a ```--home``` string +argument: + + ./dump1090 --interactive --home "1600 Pennsylvania Ave NW, Washington, DC 20500" + +This will center the map, draw a general radar indicator, and use this +base for calculating directionality and distance from you to the various +transponders on the map. + Using files as source of data --- diff --git a/dump1090.c b/dump1090.c index 60882fc73..8d8d0f801 100644 --- a/dump1090.c +++ b/dump1090.c @@ -422,6 +422,9 @@ void showHelp(void) { "--net-buffer TCP buffer size 64Kb * (2^n) (default: n=0, 64Kb)\n" "--lat Reference/receiver latitude for surface posn (opt)\n" "--lon Reference/receiver longitude for surface posn (opt)\n" +"--home Google Maps Search String for setting \"Site\" in web UI (opt; overrides lat/long)\n" +" (search strings can be addresses, human text, or lat/lon\n" +" coords, eg. \"Voghera, Italy\" or \"Grand Canyon, AZ, US\")\n" "--fix Enable 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" @@ -445,6 +448,13 @@ void showHelp(void) { " p = Log frames with bad preamble\n" " n = Log network debugging info\n" " j = Log frames to frames.js, loadable by debug.html\n" +"\n" +"When running in --net mode, dump1090 will look for './public_html' by default\n" +"to serve supporting files. This can be overridden with an environment setting\n" +"of DUMP1090_WEB (e.g., \"DUMP1090_WEB=/path/to/the/dump1090/public_html\" - no\n" +"trailing slash should be used). Once set, the binary can be run from any path\n" +"and it will be able to serve up the content correctly.\n" +"\n" ); } @@ -600,62 +610,62 @@ void backgroundTasks(void) { // int verbose_device_search(char *s) { - int i, device_count, device, offset; - char *s2; - char vendor[256], product[256], serial[256]; - device_count = rtlsdr_get_device_count(); - if (!device_count) { - fprintf(stderr, "No supported devices found.\n"); - return -1; - } - fprintf(stderr, "Found %d device(s):\n", device_count); - for (i = 0; i < device_count; i++) { - rtlsdr_get_device_usb_strings(i, vendor, product, serial); - fprintf(stderr, " %d: %s, %s, SN: %s\n", i, vendor, product, serial); - } - fprintf(stderr, "\n"); - /* does string look like raw id number */ - device = (int)strtol(s, &s2, 0); - if (s2[0] == '\0' && device >= 0 && device < device_count) { - fprintf(stderr, "Using device %d: %s\n", - device, rtlsdr_get_device_name((uint32_t)device)); - return device; - } - /* does string exact match a serial */ - for (i = 0; i < device_count; i++) { - rtlsdr_get_device_usb_strings(i, vendor, product, serial); - if (strcmp(s, serial) != 0) { - continue;} - device = i; - fprintf(stderr, "Using device %d: %s\n", - device, rtlsdr_get_device_name((uint32_t)device)); - return device; - } - /* does string prefix match a serial */ - for (i = 0; i < device_count; i++) { - rtlsdr_get_device_usb_strings(i, vendor, product, serial); - if (strncmp(s, serial, strlen(s)) != 0) { - continue;} - device = i; - fprintf(stderr, "Using device %d: %s\n", - device, rtlsdr_get_device_name((uint32_t)device)); - return device; - } - /* does string suffix match a serial */ - for (i = 0; i < device_count; i++) { - rtlsdr_get_device_usb_strings(i, vendor, product, serial); - offset = strlen(serial) - strlen(s); - if (offset < 0) { - continue;} - if (strncmp(s, serial+offset, strlen(s)) != 0) { - continue;} - device = i; - fprintf(stderr, "Using device %d: %s\n", - device, rtlsdr_get_device_name((uint32_t)device)); - return device; - } - fprintf(stderr, "No matching devices found.\n"); - return -1; + int i, device_count, device, offset; + char *s2; + char vendor[256], product[256], serial[256]; + device_count = rtlsdr_get_device_count(); + if (!device_count) { + fprintf(stderr, "No supported devices found.\n"); + return -1; + } + fprintf(stderr, "Found %d device(s):\n", device_count); + for (i = 0; i < device_count; i++) { + rtlsdr_get_device_usb_strings(i, vendor, product, serial); + fprintf(stderr, " %d: %s, %s, SN: %s\n", i, vendor, product, serial); + } + fprintf(stderr, "\n"); + /* does string look like raw id number */ + device = (int)strtol(s, &s2, 0); + if (s2[0] == '\0' && device >= 0 && device < device_count) { + fprintf(stderr, "Using device %d: %s\n", + device, rtlsdr_get_device_name((uint32_t)device)); + return device; + } + /* does string exact match a serial */ + for (i = 0; i < device_count; i++) { + rtlsdr_get_device_usb_strings(i, vendor, product, serial); + if (strcmp(s, serial) != 0) { + continue;} + device = i; + fprintf(stderr, "Using device %d: %s\n", + device, rtlsdr_get_device_name((uint32_t)device)); + return device; + } + /* does string prefix match a serial */ + for (i = 0; i < device_count; i++) { + rtlsdr_get_device_usb_strings(i, vendor, product, serial); + if (strncmp(s, serial, strlen(s)) != 0) { + continue;} + device = i; + fprintf(stderr, "Using device %d: %s\n", + device, rtlsdr_get_device_name((uint32_t)device)); + return device; + } + /* does string suffix match a serial */ + for (i = 0; i < device_count; i++) { + rtlsdr_get_device_usb_strings(i, vendor, product, serial); + offset = strlen(serial) - strlen(s); + if (offset < 0) { + continue;} + if (strncmp(s, serial+offset, strlen(s)) != 0) { + continue;} + device = i; + fprintf(stderr, "Using device %d: %s\n", + device, rtlsdr_get_device_name((uint32_t)device)); + return device; + } + fprintf(stderr, "No matching devices found.\n"); + return -1; } // //========================================================================= @@ -741,6 +751,8 @@ int main(int argc, char **argv) { Modes.fUserLat = atof(argv[++j]); } else if (!strcmp(argv[j],"--lon") && more) { Modes.fUserLon = atof(argv[++j]); + } else if (!strcmp(argv[j],"--home") && more) { + Modes.home = strdup(argv[++j]); } else if (!strcmp(argv[j],"--debug") && more) { char *f = argv[++j]; while(*f) { diff --git a/dump1090.h b/dump1090.h index 9ad4de037..08769a96a 100644 --- a/dump1090.h +++ b/dump1090.h @@ -322,6 +322,7 @@ struct { // Internal state // User details double fUserLat; // Users receiver/antenna lat/lon needed for initial surface location double fUserLon; // Users receiver/antenna lat/lon needed for initial surface location + char *home; // Address lookup for net:map interface. int bUserFlags; // Flags relating to the user details // Interactive mode diff --git a/net_io.c b/net_io.c index a979883f5..e8d943371 100644 --- a/net_io.c +++ b/net_io.c @@ -47,10 +47,10 @@ // Networking "stack" initialization // struct service { - char *descr; - int *socket; - int port; - int enabled; + char *descr; + int *socket; + int port; + int enabled; }; struct service services[MODES_NET_SERVICES_NUM]; @@ -58,16 +58,16 @@ struct service services[MODES_NET_SERVICES_NUM]; void modesInitNet(void) { int j; - struct service svc[MODES_NET_SERVICES_NUM] = { - {"Raw TCP output", &Modes.ros, Modes.net_output_raw_port, 1}, - {"Raw TCP input", &Modes.ris, Modes.net_input_raw_port, 1}, - {"Beast TCP output", &Modes.bos, Modes.net_output_beast_port, 1}, - {"Beast TCP input", &Modes.bis, Modes.net_input_beast_port, 1}, - {"HTTP server", &Modes.https, Modes.net_http_port, 1}, - {"Basestation TCP output", &Modes.sbsos, Modes.net_output_sbs_port, 1} - }; + struct service svc[MODES_NET_SERVICES_NUM] = { + {"Raw TCP output", &Modes.ros, Modes.net_output_raw_port, 1}, + {"Raw TCP input", &Modes.ris, Modes.net_input_raw_port, 1}, + {"Beast TCP output", &Modes.bos, Modes.net_output_beast_port, 1}, + {"Beast TCP input", &Modes.bis, Modes.net_input_beast_port, 1}, + {"HTTP server", &Modes.https, Modes.net_http_port, 1}, + {"Basestation TCP output", &Modes.sbsos, Modes.net_output_sbs_port, 1} + }; - memcpy(&services, &svc, sizeof(svc));//services = svc; + memcpy(&services, &svc, sizeof(svc));//services = svc; Modes.clients = NULL; @@ -83,19 +83,19 @@ void modesInitNet(void) { #endif for (j = 0; j < MODES_NET_SERVICES_NUM; j++) { - services[j].enabled = (services[j].port != 0); - if (services[j].enabled) { - int s = anetTcpServer(Modes.aneterr, services[j].port, Modes.net_bind_address); - if (s == -1) { - fprintf(stderr, "Error opening the listening port %d (%s): %s\n", - services[j].port, services[j].descr, Modes.aneterr); - exit(1); - } - anetNonBlock(Modes.aneterr, s); - *services[j].socket = s; - } else { - if (Modes.debug & MODES_DEBUG_NET) printf("%s port is disabled\n", services[j].descr); - } + services[j].enabled = (services[j].port != 0); + if (services[j].enabled) { + int s = anetTcpServer(Modes.aneterr, services[j].port, Modes.net_bind_address); + if (s == -1) { + fprintf(stderr, "Error opening the listening port %d (%s): %s\n", + services[j].port, services[j].descr, Modes.aneterr); + exit(1); + } + anetNonBlock(Modes.aneterr, s); + *services[j].socket = s; + } else { + if (Modes.debug & MODES_DEBUG_NET) printf("%s port is disabled\n", services[j].descr); + } } #ifndef _WIN32 @@ -114,28 +114,28 @@ struct client * modesAcceptClients(void) { struct client *c; for (j = 0; j < MODES_NET_SERVICES_NUM; j++) { - if (services[j].enabled) { - fd = anetTcpAccept(Modes.aneterr, *services[j].socket, NULL, &port); - if (fd == -1) continue; - - anetNonBlock(Modes.aneterr, fd); - c = (struct client *) malloc(sizeof(*c)); - c->service = *services[j].socket; - c->next = Modes.clients; - c->fd = fd; - c->buflen = 0; - Modes.clients = c; - anetSetSendBuffer(Modes.aneterr,fd, (MODES_NET_SNDBUF_SIZE << Modes.net_sndbuf_size)); - - if (*services[j].socket == Modes.sbsos) Modes.stat_sbs_connections++; - if (*services[j].socket == Modes.ros) Modes.stat_raw_connections++; - if (*services[j].socket == Modes.bos) Modes.stat_beast_connections++; - - j--; // Try again with the same listening port - - if (Modes.debug & MODES_DEBUG_NET) - printf("Created new client %d\n", fd); - } + if (services[j].enabled) { + fd = anetTcpAccept(Modes.aneterr, *services[j].socket, NULL, &port); + if (fd == -1) continue; + + anetNonBlock(Modes.aneterr, fd); + c = (struct client *) malloc(sizeof(*c)); + c->service = *services[j].socket; + c->next = Modes.clients; + c->fd = fd; + c->buflen = 0; + Modes.clients = c; + anetSetSendBuffer(Modes.aneterr,fd, (MODES_NET_SNDBUF_SIZE << Modes.net_sndbuf_size)); + + if (*services[j].socket == Modes.sbsos) Modes.stat_sbs_connections++; + if (*services[j].socket == Modes.ros) Modes.stat_raw_connections++; + if (*services[j].socket == Modes.bos) Modes.stat_beast_connections++; + + j--; // Try again with the same listening port + + if (Modes.debug & MODES_DEBUG_NET) + printf("Created new client %d\n", fd); + } } return Modes.clients; } @@ -169,7 +169,7 @@ void modesFreeClient(struct client *c) { // Close the client connection and mark it as closed // void modesCloseClient(struct client *c) { - close(c->fd); + close(c->fd); if (c->service == Modes.sbsos) { if (Modes.stat_sbs_connections) Modes.stat_sbs_connections--; } else if (c->service == Modes.ros) { @@ -697,6 +697,43 @@ char *aircraftsToJson(int *len) { // //========================================================================= // + +// +//========================================================================= +// +// Return a home location using the home string if set, or lat/long +// +char *locationToJson(int *len) { + unsigned int buflen = 1024; // The initial buffer is incremented as needed + char *buf = (char *) malloc(buflen), *p = buf; + int l; + + if (Modes.home != NULL) { + // Prevent buffer overrun by making it Modes.home length + // plus 1024 (because we sure aren't going to use that + // much around it, so we're nice and safe). + if ( buflen < strlen( Modes.home ) ) { + buflen += strlen( Modes.home ); + buf = (char *) realloc(buf, buflen); + } + l = snprintf( p, buflen, "{\"home\":\"%s\"}\n", Modes.home ); + } else if (Modes.fUserLat || Modes.fUserLon) { + l = snprintf( + p, buflen, + "{\"lat\":%f, \"lon\":%f}\n", + Modes.fUserLat, Modes.fUserLon + ); + } else { + 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_CSS "text/css;charset=utf-8" #define MODES_CONTENT_TYPE_JSON "application/json;charset=utf-8" @@ -718,6 +755,7 @@ int handleHTTPRequest(struct client *c, char *p) { char ctype[48]; char getFile[1024]; char *ext; + char *web_home; if (Modes.debug & MODES_DEBUG_NET) printf("\nHTTP request: %s\n", c->buf); @@ -746,11 +784,17 @@ int handleHTTPRequest(struct client *c, char *p) { printf("HTTP requested URL: %s\n\n", url); } - if (strlen(url) < 2) { - snprintf(getFile, sizeof getFile, "%s/gmap.html", HTMLPATH); // Default file - } else { - snprintf(getFile, sizeof getFile, "%s/%s", HTMLPATH, url); - } + web_home = getenv( "DUMP1090_WEB" ); + if ( !web_home ) { + web_home = HTMLPATH; + } + snprintf( + getFile, + sizeof getFile, + "%s/%s", + web_home, + (strlen(url) < 2 ? "gmap.html" : url ) + ); // Use default file if no valid url // Select the content to send, we have just two so far: // "/" -> Our google map application. @@ -759,14 +803,17 @@ int handleHTTPRequest(struct client *c, char *p) { statuscode = 200; content = aircraftsToJson(&clen); //snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JSON); + } else if (strstr(url, "/loc.json")) { + statuscode = 200; + content = locationToJson(&clen); } else { struct stat sbuf; int fd = -1; char *rp, *hrp; rp = realpath(getFile, NULL); - hrp = realpath(HTMLPATH, NULL); - hrp = (hrp ? hrp : HTMLPATH); + hrp = realpath(web_home, NULL); + hrp = (hrp ? hrp : web_home); clen = -1; content = strdup("Server error occured"); if (rp && (!strncmp(hrp, rp, strlen(hrp)))) { @@ -880,9 +927,9 @@ void modesReadFromClient(struct client *c, char *sep, if (nread < 0) {errno = WSAGetLastError();} #endif if (nread == 0) { - modesCloseClient(c); - return; - } + modesCloseClient(c); + return; + } // If we didn't get all the data we asked for, then return once we've processed what we did get. if (nread != left) { diff --git a/public_html/config.js b/public_html/config.js index f7d8e2eb1..a008e6cc7 100644 --- a/public_html/config.js +++ b/public_html/config.js @@ -7,10 +7,10 @@ // -- Output Settings ------------------------------------- // Show metric values -Metric = false; // true or false +Metric = false; // true or false (Boolean) // -- Map settings ---------------------------------------- -// The Latitude and Longitude in decimal format +// The default Latitude and Longitude in decimal format CONST_CENTERLAT = 45.0; CONST_CENTERLON = 9.0; // The google maps zoom level, 0 - 16, lower is further out @@ -23,12 +23,39 @@ SelectedColor = "rgb(225, 225, 225)"; StaleColor = "rgb(190, 190, 190)"; // -- Site Settings --------------------------------------- -SiteShow = false; // true or false -// The Latitude and Longitude in decimal format -SiteLat = 45.0; -SiteLon = 9.0; +// If SiteFromLoc is true, a call will be made to the server +// to fetch any location settings passed to it on startup in +// the form of --lat and --lon, or --home (which can be a +// free text string which will use Google Maps to resolve to +// a known location). If a location is successfully identified +// from the server's data, SiteShow will automatically be set +// to "true" as well, and SiteLat & SiteLon will be updated +// to the identified coordinates. +SiteFromLoc = true; + +SiteShow = false; // Boolean +// The Latitude and Longitude in decimal format (use defaults) +SiteLat = CONST_CENTERLAT; +SiteLon = CONST_CENTERLON; SiteCircles = true; // true or false (Only shown if SiteShow is true) // In nautical miles or km (depending settings value 'Metric') SiteCirclesDistances = new Array(100,150,200); +SiteSweep = true; // Draw a line from the Site to the selected Plane +SweepColor = 'rgb(0, 255, 255)'; // ...in this color + +// If true, turns projections on. Projections are a line in front +// of a plane based on its last known heading and speed, set +// ProjectionDist seconds in the future. This allows for interpolation +// between positional updates, and in practice has proven remarkably +// accurate, though tight turns with infrequent updates will be off. +// Note: Does not account for acceleration, deceleration, or curved +// paths. +PlaneProjection = true; +ProjectionColor = 'rgb(0, 0, 255)'; // Color of the projection line +ProjectionDist = 60; // Time in seconds +// Shows a smaller, lighter plane icon (derived from the plane's icon +// setting, whatever that may be) moving along the projection line at +// a rate consistent with the last known speed. +ProjectionAnimation = true; diff --git a/public_html/gmap.html b/public_html/gmap.html index 9ab2dc28d..9dfc71c16 100644 --- a/public_html/gmap.html +++ b/public_html/gmap.html @@ -1,3 +1,4 @@ + @@ -14,9 +15,9 @@ - DUMP1090 + DUMP1090 - + @@ -26,29 +27,24 @@