radiosonde_auto_rx/auto_rx/autorx/templates/index.html

1689 wiersze
84 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<title>Radiosonde Auto-RX Status</title>
<!-- Configure to work on mobile -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Import style sheets (font and icons are remote, should fix) -->
<link href="{{ url_for('static', filename='css/main.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/roboto.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/c3.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/leaflet.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/leaflet.fullscreen.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/autorx.css') }}" rel="stylesheet" >
<link id='tabulatorsheet' href="{{ url_for('static', filename='css/tabulator_midnight.min.css') }}" rel='stylesheet'>
<!-- Import local libraries -->
<script src="{{ url_for('static', filename='js/jquery-3.6.0.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/jquery-ui.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/leaflet.js') }}"></script>
<script src="{{ url_for('static', filename='js/leaflet-providers.js') }}"></script>
<script src="{{ url_for('static', filename='js/Leaflet.fullscreen.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/leaflet.edgebuffer.js') }}"></script>
<script src="{{ url_for('static', filename='js/socket.io.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/scan_chart.js') }}"></script>
<script src="{{ url_for('static', filename='js/c3.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/d3.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/utils.js') }}"></script>
<script src="{{ url_for('static', filename='js/autorxapi.js') }}"></script>
<script src="{{ url_for('static', filename='js/tabulator.min.js') }}"></script>
<script>
var autorx_config = {
lat: 0.0,
lon: 0.0
};
var sonde_positions = {};
var sonde_currently_following = "none";
// Function to change CSS options when changing dark mode.
function changeTheme(dark) {
if (dark == false) {
document.body.style.background = 'white';
if (document.getElementById("log-tray").style.color == "rgb(255, 0, 0)") {
$('#main span').css('color', 'black')
$('#log-tray').css('color', 'red');
} else {
$('#main span').css('color', 'black')
}
$('#main p').css('color', 'black')
$('.modal-content p').css('color', 'black')
$('.modal-content h2').css('color', 'black')
$('.modal-content span').css('color', 'black')
$('.modal-content').css('background-color', 'white')
$('.close').css('color', 'black')
$('#myBtn1').css('color', 'black')
$('#myBtn2').css('color', 'black')
$('#scanid').css('color', 'black')
$('.c3-axis-y').css('fill', 'black')
$('.c3-axis-x').css('fill', 'black')
$('.c3-legend-item text').css('fill', 'black')
$('.domain').css('stroke', 'black')
$('.tick line').css('stroke', 'black')
$('#mapid span').css('color', 'black')
$('.sidenav').css('background-color', '#111')
$('.settings').css('background-color', '#111')
$('#tabulatorsheet').attr('href', '{{ url_for("static", filename="css/tabulator_simple.min.css") }}');
} else {
document.body.style.background = '#121212';
if (document.getElementById("log-tray").style.color == "rgb(255, 0, 0)") {
$('#main span').css('color', 'white')
$('#log-tray').css('color', 'red');
} else {
$('#main span').css('color', 'white')
}
$('#main p').css('color', 'white')
$('.modal-content p').css('color', 'white')
$('.modal-content h2').css('color', 'white')
$('.modal-content span').css('color', 'white')
$('.modal-content').css('background-color', 'black')
$('.close').css('color', 'white')
$('#myBtn1').css('color', 'white')
$('#myBtn2').css('color', 'white')
$('#scanid').css('color', 'white')
$('.c3-axis-y').css('fill', 'white')
$('.c3-axis-x').css('fill', 'white')
$('.c3-legend-item text').css('fill', 'white')
$('.domain').css('stroke', 'white')
$('.tick line').css('stroke', 'white')
$('#mapid span').css('color', 'black')
$('.sidenav').css('background-color', '#414141')
$('.settings').css('background-color', '#414141')
$('#tabulatorsheet').attr('href', '{{ url_for("static", filename="css/tabulator_midnight.min.css") }}');
}
}
$( document ).ready(function() {
namespace = '/update_status';
var socket_path = "{{ url_for("static", filename="") }}".replace('static/', 'socket.io')
var socket = io.connect(location.origin+namespace, {'path': socket_path});
$.ajax({
// Get station.cfg file.
url: "get_config",
dataType: 'json',
async: false,
success: function(data) {
autorx_config = data;
// Update station callsign area
_station_call = autorx_config['habitat_uploader_callsign'];
if (autorx_config['aprs_user'] !== "N0CALL"){
_station_call += " / " + autorx_config['aprs_user'];
}
$('#station_callsign').text(_station_call);
// Update webpage title
// document.title = _station_call + " Auto-RX Status";
document.title = autorx_config['habitat_uploader_callsign'] + " - Radiosonde Auto-RX Status";
if(autorx_config["web_control"] == false){
$("#password-header").html("<h2>Web Control Disabled</h2>");
disable_web_controls();
}
}
});
$.ajax({
// Get local version number.
url: "get_version",
dataType: 'json',
async: true,
success: function(data) {
// Update the current version field.
if (getCookie('version') == 'false') {}
else if (getCookie('version') == 'true') {
$('#currentversion').text(data.current);
} else if ((window.innerWidth/window.innerHeight) > 1) {
$('#currentversion').text(data.current);
}
// Update the latest version area.
if(data.latest == 'Latest'){
//pass
} else if (data.latest == "Unknown"){
//pass
} else {
if (data.latest.includes("-beta")) {
$("#footertext").html("Update Available: <a href='https://github.com/projecthorus/radiosonde_auto_rx/commits/testing' target='_blank'>" + data.latest + "</a>");
} else {
$("#footertext").html("Update Available: <a href='https://github.com/projecthorus/radiosonde_auto_rx/releases' target='_blank'>" + data.latest + "</a>");
}
}
}
});
socket.on('log_event', function(msg) {
// New log entry received.
var log_time = new Date(msg.timestamp);
// Check if time is UTC mode.
if (getCookie('UTC') == 'false') {
// Check if entry is important.
var log_time_converted = log_time.toLocaleString(window.navigator.language,{hourCycle:'h23', year:"numeric", month:"2-digit", day:'2-digit', hour:'2-digit',minute:'2-digit', second:'2-digit'});
if (msg.level == "INFO" || msg.level == "DEBUG") {
var log_entry = "<tr><td>" + log_time_converted + " – " + msg.level + "<br><em style='font-size: 15px;'>" + msg.msg + "</em></td></tr>";
} else {
// If entry is important colour text red.
var log_entry = "<tr><td>" + log_time_converted + " – " + msg.level + "<br><em style='font-size: 15px; color:red;'>" + msg.msg + "</em></td></tr>"
if (msg.level == "ERROR" || msg.level == "CRITICAL") {
if (document.getElementById("mySidenav").offsetWidth == 0) {
$('#log-tray').css('color', 'red');
}
}
}
} else {
if (msg.level == "INFO" || msg.level == "DEBUG") {
var log_entry = "<tr><td>" + new Date(msg.timestamp).toISOString().replace("T", " ").replace("Z", "").slice(0, -4) + " UTC – " + msg.level + "<br><em style='font-size: 15px;'>" + msg.msg + "</em></td></tr>";
} else {
// If entry is important colour text red.
var log_entry = "<tr><td>" + new Date(msg.timestamp).toISOString().replace("T", " ").replace("Z", "").slice(0, -4) + " UTC – " + msg.level + "<br><em style='font-size: 15px; color:red;'>" + msg.msg + "</em></td></tr>";
if (msg.level == "ERROR" || msg.level == "CRITICAL") {
if (document.getElementById("mySidenav").offsetWidth == 0) {
$('#log-tray').css('color', 'red');
}
}
}
}
// Append entry to log table.
$('#log_data > tbody').prepend(log_entry);
});
setup_scan_chart();
socket.on('scan_event', function(msg) {
// There is Scan data ready for us!
// Grab the latest set of data.
$.getJSON("get_scan_data", function(data){
// Load the data into our data stores.
scan_chart_spectra.columns[0] = ['x_spectra'].concat(data.freq);
scan_chart_spectra.columns[1] = ['Spectra'].concat(data.power);
scan_chart_peaks.columns[0] = ['x_peaks'].concat(data.peak_freq);
scan_chart_peaks.columns[1] = ['Peaks'].concat(data.peak_lvl);
scan_chart_threshold.columns[0] = ['x_thresh'].concat([data.freq[0],data.freq[data.freq.length-1]]);
scan_chart_threshold.columns[1] = ['Threshold'].concat([data.threshold+autorx_config.snr_threshold,data.threshold+autorx_config.snr_threshold]);
scan_chart_latest_timestamp = data.timestamp;
// Do not update the scan plot if the tab is hidden, or we have the scan plot hidden.
if( (document.visibilityState == "hidden") || (document.getElementById("showscanbutton").checked == false) ){
return;
} else {
redraw_scan_chart(data);
}
}
);
});
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "visible" && document.getElementById("showscanbutton").checked) {
redraw_scan_chart();
}
});
socket.on('task_event', function(msg){
update_task_list();
});
// Update task list now.
update_task_list();
// List of available map layers.
var Mapnik = L.tileLayer.provider("OpenStreetMap.Mapnik", {edgeBufferTiles: 2});
var DarkMatter = L.tileLayer.provider("CartoDB.DarkMatter", {edgeBufferTiles: 2});
var Terrain = L.tileLayer.provider("Stamen.Terrain", {edgeBufferTiles: 2});
var WorldImagery = L.tileLayer.provider("Esri.WorldImagery", {edgeBufferTiles: 2});
var Voyager = L.tileLayer.provider("CartoDB.Voyager", {edgeBufferTiles: 2});
var OpenTopoMap = L.tileLayer.provider("OpenTopoMap", {edgeBufferTiles: 2});
// Add maps to baseMaps.
var baseMaps = {
"Mapnik": Mapnik,
"DarkMatter": DarkMatter,
"WorldImagery": WorldImagery,
"Terrain": Terrain,
"Voyager": Voyager,
"OpenTopoMap": OpenTopoMap
};
// Check if user has preffered map theme.
var x = getCookie('theme');
if (x) {
mapTheme = x;
} else {
if (getCookie('dark') == "false") {
mapTheme = "Mapnik"
} else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
mapTheme = "DarkMatter"
} else {
mapTheme = "Mapnik"
}
}
// Home Icon.
homeIcon = L.icon({
iconUrl: "{{ url_for('static', filename='img/antenna-green.png') }}",
iconSize: [26, 34],
iconAnchor: [13, 34]
});
// Home Icon for dark mode.
homeIconDark = L.icon({
iconUrl: "{{ url_for('static', filename='img/antenna-green-dark.png') }}",
iconSize: [26, 34],
iconAnchor: [13, 34]
});
// Create map object.
mymap = L.map('mapid').setView([autorx_config.station_lat, autorx_config.station_lon], 8);
mymap.addControl(new L.Control.Fullscreen());
if (mapTheme != 'DarkMatter' && mapTheme != 'WorldImagery') {
home_marker = L.marker([autorx_config.station_lat, autorx_config.station_lon, autorx_config.alt],
{title: 'Receiver Location', icon: homeIcon}
).addTo(mymap);
} else {
home_marker = L.marker([autorx_config.station_lat, autorx_config.station_lon, autorx_config.alt],
{title: 'Receiver Location', icon: homeIconDark}
).addTo(mymap);
}
L.control.layers(baseMaps).addTo(mymap);
baseMaps[mapTheme].addTo(mymap);
// Update preffered them cookie on layer change.
mymap.on('baselayerchange', function(e) {
setCookie("theme", e['name'], 365);
if(e['name'] == "DarkMatter" || e['name'] == "WorldImagery"){
home_marker.setIcon(homeIconDark);
}else{
home_marker.setIcon(homeIcon);
}
});
// Check if user has preffered map visiblity.
if (getCookie('map') == 'false') {
document.getElementById("showmapbutton").checked = false;
document.getElementById("mapid").style.display = "none";
} else {
document.getElementById("showmapbutton").checked = true;
}
// Check if user has preffered table visiblity.
if (getCookie('table') == 'false') {
document.getElementById("showtablebutton").checked = false;
document.getElementById("tableid").style.display = "none";
} else {
document.getElementById("showtablebutton").checked = true;
}
// Check if user has preffered follow latest sonde selection.
if (getCookie('follow') == 'false') {
document.getElementById("sondeAutoFollow").checked = false;
} else {
document.getElementById("sondeAutoFollow").checked = true;
}
// Check if user has UTC time selection.
if (getCookie('UTC') == 'false') {
document.getElementById("showUTCbutton").checked = false;
} else {
document.getElementById("showUTCbutton").checked = true;
}
// Check if user has UTC time selection.
if (getCookie('imperial') == 'true') {
document.getElementById("showimperialbutton").checked = true;
} else {
document.getElementById("showimperialbutton").checked = false;
}
// Check if user has version shown selection.
if (getCookie('version') == 'false') {
document.getElementById("showversionbutton").checked = false;
} else if (getCookie('version') == 'true') {
document.getElementById("showversionbutton").checked = true;
} else if ((window.innerWidth/window.innerHeight) > 1) {
document.getElementById("showversionbutton").checked = true;
} else {
document.getElementById("showversionbutton").checked = false;
}
// Check if user has preffered scan chart visiblity.
if (getCookie('scan') == 'true') {
document.getElementById("showscanbutton").checked = true;
document.getElementById("scanid").style.display = "block";
} else {
document.getElementById("showscanbutton").checked = false;
document.getElementById("scanid").style.display = "none";
}
// Check if user has dark mode set.
if (getCookie('dark') == 'true') {
document.getElementById("showdarkbutton").checked = true;
changeTheme(true);
} else if (getCookie('dark') == 'false') {
document.getElementById("showdarkbutton").checked = false;
changeTheme(false);
} else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.getElementById("showdarkbutton").checked = true;
changeTheme(true);
} else {
document.getElementById("showdarkbutton").checked = false;
changeTheme(false);
}
// Check if dark mode button has been ticked.
$('#showdarkbutton').change(function() {
if ($(this).is(":checked")) {
setCookie("dark", 'true', 365);
changeTheme(true);
} else {
setCookie("dark", 'false', 365);
changeTheme(false);
}
});
// Check if imperial units button has been ticked.
$('#showimperialbutton').change(function() {
if ($(this).is(":checked")) {
setCookie("imperial", 'true', 365);
location.reload();
} else {
setCookie("imperial", 'false', 365);
location.reload();
}
});
// Check if show version button has been ticked.
$('#showversionbutton').change(function() {
if ($(this).is(":checked")) {
setCookie("version", 'true', 365);
location.reload();
} else {
setCookie("version", 'false', 365);
location.reload();
}
});
// Check if UTC button has been ticked.
$('#showUTCbutton').change(function() {
if ($(this).is(":checked")) {
setCookie("UTC", 'true', 365);
for (var i = 0; i < Object.keys(sonde_positions).length; i++) {
table.getRow(Object.keys(sonde_positions)[i]).reformat();
}
} else {
setCookie("UTC", 'false', 365);
for (var i = 0; i < Object.keys(sonde_positions).length; i++) {
table.getRow(Object.keys(sonde_positions)[i]).reformat();
}
}
});
// Check if settings div has been scrolled.
$('#scrollsettingsid').scroll(function() {
if ((document.getElementById("scrollsettingsid").scrollHeight - document.getElementById("scrollsettingsid").scrollTop - document.getElementById("scrollsettingsid").clientHeight) < 1 ) {
document.getElementById("downdiv").style.display = "none";
} else {
document.getElementById("downdiv").style.display = "block";
}
});
// Check if pagination selector has been ticked.
$('#paginationSelector').change(function() {
setCookie("pagination", this.value, 365);
table.setPageSize(this.value);
});
// Check if cookie exists for entries to display per page in table
if (getCookie('pagination') != null) {
pagination_size = parseInt(getCookie('pagination'));
$('#paginationSelector option[value="'+ getCookie('pagination') +'"]').attr("selected",true);
} else {
if (($( window ).width()/$( window ).height()) > 1) {
pagination_size = 6;
$('#paginationSelector option[value="6"]').attr("selected",true);
} else {
pagination_size = 3;
$('#paginationSelector option[value="3"]').attr("selected",true);
}
}
// Create Tabulator table.
table = new Tabulator("#telem_table", {
index:"realid",
//placeholder:"No Sonde Data Available", Does not work when some columns are hidden
// Split into pages for over 6 entries.
pagination:"local",
paginationSize:pagination_size,
layout:"fitDataFill",
resizableColumns:"header",
layoutColumnsOnNewData:true,
columns:[ //Define Table Columns
{title:"SDR", field:"sdr_device_idx", headerSort:true},
{title:"Age", field:"age", headerSort:true},
{title:"Type", field:"type", headerSort:true},
{title:'Freq (MHz)', field:"freq", headerSort:true},
{title:"ID", field:"id", width:125, headerSort:true, formatter:function(cell, formatterParams, onRendered){
_cell_data = cell.getData();
_id = _cell_data.id.replace(/^(DFM|M10|M20|IMET|IMET5|IMET54|MRZ|IMS100|RS11G|MTS01|WXR)-/,"");
_sondehub_id = _cell_data.id.replace(/^(DFM|M10|M20|IMET|IMET5|IMET54|MRZ|IMS100|RS11G|MTS01|WXR)-/,"");
// Add Sondehub Link
_id += "&nbsp;<a href='http://sondehub.org/" + _sondehub_id + "' title='View on Sondehub' target='_blank'>" + "<img src='{{ url_for('static', filename='img/sondehub.png')}}'/>" + "</a>";
// Add Radiosondy Link
if(_cell_data.aprsid != null){
_aprs_id = _cell_data.aprsid.trim();
_id += "<a href='https://radiosondy.info/sonde_archive.php?sondenumber=" + _aprs_id + "' title='View on Radiosondy.info' target='_blank'>" + "<img src='{{ url_for('static', filename='img/radiosondy.png')}}'/>" + "</a>";
} else {
_aprs_id = null;
}
return _id;
}},
{title:"Time", field:"datetime", width:180, headerSort:true, formatter:function(cell, formatterParams, onRendered){
if (getCookie('UTC') == 'false') {
var temp_time = new Date(cell.getValue());
var temp_converted = temp_time.toLocaleString(window.navigator.language,{hourCycle:'h23', year:"numeric", month:"2-digit", day:'2-digit', hour:'2-digit',minute:'2-digit', second:'2-digit'});
if (temp_converted == "Invalid Date") {
return;
} else {
return temp_converted;
}
} else {
return cell.getValue();
}
}
},
{title:"Frame", field:"frame", headerSort:true},
{title:"Latitude", field:"lat", width:80, formatter:'html', headerSort:false},
{title:"Longitude", field:"lon", width:80, formatter:'html', headerSort:false},
{title:"Alt", field:"alt", headerSort:true, formatter:function(cell, formatterParams, onRendered){
if (getCookie('imperial') == 'true') {
return (Math.round((cell.getValue()*3.28084) * 10) / 10);
} else {
return cell.getValue();
}
}, titleFormatter:function(cell, formatterParams, onRendered){
if (getCookie('imperial') == 'true') {
return cell.getValue() + " (ft)";
} else {
return cell.getValue() + " (m)";
}
}
},
{title:"Vel", field:"vel_h", headerSort:false, formatter:function(cell, formatterParams, onRendered){
if (getCookie('imperial') == 'true') {
return (Math.round((cell.getValue()*0.62137) * 10) / 10);
} else {
return cell.getValue();
}
}, titleFormatter:function(cell, formatterParams, onRendered){
if (getCookie('imperial') == 'true') {
return cell.getValue() + " (mph)";
} else {
return cell.getValue() + " (kph)";
}
}
},
{title:"Asc", field:"vel_v", headerSort:false, formatter:function(cell, formatterParams, onRendered){
if (getCookie('imperial') == 'true') {
return (Math.round((cell.getValue()*196.85) * 10) / 10);
} else {
return cell.getValue();
}
}, titleFormatter:function(cell, formatterParams, onRendered){
if (getCookie('imperial') == 'true') {
return cell.getValue() + " (ft/min)";
} else {
return cell.getValue() + " (m/s)";
}
}
},
{title:"Temp", field:"temp", headerSort:false, formatter:function(cell, formatterParams, onRendered){
if (getCookie('imperial') == 'true') {
return (Math.round(((cell.getValue()*9/5) + 32) * 10) / 10);
} else {
return cell.getValue();
}
}, titleFormatter:function(cell, formatterParams, onRendered){
if (getCookie('imperial') == 'true') {
return cell.getValue() + " (°F)";
} else {
return cell.getValue() + " (°C)";
}
}
},
{title:"RH (%)", field:"humidity", headerSort:false},
{title:"Az (°)", field:"azimuth", headerSort:false},
{title:"El (°)", field:"elevation", headerSort:false},
{title:"Range", field:"range", headerSort:true, formatter:function(cell, formatterParams, onRendered){
if (getCookie('imperial') == 'true') {
return (Math.round((cell.getValue()*0.621371) * 10) / 10);
} else {
return cell.getValue();
}
}, titleFormatter:function(cell, formatterParams, onRendered){
if (getCookie('imperial') == 'true') {
return cell.getValue() + " (mi)";
} else {
return cell.getValue() + " (km)";
}
}
},
{title:"SNR (dB)", field:"snr", headerSort:true},
{title:"Other", field:"other", width:140, headerSort:false},
{title:"Real ID", field:"realid", visible:false}
],
rowContext:function(e, row){
e.preventDefault();
//Highlight Sonde on map when row selected
for (var i = 0; i < Object.keys(sonde_positions).length; i++) {
console.log(Object.keys(sonde_positions)[i]);
if (Object.keys(sonde_positions)[i] != row['_row']['data']['id'].replace(/(<([^>]+)>)/gi, "")) {
sonde_positions[Object.keys(sonde_positions)[i]]['path'].setStyle({
color: sonde_positions[Object.keys(sonde_positions)[i]]['colour']
});
if (sonde_positions[Object.keys(sonde_positions)[i]]['latest_data']['vel_v'] < 0){
sonde_positions[Object.keys(sonde_positions)[i]].marker.setIcon(sondeDescentIcons[sonde_positions[Object.keys(sonde_positions)[i]]['colour']]);
}else{
sonde_positions[Object.keys(sonde_positions)[i]].marker.setIcon(sondeAscentIcons[sonde_positions[Object.keys(sonde_positions)[i]]['colour']]);
}
}
}
if (sonde_positions[row['_row']['data']['id'].replace(/(<([^>]+)>)/gi, "")]['path']['options']['color'] != 'white') {
selected_sonde = row['_row']['data']['id'].replace(/(<([^>]+)>)/gi, "");
sonde_positions[row['_row']['data']['id'].replace(/(<([^>]+)>)/gi, "")]['path'].setStyle({
color: 'white'
});
/*
if (sonde_positions[row['_row']['data']['id'].replace(/(<([^>]+)>)/gi, "")]['latest_data']['vel_v'] < 0){
sonde_positions[row['_row']['data']['id'].replace(/(<([^>]+)>)/gi, "")].marker.setIcon(sondeDescentIcons['white']);
}else{
sonde_positions[row['_row']['data']['id'].replace(/(<([^>]+)>)/gi, "")].marker.setIcon(sondeAscentIcons['white']);
}
*/
} else {
selected_sonde = "";
sonde_positions[row['_row']['data']['id'].replace(/(<([^>]+)>)/gi, "")]['path'].setStyle({
color: sonde_positions[row['_row']['data']['id'].replace(/(<([^>]+)>)/gi, "")]['colour']
});
/*
if (sonde_positions[row['_row']['data']['id'].replace(/(<([^>]+)>)/gi, "")]['latest_data']['vel_v'] < 0){
sonde_positions[row['_row']['data']['id'].replace(/(<([^>]+)>)/gi, "")].marker.setIcon(sondeDescentIcons[sonde_positions[row['_row']['data']['id'].replace(/(<([^>]+)>)/gi, "")]['colour']]);
}else{
sonde_positions[row['_row']['data']['id'].replace(/(<([^>]+)>)/gi, "")].marker.setIcon(sondeAscentIcons[sonde_positions[row['_row']['data']['id'].replace(/(<([^>]+)>)/gi, "")]['colour']]);
}
*/
}
}
});
// Update Tabulator table.
function updateTelemetryTable(){
var telem_data = [];
if (jQuery.isEmptyObject(sonde_positions)){
telem_data = [];
}else{
var sonde_id_list = Object.getOwnPropertyNames(sonde_positions).reverse();
//for (sonde_id in sonde_id_list){
sonde_id_list.forEach( function(sonde_id){
var sonde_id_data = Object.assign({},sonde_positions[sonde_id].latest_data);
var sonde_id_age = Date.now() - sonde_positions[sonde_id].age;
if (sonde_id_age>(1000*autorx_config.rx_timeout)){
sonde_id_data.sdr_device_idx = "";
sonde_id_data.age = "old";
// Remove the LOS line
if(sonde_positions[sonde_id].hasOwnProperty("los_path")){
sonde_positions[sonde_id].los_path.remove();
delete sonde_positions[sonde_id].los_path;
}
}else{
sonde_id_data.age = (sonde_id_age/1000.0).toFixed(0) + " s";
}
// If we have a station lat/lon/alt set, calculate az/el/range.
if (autorx_config.station_lat != 0.0){
// There is a station lat/lon set.
var _bal = {lat:sonde_id_data.lat, lon:sonde_id_data.lon, alt:sonde_id_data.alt};
var _station = {lat:autorx_config.station_lat, lon:autorx_config.station_lon, alt:autorx_config.station_alt};
var _look_angles = calculate_lookangles(_station, _bal);
sonde_id_data.azimuth = _look_angles.azimuth.toFixed(1);
if (_look_angles.elevation >= 10){
sonde_id_data.elevation = _look_angles.elevation.toFixed(1);
}else{
sonde_id_data.elevation = _look_angles.elevation.toFixed(2);
}
sonde_id_data.range = (_look_angles.range/1000).toFixed(1);
} else{
// Insert blank data.
sonde_id_data.azimuth = "";
sonde_id_data.elevation = "";
sonde_id_data.range = "";
}
// Modify some of the fields to fixed point values.
// Add Geo ref links to lat/lon fields.
temp_lat = "<a href='geo:" + sonde_id_data.lat.toFixed(5) + "," + sonde_id_data.lon.toFixed(5) + "'>" + sonde_id_data.lat.toFixed(5) + "</a>";
temp_lon = "<a href='geo:" + sonde_id_data.lat.toFixed(5) + "," + sonde_id_data.lon.toFixed(5) + "'>" + sonde_id_data.lon.toFixed(5) + "</a>";
sonde_id_data.lat = temp_lat;
sonde_id_data.lon = temp_lon;
sonde_id_data.alt = sonde_id_data.alt.toFixed(1);
sonde_id_data.vel_v = sonde_id_data.vel_v.toFixed(1);
sonde_id_data.vel_h = (sonde_id_data.vel_h*3.6).toFixed(1);
// Add a link to HabHub if we have habitat enabled.
// if (autorx_config.sondehub_enabled == true) {
// sonde_id_data.id = "<a href='http://sondehub.org/" + sonde_id.replace(/^(DFM|M10|M20|IMET|IMET54|MRZ)-/,"") + "' target='_blank'>" + sonde_id + "</a>";
// // These links are only going to work for Vaisala radiosondes since the APRS callsign is never passed through to the web interface,
// // and the APRS callsigns for everything other than RS41s and RS92s is different to the 'full' serials
// } else if (autorx_config.aprs_enabled == true && autorx_config.aprs_server == "radiosondy.info") {
// sonde_id_data.id = "<a href='https://radiosondy.info/sonde_archive.php?sondenumber=" + sonde_id + "' target='_blank'>" + sonde_id + "</a>";
// } else if (autorx_config.aprs_enabled == true) {
// sonde_id_data.id = "<a href='https://aprs.fi/#!call=" + sonde_id + "&timerange=3600&tail=3600' target='_blank'>" + sonde_id + "</a>";
// }
sonde_id_data.realid = sonde_id;
// Add SNR data, if it exists.
if (sonde_id_data.hasOwnProperty('snr')){
sonde_id_data.snr = sonde_id_data.snr.toFixed(1);
}
// Add data into the 'other' field.
sonde_id_data.other = "";
// Burst timer for RS41s
if (sonde_id_data.hasOwnProperty('bt')){
if ((sonde_id_data.bt >= 0) && (sonde_id_data.bt < 65535)) {
sonde_id_data.other += "BT " + new Date(sonde_id_data.bt*1000).toISOString().substr(11, 8) + " ";
}
}
if (sonde_id_data.hasOwnProperty('batt')){
sonde_id_data.other += sonde_id_data.batt.toFixed(1) + " V";
}
telem_data.push(sonde_id_data);
});
}
table.updateOrAddData(telem_data);
// Hide table page navigation if only one page.
if(table.getPageMax() == 1){
$(".tabulator-footer").hide();
}else{
$(".tabulator-footer").show();
}
}
// Invalidate map size to fix problems with elements resizing.
mymap.invalidateSize();
var initial_load_complete = false;
selected_sonde = "";
$.ajax({ // Get archived data.
url: "get_telemetry_archive",
dataType: 'json',
async: true,
success: function(data) {
for (sonde_id in data){
var telem = data[sonde_id].latest_telem;
sonde_positions[sonde_id] = {
latest_data: telem,
age: 0,
colour: colour_values[colour_idx]
};
// Create markers
sonde_positions[sonde_id].path = L.polyline(data[sonde_id].path,{title:telem.id + " Path", color:sonde_positions[sonde_id].colour}).addTo(mymap);
if (getCookie('imperial') == 'true'){
_alt = (telem.alt*3.28084).toFixed(0) + 'ft ';
_vel_v = (telem.vel_v*3.28084).toFixed(0) + 'ft/s ';
_vel_h = (telem.vel_h*2.23694).toFixed(0) + 'mph ';
} else {
_alt = telem.alt.toFixed(0) + 'm ';
_vel_v = telem.vel_v.toFixed(1) + 'm/s ';
_vel_h = (telem.vel_h*3.6).toFixed(0) + 'km/h ';
}
sonde_positions[sonde_id].marker = L.marker([telem.lat, telem.lon, telem.alt],{icon: sondeAscentIcons[sonde_positions[sonde_id].colour]})
.bindTooltip('<div class="tooltip-container">' + sonde_id + '<div class="tooltip-container" style="color: #005ec1;">' + _alt + _vel_v + _vel_h + '</div></div>',{permanent:false, direction:'right', className: 'sondeTooltip', offset: [10,0], interactive: false, opacity: 0.7 })
.addTo(mymap);
// if(autorx_config.station_lat != 0.0){
// sonde_positions[sonde_id].los_path = L.polyline([],
// {
// color:los_color,
// opacity:los_opacity
// }
// ).addTo(mymap);
// }
if (telem.vel_v < 0){
sonde_positions[sonde_id].marker.setIcon(sondeDescentIcons[sonde_positions[sonde_id].colour]);
}
colour_idx = (colour_idx+1)%colour_values.length;
}
updateTelemetryTable();
initial_load_complete = true;
}
});
socket.on('station_update', function(msg) {
// Station update messages indicate a move of the station location, as updated
// by a GPS receiver.
if(initial_load_complete == false){
// If we have not completed our initial load of telemetry data, discard this data.
return
}
// Update the marker position.
home_marker.setLatLng([msg.lat, msg.lon, msg.alt]).update();
// Update the autorx_config object, which is used to calculate relative look angles for the telemetry table.
autorx_config.station_lat = msg.lat;
autorx_config.station_lon = msg.lon;
autorx_config.station_alt = msg.alt;
});
socket.on('telemetry_event', function(msg) {
// Telemetry Event messages contain the entire telemetry dictionary, as produced by the SondeDecoder class.
// This includes the fields: ['frame', 'id', 'datetime', 'lat', 'lon', 'alt', 'temp', 'type', 'freq', 'freq_float']
if(initial_load_complete == false){
// If we have not completed our initial load of telemetry data, discard this data.
return
}
// Have we seen this sonde before?
if (sonde_positions.hasOwnProperty(msg.id) == false){
// Nope, add a property to the sonde_positions object, and setup markers for the sonde.
sonde_positions[msg.id] = {
latest_data : msg,
age : Date.now(),
colour : colour_values[colour_idx]
};
// Create markers
sonde_positions[msg.id].path = L.polyline([[msg.lat, msg.lon, msg.alt]],{title:msg.id + " Path", color:sonde_positions[msg.id].colour}).addTo(mymap);
if (getCookie('imperial') == 'true'){
_alt = (msg.alt*3.28084).toFixed(0) + 'ft ';
_vel_v = (msg.vel_v*3.28084).toFixed(0) + 'ft/s ';
_vel_h = (msg.vel_h*2.23694).toFixed(0) + 'mph ';
} else {
_alt = msg.alt.toFixed(0) + 'm ';
_vel_v = msg.vel_v.toFixed(1) + 'm/s ';
_vel_h = (msg.vel_h*3.6).toFixed(0) + 'km/h ';
}
sonde_positions[msg.id].marker = L.marker([msg.lat, msg.lon, msg.alt],{title:msg.id, icon: sondeAscentIcons[sonde_positions[msg.id].colour]})
.bindTooltip('<div class="tooltip-container">' + msg.id + '<div class="tooltip-container" style="color: #005ec1;">' + _alt + _vel_v + _vel_h + '</div></div>',{permanent:false, direction:'right', className: 'sondeTooltip', offset: [10,0], interactive: false, opacity: 0.7})
.addTo(mymap);
// If there is a station location defined, show the path from the station to the sonde.
if(autorx_config.station_lat != 0.0){
sonde_positions[msg.id].los_path = L.polyline([[autorx_config.station_lat, autorx_config.station_lon],[msg.lat, msg.lon]],
{
color:los_color,
opacity:los_opacity
}
).addTo(mymap);
}
colour_idx = (colour_idx+1)%colour_values.length;
// If this is our first sonde since the browser has been opened, follow it.
if (Object.keys(sonde_positions).length == 1){
sonde_positions[msg.id].following = true;
}
} else {
// Yep - update the sonde_positions entry.
sonde_positions[msg.id].latest_data = msg;
sonde_positions[msg.id].age = Date.now();
sonde_positions[msg.id].path.addLatLng([msg.lat, msg.lon, msg.alt]);
sonde_positions[msg.id].marker.setLatLng([msg.lat, msg.lon, msg.alt]).update();
if (getCookie('imperial') == 'true'){
_alt = (msg.alt*3.28084).toFixed(0) + 'ft ';
_vel_v = (msg.vel_v*3.28084).toFixed(0) + 'ft/s ';
_vel_h = (msg.vel_h*2.23694).toFixed(0) + 'mph ';
} else {
_alt = msg.alt.toFixed(0) + 'm ';
_vel_v = msg.vel_v.toFixed(1) + 'm/s ';
_vel_h = (msg.vel_h*3.6).toFixed(0) + 'km/h ';
}
sonde_positions[msg.id].marker.setTooltipContent('<div class="tooltip-container">' + msg.id + '<div class="tooltip-container" style="color: #005ec1;">' + _alt + _vel_v + _vel_h +'</div></div>');
if (msg.vel_v < 0){
if (selected_sonde == msg.id) {
sonde_positions[msg.id].marker.setIcon(sondeDescentIcons['white']);
} else {
sonde_positions[msg.id].marker.setIcon(sondeDescentIcons[sonde_positions[msg.id].colour]);
}
}else{
if (selected_sonde == msg.id) {
sonde_positions[msg.id].marker.setIcon(sondeAscentIcons['white']);
} else {
sonde_positions[msg.id].marker.setIcon(sondeAscentIcons[sonde_positions[msg.id].colour]);
}
}
if(autorx_config.station_lat != 0.0){
if(sonde_positions[msg.id].hasOwnProperty("los_path")){
sonde_positions[msg.id].los_path.setLatLngs([[autorx_config.station_lat, autorx_config.station_lon],[msg.lat, msg.lon]]);
} else {
sonde_positions[msg.id].los_path = L.polyline([[autorx_config.station_lat, autorx_config.station_lon],[msg.lat, msg.lon]],
{
color:los_color,
opacity:los_opacity
}
).addTo(mymap);
}
}
}
// Update the telemetry table display
//updateTelemetryText();
updateTelemetryTable();
// Are we currently following any other sondes?
if (sonde_currently_following == "none"){
// If not, follow this one!
sonde_currently_following = msg.id;
}
// Is sonde following enabled?
if (document.getElementById("sondeAutoFollow").checked == true){
// If we are currently following this sonde, snap the map to it.
if (msg.id == sonde_currently_following){
mymap.panTo([msg.lat,msg.lon], { duration: 2.5, easeLinearity: 0.9 });
}
}
});
// Sonde-Following Logic. May need to adjust timeouts.
var sonde_follow_timeout = 30000; // 30 Seconds - reasonable timeout.
// Every X seconds, check if the currently followed sonde is still getting regular data.
// If not, clear the currently_following flag to allow another sonde to be auto tracked.
window.setInterval(function () {
if (sonde_currently_following == "none"){
return;
}
var now_time = Date.now();
if ( (now_time-sonde_positions[sonde_currently_following].age) > sonde_follow_timeout){
sonde_currently_following = "none";
}
}, sonde_follow_timeout);
// Update telemetry table every second (this is mainly to update the age field)
window.setInterval(function(){
updateTelemetryTable();
}, 1000);
// Tell program we are connected and ready for data.
socket.on('connect', function() {
socket.emit('client_connected', {data: 'I\'m connected!'});
});
// Function to change table columns visible.
$(document).on('change', 'form input', function() {
var checked = $(this).is(":checked");
if (checked == false) {
var cookiesend = 'false';
} else {
var cookiesend = 'true';
}
var index = $(this).attr("class");
// Set cookie for columns to show in future.
setCookie("col" + index, cookiesend, 365);
// Update Tabulator table with selected columns visible.
if(checked) {
switch(index) {
case "0":
table.showColumn("sdr_device_idx");
break;
case "1":
table.showColumn("age");
break;
case "2":
table.showColumn("type");
break;
case "3":
table.showColumn("freq");
break;
case "4":
table.showColumn("id");
break;
case "5":
table.showColumn("datetime");
break;
case "6":
table.showColumn("frame");
break;
case "7":
table.showColumn("lat");
break;
case "8":
table.showColumn("lon");
break;
case "9":
table.showColumn("alt");
break;
case "10":
table.showColumn("vel_h");
break;
case "11":
table.showColumn("vel_v");
break;
case "12":
table.showColumn("temp");
break;
case "13":
table.showColumn("humidity");
break;
case "14":
table.showColumn("azimuth");
break;
case "15":
table.showColumn("elevation");
break;
case "16":
table.showColumn("range");
break;
case "17":
table.showColumn("snr");
break;
case "18":
table.showColumn("other");
break;
}
table.redraw();
} else {
switch(index) {
case "0":
table.hideColumn("sdr_device_idx");
break;
case "1":
table.hideColumn("age");
break;
case "2":
table.hideColumn("type");
break;
case "3":
table.hideColumn("freq");
break;
case "4":
table.hideColumn("id");
break;
case "5":
table.hideColumn("datetime");
break;
case "6":
table.hideColumn("frame");
break;
case "7":
table.hideColumn("lat");
break;
case "8":
table.hideColumn("lon");
break;
case "9":
table.hideColumn("alt");
break;
case "10":
table.hideColumn("vel_h");
break;
case "11":
table.hideColumn("vel_v");
break;
case "12":
table.hideColumn("temp");
break;
case "13":
table.hideColumn("humidity");
break;
case "14":
table.hideColumn("azimuth");
break;
case "15":
table.hideColumn("elevation");
break;
case "16":
table.hideColumn("range");
break;
case "17":
table.hideColumn("snr");
break;
case "18":
table.hideColumn("other");
break;
}
table.redraw();
}
});
// Runs once at page load to set which Tabulator columns to show/hide per set cookies
for (i = 0; i < 19; i++) {
var show = getCookie("col"+i);
if (show == 'false') {
document.getElementById("checkbox" + i).checked = false;
switch(i) {
case 0:
table.hideColumn("sdr_device_idx");
break;
case 1:
table.hideColumn("age");
break;
case 2:
table.hideColumn("type");
break;
case 3:
table.hideColumn("freq");
break;
case 4:
table.hideColumn("id");
break;
case 5:
table.hideColumn("datetime");
break;
case 6:
table.hideColumn("frame");
break;
case 7:
table.hideColumn("lat");
break;
case 8:
table.hideColumn("lon");
break;
case 9:
table.hideColumn("alt");
break;
case 10:
table.hideColumn("vel_h");
break;
case 11:
table.hideColumn("vel_v");
break;
case 12:
table.hideColumn("temp");
break;
case 13:
table.hideColumn("humidity");
break;
case 14:
table.hideColumn("azimuth");
break;
case 15:
table.hideColumn("elevation");
break;
case 16:
table.hideColumn("range");
break;
case 17:
table.hideColumn("snr");
break;
case 18:
table.hideColumn("other");
break;
}
} else if (show == 'true') {
document.getElementById("checkbox" + i).checked = true;
} else {
if ($( window ).width() > 1200) {
document.getElementById("checkbox" + i).checked = true;
} else { // If no cookies are set on mobile device show limited number for better experience.
if ([1,4,9,16].includes(i)) {
setCookie("col" + i, 'true', 365);
document.getElementById("checkbox" + i).checked = true;
}
if ([0,2,3,5,6,7,8,10,11,12,13,14,15,17,18].includes(i)) {
setCookie("col" + i, 'false', 365);
document.getElementById("checkbox" + i).checked = false;
switch(i) {
case 0:
table.hideColumn("sdr_device_idx");
break;
case 1:
table.hideColumn("age");
break;
case 2:
table.hideColumn("type");
break;
case 3:
table.hideColumn("freq");
break;
case 4:
table.hideColumn("id");
break;
case 5:
table.hideColumn("datetime");
break;
case 6:
table.hideColumn("frame");
break;
case 7:
table.hideColumn("lat");
break;
case 8:
table.hideColumn("lon");
break;
case 9:
table.hideColumn("alt");
break;
case 10:
table.hideColumn("vel_h");
break;
case 11:
table.hideColumn("vel_v");
break;
case 12:
table.hideColumn("temp");
break;
case 13:
table.hideColumn("humidity");
break;
case 14:
table.hideColumn("azimuth");
break;
case 15:
table.hideColumn("elevation");
break;
case 16:
table.hideColumn("range");
break;
case 17:
table.hideColumn("snr");
break;
case 18:
table.hideColumn("other");
break;
}
}
}
}
}
table.redraw();
});
// Function to open/close left log menu along with adjusting other elements so they render correctly.
function changeNav() {
if (getCookie('dark') == 'false') {
$('#log-tray').css('color', 'black');
} else if (getCookie('dark') == 'true') {
$('#log-tray').css('color', 'white');
} else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
$('#log-tray').css('color', 'white');
} else {
$('#log-tray').css('color', 'black');
}
var x = document.getElementById("closebtn");
var y = document.getElementById('mapid');
if (document.getElementById("mySidenav").style.width == "0px" || document.getElementById("mySidenav").style.width == 0) {
var myDiv = document.getElementById('sidenavtable');
myDiv.scrollTop = 0;
if ((window.innerWidth/window.innerHeight) > 1) { // 350px wide on desktop.
x.style.display = "none";
if (getCookie('map') == true || document.getElementById("showmapbutton").checked == true) {
y.style.display = "block";
}
document.getElementById("mySidenav").style.width = "350px";
document.getElementById("main").style.marginLeft = "350px";
document.getElementById("mySidenav").style.borderRadius = "0px 25px 25px 0px";
mymap.invalidateSize();
setTimeout(scan_chart_obj.resize,500);
} else { // Fullsize on mobile.
x.style.display = "block";
y.style.display = "none";
document.getElementById("mySidenav").style.width = "100%";
document.getElementById("main").style.marginLeft = "0";
document.getElementById("mySidenav").style.borderRadius = "0px";
}
} else {
x.style.display = "none";
if (getCookie('map') == true || document.getElementById("showmapbutton").checked == true) {
y.style.display = "block";
}
document.getElementById("mySidenav").style.width = 0;
document.getElementById("main").style.marginLeft = 0;
mymap.invalidateSize();
setTimeout(scan_chart_obj.resize,500);
}
}
// Function to open/close right settings menu along with adjusting other elements so they render correctly.
function changeSettings() {
var y = document.getElementById('mapid');
if (document.getElementById("mySettings").style.width == "0px" || document.getElementById("mySettings").style.width == 0) {
if ((window.innerWidth/window.innerHeight) > 1) { // 350px wide on desktop.
if (getCookie('map') == true || document.getElementById("showmapbutton").checked == true) {
y.style.display = "block";
}
document.getElementById("mySettings").style.width = "350px";
document.getElementById("main").style.marginRight = "350px";
document.getElementById("mySettings").style.borderRadius = "25px 0px 0px 25px";
mymap.invalidateSize();
setTimeout(scan_chart_obj.resize,500);
setTimeout(showDown,500);
} else { // Fullsize on mobile.
y.style.display = "none";
document.getElementById("mySettings").style.width = "100%";
document.getElementById("main").style.marginRight = "0";
document.getElementById("mySettings").style.borderRadius = "0px";
}
} else {
if (getCookie('map') == true || document.getElementById("showmapbutton").checked == true) {
y.style.display = "block";
}
document.getElementById("mySettings").style.width = 0;
document.getElementById("main").style.marginRight = 0;
mymap.invalidateSize();
setTimeout(scan_chart_obj.resize,500);
setTimeout(showDown,500);
}
}
function showDown () {
if ((document.getElementById("scrollsettingsid").scrollHeight - document.getElementById("scrollsettingsid").scrollTop - document.getElementById("scrollsettingsid").clientHeight) < 1 ) {
document.getElementById("downdiv").style.display = "none";
} else {
document.getElementById("downdiv").style.display = "block";
}
}
// Show/hide map on button press and update cookies.
function showMap(element) {
if (element.checked == false) {
document.getElementById("mapid").style.display = "none";
setCookie("map", 'false', 365);
} else {
document.getElementById("mapid").style.display = "block";
setCookie("map", 'true', 365);
mymap.invalidateSize();
}
}
// Show/hide scan chart on button press and update cookies.
function showScan(element) {
if (element.checked == false) {
document.getElementById("scanid").style.display = "none";
setCookie("scan", 'false', 365);
mymap.invalidateSize();
} else {
document.getElementById("scanid").style.display = "block";
setCookie("scan", 'true', 365);
mymap.invalidateSize();
redraw_scan_chart();
setTimeout(scan_chart_obj.resize,500);
}
}
// Enable/disable auto follow on button press and update cookies.
function autoFollow(element) {
if (element.checked == false) {
setCookie("follow", 'false', 365);
} else {
setCookie("follow", 'true', 365);
}
}
// Show/hide table on button press and update cookies.
function showTable(element) {
if (element.checked == false) {
document.getElementById("tableid").style.display = "none";
setCookie("table", 'false', 365);
} else {
document.getElementById("tableid").style.display = "block";
setCookie("table", 'true', 365);
}
}
// Set given cookie name and value.
function setCookie(name,value) {
localStorage.setItem(name, value);
}
// Return cookie value given name.
function getCookie(name) {
return localStorage.getItem(name);
}
// Reset specific cookie.
function eraseCookie(name) {
localStorage.removeItem(name);
}
// Reset all cookies.
function deleteAllCookies() {
localStorage.clear();
location.reload();
}
// When the user clicks on the button, open the modal
function openModal() {
document.getElementById("myModal").style.display = "block";
changeSettings()
if (getCookie("password") === null) {} else {
verify_password();
}
}
// When the user clicks on close button, close the modal
function closeModal() {
document.getElementById("myModal").style.display = "none";
$('#password-input').val('');
}
// When the user clicks anywhere outside of the modal, close it
$(window).click(function(e) {
if (e.target == document.getElementById("myModal")) {
document.getElementById("myModal").style.display = "none";
$('#password-input').val('');
}
});
let vh = window.innerHeight * 0.01;
document.documentElement.style.setProperty('--vh', `${vh}px`);
</script>
</head>
<body>
<!-- Wrapper for entire body to ensure flex works -->
<div class="wrapper">
<!-- Wrapper for log sidebar -->
<div id="mySidenav" class="sidenav">
<div class="headerdiv">
<a href="javascript:void(0)" class="closebtn" id="closebtn" onclick="changeNav()">&#10006;</a>
<img src="{{ url_for('static', filename='img/autorx_logo.png') }}" alt="Radiosonde Auto-RX Button">
<h2>Log</h2>
</div>
<div class="sidenavtable" id="sidenavtable">
<table style="width:100%" id="log_data">
<tbody>
</tbody>
</table>
</div>
</div>
<!-- Wrapper for settings sidebar -->
<div id="mySettings" class="settings">
<a href="javascript:void(0)" class="closebtn2" id="closebtn2" onclick="changeSettings()">&#10006;</a>
<h1 style="color: white; text-align: center; margin: -0.25em;">Settings</h1>
<div class="scrollsettings" id="scrollsettingsid" style="margin-top: 20px;">
<h2 style="display:inline; vertical-align:middle; margin-left: 35px; margin-bottom: 0.3em;">Table Options</h2>
<form>
<input type="checkbox" class="0" id="checkbox0" checked>
<label> SDR</label><br>
<input type="checkbox" class="1" id="checkbox1" checked>
<label> Age</label><br>
<input type="checkbox" class="2" id="checkbox2" checked>
<label> Type</label><br>
<input type="checkbox" class="3" id="checkbox3" checked>
<label> Frequency</label><br>
<input type="checkbox" class="4" id="checkbox4" checked>
<label> ID</label><br>
<input type="checkbox" class="5" id="checkbox5" checked>
<label> Time</label><br>
<input type="checkbox" class="6" id="checkbox6" checked>
<label> Frame</label><br>
<input type="checkbox" class="7" id="checkbox7" checked>
<label> Latitude</label><br>
<input type="checkbox" class="8" id="checkbox8" checked>
<label> Longitude</label><br>
<input type="checkbox" class="9" id="checkbox9" checked>
<label> Altitude</label><br>
<input type="checkbox" class="10" id="checkbox10" checked>
<label> Velocity</label><br>
<input type="checkbox" class="11" id="checkbox11" checked>
<label> Ascent Rate</label><br>
<input type="checkbox" class="12" id="checkbox12" checked>
<label> Temperature</label><br>
<input type="checkbox" class="13" id="checkbox13" checked>
<label> Humidity</label><br>
<input type="checkbox" class="14" id="checkbox14" checked>
<label> Azimuth</label><br>
<input type="checkbox" class="15" id="checkbox15" checked>
<label> EI</label><br>
<input type="checkbox" class="16" id="checkbox16" checked>
<label> Range</label><br>
<input type="checkbox" class="17" id="checkbox17" checked>
<label> SNR</label><br>
<input type="checkbox" class="18" id="checkbox18" checked>
<label> Other</label><br><br>
</form>
<div style="margin-left:40px;">
<h2 style="display:inline;vertical-align:middle;">Show Table</h2>
&nbsp;
<div style="display:inline;vertical-align:middle;">
<label class="switch">
<input type="checkbox" onchange="showTable(this)" id="showtablebutton">
<span class="slider round"></span>
</label>
</div>
<br>
<br>
<h2 style="display:inline;vertical-align:middle;">Show Scan Plot</h2>
&nbsp;
<div style="display:inline;vertical-align:middle;">
<label class="switch">
<input type="checkbox" onchange="showScan(this)" id="showscanbutton">
<span class="slider round"></span>
</label>
</div>
<br>
<br>
<h2 style="display:inline;vertical-align:middle;">Show Map</h2>
&nbsp;
<div style="display:inline;vertical-align:middle;">
<label class="switch">
<input type="checkbox" onchange="showMap(this)" id="showmapbutton">
<span class="slider round"></span>
</label>
</div>
<br>
<br>
<h2 style="display:inline;vertical-align:middle;">Dark Mode</h2>
&nbsp;
<div style="display:inline;vertical-align:middle;">
<label class="switch">
<input type="checkbox" id="showdarkbutton">
<span class="slider round"></span>
</label>
</div>
<br>
<br>
<h2 style="display:inline;vertical-align:middle;">Show UTC Time</h2>
&nbsp;
<div style="display:inline;vertical-align:middle;">
<label class="switch">
<input type="checkbox" id="showUTCbutton">
<span class="slider round"></span>
</label>
</div>
<br>
<br>
<h2 style="display:inline;vertical-align:middle;">Set Pagination Size</h2>
&nbsp;
<div style="display:inline;vertical-align:middle;">
<select id="paginationSelector" style="font-size: 1.2rem;">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
<option value="4">Four</option>
<option value="5">Five</option>
<option value="6">Six</option>
<option value="7">Seven</option>
<option value="8">Eight</option>
<option value="9">Nine</option>
<option value="10">Ten</option>
</select>
</div>
<br>
<br>
<h2 style="display:inline;vertical-align:middle;">Follow Sonde</h2>
&nbsp;
<div style="display:inline;vertical-align:middle;">
<label class="switch">
<input type="checkbox" onchange="autoFollow(this)" id="sondeAutoFollow">
<span class="slider round"></span>
</label>
</div>
<br>
<br>
<h2 style="display:inline;vertical-align:middle;">Show Imperial Units</h2>
&nbsp;
<div style="display:inline;vertical-align:middle;">
<label class="switch">
<input type="checkbox" id="showimperialbutton">
<span class="slider round"></span>
</label>
</div>
<br>
<br>
<h2 style="display:inline;vertical-align:middle;">Show Software Version</h2>
&nbsp;
<div style="display:inline;vertical-align:middle;">
<label class="switch">
<input type="checkbox" id="showversionbutton">
<span class="slider round"></span>
</label>
</div>
<br>
<br>
<h2 style="display:inline;vertical-align:middle;">Live KML</h2>
&nbsp;
<div style="display:inline;vertical-align:middle;">
<button onclick="window.location.href='rs.kml'">SHOW</button>
</div>
<br>
<br>
<h2 style="display:inline;vertical-align:middle;">Reset Page</h2>
&nbsp;
<div style="display:inline;vertical-align:middle;">
<button onclick="deleteAllCookies()">RESET</button>
</div>
<br>
<br>
<h2 style="display:inline;vertical-align:middle;">Controls</h2>
&nbsp;
<div style="display:inline;vertical-align:middle;">
<button id="open-controls" onclick="openModal()">OPEN</button>
</div>
<br>
<br>
<h2 style="display:inline;vertical-align:middle;">Historical View</h2>
&nbsp;
<div style="display:inline;vertical-align:middle;">
<button onclick="window.location.href='historical.html'">OPEN</button>
</div>
<br>
<br>
<br>
</div>
</div>
<div id="downdiv">
<i class="icon-angle-down" style="font-size:60px;opacity:1;"></i>
</div>
</div>
<!-- Wrapper for main screen -->
<div id="main" onload="loadMap();">
<div>
<span style="font-size:3vh;font-size:calc(var(--vh, 1vh) * 3);cursor:pointer;" onclick="changeNav()"><span id="log-tray">&#9776;</span> Radiosonde Auto-RX <span id="currentversion" style="white-space:nowrap"></span></span>
</div>
<span style="font-size:2vh;font-size:calc(var(--vh, 1vh) * 2);" id="footertext"></span>
<p style="font-size:2vh;font-size:calc(var(--vh, 1vh) * 2);">Station: <span id="station_callsign">???</span></p>
<p style="font-size:2vh;font-size:calc(var(--vh, 1vh) * 2);">Tasking: <span id="task_status"></span></p>
<div id="tableid">
<div id="telem_table"></div>
</div>
<div id="scanid">
<h2>Scan Results</h2>
<div id='scan_results'>No scan data yet...</div>
<div id="scan_chart" style="width:100%;"></div>
</div>
<br>
<div id="mapid"></div>
<i id="myBtn1" onclick="changeSettings()" class="icon-cog" style="font-size:4vh;font-size:calc(var(--vh, 1vh) * 4);"></i>
<a href="historical.html" id="myBtn2"><i class="icon-history" style="font-size:4vh;font-size:calc(var(--vh, 1vh) * 4);"></i></a>
</div>
<!-- Wrapper for advanced control modal -->
<div id="myModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<span class="close" onclick="closeModal()">&times;</span>
<h2>Advanced Controls</h2>
</div>
<div class="modal-body">
<div id="password-field">
<span id="password-header"><h2>No Password Entered</h2></span>
<input style="display:inline;vertical-align:middle;" type="text" id="password-input" placeholder="Password" >
<div style="display:inline;vertical-align:middle;">
<button id="verify-password" onclick="verify_password();$('#password-input').val('');">Submit</button>
</div>
</div>
<div id="controls" style="visibility:hidden;display:none;">
<h2>Decoder Control</h2>
<p>Start Decoder</p>
<input style="display:inline;vertical-align:middle;" type="text" id="frequency-input" placeholder="Frequency (MHz)">
<select style="display:inline;vertical-align:middle;" class="control" id="sonde-type-select">
<option value="RS41" selected>RS41</option>
<option value="RS92">RS92</option>
<option value="DFM">DFM</option>
<option value="M10">M10</option>
<option value="M20">M20</option>
<option value="LMS6">LMS6 (400 MHz)</option>
<option value="MK2LMS">LMS6 (1680 MHz)</option>
<option value="IMET">iMet-4</option>
<option value="IMET5">iMet-50/54</option>
<option value="MEISEI">iMS-100</option>
<option value="MRZ">MRZ-H1</option>
<option value="MTS01">MTS01</option>
<option value="WXR301">WXR301</option>
</select>
<div style="display:inline;vertical-align:middle;">
<button id="start-decoder" onclick="start_decoder();">Start</button>
</div>
<br>
<p>Stop Decoder</p>
<select style="display:inline;vertical-align:middle;" class="control" id="stop-frequency-select">
<option value="0" disabled selected>No Decoders</option>
</select>
<div style="display:inline;vertical-align:middle;">
<button id="stop-decoder" onclick="stop_decoder();">Stop</button>
</div>
<h2>Scanner Control</h2>
<p>Scanner</p>
<div style="display:inline;vertical-align:middle;">
<button id="enable-scanner" onclick="enable_scanner();">Enable</button>
</div>
<div style="display:inline;vertical-align:middle;">
<button id="disable-scanner" onclick="disable_scanner();">Disable</button>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>