Listening for events…

Scope freeze — RadNet near-real-time gamma series (radiation_monitor refinement)

Decided 2026-06-27 (Mike). A REFINEMENT of the existing radiation_monitor Locationdex kind, not a new kind. Mike's call when the URL came in: place it as a second measured series on the same station slots ("as few dex categories as possible"). The first series is the RadNet lab assay product (radionuclide_concentration); this adds the near-real-time gamma monitor.

Source

EPA RadNet near-real-time gamma archive, https://www.epa.gov/radnet/radnet-csv-file-downloads. One ZIP per city, yearly CSVs inside (2007→present). 140 ZIPs / 137 distinct cities. Every CSV row is one roughly-hourly reading at the city's fixed gamma monitor:

  • DOSE EQUIVALENT RATE (nSv/h) — gamma dose rate. Populated from ~2011 onward; null in the early years.
  • GAMMA COUNT RATE R02..R09 (CPM) — raw counts per energy band. The complete scalar, present across the whole 2007→present record.

This is the "finer radiation source (exact monitor coords, gamma gross-count series)" Mike flagged in docs/dex-triage-surfaced.md to refine the kind.

Measured reality — IN

A detector counting actual gammas. Direct measurement, no model. Passes the bright line (feedback_measured_reality_only).

Two series per slot — "data is data"

The lab assay series and the gamma series have different instruments, cadences, and coverage spans. Both are carried (feedback_data_is_data_partial_coverage); neither is dropped for covering fewer years than the other. Within the gamma series itself, the dose-rate column is shorter (≈2011→) than the count-rate bands (2007→); the summary reports each span separately (n_dose_rate vs n_observations) rather than discarding the early count-only years.

Storage — spine-parquet sidecar, NOT the primary store

The full hourly series is ~21M readings, ≈35× the 591k-row lab feed. A Locationdex slot only needs a breadth summary, so the hourly series is staged as ONE place-sorted spine-parquet sidecar — data/location_storehouse/radiation_monitor/gamma_spine.parquet — with a per-station summary json (gamma_summary.json) that the slot builder reads. This is the same "large series → spine-parquet, summarize onto the slot" pattern used by drought and the severe-wx split, and it keeps 21M rows out of the primary observations table. A paper needing row-level gamma reads the parquet directly.

Built by src/terrapulse/monitor/radiation_monitor_gamma.py; attached to slots by radiation_monitor_locationdex.py (gamma_series_summary / has_gamma_series / epa_radnet_gamma added to the slot's sources).

Decisions / gotchas frozen here

  • Timestamps are UTC. The EPA page states: "Near-real-time air data results are displayed in Coordinated Universal Time (UTC)." No local-time conversion is applied. (The bulk CSVs carry the raw UTC stamp; the page's "convert to local" widget is a display-only feature.)
  • City → loc_num mapping. The gamma archive is keyed by city (AL: BIRMINGHAM); the slots are keyed by RadNet loc_num. All 137 gamma cities match an existing slot by (state, city). A city with multiple station slots (≈22 of 311 cities have >1 loc_num) shares its one city-level gamma monitor across those slots — the gamma summary is attached to each.
  • curl, not urllib/httpx — the known sandbox hang.
  • Energy-band semantics. R02..R09 are EPA's detector energy windows (gross counts per band), not analyte concentrations. The slot summary keeps them as a count of bands plus the dose-rate sub-summary; it does not invent a single "radiation level" scalar.

Deferred (not in v1)

  • Live edge. The bulk CSVs are posted in yearly batches; a near-real-time current-year pull (EPA also exposes a per-station JSON API) is a separate decision, same as the lab feed.
  • PG ingest of the hourly series. If a paper needs SQL-level access to individual gamma readings, register epa_radnet_gamma as a measured PG feed from the spine-parquet then.
  • Per-band physical calibration (energy-window → keV mapping, dose reconstruction).
  • Exact monitor coordinates. Slots remain city-centroid (geocoded, flagged), inherited from the lab kind; the near-real-time page does not publish per-monitor lat/lon in the CSVs.
Live Feed