Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions DEVELOPER_README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Getting started

To get a copy of the code and run a test web server:

1. [Fork the repository](https://github.com/projecthorus/sondehub-tracker/fork) by visiting [https://github.com/projecthorus/sondehub-tracker/fork](https://github.com/projecthorus/sondehub-tracker/fork).
2. Clone the repository with your git tool of choice.
3. Run `build.sh` to compile the javascript files. (This requires Java to be installed and in your path.)
4. Run `python serve.py` to run a simple web server to (This requires python 3.x)
5. Visit [http://localhost:8000](http://localhost:8000) to view the local version of the server!
11 changes: 11 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM alpine:latest

RUN apk add --no-cache python3 openjdk11 sed git

WORKDIR /app
ADD . .

RUN sh build.sh

ENTRYPOINT ["python3", "serve.py"]
EXPOSE 8000
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,10 @@ Requirements: Java

$ git clone https://github.com/projecthorus/sondehub-tracker.git
$ ./build.sh
$ python serve.py

Visit [http://localhost:8000](http://localhost:8000) to view the local version of the tracker!

## Original design

Author: Daniel Saul [@danielsaul](https://github.com/danielsaul)




Binary file added img/splash/splash-narrow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/splash/splash-wide.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
<div id="loading">
<div>
<img src="img/sondehub_logo.png" />
<img src="img/splash/splash-wide.png" style="transform: translate(-50%,250%);"/>
<div class="bar" style="display: none"><div class="complete"></div></div>
</div>
</div>
Expand Down Expand Up @@ -210,6 +211,13 @@ <h4>Visibility</h4>
<input type="checkbox" id="opt_layers_launches">
</div>
</div>
<div class="row option">
<span><b>Only show visible on sidebar</b></span>
<div class="switch off" id="sw_selective_sidebar">
<span class="thumb"></span>
<input type="checkbox" id="opt_selective_sidebar">
</div>
</div>
<h4>Other</h4>
<hr/>
<div class="row option">
Expand Down
4 changes: 4 additions & 0 deletions js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,7 @@ $(window).ready(function() {
'#sw_hide_horizon',
'#sw_hide_titles',
'#sw_layers_launches',
'#sw_selective_sidebar',
"#sw_nowelcome",
"#sw_interpolate",
];
Expand Down Expand Up @@ -820,6 +821,9 @@ $(window).ready(function() {
if(on) map.overlayMapTypes.setAt("1", overlayAPRS);
else map.overlayMapTypes.setAt("1", null);
break;
case "opt_selective_sidebar":
sidebar_update();
break;
case "opt_layers_launches":
if(on) {
map.removeLayer(launches);
Expand Down
58 changes: 49 additions & 9 deletions js/tracker.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ var focusID = 0;

var receiverCanvas = null;

var sondePrefix = ["RS92", "RS92-SGP", "RS92-NGP", "RS41", "RS41-SG", "RS41-SGP", "RS41-SGM", "DFM", "DFM06", "DFM09", "DFM17", "M10", "M20", "iMet-4", "iMet-54", "LMS6", "LMS6-400", "LMS6-1680", "iMS-100", "MRZ", "chase"];
var sondePrefix = ["RS92", "RS92-SGP", "RS92-NGP", "RS41", "RS41-SG", "RS41-SGP", "RS41-SGM", "DFM", "DFM06", "DFM09", "DFM17", "M10", "M20", "iMet-1", "iMet-4", "iMet-54", "LMS6", "LMS6-400", "LMS6-1680", "iMS-100", "MRZ", "chase"];
var sondeCodes = {
"07":"iMet-1", "11":"LMS6-403", "13":"RS92", "14":"RS92", "17":"DFM-09", "18":"DFM-06", "19":"MRZ-N1", "22":"RS-11G", "23":"RS41", "24":"RS41", "34":"iMet-4", "35":"iMS-100", "41":"RS41", "42":"RS41", "52":"RS92-NGP",
"54":"DFM-17", "62":"MRZ-3MK", "63":"M20", "77":"M10", "82":"LMS6-1680", "84":"iMet-54"
Expand Down Expand Up @@ -878,6 +878,7 @@ function load() {

map.on('moveend', function (e) {
lhash_update();
sidebar_update();
});

map.on('baselayerchange', function (e) {
Expand Down Expand Up @@ -926,6 +927,7 @@ function load() {

map.on('moveend', function() {
lhash_update();
sidebar_update();
});
map.on('baselayerchange', function() {
lhash_update();
Expand Down Expand Up @@ -1053,6 +1055,24 @@ function panToRecovery(rcallsign) {
}
}

function sidebar_update() {
if (offline.get('opt_selective_sidebar')) {
for (let serial in vehicles) {
if (map.getBounds().contains(vehicles[serial].marker.getLatLng())) {
$("#main .vehicle"+vehicles[serial].uuid).show();
} else {
if (!($("#main .vehicle"+vehicles[serial].uuid).hasClass("follow"))) {
$("#main .vehicle"+vehicles[serial].uuid).hide();
}
}
}
} else {
for (let serial in vehicles) {
$("#main .vehicle"+vehicles[serial].uuid).show();
}
}
}

function title_case(s) {
return s.replace(/\w\S*/g, function(txt) {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
Expand Down Expand Up @@ -1528,6 +1548,14 @@ function updateVehicleInfo(vcallsign, newPosition) {
$('.landscape').append('<div class="row vehicle'+vehicle.uuid+'" data-vcallsign="'+vcallsign+'"></div>');
}

if (offline.get('opt_selective_sidebar')) {
if (map.getBounds().contains(vehicles[vcallsign].marker.getLatLng())) {
$("#main .vehicle"+vehicle.uuid).show();
} else {
$("#main .vehicle"+vehicle.uuid).hide();
}
}

} else if(elm.attr('data-vcallsign') === undefined) {
elm.attr('data-vcallsign', vcallsign);
}
Expand All @@ -1554,7 +1582,9 @@ function updateVehicleInfo(vcallsign, newPosition) {
roundNumber(newPosition.gps_lat, 5) + ', ' + roundNumber(newPosition.gps_lon, 5) +'</a>' +
' <i class="icon-location"></i>';
} else {
coords_text = roundNumber(newPosition.gps_lat, 5) + ', ' + roundNumber(newPosition.gps_lon, 5);
coords_text = '<a id="launch_mapapp" href="https://www.google.com/maps/search/?api=1&query='+newPosition.gps_lat+','+newPosition.gps_lon+'" target="_blank" rel="noopener noreferrer">' +
roundNumber(newPosition.gps_lat, 5) + ', ' + roundNumber(newPosition.gps_lon, 5) +'</a>' +
' <i class="icon-location"></i>';
}

// format altitude strings
Expand Down Expand Up @@ -2563,8 +2593,16 @@ var marker_rotate_setup = function(marker, image_src) {
marker.rotated = false;
if(image_src in icon_cache) {
marker.iconImg = icon_cache[image_src];
marker.setCourse(90);
marker.setLatLng(marker.getLatLng());

if (marker.iconImg.complete){
marker.setCourse(90);
marker.setLatLng(marker.getLatLng());
}else{
marker.iconImg.addEventListener("load", function() {
marker.setCourse(90);
marker.setLatLng(marker.getLatLng());
})
}
}
else {
marker.iconImg = new Image();
Expand Down Expand Up @@ -3455,7 +3493,7 @@ function refresh() {
refreshSingle(wvar.query);
} else {
response = formatData(data, false);
update(response);
update(response, true);
$("#stTimer").attr("data-timestamp", response.fetch_timestamp);
}
$("#stText").text("");
Expand Down Expand Up @@ -3583,7 +3621,7 @@ function refreshSingle(serial) {
dataType: "json",
success: function(data, textStatus) {
response = formatData(data, false);
update(response);
update(response, true);
singleRecovery(serial);
$("#stText").text("");
},
Expand Down Expand Up @@ -3625,7 +3663,7 @@ function refreshSingleNew(serial) {
dataType: "json",
success: function(data, textStatus) {
response = formatData(data, false);
update(response);
update(response, true);
},
error: function() {
ajax_inprogress_single_new = false;
Expand Down Expand Up @@ -4340,14 +4378,16 @@ var ssdv = {};
var status = "";
var bs_idx = 0;

function update(response) {
function update(response, none) {
if (response === null ||
!response.positions ||
!response.positions.position ||
!response.positions.position.length) {

// if no vehicles are found, this will remove the spinner and put a friendly message
$("#main .empty").html("<span>No vehicles :(</span>");
if (none) {
$("#main .empty").html("<span>No vehicles :(</span>");
}

return;
}
Expand Down
79 changes: 69 additions & 10 deletions js/xdata.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ function lerp(x, y, a){
return x * (1 - a) + y * a
}


function get_oif411_Cef(pressure){
// Get the Pump efficiency correction value for a given pressure.

Expand All @@ -40,6 +39,64 @@ function get_oif411_Cef(pressure){
return 1.0;
}

function parseOzonesonde(xdata, pressure) {
// Attempt to parse an XDATA string from an ECC Ozonesonde
// Returns an object with parameters to be added to the sondes telemetry.
//
// References:
// https://gml.noaa.gov/aftp/user/jordan/iMet%20Radiosonde%20Protocol.pdf
//
// Sample data: 01010349FDC54296 (length = 16 characters)

// Run some checks over the input
if(xdata.length != 16){
// Invalid Ozonesonde dataset
return {};
}

if(xdata.substr(0,2) !== '01'){
// Not an Ozonesonde (shouldn't get here)
return {};
}

var _output = {};

// Instrument number is common to all XDATA types.
_output['ozonesonde_instrument_number'] = parseInt(xdata.substr(2,2),16);

// Cell Current
_cell_current = parseInt(xdata.substr(4,4),16)*0.001; // uA
_output['ozonesonde_cell_current'] = Math.round(_cell_current * 1000) / 1000; // 3 DP

// Pump Temperature
_pump_temperature = parseInt(xdata.substr(8,4),16);
if ((_pump_temperature & 0x8000) > 0) {
_pump_temperature = _pump_temperature - 0x10000;
}
_pump_temperature = _pump_temperature*0.01; // Degrees C
_output['ozonesonde_pump_temperature'] = Math.round(_pump_temperature * 100) / 100; // 2 DP

// Pump Current
_pump_current = parseInt(xdata.substr(12,2),16); // mA
_output['ozondesonde_pump_current'] = Math.round(_pump_current * 10) / 10; // 1 DP

// Battery Voltage
_battery_voltage = parseInt(xdata.substr(14,2),16)*0.1; // Volts
_output['ozondesonde_battery_voltage'] = Math.round(_battery_voltage * 10) / 10; // 1 DP

// Now attempt to calculate the O3 partial pressure (copy OIF411 calculations)

// Calibration values
Ibg = 0.0; // The BOM appear to use a Ozone background current value of 0 uA
Cef = get_oif411_Cef(pressure); // Calculate the pump efficiency correction.
FlowRate = 28.5; // Use a 'nominal' value for Flow Rate (seconds per 100mL).

_O3_partial_pressure = (4.30851e-4)*(_output['ozonesonde_cell_current'] - Ibg)*(_output['ozonesonde_pump_temperature']+273.15)*FlowRate*Cef; // mPa
_output['ozondesonde_O3_partial_pressure'] = Math.round(_O3_partial_pressure * 1000) / 1000; // 3 DP

return _output
}

function parseOIF411(xdata, pressure){
// Attempt to parse an XDATA string from an OIF411 Ozone Sounder
// Returns an object with parameters to be added to the sondes telemetry.
Expand Down Expand Up @@ -159,36 +216,36 @@ function parseCOBALD(xdata) {
// Internal temperature
_internal_temperature = parseInt(xdata.substr(7,3),16);
if ((_internal_temperature & 0x800) > 0) {
_internal_temperature = _internal_temperature - 0x1000;
_internal_temperature = _internal_temperature - 0x1000;
}
_internal_temperature = _internal_temperature/8; // Degrees C (-40 - 50)
_output['cobald_internal_temperature'] = Math.round(_internal_temperature * 100) / 100; // 2 DP

// Blue backscatter
_blue_backscatter = parseInt(xdata.substr(10,6),16);
if ((_blue_backscatter & 0x800000) > 0) {
_blue_backscatter = _blue_backscatter - 0x1000000;
_blue_backscatter = _blue_backscatter - 0x1000000;
}
_output['cobald_blue_backscatter'] = _blue_backscatter; // (0 - 1000000)

// Red backckatter
_red_backscatter = parseInt(xdata.substr(16,6),16);
if ((_red_backscatter & 0x800000) > 0) {
_red_backscatter = _red_backscatter - 0x1000000;
_red_backscatter = _red_backscatter - 0x1000000;
}
_output['cobald_red_backscatter'] = _red_backscatter; // (0 - 1000000)

// Blue monitor
_blue_monitor = parseInt(xdata.substr(22,4),16);
if ((_blue_monitor & 0x8000) > 0) {
_blue_monitor = _blue_monitor - 0x10000;
_blue_monitor = _blue_monitor - 0x10000;
}
_output['cobald_blue_monitor'] = _blue_monitor; // (-32768 - 32767)

// Red monitor
_red_monitor = parseInt(xdata.substr(26,4),16);
if ((_red_monitor & 0x8000) > 0) {
_red_monitor = _red_monitor - 0x10000;
_red_monitor = _red_monitor - 0x10000;
}
_output['cobald_red_monitor'] = _red_monitor; // (-32768 - 32767)

Expand Down Expand Up @@ -565,10 +622,12 @@ function parseXDATA(data, pressure, temperature){
_instrument = _current_xdata.substr(0,2);

if (_instrument === '01') {
// V7
// 0102 time=1001 cnt=0 rpm=0
// 0102 time=1001 cnt=7 rpm=419
if (!_instruments.includes("V7")) _instruments.push('V7');
if (_current_xdata.length = 16) {
// Ozonesonde
_xdata_temp = parseOzonesonde(_current_xdata, pressure);
_output = Object.assign(_output,_xdata_temp);
if (!_instruments.includes("Ozonesonde")) _instruments.push('Ozonesonde');
}
} else if (_instrument === '05'){
// OIF411
_xdata_temp = parseOIF411(_current_xdata, pressure);
Expand Down