Skip to content

Latest commit

 

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

dead-air

dead-air

awesome-ml-systems Hopsworks

Doghouse project. It works, but the prediction is post factum: the bands have already fallen when it fires, and the few-minute "lead" is only against the official bulletin's publishing lag, not the flare. We beat latency, not the event. Full post-mortem in the awesome-ml-systems dog house. Kept public, and the UI is genuinely nice. Fork it if you can do better.

Can you hear a solar flare black out the shortwave bands before the space-weather bulletin says so? An X-ray flare ionizes the dayside D-layer within minutes and HF propagation collapses. Thousands of amateur WSPR beacons are an unintentional planetary ionosonde, and when the dayside bands fall silent together the collapse is visible in the firehose. dead-air reads that collapse live from the independent WSPR data, at best a few minutes ahead of the official bulletin's publishing lag.

the live scope: per-band dayside deficit over a wireframe Earth, observed vs expected per HF layer, the shield ruptures on the footprint region when a collapse fires

The live scope: each frequency layer shows observed vs expected spot rate over the dayside, the terminator tracks the real sun, and the shield ruptures on the footprint region when the bands fall silent together.

the bands go quiet at a held-out M6.7 flare: dayside deficit z per band, the GOES peak marked

Every dayside band dives together as the D-layer absorbs, minutes around the flare peak. That simultaneous multi-band collapse is the signature the detector fires on.

The result

A normalcy model predicts the expected dayside spot rate per band, path geometry and hour. The scorer turns the shortfall into a robust per-band z, and flags a collapse when three or more dayside bands stay quiet together. The GOES M/X flare record is the truth the events are scored against, on a time-held-out window (train through 2026-02, validate 2026-03/04, hold out 2026-05 to 07). No shuffling.

metric (held-out, vs GOES M1+, v5 regional) value
event recall 0.26
event precision 0.23
median lead to flare peak -10 min (fires after the peak)
per-bucket normalcy error (MAE, log spots) 0.19

Read that honestly: this is post factum. The bands have already dropped when the detector fires, and on the held-out window the median lead is negative (it fires after the peak). Any apparent earliness is against the bulletin's publishing lag, not the event: we beat latency, not foresight. The multi-band signature is real (a held-out M6.7 fires two collapse windows, an M5.8 stays below the three-band bar), the operational value is not.

normalcy model, predicted vs observed spots per bucket

The row-level normalcy model hugs the diagonal (MAE 0.19 in log spots): the deficit the detector reads is a real shortfall against a calibrated expectation, not model error.

v1.5 -- regional footprint

The detector localizes a collapse to a footprint cell. The path midpoint is binned to a coarse geographic grid (45 by 45 degrees) and the deficit, the robust z and the event flag are computed per (band, region), logged to dead_air_predictions v2. The app shows the worst-hit region per band and places the shield rupture on that cell. The GBM is unchanged: region derives from the path midpoint, already a model feature, so only the deficit aggregation grain and the frozen z calibration move to the regional level.

Regional calibration (held-out, vs GOES M1+, z=1.25, three bands per cell):

metric v1 global current (v5 regional, 45 deg)
event recall 0.41 0.26
event precision 0.15 0.23
median lead to peak 3 min +6.5 val, -10 holdout
normalcy MAE (log spots) 0.19 0.19

A finer cell isolates the footprint but splits a band stack across cells, so recall falls. The training pipeline searches the cell size, z and the bands-per-cell threshold on validation by F0.5 (precision-weighted), and the winning cell rides in the model bundle so serve bins identically. That search confirmed 45 deg cells, z=1.25, three bands: 30 splits the stack, 60 loses precision. Threshold and cell tuning are at their ceiling (~0.23 precision); the next lever is matching the collapse footprint to the subsolar patch, a detector rebuild, not a knob.

Caveats

Read these before quoting the number anywhere.

  • Recall is bounded by observing geometry. A flare blacks out the sunlit hemisphere. When the dayside sits over an empty ocean at local midnight there is almost no WSPR traffic under it to go quiet, so that flare cannot be seen from the firehose at all. Recall against every M1+ flare therefore has a hard ceiling no model reaches.
  • The label is a proxy for the collapse. The detector flags propagation collapse. A GOES flare is one cause, the one with an official timestamped record to score against. Geomagnetic storms and absorption events also silence the bands, so some fired events with no matching flare are real collapses, not pure noise, which the precision number cannot separate.
  • Precision is the weak point (~0.23). The detector scores each footprint cell on its own, so a natural regional lull can still trip it. Matching the collapse footprint to the subsolar patch, which would separate a flare from a local lull, is the real next lever and is NOT built. Threshold and cell tuning are maxed out.
  • Mid-class flares are partial. An M1 to M4 flare hits the lowest bands first and often does not reach three bands. The three-band bar is deliberate: it holds out for broad collapses and lets narrow ones pass.

