diff --git a/.gitignore b/.gitignore index 0984522..90a0967 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ testfiles/*.bin misc frames.js .*.swp +~* + diff --git a/dump1090.c b/dump1090.c index f6aad6f..dc1daeb 100644 --- a/dump1090.c +++ b/dump1090.c @@ -3215,6 +3215,7 @@ int decodeHexMessage(struct client *c) { /* Return a description of planes in json. */ char *aircraftsToJson(int *len) { + time_t now = time(NULL); struct aircraft *a = Modes.aircrafts; int buflen = 1024; /* The initial buffer is incremented as needed. */ char *buf = (char *) malloc(buflen), *p = buf; @@ -3230,23 +3231,23 @@ char *aircraftsToJson(int *len) { altitude = (int) (altitude / 3.2828); speed = (int) (speed * 1.852); } - - if (a->bFlags & MODES_ACFLAGS_LATLON_VALID) { - l = snprintf(p,buflen, - "{\"hex\":\"%06x\", \"flight\":\"%s\", \"lat\":%f, " - "\"lon\":%f, \"altitude\":%d, \"track\":%d, " - "\"speed\":%d},\n", - a->addr, a->flight, a->lat, a->lon, a->altitude, a->track, - a->speed); - p += l; buflen -= l; - /* Resize if needed. */ - if (buflen < 256) { - int used = p-buf; - buflen += 1024; /* Our increment. */ - buf = (char *) realloc(buf,used+buflen); - p = buf+used; - } + + l = snprintf(p,buflen, + "{\"hex\":\"%06x\", \"squawk\":\"%04x\", \"flight\":\"%s\", \"lat\":%f, " + "\"lon\":%f, \"altitude\":%d, \"track\":%d, " + "\"speed\":%d, \"messages\":%ld, \"seen\":%d},\n", + a->addr, a->modeA, a->flight, a->lat, a->lon, a->altitude, a->track, + a->speed, a->messages, (int)(now - a->seen)); + p += l; buflen -= l; + + /* Resize if needed. */ + if (buflen < 256) { + int used = p-buf; + buflen += 1024; // Our increment. + buf = (char *) realloc(buf,used+buflen); + p = buf+used; } + a = a->next; } /* Remove the final comma if any, and closes the json array. */ @@ -3263,7 +3264,9 @@ char *aircraftsToJson(int *len) { } #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" +#define MODES_CONTENT_TYPE_JS "application/javascript;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 @@ -3276,22 +3279,24 @@ int handleHTTPRequest(struct client *c) { int clen, hdrlen; int httpver, keepalive; char *p, *url, *content; - char *ctype; + char ctype[48]; + char getFile[1024]; + char *ext; if (Modes.debug & MODES_DEBUG_NET) printf("\nHTTP request: %s\n", c->buf); - /* Minimally parse the request. */ + // Minimally parse the request. httpver = (strstr(c->buf, "HTTP/1.1") != NULL) ? 11 : 10; if (httpver == 10) { - /* HTTP 1.0 defaults to close, unless otherwise specified. */ + // HTTP 1.0 defaults to close, unless otherwise specified. keepalive = strstr(c->buf, "Connection: keep-alive") != NULL; } else if (httpver == 11) { - /* HTTP 1.1 defaults to keep-alive, unless close is specified. */ + // HTTP 1.1 defaults to keep-alive, unless close is specified. keepalive = strstr(c->buf, "Connection: close") == NULL; } - /* Identify he URL. */ + // Identify he URL. p = strchr(c->buf,' '); if (!p) return 1; /* There should be the method and a space... */ url = ++p; /* Now this should point to the requested URL. */ @@ -3303,35 +3308,52 @@ int handleHTTPRequest(struct client *c) { printf("\nHTTP keep alive: %d\n", keepalive); printf("HTTP requested URL: %s\n\n", url); } + + if (strlen(url) < 2) { + snprintf(getFile, sizeof getFile, "./public_html/gmap.html"); // Default file + } else { + snprintf(getFile, sizeof getFile, "./public_html%s", 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; + //snprintf(ctype, sizeof 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) - { + if (stat(getFile, &sbuf) != -1 && (fd = open(getFile, O_RDONLY)) != -1) { content = (char *) malloc(sbuf.st_size); - if (read(fd,content,sbuf.st_size) == -1) { - snprintf(content,sbuf.st_size,"Error reading from file: %s", - strerror(errno)); + if (read(fd, content, sbuf.st_size) == -1) { + snprintf(content, sbuf.st_size, "Error reading from file: %s", strerror(errno)); } clen = sbuf.st_size; } else { char buf[128]; - - clen = snprintf(buf,sizeof(buf),"Error opening HTML file: %s", - strerror(errno)); + 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; + + if (fd != -1) { + close(fd); + } + } + + // Get file extension and content type + snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_HTML); // Default content type + ext = strrchr(getFile, '.'); + + if (strlen(ext) > 0) { + if (strstr(ext, ".json")) { + snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JSON); + } else if (strstr(ext, ".css")) { + snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_CSS); + } else if (strstr(ext, ".js")) { + snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JS); + } } /* Create the header and send the reply. */ @@ -3346,13 +3368,12 @@ int handleHTTPRequest(struct client *c) { keepalive ? "keep-alive" : "close", clen); - if (Modes.debug & MODES_DEBUG_NET) + if (Modes.debug & MODES_DEBUG_NET) { printf("HTTP Reply header:\n%s", hdr); + } - /* Send header and content. */ - if (write(c->fd, hdr, hdrlen) == -1 || - write(c->fd, content, clen) == -1) - { + // Send header and content. + if (write(c->fd, hdr, hdrlen) == -1 || write(c->fd, content, clen) == -1) { free(content); return 1; } diff --git a/gmap.html b/gmap.html deleted file mode 100644 index b39a30a..0000000 --- a/gmap.html +++ /dev/null @@ -1,180 +0,0 @@ - - - - - - - - - - - -
-
-
-

Dump1090

-

-

Click on a plane for info.

-
-
- - diff --git a/public_html/gmap.html b/public_html/gmap.html new file mode 100644 index 0000000..e7f2ffb --- /dev/null +++ b/public_html/gmap.html @@ -0,0 +1,23 @@ + + + + + + + + + + + + +
+
+
+

Dump1090 - 00:00:00 UTC

+

+

Click on a plane for info.

+
+ +
+ + diff --git a/public_html/script.js b/public_html/script.js new file mode 100644 index 0000000..1666f64 --- /dev/null +++ b/public_html/script.js @@ -0,0 +1,216 @@ +Map = null; +CenterLat = 45.0; +CenterLon = 9.0; +ZoomLvl = 5; +Planes = {}; +PlanesOnMap = 0; +PlanesOnGrid = 0; +Selected = null; + +if (localStorage['CenterLat']) { CenterLat = Number(localStorage['CenterLat']); } +if (localStorage['CenterLon']) { CenterLon = Number(localStorage['CenterLon']); } +if (localStorage['ZoomLvl']) { ZoomLvl = Number(localStorage['ZoomLvl']); } + +function getIconForPlane(plane) { + var r = 255, g = 255, b = 0; + var maxalt = 40000; // Max altitude in the average case + var invalt = maxalt-plane.altitude; + var selected = (Selected == plane.hex); + + if (invalt < 0) invalt = 0; + b = parseInt(255/maxalt*invalt); + return { + strokeWeight: (selected ? 2 : 1), + path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW, + scale: 5, + fillColor: 'rgb('+r+','+g+','+b+')', + fillOpacity: 0.9, + rotation: plane.track + }; +} + +function selectPlane() { + if (!Planes[this.planehex]) return; + var old = Selected; + Selected = this.planehex; + if (Planes[old]) { + /* Remove the highlight in the previously selected plane. */ + Planes[old].marker.setIcon(getIconForPlane(Planes[old])); + } + Planes[Selected].marker.setIcon(getIconForPlane(Planes[Selected])); + refreshSelectedInfo(); +} + +function refreshGeneralInfo() { + var i = document.getElementById('geninfo'); + + i.innerHTML = PlanesOnGrid + ' planes on grid.
'; + i.innerHTML += PlanesOnMap + ' planes on map.'; +} + +function refreshSelectedInfo() { + var i = document.getElementById('selinfo'); + var p = Planes[Selected]; + + if (!p) return; + var html = 'ICAO: '+p.hex+'
'; + if (p.flight.length) { + html += ''+p.flight+'
'; + } + html += 'Altitude: '+p.altitude+' feet
'; + html += 'Speed: '+p.speed+' knots
'; + html += 'Coordinates: '+p.lat+', '+p.lon+'
'; + html += 'Messages: '+p.messages+'
'; + html += 'Seen: '+p.seen+' sec
'; + i.innerHTML = html; +} + +function fetchData() { + $.getJSON('/data.json', function(data) { + var stillhere = {} + PlanesOnMap = 0; + + for (var j=0; j < data.length; j++) { + var plane = data[j]; + stillhere[plane.hex] = true; + plane.flight = $.trim(plane.flight); + + if (plane.lat != 0 && plane.lon != 0) { + // Show only planes with position + var marker = null; + PlanesOnMap++; + + if (Planes[plane.hex]) { + // Move and refresh old plane on map + var myplane = Planes[plane.hex]; + marker = myplane.marker; + var icon = marker.getIcon(); + var newpos = new google.maps.LatLng(plane.lat, plane.lon); + marker.setPosition(newpos); + marker.setIcon(getIconForPlane(plane)); + myplane.altitude = plane.altitude; + myplane.speed = plane.speed; + myplane.lat = plane.lat; + myplane.lon = plane.lon; + myplane.track = plane.track; + myplane.flight = plane.flight; + myplane.seen = plane.seen; + myplane.messages = plane.messages; + if (myplane.hex == Selected) + refreshSelectedInfo(); + } else { + // Add new plane to map + marker = new google.maps.Marker({ + position: new google.maps.LatLng(plane.lat, plane.lon), + map: Map, + icon: getIconForPlane(plane) + }); + plane.marker = marker; + marker.planehex = plane.hex; + Planes[plane.hex] = plane; + + // Trap clicks for this marker. + google.maps.event.addListener(marker, 'click', selectPlane); + } + + if (plane.flight.length == 0) { + marker.setTitle(plane.hex) + } else { + marker.setTitle(plane.flight+' ('+plane.hex+')') + } + } + } + + PlanesOnGrid = data.length; + + /* Remove idle planes. */ + for (var p in Planes) { + if (!stillhere[p]) { + Planes[p].marker.setMap(null); + delete Planes[p]; + } + } + }); +} + +function checkTime(i) { + if (i < 10) { + return "0" + i; + } + return i; +} + +function printTime() { + var currentTime = new Date(); + var hours = checkTime(currentTime.getUTCHours()); + var minutes = checkTime(currentTime.getUTCMinutes()); + var seconds = checkTime(currentTime.getUTCSeconds()); + + if (document.getElementById) { + document.getElementById('utcTime').innerHTML = + hours + ":" + minutes + ":" + seconds; + } +} + +function placeFooter() { + var windHeight = $(window).height(); + var footerHeight = $('#info_footer').height(); + var offset = parseInt(windHeight) - parseInt(footerHeight); + + var footerWidth = parseInt($('#info_footer').width()); + var infoWidth = parseInt($('#info').width()); + var marginLeft = parseInt((infoWidth / 2) - (footerWidth / 2)); + + $('#info_footer').css('top', offset); + $('#info_footer').css('margin-left', marginLeft); +} + +function resetMap() { + localStorage['CenterLat'] = 45.0; + localStorage['CenterLon'] = 9.0; + localStorage['ZoomLvl'] = 5; + Map.setZoom(parseInt(localStorage['ZoomLvl'])); + Map.setCenter(new google.maps.LatLng(parseInt(localStorage['CenterLat']), parseInt(localStorage['CenterLon']))); + Selected = null; + document.getElementById('selinfo').innerHTML = ''; +} + +function initialize() { + var mapOptions = { + center: new google.maps.LatLng(CenterLat, CenterLon), + zoom: ZoomLvl, + mapTypeId: google.maps.MapTypeId.ROADMAP + }; + Map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions); + + // show footer at info-area + $(function(){ + $(window).resize(function(e){ + placeFooter(); + }); + placeFooter(); + // hide it before it's positioned + $('#info_footer').css('display','inline'); + }); + + // Listener for newly created Map + google.maps.event.addListener(Map, 'center_changed', function() { + localStorage['CenterLat'] = Map.getCenter().lat(); + localStorage['CenterLon'] = Map.getCenter().lng(); + }); + + google.maps.event.addListener(Map, 'zoom_changed', function() { + localStorage['ZoomLvl'] = Map.getZoom(); + }); + + // Setup our timer to poll from the server. + window.setInterval(function() { + fetchData(); + refreshGeneralInfo(); + }, 1000); + + // Faster timer, smoother things + window.setInterval(function() { + printTime(); + }, 250); +} diff --git a/public_html/style.css b/public_html/style.css new file mode 100644 index 0000000..8c58273 --- /dev/null +++ b/public_html/style.css @@ -0,0 +1,34 @@ +html { height: 100% } +body { height: 100%; margin: 0; padding: 0 } +#map_canvas { height: 100% } +#info { +position: absolute; +width:20%; +height:100%; +bottom:0px; +right:0px; +top:0px; +background-color: white; +border-left:1px #666 solid; +font-family:Helvetica; +} +#info div { +padding:0px; +padding-left:10px; +margin:0px; +} +#info div h1 { +margin-top:10px; +font-size:16px; +} +#info div p { +font-size:14px; +color:#333; +} +#info_footer { +position: absolute; +display: none; +text-align: center; +padding:0px; +margin:0px; +}