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;
+}