diff --git a/build.sh b/build.sh index 99c8903..fd6e6d2 100755 --- a/build.sh +++ b/build.sh @@ -25,6 +25,9 @@ java -jar "../tools/yuicompressor-2.4.8.jar" --type=js --disable-optimizations - java -jar "../tools/yuicompressor-2.4.8.jar" --type=js --disable-optimizations --nomunge tracker.js >> mobile.js java -jar "../tools/yuicompressor-2.4.8.jar" --type=js --disable-optimizations --nomunge app.js >> mobile.js java -jar "../tools/yuicompressor-2.4.8.jar" --type=js --disable-optimizations --nomunge colour-map.js >> mobile.js +java -jar "../tools/yuicompressor-2.4.8.jar" --type=js --disable-optimizations --nomunge xdata.js >> mobile.js +java -jar "../tools/yuicompressor-2.4.8.jar" --type=js --disable-optimizations --nomunge station.js >> mobile.js +java -jar "../tools/yuicompressor-2.4.8.jar" --type=js --disable-optimizations --nomunge format.js >> mobile.js #compile plot lib and config java -jar "../tools/yuicompressor-2.4.8.jar" --type=js --disable-optimizations --nomunge _jquery.flot.js >> init_plot.js diff --git a/js/format.js b/js/format.js new file mode 100644 index 0000000..a1ee090 --- /dev/null +++ b/js/format.js @@ -0,0 +1,325 @@ +/* SondeHub Tracker Format Incoming Data + * + * Author: Luke Prior + */ + +function formatData(data, live) { + var response = {}; + response.positions = {}; + var dataTemp = []; + if (live) { // Websockets + if (!data.length) { // handle single sonde + data = {"entry": data}; + } + for (let entry in data) { + var dataTempEntry = {}; + var station = data[entry].uploader_callsign; + dataTempEntry.callsign = {}; + //check if other stations also received this packet + if (vehicles.hasOwnProperty(data[entry].serial)) { + if (data[entry].datetime == vehicles[data[entry].serial].curr_position.gps_time) { + for (let key in vehicles[data[entry].serial].curr_position.callsign) { + if (vehicles[data[entry].serial].curr_position.callsign.hasOwnProperty(key)) { + if (key != station) { + dataTempEntry.callsign[key] = {}; + if (vehicles[data[entry].serial].curr_position.callsign[key].hasOwnProperty("snr")) { + dataTempEntry.callsign[key].snr = vehicles[data[entry].serial].curr_position.callsign[key].snr; + } + if (vehicles[data[entry].serial].curr_position.callsign[key].hasOwnProperty("rssi")) { + dataTempEntry.callsign[key].rssi = vehicles[data[entry].serial].curr_position.callsign[key].rssi; + } + if (vehicles[data[entry].serial].curr_position.callsign[key].hasOwnProperty("frequency")) { + dataTempEntry.callsign[key].frequency = vehicles[data[entry].serial].curr_position.callsign[key].frequency; + } + } + } + } + } + } + dataTempEntry.callsign[station] = {}; + if (data[entry].snr) { + dataTempEntry.callsign[station].snr = data[entry].snr; + } + if (data[entry].rssi) { + dataTempEntry.callsign[station].rssi = data[entry].rssi; + } + if (data[entry].frequency) { + dataTempEntry.callsign[station].frequency = data[entry].frequency; + } + dataTempEntry.gps_alt = data[entry].alt; + dataTempEntry.gps_lat = data[entry].lat; + dataTempEntry.gps_lon = data[entry].lon; + if (data[entry].heading) { + dataTempEntry.gps_heading = data[entry].heading; + } + dataTempEntry.gps_time = data[entry].datetime; + dataTempEntry.server_time = data[entry].datetime; + dataTempEntry.vehicle = data[entry].serial; + dataTempEntry.position_id = data[entry].serial + "-" + data[entry].datetime; + dataTempEntry.data = {}; + if (data[entry].batt) { + dataTempEntry.data.batt = data[entry].batt; + } + if (data[entry].burst_timer) { + dataTempEntry.data.burst_timer = data[entry].burst_timer; + } + if (data[entry].frequency) { + dataTempEntry.data.frequency = data[entry].frequency; + } + if (data[entry].tx_frequency) { + dataTempEntry.data.frequency_tx = data[entry].tx_frequency; + } + if (data[entry].hasOwnProperty("humidity")) { + dataTempEntry.data.humidity = data[entry].humidity; + } + if (data[entry].manufacturer) { + dataTempEntry.data.manufacturer = data[entry].manufacturer; + } + if (data[entry].hasOwnProperty("pressure")) { + dataTempEntry.data.pressure = data[entry].pressure; + } + if (data[entry].sats) { + dataTempEntry.data.sats = data[entry].sats; + } + if (data[entry].hasOwnProperty("temp")) { + dataTempEntry.data.temperature_external = data[entry].temp; + } + if (data[entry].type) { + dataTempEntry.data.type = data[entry].type; + dataTempEntry.type = data[entry].type; + } + if (data[entry].subtype) { + dataTempEntry.data.type = data[entry].subtype; + dataTempEntry.type = data[entry].subtype; + } + if (data[entry].xdata) { + dataTempEntry.data.xdata = data[entry].xdata; + + if (data[entry].hasOwnProperty("pressure")) { + xdata_pressure = data[entry].pressure; + } else { + xdata_pressure = 1100.0; + } + + var tempXDATA = parseXDATA(data[entry].xdata, xdata_pressure); + for (let field in tempXDATA) { + if (tempXDATA.hasOwnProperty(field)) { + if (field == "xdata_instrument") { + dataTempEntry.data.xdata_instrument = tempXDATA.xdata_instrument.join(', '); + } else { + dataTempEntry.data[field] = tempXDATA[field]; + } + } + } + } + if (data[entry].serial.toLowerCase() != "xxxxxxxx") { + dataTemp.push(dataTempEntry); + } + } + } else if (data.length == null) { // Elasticsearch + for (let key in data) { + if (data.hasOwnProperty(key)) { + if (typeof data[key] === 'object') { + for (let i in data[key]) { + var dataTempEntry = {}; + var station = data[key][i].uploader_callsign; + dataTempEntry.callsign = {}; + dataTempEntry.callsign[station] = {}; + if (data[key][i].snr) { + dataTempEntry.callsign[station].snr = data[key][i].snr; + } + if (data[key][i].rssi) { + dataTempEntry.callsign[station].rssi = data[key][i].rssi; + } + if (data[key][i].frequency) { + dataTempEntry.callsign[station].frequency = data[key][i].frequency; + } + dataTempEntry.gps_alt = data[key][i].alt; + dataTempEntry.gps_lat = data[key][i].lat; + dataTempEntry.gps_lon = data[key][i].lon; + if (data[key][i].heading) { + dataTempEntry.gps_heading = data[key][i].heading; + } + dataTempEntry.gps_time = data[key][i].datetime; + dataTempEntry.server_time = data[key][i].datetime; + dataTempEntry.vehicle = data[key][i].serial; + dataTempEntry.position_id = data[key][i].serial + "-" + data[key][i].datetime; + dataTempEntry.data = {}; + if (data[key][i].batt) { + dataTempEntry.data.batt = data[key][i].batt; + } + if (data[key][i].burst_timer) { + dataTempEntry.data.burst_timer = data[key][i].burst_timer; + } + if (data[key][i].frequency) { + dataTempEntry.data.frequency = data[key][i].frequency; + } + if (data[key][i].tx_frequency) { + dataTempEntry.data.frequency_tx = data[key][i].tx_frequency; + } + if (data[key][i].hasOwnProperty("humidity")) { + dataTempEntry.data.humidity = data[key][i].humidity; + } + if (data[key][i].manufacturer) { + dataTempEntry.data.manufacturer = data[key][i].manufacturer; + } + if (data[key][i].hasOwnProperty("pressure")) { + dataTempEntry.data.pressure = data[key][i].pressure; + } + if (data[key][i].sats) { + dataTempEntry.data.sats = data[key][i].sats; + } + if (data[key][i].hasOwnProperty("temp")) { + dataTempEntry.data.temperature_external = data[key][i].temp; + } + if (data[key][i].type) { + dataTempEntry.data.type = data[key][i].type; + dataTempEntry.type = data[key][i].type; + } + if (data[key][i].subtype) { + dataTempEntry.data.type = data[key][i].subtype; + dataTempEntry.type = data[key][i].subtype; + } + if (data[key][i].xdata) { + dataTempEntry.data.xdata = data[key][i].xdata; + if (data[key][i].hasOwnProperty("pressure")) { + xdata_pressure = data[key][i].pressure; + } else { + xdata_pressure = 1100.0; + } + var tempXDATA = parseXDATA(data[key][i].xdata, xdata_pressure); + for (let field in tempXDATA) { + if (tempXDATA.hasOwnProperty(field)) { + if (field == "xdata_instrument") { + dataTempEntry.data.xdata_instrument = tempXDATA.xdata_instrument.join(', '); + } else { + dataTempEntry.data[field] = tempXDATA[field]; + } + } + } + } + if (data[key][i].serial.toLowerCase() != "xxxxxxxx") { + dataTemp.push(dataTempEntry); + } + } + } + } + } + } else { // AWS + for (var i = data.length - 1; i >= 0; i--) { + if (data[i].hasOwnProperty('subtype') && data[i].subtype == "SondehubV1") { // SondeHub V1 + var dataTempEntry = {}; + var station = data[i].uploader_callsign; + dataTempEntry.callsign = {}; + dataTempEntry.callsign[station] = {}; + dataTempEntry.gps_alt = parseFloat(data[i].alt); + dataTempEntry.gps_lat = parseFloat(data[i].lat); + dataTempEntry.gps_lon = parseFloat(data[i].lon); + dataTempEntry.gps_time = data[i].time_received; + dataTempEntry.server_time = data[i].time_received; + dataTempEntry.vehicle = data[i].serial; + dataTempEntry.position_id = data[i].serial + "-" + data[i].time_received; + dataTempEntry.data = {}; + if (data[i].humidity) { + dataTempEntry.data.humidity = parseFloat(data[i].humidity); + } + if (data[i].temp) { + dataTempEntry.data.temperature_external = parseFloat(data[i].temp); + } + dataTemp.push(dataTempEntry); + } else { // SondeHub V2 + var dataTempEntry = {}; + var station = data[i].uploader_callsign; + dataTempEntry.callsign = {}; + dataTempEntry.callsign[station] = {}; + if (data[i].snr) { + dataTempEntry.callsign[station].snr = data[i].snr; + } + if (data[i].rssi) { + dataTempEntry.callsign[station].rssi = data[i].rssi; + } + if (data[i].frequency) { + dataTempEntry.callsign[station].frequency = data[i].frequency; + } + dataTempEntry.gps_alt = data[i].alt; + dataTempEntry.gps_lat = data[i].lat; + dataTempEntry.gps_lon = data[i].lon; + if (data[i].heading) { + dataTempEntry.gps_heading = data[i].heading; + } + dataTempEntry.gps_time = data[i].datetime; + dataTempEntry.server_time = data[i].datetime; + dataTempEntry.vehicle = data[i].serial; + dataTempEntry.position_id = data[i].serial + "-" + data[i].datetime; + dataTempEntry.data = {}; + if (data[i].batt) { + dataTempEntry.data.batt = data[i].batt; + } + if (data[i].burst_timer) { + dataTempEntry.data.burst_timer = data[i].burst_timer; + } + if (data[i].frequency) { + dataTempEntry.data.frequency = data[i].frequency; + } + if (data[i].tx_frequency) { + dataTempEntry.data.frequency_tx = data[i].tx_frequency; + } + if (data[i].hasOwnProperty("humidity")) { + dataTempEntry.data.humidity = data[i].humidity; + } + if (data[i].manufacturer) { + dataTempEntry.data.manufacturer = data[i].manufacturer; + } + if (data[i].hasOwnProperty("pressure")) { + dataTempEntry.data.pressure = data[i].pressure; + } + if (data[i].sats) { + dataTempEntry.data.sats = data[i].sats; + } + if (data[i].hasOwnProperty("temp")) { + dataTempEntry.data.temperature_external = data[i].temp; + } + if (data[i].type && data[i].type == "payload_telemetry") { // SondeHub V1.5 data? + var comment = data[i].comment.split(" "); + if (v1types.hasOwnProperty(comment[0])) { + dataTempEntry.data.type = v1types[comment[0]]; + dataTempEntry.type = v1types[comment[0]]; + if (v1manufacturers.hasOwnProperty(dataTempEntry.type)) { + dataTempEntry.data.manufacturer = v1manufacturers[dataTempEntry.type]; + } + } + dataTempEntry.data.frequency = comment[2]; + } else if (data[i].type) { + dataTempEntry.data.type = data[i].type; + dataTempEntry.type = data[i].type; + } + if (data[i].subtype) { + dataTempEntry.data.type = data[i].subtype; + dataTempEntry.type = data[i].subtype; + } + if (data[i].xdata) { + dataTempEntry.data.xdata = data[i].xdata; + if (data[i].hasOwnProperty("pressure")) { + xdata_pressure = data[i].pressure; + } else { + xdata_pressure = 1100.0; + } + var tempXDATA = parseXDATA(data[i].xdata, xdata_pressure); + for (let field in tempXDATA) { + if (tempXDATA.hasOwnProperty(field)) { + if (field == "xdata_instrument") { + dataTempEntry.data.xdata_instrument = tempXDATA.xdata_instrument.join(', '); + } else { + dataTempEntry.data[field] = tempXDATA[field]; + } + } + } + } + dataTemp.push(dataTempEntry); + } + } + } + response.positions.position = dataTemp; + response.fetch_timestamp = Date.now(); + return response; +} \ No newline at end of file diff --git a/js/station.js b/js/station.js new file mode 100644 index 0000000..20ea504 --- /dev/null +++ b/js/station.js @@ -0,0 +1,963 @@ +/* SondeHub Tracker Station Popup Functions + * + * Author: Luke Prior + */ + +var launches_url = "https://api.v2.sondehub.org/sites"; +var sites = null; + +// Calculate the number of historical records for selected date range +// Set selectable months for selected year +function getSelectedNumber (station) { + var popup = $("#popup" + station); + var targetyear = popup.find("#yearList option:selected").val(); + var targetmonth = popup.find("#monthList option:selected").val(); + var count = 0; + var data = stationHistoricalData[station]; + + // Calculate count + for (let year in data) { + if (data.hasOwnProperty(year)) { + if (year == targetyear || targetyear == "all") { + for (let month in data[year]) { + if (data[year].hasOwnProperty(month)) { + if (month == targetmonth || targetmonth == "all" || targetyear == "all") { + count += data[year][month].length; + } + } + } + } + } + } + + // Update selected field & hide months if no data + var months = ["all", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"]; + popup.find('#yearList option').each(function() { + if ($(this).is(':selected')) { + $(this).attr("selected", "selected"); + var selectedYear = $(this).val(); + if (selectedYear != "all") { + months = Object.keys(data[selectedYear]); + months.push("all"); + } + } else { + $(this).attr("selected", false); + } + }); + + popup.find('#monthList option').each(function() { + if (!months.includes($(this).val())) { + $(this).hide(); + } else { + $(this).show(); + } + if ($(this).is(':selected')) { + $(this).attr("selected", "selected"); + } else { + $(this).attr("selected", false); + } + }); + + // Update popup + popup.find("#launchCount").text(count); +} + +// Get initial summary data for station, courtesy of TimMcMahon +function getHistorical (id, callback, continuation) { + var prefix = 'launchsites/' + id + '/'; + var params = { + Prefix: prefix, + }; + + if (typeof continuation !== 'undefined') { + params.ContinuationToken = continuation; + } else { + tempLaunchData = {}; + } + + s3.makeUnauthenticatedRequest('listObjectsV2', params, function(err, data) { + if (err) { + console.log(err, err.stack); + } else { + var tempSerials = []; + for (var i = 0; i < data.Contents.length; i++) { + // Sort data into year and month groups + var date = data.Contents[i].Key.substring(prefix.length).substring(0,10); + var year = date.substring(0,4); + var month = date.substring(5,7); + var serial = data.Contents[i].Key.substring(prefix.length+11).slice(0, -5); + if (tempLaunchData.hasOwnProperty(year)) { + if (tempLaunchData[year].hasOwnProperty(month)) { + if (!tempSerials.includes(serial)) { + tempSerials.push(serial) + tempLaunchData[year][month].push(data.Contents[i].Key); + } + } else { + tempLaunchData[year][month] = []; + tempSerials = []; + tempSerials.push(serial) + tempLaunchData[year][month].push(data.Contents[i].Key); + } + } else { + tempLaunchData[year] = {}; + tempLaunchData[year][month] = []; + tempSerials = []; + tempSerials.push(serial) + tempLaunchData[year][month].push(data.Contents[i].Key); + } + } + if (data.IsTruncated == true) { + // Requests are limited to 1000 entries so multiple may be required + getHistorical(id, callback, data.NextContinuationToken); + } else { + callback(tempLaunchData); + } + } + }); +} + +// Download summary data from AWS S3 +function downloadHistorical (suffix) { + var url = "https://sondehub-history.s3.amazonaws.com/" + suffix; + var ajaxReq = $.ajax({ + type: "GET", + url: url, + dataType: "json", + tryCount : 0, + retryLimit : 3, // Retry max of 3 times + error : function(xhr, textStatus, errorThrown ) { + if (textStatus == 'timeout') { + this.tryCount++; + if (this.tryCount <= this.retryLimit) { + //try again + $.ajax(this); + return; + } + return; + } + } + }); + historicalAjax.push(ajaxReq); + return ajaxReq; +} + +// Draw historic summaries to map +function drawHistorical (data, station) { + var landing = data[2]; + var serial = landing.serial; + var time = landing.datetime; + + if (!historicalPlots[station].sondes.hasOwnProperty(serial)) { + + historicalPlots[station].sondes[serial] = {}; + + // Using last known alt to detmine colour + var minAlt = 0; + var actualAlt = landing.alt; + var maxAlt = 10000; + + if (actualAlt > maxAlt) { + actualAlt = maxAlt; + } else if (actualAlt < minAlt) { + actualAlt = minAlt; + } + + var normalisedAlt = ((actualAlt-minAlt)/(maxAlt-minAlt)); + var iconColour = ConvertRGBtoHex(evaluate_cmap(normalisedAlt, 'turbo')); + + // Check if we have recovery data for it + var recovered = false; + if (historicalPlots[station].data.hasOwnProperty("recovered")) { + if (historicalPlots[station].data.recovered.hasOwnProperty(serial)) { + var recovery_info = historicalPlots[station].data.recovered[serial]; + recovered = true; + } + } + + var popup = L.popup(); + + html = "