mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2026-06-23 21:25:12 +00:00
Show a warning instead of an empty canvas if the ionosonde station has no data, and also show a warning if we have data but it's old.
This commit is contained in:
@@ -35,6 +35,16 @@ class GIROIonosonde(SolarConditionsProvider):
|
||||
stations.append({"ursi": row[0].strip(), "name": row[1].strip()})
|
||||
return stations
|
||||
|
||||
def setup(self, solar_conditions, solar_conditions_cache):
|
||||
"""Prepopulate the ionosonde_data map with known URSI and station names, so that the API exposes this structure
|
||||
even before we actually have any data in it."""
|
||||
|
||||
super().setup(solar_conditions, solar_conditions_cache)
|
||||
self.update_data({"ionosonde_data": {
|
||||
s["ursi"]: {"ursi": s["ursi"], "name": s["name"], "fof2": None, "muf": None}
|
||||
for s in self._stations
|
||||
}})
|
||||
|
||||
def start(self):
|
||||
logging.info(f"Set up query of GIRO ionosonde data API every {POLL_INTERVAL} seconds.")
|
||||
self._thread = Thread(target=self._run, daemon=True)
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
<p>This software is dedicated to the memory of Tom G1PJB, SK, a friend and colleague who sadly passed away around the time I started writing it in Autumn 2025. I was looking forward to showing it to you when it was done.</p>
|
||||
</div>
|
||||
|
||||
<script src="/js/common.js?v=1778925881"></script>
|
||||
<script src="/js/common.js?v=1778927183"></script>
|
||||
<script>$(document).ready(function() { $("#nav-link-about").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||
|
||||
{% end %}
|
||||
@@ -69,8 +69,8 @@
|
||||
|
||||
</div>
|
||||
|
||||
<script src="/js/common.js?v=1778925881"></script>
|
||||
<script src="/js/add-spot.js?v=1778925881"></script>
|
||||
<script src="/js/common.js?v=1778927183"></script>
|
||||
<script src="/js/add-spot.js?v=1778927183"></script>
|
||||
<script>$(document).ready(function() { $("#nav-link-add-spot").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||
|
||||
{% end %}
|
||||
@@ -70,8 +70,8 @@
|
||||
|
||||
</div>
|
||||
|
||||
<script src="/js/common.js?v=1778925881"></script>
|
||||
<script src="/js/alerts.js?v=1778925881"></script>
|
||||
<script src="/js/common.js?v=1778927183"></script>
|
||||
<script src="/js/alerts.js?v=1778927183"></script>
|
||||
<script>$(document).ready(function() { $("#nav-link-alerts").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||
|
||||
{% end %}
|
||||
@@ -76,9 +76,9 @@
|
||||
<script>
|
||||
let spotProvidersEnabledByDefault = {% raw json_encode(web_ui_options["spot-providers-enabled-by-default"]) %};
|
||||
</script>
|
||||
<script src="/js/common.js?v=1778925881"></script>
|
||||
<script src="/js/spotsbandsandmap.js?v=1778925881"></script>
|
||||
<script src="/js/bands.js?v=1778925881"></script>
|
||||
<script src="/js/common.js?v=1778927183"></script>
|
||||
<script src="/js/spotsbandsandmap.js?v=1778927183"></script>
|
||||
<script src="/js/bands.js?v=1778927183"></script>
|
||||
<script>$(document).ready(function() { $("#nav-link-bands").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||
|
||||
{% end %}
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
<title>Spothole</title>
|
||||
|
||||
<link rel="stylesheet" href="/css/style.css?v=1778925881" type="text/css">
|
||||
<link rel="stylesheet" href="/css/style.css?v=1778927183" type="text/css">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
|
||||
<link href="/fa/css/fontawesome.min.css" rel="stylesheet" />
|
||||
@@ -52,9 +52,9 @@
|
||||
integrity="sha384-L1eE4eD41kpBIWe2I0eHy+GnEUC4RIpcvibVW2JCminuPlTl+2Bc528iPdVMg5Dn"
|
||||
crossorigin="anonymous"></script>
|
||||
|
||||
<script src="https://misc.ianrenton.com/jsutils/utils.js?v=1778925881"></script>
|
||||
<script src="https://misc.ianrenton.com/jsutils/ui-ham.js?v=1778925881"></script>
|
||||
<script src="https://misc.ianrenton.com/jsutils/geo.js?v=1778925881"></script>
|
||||
<script src="https://misc.ianrenton.com/jsutils/utils.js?v=1778927183"></script>
|
||||
<script src="https://misc.ianrenton.com/jsutils/ui-ham.js?v=1778927183"></script>
|
||||
<script src="https://misc.ianrenton.com/jsutils/geo.js?v=1778927183"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -176,7 +176,7 @@
|
||||
{% if has_giro_ionosonde %}
|
||||
<div class="card mt-5">
|
||||
<div class="card-header">
|
||||
Critical & Maximum Usable Frequencies
|
||||
Ionosonde Data
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
@@ -186,7 +186,7 @@
|
||||
</select>
|
||||
</div>
|
||||
<div id="ionosonde-latest" class="mb-3"></div>
|
||||
<canvas id="ionosonde-chart" class="mt-3 mb-3 d-none d-md-block"></canvas>
|
||||
<canvas id="ionosonde-chart" class="mt-3 mb-3 hideonmobile"></canvas>
|
||||
<div class="form-text mt-2">Data from the <a href="https://lgdc.uml.edu/">Lowell GIRO Data Center</a>.</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -249,8 +249,8 @@
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.9/dist/chart.umd.min.js"></script>
|
||||
<script src="/js/common.js?v=1778925881"></script>
|
||||
<script src="/js/conditions.js?v=1778925881"></script>
|
||||
<script src="/js/common.js?v=1778927183"></script>
|
||||
<script src="/js/conditions.js?v=1778927183"></script>
|
||||
<script>$(document).ready(function () {
|
||||
$("#nav-link-conditions").addClass("active");
|
||||
}); <!-- highlight active page in nav --></script>
|
||||
|
||||
@@ -94,9 +94,9 @@
|
||||
<script>
|
||||
let spotProvidersEnabledByDefault = {% raw json_encode(web_ui_options["spot-providers-enabled-by-default"]) %};
|
||||
</script>
|
||||
<script src="/js/common.js?v=1778925881"></script>
|
||||
<script src="/js/spotsbandsandmap.js?v=1778925881"></script>
|
||||
<script src="/js/map.js?v=1778925881"></script>
|
||||
<script src="/js/common.js?v=1778927183"></script>
|
||||
<script src="/js/spotsbandsandmap.js?v=1778927183"></script>
|
||||
<script src="/js/map.js?v=1778927183"></script>
|
||||
<script>$(document).ready(function() { $("#nav-link-map").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||
|
||||
{% end %}
|
||||
@@ -104,9 +104,9 @@
|
||||
<script>
|
||||
let spotProvidersEnabledByDefault = {% raw json_encode(web_ui_options["spot-providers-enabled-by-default"]) %};
|
||||
</script>
|
||||
<script src="/js/common.js?v=1778925881"></script>
|
||||
<script src="/js/spotsbandsandmap.js?v=1778925881"></script>
|
||||
<script src="/js/spots.js?v=1778925881"></script>
|
||||
<script src="/js/common.js?v=1778927183"></script>
|
||||
<script src="/js/spotsbandsandmap.js?v=1778927183"></script>
|
||||
<script src="/js/spots.js?v=1778927183"></script>
|
||||
<script>$(document).ready(function() { $("#nav-link-spots").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||
|
||||
{% end %}
|
||||
@@ -59,8 +59,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/common.js?v=1778925881"></script>
|
||||
<script src="/js/status.js?v=1778925881"></script>
|
||||
<script src="/js/common.js?v=1778927183"></script>
|
||||
<script src="/js/status.js?v=1778927183"></script>
|
||||
<script>
|
||||
$(document).ready(function() { $("#nav-link-status").addClass("active"); }); <!-- highlight active page in nav -->
|
||||
</script>
|
||||
|
||||
@@ -1691,9 +1691,8 @@ components:
|
||||
type: object
|
||||
nullable: true
|
||||
description: >
|
||||
Ionosonde measurements from the GIRO Data Center, keyed by URSI station code. Only
|
||||
stations for which data was successfully retrieved are included. Null if the
|
||||
GIROIonosonde provider has not yet completed its first poll or if this data source is disabled.
|
||||
Ionosonde measurements from the GIRO Data Center, keyed by URSI station code. All known stations are
|
||||
included, but not all of them may contain data.
|
||||
additionalProperties:
|
||||
$ref: '#/components/schemas/IonosondeStation'
|
||||
|
||||
@@ -1712,7 +1711,7 @@ components:
|
||||
fof2:
|
||||
type: object
|
||||
nullable: true
|
||||
description: F2 layer critical frequency (foF2) measurements in MHz, keyed by UNIX timestamp (UTC seconds since epoch) of each measurement.
|
||||
description: F2 layer critical frequency (foF2) measurements in MHz, keyed by UNIX timestamp (UTC seconds since epoch) of each measurement. Can be null if there is no data.
|
||||
additionalProperties:
|
||||
type: number
|
||||
example:
|
||||
@@ -1721,7 +1720,7 @@ components:
|
||||
muf:
|
||||
type: object
|
||||
nullable: true
|
||||
description: Maximum Usable Frequency (MUF) for a 3000 km path in MHz, keyed by UNIX timestamp (UTC seconds since epoch) of each measurement.
|
||||
description: Maximum Usable Frequency (MUF) for a 3000 km path in MHz, keyed by UNIX timestamp (UTC seconds since epoch) of each measurement. Can be null if there is no data.
|
||||
additionalProperties:
|
||||
type: number
|
||||
example:
|
||||
|
||||
@@ -380,7 +380,9 @@ function populateIonosondeDropdown(data) {
|
||||
// Render the foF2/MUF data and line chart for the currently selected station
|
||||
function renderIonosondeData() {
|
||||
// First make sure that we have some data, that a station entry is selected in the drop-down box, and that the
|
||||
// data contains an entry for that station. If not, bail out at this point.
|
||||
// data contains an entry for that station. If not, this represents an odd state (over and above just "no data for
|
||||
// this station", so bail out at this point. The user will have to reselect something from the list, or wait until
|
||||
// the API is behaving itself again.
|
||||
if (!ionosondeData) return;
|
||||
const ursi = $('#ionosonde-station').val();
|
||||
if (!ursi) return;
|
||||
@@ -406,7 +408,12 @@ function renderIonosondeData() {
|
||||
const fof2Entries = toSeries(station.fof2);
|
||||
const mufEntries = toSeries(station.muf);
|
||||
const allTs = [...fof2Entries, ...mufEntries].map(e => e.ts);
|
||||
if (allTs.length === 0) return;
|
||||
if (allTs.length === 0) {
|
||||
$('#ionosonde-latest').html('<div class="alert alert-warning mt-2 mb-0 py-2">No data available for this station.</div>');
|
||||
$('#ionosonde-chart').hide();
|
||||
if (ionosondeChart) { ionosondeChart.destroy(); ionosondeChart = null; }
|
||||
return;
|
||||
}
|
||||
|
||||
// Populate latest values summary (visible on all screen sizes)
|
||||
const latestFof2 = fof2Entries.length ? fof2Entries[fof2Entries.length - 1].val : null;
|
||||
@@ -418,12 +425,16 @@ function renderIonosondeData() {
|
||||
const latestDate = moment.utc(maxTs * 1000);
|
||||
latestTimeStr = latestDate.format('DD MMM YYYY HH:mm [UTC]') + ' (' + latestDate.fromNow() + ')';
|
||||
}
|
||||
const staleWarning = (maxTs !== null && (Date.now() / 1000 - maxTs) > 12 * 3600)
|
||||
? '<div class="alert alert-warning mt-2 mb-0 py-2">Data is more than 12 hours old!</div>'
|
||||
: '';
|
||||
$('#ionosonde-latest').html(
|
||||
'<div class="row border-bottom align-items-center me-0">' +
|
||||
'<div class="col-12 col-md-6 py-2 text-muted">Latest values as of ' + latestTimeStr + '</div>' +
|
||||
'<div class="col-12 col-md-2 py-2">foF2: <strong>' + (latestFof2 !== null ? latestFof2.toFixed(2) + ' MHz' : 'N/A') + '</strong></div>' +
|
||||
'<div class="col-12 col-md-4 py-2">MUF (3000 km): <strong>' + (latestMuf !== null ? latestMuf.toFixed(2) + ' MHz' : 'N/A') + '</strong></div>' +
|
||||
'</div>' +
|
||||
staleWarning +
|
||||
'</div>'
|
||||
);
|
||||
|
||||
@@ -564,6 +575,9 @@ function renderIonosondeData() {
|
||||
},
|
||||
plugins: [bandLinesPlugin],
|
||||
});
|
||||
|
||||
// Chart canvas is normally hidden until we get here with some definitely good data. Now we have that so show it
|
||||
$('#ionosonde-chart').show();
|
||||
}
|
||||
|
||||
// Called when the ionosonde station select changes
|
||||
|
||||
Reference in New Issue
Block a user