mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2026-04-30 02:35:57 +00:00
Simplify API for band conditions #92
This commit is contained in:
@@ -95,18 +95,6 @@ class HFBandCondition:
|
||||
condition: str = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class VHFCondition:
|
||||
"""Data class representing a VHF propagation condition."""
|
||||
|
||||
# Phenomenon name, e.g. "E-Skip", "Sporadic E"
|
||||
phenomenon: str = None
|
||||
# Geographic location this applies to, e.g. "Europe", "N America"
|
||||
location: str = None
|
||||
# Condition description, e.g. "Band Closed", "Enhanced", "Good"
|
||||
condition: str = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class SolarConditions:
|
||||
"""Data class representing current solar and propagation conditions."""
|
||||
@@ -139,13 +127,13 @@ class SolarConditions:
|
||||
geomag_field: str = None
|
||||
# Geomagnetic background noise level, e.g. "S0", "S1", "S2"
|
||||
geomag_noise: str = None
|
||||
# HF band propagation conditions
|
||||
hf_conditions: list = None # list[HFBandCondition]
|
||||
# VHF propagation phenomena
|
||||
vhf_conditions: list = None # list[VHFCondition]
|
||||
# HF band propagation conditions, keyed by "{band}-{time}" e.g. "80m-40m-day"
|
||||
hf_conditions: dict = None
|
||||
# VHF propagation conditions, keyed by condition name
|
||||
vhf_conditions: dict = None
|
||||
|
||||
# Derived values (populated by infer_descriptions())
|
||||
# HF radio blackout risk, derived from x_ray
|
||||
# HF radio blackout risk description, derived from x_ray
|
||||
blackout_desc: str = None
|
||||
# Solar radiation storm level description, derived from proton_flux
|
||||
proton_flux_desc: str = None
|
||||
@@ -157,7 +145,7 @@ class SolarConditions:
|
||||
geomag_storm_scale: int = None
|
||||
# Overall HF band conditions summary, derived from sfi
|
||||
band_conditions_desc: str = None
|
||||
# Electron flux level, derived from electron_flux
|
||||
# Electron flux description, derived from electron_flux
|
||||
electron_flux_desc: str = None
|
||||
|
||||
def infer_descriptions(self):
|
||||
|
||||
@@ -4,7 +4,7 @@ from xml.etree import ElementTree
|
||||
import pytz
|
||||
from dateutil import parser as dateutil_parser, tz as dateutil_tz
|
||||
|
||||
from data.solar_conditions import HFBandCondition, VHFCondition
|
||||
|
||||
from solarconditionsproviders.http_solar_conditions_provider import HTTPSolarConditionsProvider
|
||||
|
||||
POLL_INTERVAL = 3600 # 1 hour
|
||||
@@ -48,7 +48,7 @@ class HamQSL(HTTPSolarConditionsProvider):
|
||||
return default
|
||||
|
||||
# Process HF band conditions
|
||||
hf_conditions = []
|
||||
hf_conditions = {}
|
||||
calc = sd.find("calculatedconditions")
|
||||
if calc is not None:
|
||||
for band_el in calc.findall("band"):
|
||||
@@ -56,18 +56,15 @@ class HamQSL(HTTPSolarConditionsProvider):
|
||||
time = band_el.get("time")
|
||||
condition = band_el.text.strip() if band_el.text else None
|
||||
if name and time and condition:
|
||||
hf_conditions.append(HFBandCondition(band=name, time=time, condition=condition))
|
||||
hf_conditions[f"{name}-{time}"] = condition
|
||||
|
||||
# Process VHF propagation conditions
|
||||
vhf_conditions = []
|
||||
vhf_map = {}
|
||||
vhf = sd.find("calculatedvhfconditions")
|
||||
if vhf is not None:
|
||||
for ph_el in vhf.findall("phenomenon"):
|
||||
vhf_conditions.append(VHFCondition(
|
||||
phenomenon=ph_el.get("name"),
|
||||
location=ph_el.get("location"),
|
||||
condition=ph_el.text.strip() if ph_el.text else None
|
||||
))
|
||||
key = (ph_el.get("name"), ph_el.get("location"))
|
||||
vhf_map[key] = ph_el.text.strip() if ph_el.text else None
|
||||
|
||||
# Parse the "updated" timestamp string (format: "28 Mar 2026 0949 GMT") to UTC epoch seconds.
|
||||
updated = None
|
||||
@@ -100,5 +97,11 @@ class HamQSL(HTTPSolarConditionsProvider):
|
||||
"geomag_field": (lambda v: "Unsettled" if v == "Unsettld" else v)(text("geomagfield").title()) if text("geomagfield") else None,
|
||||
"geomag_noise": text("signalnoise"),
|
||||
"hf_conditions": hf_conditions,
|
||||
"vhf_conditions": vhf_conditions
|
||||
"vhf_conditions": {
|
||||
"vhf_aurora_northern_hemi": vhf_map.get(("vhf-aurora", "northern_hemi")),
|
||||
"es_2m_europe": vhf_map.get(("E-Skip", "europe")),
|
||||
"es_4m_europe": vhf_map.get(("E-Skip", "europe_4m")),
|
||||
"es_6m_europe": vhf_map.get(("E-Skip", "europe_6m")),
|
||||
"es_2m_na": vhf_map.get(("E-Skip", "north_america")),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -67,7 +67,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=1774768423"></script>
|
||||
<script src="/js/common.js?v=1774769497"></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=1774768423"></script>
|
||||
<script src="/js/add-spot.js?v=1774768423"></script>
|
||||
<script src="/js/common.js?v=1774769497"></script>
|
||||
<script src="/js/add-spot.js?v=1774769497"></script>
|
||||
<script>$(document).ready(function() { $("#nav-link-add-spot").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||
|
||||
{% end %}
|
||||
@@ -56,8 +56,8 @@
|
||||
|
||||
</div>
|
||||
|
||||
<script src="/js/common.js?v=1774768423"></script>
|
||||
<script src="/js/alerts.js?v=1774768423"></script>
|
||||
<script src="/js/common.js?v=1774769497"></script>
|
||||
<script src="/js/alerts.js?v=1774769497"></script>
|
||||
<script>$(document).ready(function() { $("#nav-link-alerts").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||
|
||||
{% end %}
|
||||
@@ -62,9 +62,9 @@
|
||||
<script>
|
||||
let spotProvidersEnabledByDefault = {% raw json_encode(web_ui_options["spot-providers-enabled-by-default"]) %};
|
||||
</script>
|
||||
<script src="/js/common.js?v=1774768423"></script>
|
||||
<script src="/js/spotsbandsandmap.js?v=1774768423"></script>
|
||||
<script src="/js/bands.js?v=1774768423"></script>
|
||||
<script src="/js/common.js?v=1774769497"></script>
|
||||
<script src="/js/spotsbandsandmap.js?v=1774769497"></script>
|
||||
<script src="/js/bands.js?v=1774769497"></script>
|
||||
<script>$(document).ready(function() { $("#nav-link-bands").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||
|
||||
{% end %}
|
||||
@@ -46,10 +46,10 @@
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/tinycolor2@1.6.0/cjs/tinycolor.min.js"></script>
|
||||
|
||||
<script src="https://misc.ianrenton.com/jsutils/utils.js?v=1774768423"></script>
|
||||
<script src="https://misc.ianrenton.com/jsutils/storage.js?v=1774768423"></script>
|
||||
<script src="https://misc.ianrenton.com/jsutils/ui-ham.js?v=1774768423"></script>
|
||||
<script src="https://misc.ianrenton.com/jsutils/geo.js?v=1774768423"></script>
|
||||
<script src="https://misc.ianrenton.com/jsutils/utils.js?v=1774769497"></script>
|
||||
<script src="https://misc.ianrenton.com/jsutils/storage.js?v=1774769497"></script>
|
||||
<script src="https://misc.ianrenton.com/jsutils/ui-ham.js?v=1774769497"></script>
|
||||
<script src="https://misc.ianrenton.com/jsutils/geo.js?v=1774769497"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -59,23 +59,23 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Sporadic-E 6m (Europe)</td>
|
||||
<td id="vhf-conditions-es-6m-eu"></td>
|
||||
<td id="vhf-conditions-es_6m_europe"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sporadic-E 4m (Europe)</td>
|
||||
<td id="vhf-conditions-es-4m-eu"></td>
|
||||
<td id="vhf-conditions-es_4m_europe"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sporadic-E 2m (Europe)</td>
|
||||
<td id="vhf-conditions-es-2m-eu"></td>
|
||||
<td id="vhf-conditions-es_2m_europe"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sporadic-E 2m (North America)</td>
|
||||
<td id="vhf-conditions-es-2m-na"></td>
|
||||
<td id="vhf-conditions-es_2m_na"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Aurora (Northern Hemisphere)</td>
|
||||
<td id="vhf-conditions-aurora"></td>
|
||||
<td id="vhf-conditions-vhf_aurora_northern_hemi"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Aurora Minimum Latitude</td>
|
||||
@@ -87,7 +87,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-text mt-2">Data from <a href="https://hamqsl.net">HamQSL.net</a>.</div>
|
||||
<div class="form-text mt-3">Data from <a href="https://hamqsl.net">HamQSL.net</a>.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -100,8 +100,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/common.js?v=1774768423"></script>
|
||||
<script src="/js/conditions.js?v=1774768423"></script>
|
||||
<script src="/js/common.js?v=1774769497"></script>
|
||||
<script src="/js/conditions.js?v=1774769497"></script>
|
||||
<script>$(document).ready(function() { $("#nav-link-conditions").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||
|
||||
{% end %}
|
||||
@@ -70,9 +70,9 @@
|
||||
<script>
|
||||
let spotProvidersEnabledByDefault = {% raw json_encode(web_ui_options["spot-providers-enabled-by-default"]) %};
|
||||
</script>
|
||||
<script src="/js/common.js?v=1774768423"></script>
|
||||
<script src="/js/spotsbandsandmap.js?v=1774768423"></script>
|
||||
<script src="/js/map.js?v=1774768423"></script>
|
||||
<script src="/js/common.js?v=1774769497"></script>
|
||||
<script src="/js/spotsbandsandmap.js?v=1774769497"></script>
|
||||
<script src="/js/map.js?v=1774769497"></script>
|
||||
<script>$(document).ready(function() { $("#nav-link-map").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||
|
||||
{% end %}
|
||||
@@ -87,9 +87,9 @@
|
||||
<script>
|
||||
let spotProvidersEnabledByDefault = {% raw json_encode(web_ui_options["spot-providers-enabled-by-default"]) %};
|
||||
</script>
|
||||
<script src="/js/common.js?v=1774768423"></script>
|
||||
<script src="/js/spotsbandsandmap.js?v=1774768423"></script>
|
||||
<script src="/js/spots.js?v=1774768423"></script>
|
||||
<script src="/js/common.js?v=1774769497"></script>
|
||||
<script src="/js/spotsbandsandmap.js?v=1774769497"></script>
|
||||
<script src="/js/spots.js?v=1774769497"></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=1774768423"></script>
|
||||
<script src="/js/status.js?v=1774768423"></script>
|
||||
<script src="/js/common.js?v=1774769497"></script>
|
||||
<script src="/js/status.js?v=1774769497"></script>
|
||||
<script>
|
||||
$(document).ready(function() { $("#nav-link-status").addClass("active"); }); <!-- highlight active page in nav -->
|
||||
</script>
|
||||
|
||||
@@ -1356,47 +1356,6 @@ components:
|
||||
description: Regex that matches this SIG's reference IDs. Generally for Spothole's own internal use, clients probably won't need this.
|
||||
example: "[A-Z]{2}\\-\\d+"
|
||||
|
||||
HFBandCondition:
|
||||
type: object
|
||||
description: HF propagation conditions for a group of bands at a particular time of day.
|
||||
properties:
|
||||
band:
|
||||
type: string
|
||||
description: Band group, e.g. "80m-40m", "30m-20m", "17m-15m", "10m-6m". As provided by HamQSL.
|
||||
example: "80m-40m"
|
||||
time:
|
||||
type: string
|
||||
description: Time of day these conditions apply to. As provided by HamQSL.
|
||||
enum:
|
||||
- day
|
||||
- night
|
||||
example: day
|
||||
condition:
|
||||
type: string
|
||||
description: Propagation condition assessment. As provided by HamQSL.
|
||||
enum:
|
||||
- Good
|
||||
- Fair
|
||||
- Poor
|
||||
example: Good
|
||||
|
||||
VHFCondition:
|
||||
type: object
|
||||
description: A VHF propagation phenomenon and its current condition.
|
||||
properties:
|
||||
phenomenon:
|
||||
type: string
|
||||
description: The name of the propagation phenomenon, e.g. "E-Skip", "vhf-aurora". As provided by HamQSL.
|
||||
example: "E-Skip"
|
||||
location:
|
||||
type: string
|
||||
description: The geographic region this condition applies to, e.g. "europe", "north_america", "northern_hemi". As provided by HamQSL.
|
||||
example: "europe"
|
||||
condition:
|
||||
type: string
|
||||
description: The current condition for this phenomenon and location.
|
||||
example: "Band Closed"
|
||||
|
||||
SolarConditions:
|
||||
type: object
|
||||
description: Current solar and propagation conditions. All fields may be null if no provider has successfully fetched data yet.
|
||||
@@ -1458,15 +1417,57 @@ components:
|
||||
description: Geomagnetic background noise level on HF, in S-units
|
||||
example: "S0"
|
||||
hf_conditions:
|
||||
type: array
|
||||
description: HF propagation condition assessments by band group and time of day
|
||||
items:
|
||||
$ref: '#/components/schemas/HFBandCondition'
|
||||
type: object
|
||||
description: HF propagation condition assessments, keyed by "{band}-{time}" e.g. "80m-40m-day"
|
||||
properties:
|
||||
80m-40m-day:
|
||||
type: string
|
||||
enum: [Good, Fair, Poor]
|
||||
80m-40m-night:
|
||||
type: string
|
||||
enum: [Good, Fair, Poor]
|
||||
30m-20m-day:
|
||||
type: string
|
||||
enum: [Good, Fair, Poor]
|
||||
30m-20m-night:
|
||||
type: string
|
||||
enum: [Good, Fair, Poor]
|
||||
17m-15m-day:
|
||||
type: string
|
||||
enum: [Good, Fair, Poor]
|
||||
17m-15m-night:
|
||||
type: string
|
||||
enum: [Good, Fair, Poor]
|
||||
12m-10m-day:
|
||||
type: string
|
||||
enum: [Good, Fair, Poor]
|
||||
12m-10m-night:
|
||||
type: string
|
||||
enum: [Good, Fair, Poor]
|
||||
vhf_conditions:
|
||||
type: array
|
||||
description: VHF propagation condition assessments by phenomenon and location
|
||||
items:
|
||||
$ref: '#/components/schemas/VHFCondition'
|
||||
type: object
|
||||
description: VHF propagation condition assessments, keyed by condition name
|
||||
properties:
|
||||
vhf_aurora_northern_hemi:
|
||||
type: string
|
||||
description: VHF aurora propagation condition for the northern hemisphere
|
||||
example: "Band Closed"
|
||||
es_2m_europe:
|
||||
type: string
|
||||
description: Sporadic-E propagation condition on 2m for Europe
|
||||
example: "Band Closed"
|
||||
es_4m_europe:
|
||||
type: string
|
||||
description: Sporadic-E propagation condition on 4m for Europe
|
||||
example: "Band Closed"
|
||||
es_6m_europe:
|
||||
type: string
|
||||
description: Sporadic-E propagation condition on 6m for Europe
|
||||
example: "Band Closed"
|
||||
es_2m_na:
|
||||
type: string
|
||||
description: Sporadic-E propagation condition on 2m for North America
|
||||
example: "Band Closed"
|
||||
blackout_desc:
|
||||
type: string
|
||||
description: HF radio blackout risk description, derived from the X-ray flux class.
|
||||
|
||||
@@ -1,32 +1,26 @@
|
||||
// Load solar conditions
|
||||
function loadSolarConditions() {
|
||||
$.getJSON('/api/v1/solar', function(jsonData) {
|
||||
|
||||
// HF
|
||||
|
||||
const hfConditionClass = { 'Good': 'table-success', 'Fair': 'table-warning', 'Poor': 'table-danger' };
|
||||
|
||||
if (jsonData.hf_conditions) {
|
||||
jsonData.hf_conditions.forEach(function(entry) {
|
||||
const cell = $('#hf-conditions-' + entry.band + '-' + entry.time);
|
||||
cell.text(entry.condition);
|
||||
const cls = hfConditionClass[entry.condition];
|
||||
if (cls) { cell.addClass(cls); }
|
||||
Object.entries(jsonData.hf_conditions).forEach(function([key, condition]) {
|
||||
const cell = $('#hf-conditions-' + key);
|
||||
cell.text(condition);
|
||||
cell.addClass(hfConditionClass[condition]);
|
||||
});
|
||||
}
|
||||
|
||||
const vhfIdMap = {
|
||||
'vhf-aurora|northern_hemi': 'vhf-conditions-aurora',
|
||||
'E-Skip|europe_6m': 'vhf-conditions-es-6m-eu',
|
||||
'E-Skip|europe_4m': 'vhf-conditions-es-4m-eu',
|
||||
'E-Skip|europe': 'vhf-conditions-es-2m-eu',
|
||||
'E-Skip|north_america':'vhf-conditions-es-2m-na',
|
||||
};
|
||||
// VHF
|
||||
|
||||
if (jsonData.vhf_conditions) {
|
||||
jsonData.vhf_conditions.forEach(function(entry) {
|
||||
const id = vhfIdMap[entry.phenomenon + '|' + entry.location];
|
||||
if (id) {
|
||||
const cell = $('#' + id);
|
||||
cell.text(entry.condition);
|
||||
cell.addClass(entry.condition === 'Band Closed' ? 'table-danger' : 'table-success');
|
||||
}
|
||||
Object.entries(jsonData.vhf_conditions).forEach(function([key, condition]) {
|
||||
const cell = $('#vhf-conditions-' + key);
|
||||
cell.text(condition);
|
||||
cell.addClass(condition === 'Band Closed' ? 'table-danger' : 'table-success');
|
||||
});
|
||||
}
|
||||
if (jsonData.aurora_latitude !== null && jsonData.aurora_latitude !== undefined) {
|
||||
|
||||
Reference in New Issue
Block a user