diff --git a/data/solar_conditions.py b/data/solar_conditions.py index 9ab5e98..aff0d5a 100644 --- a/data/solar_conditions.py +++ b/data/solar_conditions.py @@ -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) diff --git a/solarconditionsproviders/hamqsl.py b/solarconditionsproviders/hamqsl.py index 92e0961..1d51aa5 100644 --- a/solarconditionsproviders/hamqsl.py +++ b/solarconditionsproviders/hamqsl.py @@ -86,7 +86,7 @@ class HamQSL(HTTPSolarConditionsProvider): "sfi": int_val("solarflux"), "a_index": int_val("aindex"), "k_index": int_val("kindex"), - "x_ray": text("xray"), + "xray": text("xray"), "sunspots": int_val("sunspots"), "proton_flux": int_val("protonflux"), "electron_flux": int_val("electonflux"), diff --git a/templates/about.html b/templates/about.html index 4ca9205..5373c25 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 1878dd2..3d44636 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 c8737a5..8652045 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 28c036e..c87f918 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 5e2fd39..8a9dfa5 100644 --- a/templates/base.html +++ b/templates/base.html @@ -47,10 +47,10 @@ - - - - + + + + diff --git a/templates/conditions.html b/templates/conditions.html index 592ca66..4f594c9 100644 --- a/templates/conditions.html +++ b/templates/conditions.html @@ -111,7 +111,9 @@
X-ray Flux
-
+
+ + R
@@ -227,8 +229,8 @@
- - + + diff --git a/templates/map.html b/templates/map.html index 709c7cb..756850e 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 d568258..70f1530 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 8b06b5d..287375c 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 692022e..c95885b 100644 --- a/webassets/apidocs/openapi.yml +++ b/webassets/apidocs/openapi.yml @@ -1415,7 +1415,7 @@ components: type: integer description: 3-hour geomagnetic activity index, 0–9 example: 2 - x_ray: + xray: type: string description: Current X-ray flux class example: "B2.3" @@ -1564,10 +1564,16 @@ components: "1743638400.0": 25 "1743724800.0": 25 "1743811200.0": 25 - blackout_desc: + xray_desc: type: string description: HF radio blackout risk description, derived from the X-ray flux class. example: "No significant radio blackout" + radio_blackout_scale: + type: integer + description: HF radio blackout scale number (R0-R5), derived from the X-ray flux class. + minimum: 0 + maximum: 5 + example: 0 proton_flux_desc: type: string description: Solar radiation storm level description, derived from proton flux. diff --git a/webassets/js/conditions.js b/webassets/js/conditions.js index b235b85..a924ec9 100644 --- a/webassets/js/conditions.js +++ b/webassets/js/conditions.js @@ -53,8 +53,9 @@ function loadSolarConditions() { 'geomag_storm_scale': 'sw-geomag-storm-scale', 'geomag_storm_desc': 'sw-geomag-storm-desc', 'geomag_noise': 'sw-geomag-noise', - 'x_ray': 'sw-x-ray', - 'blackout_desc': 'sw-xray-desc', + 'xray': 'sw-xray', + 'radio_blackout_scale': 'sw-radio-blackout-scale', + 'xray_desc': 'sw-xray-desc', 'proton_flux': 'sw-proton-flux', 'solar_storm_scale': 'sw-solar-storm-scale', 'proton_flux_desc': 'sw-proton-desc', @@ -87,7 +88,7 @@ function loadSolarConditions() { kIndex < 5 ? 'bg-success-subtle' : kIndex < 6 ? 'bg-warning-subtle' : 'bg-danger-subtle'); } - const xRay = jsonData.x_ray; + const xRay = jsonData.xray; if (xRay) { const letter = xRay[0].toUpperCase(); const xRayClass = (letter === 'X') ? 'bg-danger-subtle' @@ -190,7 +191,7 @@ function renderKIndexForecast(data) { return timeStr; }, }, - grid: {color: gridColor}, + grid: {color: gridColor, offset: false}, }; // Draw a "now" line at the current time position