The idea

HF radio bounces off the ionosphere. An X-ray flare ionizes the D-layer on the sunlit side, which absorbs the lower bands first (7, 10 MHz) and the higher bands as the flare grows. The nightside is untouched. Thousands of amateur stations trading WSPR beacons report every path that completes, so a sudden multi-band dayside drop in that traffic is a sudden ionospheric disturbance in progress.

The model learns the normal spot rate per band, path geometry, hour and season, then reads the live deficit. The per-band z baseline is frozen at training time and stored with the model, so a three-hour serving window scores the deficit on the same scale as the training months, with no window-length skew.

Architecture

An FTI (feature, training, inference) system on Hopsworks. Sources arrive on their own cadence and join through the feature store, with one shared feature module so training and serving cannot skew.

flowchart LR
    wspr([wspr.live ClickHouse]):::ext
    goes([NOAA SWPC + NASA DONKI]):::ext
    psk([PSKReporter MQTT]):::ext

    subgraph FE[Feature]
        direction TB
        f1[deadair-wspr, PySpark] --> fg1[(wspr_agg)]:::hops
        f2[deadair-goes] --> fg2[(goes_flare, label)]:::hops
    end
    subgraph TR[Training]
        direction TB
        fv{{dead_air_fv}}:::hops --> t1[normalcy GBM + frozen detector] --> reg[(Model Registry)]:::hops
    end
    subgraph INF[Inference]
        direction TB
        sc[deadair-scorer, every 5 min] --> pr[(dead_air_predictions)]:::hops --> app[dead-air app]
    end

    wspr --> f1
    goes --> f2
    fg1 --> fv
    fg2 --> fv
    reg --> sc
    fg1 --> sc
    psk --> app

    classDef hops fill:#10b98122,stroke:#34d399,color:#e5e7eb;
    classDef ext fill:none,stroke:#6b7280,color:#9ca3af,stroke-dasharray:4 3;
Loading

The sources, each on a different cadence:

source cadence role
wspr.live ClickHouse ~1 min fresh spot reports, training history and serving reads from one source, no skew
NOAA SWPC GOES live M/X flare record: the label, and the bulletin the detector aims to beat
NASA DONKI FLR historical flare backfill for the training window
PSKReporter MQTT ~281 spots/s the app wall, display only

The file-by-file map:

deadair_features.py         shared, skew-free: path geometry + history baseline + detector
collect/goes.py             NOAA SWPC + NASA DONKI flare client
pipelines/wspr_pipeline.py  F1  WSPR spots -> wspr_agg                     (Hopsworks job, PySpark)
pipelines/goes_pipeline.py  F2  flares -> goes_flare                       (Hopsworks job)
pipelines/train.py          T   feature view -> dead_air_normalcy          (Hopsworks job)
pipelines/score.py          I1  wspr_agg -> dead_air_predictions, 5 min    (Hopsworks job)
app/                        I2  FastAPI app: per-band panel + GOES receipts + live wall
tools/job_resources.py      set job memory via the SDK after a deploy
tools/make_banner.py        the series banner generator
reqs/dead-air.md            the FTI specification

The detector lives in deadair_features.py next to the geometry and the history baseline, so pipelines/train.py and pipelines/score.py import the exact same code and the scorer carries no dependency on the training pipeline.

Reproduce

Clone into a Hopsworks project on the /hopsfs/... FUSE mount. Paths self-derive, nothing is hardcoded to a username. The committed job memory and chunk sizes are modest so the pipelines schedule on a small cluster. On a larger box, override them on the command line.

make wspr-backfill    # F1  WSPR history -> wspr_agg
make goes-backfill    # F2  flare history -> goes_flare
make train-job        # T   normalcy GBM + detector -> dead_air_normalcy
make wspr-schedule    # F1  incremental, every 15 min
make goes-schedule    # F2  top-up, hourly
# scorer + app deploy from pipelines/score.py and app/deploy_app.py
make train-job TRAIN_MEM=8192              # larger cluster
make wspr-backfill WSPR_MEM=8192 WSPR_CHUNK_DAYS=10

The demo

A server-rendered wall. The finger is a per-band dayside health panel from dead_air_predictions: each HF band shows its latest residual-deficit z and turns red inside a fired collapse. The receipts are the recent GOES M/X flares, so when the finger moves before a flare row appears, that gap is the lead. The wall is the PSKReporter firehose relayed over SSE, raw spots scrolling, thinning on the paths a collapse is killing. When the bands are quiet and GOES is silent the wall is still a live picture of the planet talking to itself.

About

Hear a solar flare black out the shortwave bands before the bulletin: HF propagation collapse detected from the WSPR firehose, GOES flares as labels. Real-time FTI on Hopsworks.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages