Add descriptions for solar conditions #92

This commit is contained in:
Ian Renton
2026-03-28 10:39:26 +00:00
parent 1173af6a9d
commit 2a5e0db5bc
14 changed files with 203 additions and 49 deletions

View File

@@ -1,6 +1,87 @@
import json
from dataclasses import dataclass
# Lookup tables for derived text descriptions.
# Each threshold-based table is a list of (min_value, description) pairs in descending order;
# the first entry whose threshold the value meets or exceeds is used.
BLACKOUT_DESCRIPTIONS = {
"X": "Extreme HF radio blackout on entire sunlit side",
"M": "Wide area HF radio blackout on sunlit side",
"C": "Occasional loss of HF communications on sunlit side",
"B": "No significant radio blackout",
"A": "No impact",
}
PROTON_FLUX_DESCRIPTIONS = [
(1000000, "Complete HF blackout in polar regions"),
(100000, "Partial HF blackout in polar regions"),
(10000, "Degraded HF propagation in polar regions"),
(1000, "Small effect on HF propagation in polar regions"),
(100, "Minor effect on HF propagation in polar regions"),
(10, "Very minor effect on HF propagation in polar regions"),
(0, "No impact"),
]
SOLAR_STORM_SCALES = [
(100000, 5),
(10000, 4),
(1000, 3),
(100, 2),
(10, 1),
(0, 0),
]
GEOMAG_STORM_DESCRIPTIONS = [
(9, "Solar storm. Complete HF blackout, S30+ noise"),
(8, "Solar storm. HF sporadic only, S20-30 noise"),
(7, "Solar storm. HF intermittent, S9-20 noise"),
(6, "Solar storm. HF fading at higher latitudes, S6-9 noise"),
(5, "Solar storm. HF fading at higher latitudes, S4-6 noise"),
(4, "Active. Minor HF fading at higher latitudes, S2-3 noise"),
(3, "Unsettled. Minor HF fading at higher latitudes, S2-3 noise"),
(2, "Inactive. No impact, S0-2 noise"),
(1, "Quiet. No impact, S0-2 noise"),
(0, "Quiet. No impact, S0-2 noise"),
]
GEOMAG_STORM_SCALES = [
(9, 5),
(8, 4),
(7, 3),
(6, 2),
(5, 1),
(0, 0),
]
BAND_CONDITIONS_DESCRIPTIONS = [
(200, "Reliable conditions on all bands including 6m"),
(150, "Excellent conditions on all bands up to 10m, occasional 6m openings"),
(120, "Fair to good conditions on all bands up to 10m"),
(90, "Fair conditions on bands up to 15m"),
(70, "Poor to fair conditions on bands up to 20m"),
(0, "Bands above 40m unusable"),
]
ELECTRON_FLUX_DESCRIPTIONS = [
(1000, "Partial to complete HF blackout in polar regions"),
(100, "Degraded HF propagation in polar regions"),
(10, "Minor impact on HF in polar regions"),
(0, "No impact"),
]
def _lookup_by_threshold(value, table, default=None):
"""Return the description from a threshold table for the given numeric value.
The table is a list of (min_value, description) pairs in descending order."""
if value is None:
return default
for threshold, description in table:
if value >= threshold:
return description
return default
@dataclass
class HFBandCondition:
@@ -63,6 +144,36 @@ class SolarConditions:
# VHF propagation phenomena
vhf_conditions: list = None # list[VHFCondition]
# Derived values (populated by infer_descriptions())
# HF radio blackout risk, derived from x_ray
blackout_desc: str = None
# Solar radiation storm level description, derived from proton_flux
proton_flux_desc: str = None
# Solar radiation storm scale number (S0S5), derived from proton_flux
solar_storm_scale: int = None
# Geomagnetic storm level description, derived from k_index
geomag_storm_desc: str = None
# Geomagnetic storm scale number (G0G5), derived from k_index
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_desc: str = None
def infer_descriptions(self):
"""Populate derived text description fields from the current numeric/raw field values."""
# blackout_desc: use the X-ray flux class letter (first character of x_ray)
if self.x_ray and len(self.x_ray) > 0:
self.blackout_desc = BLACKOUT_DESCRIPTIONS.get(self.x_ray[0].upper())
self.proton_flux_desc = _lookup_by_threshold(self.proton_flux, PROTON_FLUX_DESCRIPTIONS)
self.solar_storm_scale = _lookup_by_threshold(self.proton_flux, SOLAR_STORM_SCALES)
self.geomag_storm_desc = _lookup_by_threshold(self.k_index, GEOMAG_STORM_DESCRIPTIONS)
self.geomag_storm_scale = _lookup_by_threshold(self.k_index, GEOMAG_STORM_SCALES)
self.band_conditions_desc = _lookup_by_threshold(self.sfi, BAND_CONDITIONS_DESCRIPTIONS)
self.electron_flux_desc = _lookup_by_threshold(self.electron_flux, ELECTRON_FLUX_DESCRIPTIONS)
def to_json(self):
"""JSON serialise"""