diff --git a/data/solar_conditions.py b/data/solar_conditions.py
index 9a4710d..b422a14 100644
--- a/data/solar_conditions.py
+++ b/data/solar_conditions.py
@@ -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):
diff --git a/solarconditionsproviders/hamqsl.py b/solarconditionsproviders/hamqsl.py
index 8de8648..6f98bd2 100644
--- a/solarconditionsproviders/hamqsl.py
+++ b/solarconditionsproviders/hamqsl.py
@@ -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")),
+ },
}
diff --git a/templates/about.html b/templates/about.html
index 846e630..f02d61d 100644
--- a/templates/about.html
+++ b/templates/about.html
@@ -67,7 +67,7 @@
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.
-
+
{% end %}
\ No newline at end of file
diff --git a/templates/add_spot.html b/templates/add_spot.html
index 36549f5..f87c35a 100644
--- a/templates/add_spot.html
+++ b/templates/add_spot.html
@@ -69,8 +69,8 @@
-
-
+
+
{% end %}
\ No newline at end of file
diff --git a/templates/alerts.html b/templates/alerts.html
index 9e96b7b..504d549 100644
--- a/templates/alerts.html
+++ b/templates/alerts.html
@@ -56,8 +56,8 @@
-
-
+
+
{% end %}
\ No newline at end of file
diff --git a/templates/bands.html b/templates/bands.html
index 4804829..d77f2c3 100644
--- a/templates/bands.html
+++ b/templates/bands.html
@@ -62,9 +62,9 @@
-
-
-
+
+
+
{% end %}
\ No newline at end of file
diff --git a/templates/base.html b/templates/base.html
index 7532ae3..9729d50 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -46,10 +46,10 @@
crossorigin="anonymous">
-
-
-
-
+
+
+
+
diff --git a/templates/conditions.html b/templates/conditions.html
index a2ed256..de1cde2 100644
--- a/templates/conditions.html
+++ b/templates/conditions.html
@@ -59,23 +59,23 @@
| Sporadic-E 6m (Europe) |
- |
+ |
| Sporadic-E 4m (Europe) |
- |
+ |
| Sporadic-E 2m (Europe) |
- |
+ |
| Sporadic-E 2m (North America) |
- |
+ |
| Aurora (Northern Hemisphere) |
- |
+ |
| Aurora Minimum Latitude |
@@ -87,7 +87,7 @@
-
+
@@ -100,8 +100,8 @@
-
-
+
+
{% end %}
\ No newline at end of file
diff --git a/templates/map.html b/templates/map.html
index 2ba97a6..710a551 100644
--- a/templates/map.html
+++ b/templates/map.html
@@ -70,9 +70,9 @@
-
-
-
+
+
+
{% end %}
\ No newline at end of file
diff --git a/templates/spots.html b/templates/spots.html
index d78f9d6..86d61dc 100644
--- a/templates/spots.html
+++ b/templates/spots.html
@@ -87,9 +87,9 @@
-
-
-
+
+
+
{% end %}
\ No newline at end of file
diff --git a/templates/status.html b/templates/status.html
index 0739cee..3e5feac 100644
--- a/templates/status.html
+++ b/templates/status.html
@@ -59,8 +59,8 @@
-
-
+
+
diff --git a/webassets/apidocs/openapi.yml b/webassets/apidocs/openapi.yml
index 9b9d516..d6382ed 100644
--- a/webassets/apidocs/openapi.yml
+++ b/webassets/apidocs/openapi.yml
@@ -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.
diff --git a/webassets/js/conditions.js b/webassets/js/conditions.js
index 12a9538..ac61974 100644
--- a/webassets/js/conditions.js
+++ b/webassets/js/conditions.js
@@ -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) {