Radio blackout (R) scale

This commit is contained in:
Ian Renton
2026-04-04 10:45:42 +01:00
parent 429b278bca
commit d51e5184a1
13 changed files with 70 additions and 38 deletions

View File

@@ -5,7 +5,7 @@ from dataclasses import dataclass
# 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 = {
XRAY_CLASS_DESCRIPTIONS = {
"X": "Wide area HF radio blackout across sunlit side",
"M": "Occasional loss of HF communications on sunlit side",
"C": "Low absorption of HF signals on sunlit side",
@@ -71,6 +71,28 @@ ELECTRON_FLUX_DESCRIPTIONS = [
]
def _xray_blackout_scale(xray):
"""Return the NOAA Radio Blackout scale number (R0-R5) for the given X-ray flux class string
(e.g. "M4.5", "X12")."""
if not xray or len(xray) < 2:
return 0
letter = xray[0].upper()
try:
number = float(xray[1:])
except ValueError:
return 0
if letter == 'M':
return 1 if number < 5 else 2
if letter == 'X':
if number < 10:
return 3
if number < 20:
return 4
return 5
return 0
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."""
@@ -108,7 +130,7 @@ class SolarConditions:
# K-index (3-hour geomagnetic activity)
k_index: int = None
# X-ray flux class, e.g. "B2.3", "C1.0"
x_ray: str = None
xray: str = None
# Proton flux
proton_flux: int = None
# Electron flux
@@ -141,8 +163,10 @@ class SolarConditions:
blackout_forecast_r3_or_greater: dict = None
# Derived values (populated by infer_descriptions())
# HF radio blackout risk description, derived from x_ray
blackout_desc: str = None
# HF radio blackout risk description, derived from xray
xray_desc: str = None
# HF radio blackout scale number (R0-R5), derived from xray
radio_blackout_scale: int = None
# Solar radiation storm level description, derived from proton_flux
proton_flux_desc: str = None
# Solar radiation storm scale number (S0-S5), derived from proton_flux
@@ -159,10 +183,9 @@ class SolarConditions:
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())
if self.xray and len(self.xray) > 0:
self.xray_desc = XRAY_CLASS_DESCRIPTIONS.get(self.xray[0].upper())
self.radio_blackout_scale = _xray_blackout_scale(self.xray)
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)