mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2026-06-23 21:25:12 +00:00
Make ionosonde_data a map keyed by URSI, and on polling the website, replace data for the specific URSI rather than overwriting everything. This allows us to preserve data from an older lookup if the website is down or returns nothing
This commit is contained in:
@@ -161,8 +161,8 @@ class SolarConditions:
|
||||
blackout_forecast_r1r2: dict = None
|
||||
# NOAA Radio Blackout (R3 or greater) probability forecast, keyed by UNIX timestamp of start of day UTC
|
||||
blackout_forecast_r3_or_greater: dict = None
|
||||
# Ionosonde measurements from LGDC, list of dicts with keys: ursi, name, fof2, muf
|
||||
ionosonde_data: list = None
|
||||
# Ionosonde measurements from LGDC, dict keyed by URSI code, values are dicts with keys: ursi, name, fof2, muf
|
||||
ionosonde_data: dict = None
|
||||
|
||||
# Derived values (populated by infer_descriptions())
|
||||
# HF radio blackout risk description, derived from xray
|
||||
|
||||
@@ -51,34 +51,33 @@ class GIROIonosonde(SolarConditionsProvider):
|
||||
|
||||
def _poll(self):
|
||||
try:
|
||||
logging.debug(f"Polling {self.name} ionosonde data...")
|
||||
logging.debug(f"Polling GIRO ionosonde data...")
|
||||
now = datetime.now(timezone.utc)
|
||||
from_time = now - timedelta(hours=HISTORY_HOURS)
|
||||
results = []
|
||||
ionosonde_data = dict(self._solar_conditions.ionosonde_data or {})
|
||||
updated_count = 0
|
||||
|
||||
for station in self._stations:
|
||||
if self._stop_event.is_set():
|
||||
break
|
||||
ursi = station["ursi"]
|
||||
name = station["name"]
|
||||
entry = {"ursi": ursi, "name": name, "fof2": None, "muf": None}
|
||||
try:
|
||||
fof2, muf = self._fetch_station_data(ursi, from_time, now)
|
||||
entry["fof2"] = fof2
|
||||
entry["muf"] = muf
|
||||
if fof2 and muf:
|
||||
results.append(entry)
|
||||
ionosonde_data[ursi] = {"ursi": ursi, "name": name, "fof2": fof2, "muf": muf}
|
||||
updated_count += 1
|
||||
except Exception:
|
||||
logging.debug(f"Could not fetch ionosonde data for {ursi} ({name})")
|
||||
logging.warning(f"Could not fetch ionosonde data for {ursi} ({name})")
|
||||
|
||||
self.update_data({"ionosonde_data": results})
|
||||
self.update_data({"ionosonde_data": ionosonde_data})
|
||||
self.status = "OK"
|
||||
self.last_update_time = datetime.now(pytz.UTC)
|
||||
logging.debug(f"Received ionosonde data for {len(results)} stations from {self.name}.")
|
||||
logging.debug(f"Updated ionosonde data for {updated_count} stations.")
|
||||
|
||||
except Exception:
|
||||
self.status = "Error"
|
||||
logging.exception(f"Exception in GIRO Ionosonde data provider ({self.name})")
|
||||
logging.exception(f"Exception in GIRO Ionosonde data provider")
|
||||
self._stop_event.wait(timeout=1)
|
||||
|
||||
def _fetch_station_data(self, ursi, from_time, to_time):
|
||||
@@ -86,9 +85,7 @@ class GIROIonosonde(SolarConditionsProvider):
|
||||
|
||||
from_str = from_time.strftime("%Y.%m.%d+%H:%M:%S")
|
||||
to_str = to_time.strftime("%Y.%m.%d+%H:%M:%S")
|
||||
url = (
|
||||
f"{LGDC_URL}?ursiCode={ursi}&charName=foF2,MUFD&DMUF=3000&fromDate={from_str}&toDate={to_str}"
|
||||
)
|
||||
url = f"{LGDC_URL}?ursiCode={ursi}&charName=foF2,MUFD&DMUF=3000&fromDate={from_str}&toDate={to_str}"
|
||||
response = requests.get(url, headers=HTTP_HEADERS, timeout=(5, 15))
|
||||
if response.status_code != 200:
|
||||
return None, None
|
||||
@@ -104,10 +101,11 @@ class GIROIonosonde(SolarConditionsProvider):
|
||||
line = line.strip()
|
||||
if not line or line.startswith('#'):
|
||||
continue
|
||||
# Data rows: timestamp CS foF2 QD MUFD QD
|
||||
# Data rows have the following format: timestamp CS foF2 QD MUFD QD
|
||||
parts = line.split()
|
||||
if len(parts) >= 5:
|
||||
try:
|
||||
# Python 3.8 TZ parsing fudge
|
||||
ts = datetime.fromisoformat(parts[0].replace('Z', '+00:00')).timestamp()
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
@@ -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=1778924254"></script>
|
||||
<script src="/js/common.js?v=1778925881"></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=1778924254"></script>
|
||||
<script src="/js/add-spot.js?v=1778924254"></script>
|
||||
<script src="/js/common.js?v=1778925881"></script>
|
||||
<script src="/js/add-spot.js?v=1778925881"></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=1778924255"></script>
|
||||
<script src="/js/alerts.js?v=1778924255"></script>
|
||||
<script src="/js/common.js?v=1778925881"></script>
|
||||
<script src="/js/alerts.js?v=1778925881"></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=1778924254"></script>
|
||||
<script src="/js/spotsbandsandmap.js?v=1778924254"></script>
|
||||
<script src="/js/bands.js?v=1778924254"></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>$(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=1778924254" type="text/css">
|
||||
<link rel="stylesheet" href="/css/style.css?v=1778925881" 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=1778924254"></script>
|
||||
<script src="https://misc.ianrenton.com/jsutils/ui-ham.js?v=1778924254"></script>
|
||||
<script src="https://misc.ianrenton.com/jsutils/geo.js?v=1778924254"></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>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -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=1778924254"></script>
|
||||
<script src="/js/conditions.js?v=1778924254"></script>
|
||||
<script src="/js/common.js?v=1778925881"></script>
|
||||
<script src="/js/conditions.js?v=1778925881"></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=1778924255"></script>
|
||||
<script src="/js/spotsbandsandmap.js?v=1778924255"></script>
|
||||
<script src="/js/map.js?v=1778924255"></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>$(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=1778924254"></script>
|
||||
<script src="/js/spotsbandsandmap.js?v=1778924254"></script>
|
||||
<script src="/js/spots.js?v=1778924254"></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>$(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=1778924254"></script>
|
||||
<script src="/js/status.js?v=1778924254"></script>
|
||||
<script src="/js/common.js?v=1778925881"></script>
|
||||
<script src="/js/status.js?v=1778925881"></script>
|
||||
<script>
|
||||
$(document).ready(function() { $("#nav-link-status").addClass("active"); }); <!-- highlight active page in nav -->
|
||||
</script>
|
||||
|
||||
@@ -15,7 +15,7 @@ info:
|
||||
|
||||
### 1.4
|
||||
|
||||
* `/solar` response now includes `ionosonde_data`, a list of ionosonde station measurements (foF2 and MUF) sourced from the GIRO Data Center.
|
||||
* `/solar` response now includes `ionosonde_data`, which contains ionosonde station measurements (foF2 and MUF) sourced from the GIRO Data Center.
|
||||
|
||||
### 1.3
|
||||
|
||||
@@ -1688,13 +1688,13 @@ components:
|
||||
description: Electron flux impact description, derived from electron flux level.
|
||||
example: "No impact"
|
||||
ionosonde_data:
|
||||
type: array
|
||||
type: object
|
||||
nullable: true
|
||||
description: >
|
||||
Ionosonde measurements from the GIRO Data Center, covering active stations listed in the
|
||||
system. Only stations for which data was successfully retrieved are included. Null if the
|
||||
GIROIonosonde provider has not yet completed its first poll.
|
||||
items:
|
||||
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.
|
||||
additionalProperties:
|
||||
$ref: '#/components/schemas/IonosondeStation'
|
||||
|
||||
IonosondeStation:
|
||||
|
||||
@@ -115,7 +115,7 @@ function loadSolarConditions() {
|
||||
|
||||
// Ionosonde
|
||||
|
||||
if (jsonData.ionosonde_data && jsonData.ionosonde_data.length > 0) {
|
||||
if (jsonData.ionosonde_data && Object.keys(jsonData.ionosonde_data).length > 0) {
|
||||
ionosondeData = jsonData.ionosonde_data;
|
||||
populateIonosondeDropdown(ionosondeData);
|
||||
renderIonosondeData();
|
||||
@@ -366,9 +366,12 @@ function populateIonosondeDropdown(data) {
|
||||
const savedUrsi = localStorage.getItem('#ionosonde-station:value');
|
||||
const savedValue = savedUrsi ? JSON.parse(savedUrsi) : null;
|
||||
select.empty();
|
||||
data.forEach(function (station) {
|
||||
// Sort by station name rather than URSI because station name is what's displayed, and any out-of-order names might
|
||||
// confuse the user
|
||||
Object.values(data).sort((a, b) => a.name.localeCompare(b.name)).forEach(function (station) {
|
||||
select.append($('<option>', {value: station.ursi, text: station.name}));
|
||||
});
|
||||
// Select one by default if the user's localStorage has an existing selection for this
|
||||
if (savedValue && select.find('option[value="' + savedValue + '"]').length) {
|
||||
select.val(savedValue);
|
||||
}
|
||||
@@ -381,9 +384,7 @@ function renderIonosondeData() {
|
||||
if (!ionosondeData) return;
|
||||
const ursi = $('#ionosonde-station').val();
|
||||
if (!ursi) return;
|
||||
const station = ionosondeData.find(function (s) {
|
||||
return s.ursi === ursi;
|
||||
});
|
||||
const station = ionosondeData[ursi];
|
||||
if (!station) return;
|
||||
|
||||
// Set up some styles, matching the k-index chart. We use Bootstrap's "primary" and "danger" colours not for any
|
||||
|
||||
Reference in New Issue
Block a user