Radar frames are captured every 10 minutes, not the configured 5 #208

Closed
opened 2026-10-13 21:13:30 +00:00 by claude-bot · 1 comment
Collaborator

Found 2026-10-13 on dev (2.0.0-rc6) while watching frame capture during the Kansas/Oklahoma soak. Not a regression: the logic dates from #130.

Symptom

ALERT_RADAR_FRAME_INTERVAL_MINUTES is 5. On dev, the Wichita and Enid Flood Watches collected 23 frames between 17:29Z and 21:05Z, which is one every 9.8 minutes.

Cause

The capture job is scheduled at the same period as the interval it enforces (IntervalTrigger(minutes=settings.alert_radar_frame_interval_minutes)), and poll_alert_radar_frames skips an alert whose newest frame is younger than that interval:

if newest is not None and _as_utc(newest) > now - interval:
    continue

captured_at is stamped when the frame is stored, a second or two after the run started. Five minutes later the next run finds that frame 4m58s old, which is younger than five minutes, and skips it. Every alert is therefore captured on alternate runs.

Effect

  • A radar loop has half the frames intended. A 45-minute warning yields 4 or 5 frames where 9 were meant, which matters for the Explorer loop (#140): storm motion is coarser than designed.
  • The 24-frame cap (ALERT_RADAR_FRAME_MAX_PER_EVENT) currently spans about four hours rather than the two it was sized against.

Fix

  • Gate on the interval less a small allowance for scheduler and fetch jitter, so a job running at the interval's own period captures every run.
  • Test that a frame just under one interval old is treated as due, and that an immediate re-poll still is not.

Consequence to decide

Once the cadence is really 5 minutes, 24 frames cover two hours instead of four. Warnings are short, so they gain resolution and lose nothing. A long-lived watch (tonight's runs seven hours) loses coverage of its later hours. Raising the cap restores that at the cost of disk, and #158 is the place where that cost is being measured; dev's disk is at 90%.

Found 2026-10-13 on dev (`2.0.0-rc6`) while watching frame capture during the Kansas/Oklahoma soak. Not a regression: the logic dates from #130. ## Symptom `ALERT_RADAR_FRAME_INTERVAL_MINUTES` is 5. On dev, the Wichita and Enid Flood Watches collected **23 frames between 17:29Z and 21:05Z**, which is one every 9.8 minutes. ## Cause The capture job is scheduled at the same period as the interval it enforces (`IntervalTrigger(minutes=settings.alert_radar_frame_interval_minutes)`), and `poll_alert_radar_frames` skips an alert whose newest frame is younger than that interval: ```python if newest is not None and _as_utc(newest) > now - interval: continue ``` `captured_at` is stamped when the frame is stored, a second or two after the run started. Five minutes later the next run finds that frame 4m58s old, which is younger than five minutes, and skips it. Every alert is therefore captured on alternate runs. ## Effect - A radar loop has half the frames intended. A 45-minute warning yields 4 or 5 frames where 9 were meant, which matters for the Explorer loop (#140): storm motion is coarser than designed. - The 24-frame cap (`ALERT_RADAR_FRAME_MAX_PER_EVENT`) currently spans about four hours rather than the two it was sized against. ## Fix - [ ] Gate on the interval less a small allowance for scheduler and fetch jitter, so a job running at the interval's own period captures every run. - [ ] Test that a frame just under one interval old is treated as due, and that an immediate re-poll still is not. ## Consequence to decide Once the cadence is really 5 minutes, 24 frames cover two hours instead of four. Warnings are short, so they gain resolution and lose nothing. A long-lived watch (tonight's runs seven hours) loses coverage of its later hours. Raising the cap restores that at the cost of disk, and #158 is the place where that cost is being measured; dev's disk is at 90%.
Author
Collaborator

Merged via #209 (00e3c8e). CI run 259 green in 6m23s, both test tiers. Not yet released: main is ahead of v2.0.0-rc6 by this fix only.

Leaving this open until it is confirmed on a deployed build (frames arriving every 5 minutes).

The cap question has grown into a redesign, pending a decision

@rbrooks asked (2026-10-13) to capture once per location and share frames across alerts, to make sure nothing is dropped under a Severe Thunderstorm or Tornado Warning, and to guarantee the lead-in. A proposal is out for @rbrooks's decision; nothing is built.

The finding that shapes it: Iowa State's archive serves the national NEXRAD reflectivity mosaic at 5-minute steps for arbitrary past times, with the same bbox request shape used for NOAA today:

https://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0q-t.cgi?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap
  &LAYERS=nexrad-n0q-wmst&SRS=EPSG:4326&BBOX=<lon-2.5>,<lat-2.5>,<lon+2.5>,<lat+2.5>
  &WIDTH=600&HEIGHT=600&FORMAT=image/png&TRANSPARENT=true&TIME=<ISO8601 UTC>

Tested for the Wichita bbox: 2026-10-13 21:00Z and 21:05Z (today's central Kansas storms visible), 2026-09-21 01:30Z, and 2025-10-13 21:00Z all returned 600×600 transparent PNGs in under half a second, 55–190 KB each.

Proposed shape:

  • Frames keyed by (location_id, valid_at) on a 5-minute grid rather than by alert, so overlapping alerts share one image.
  • Capture as an idempotent "fill the window" job: the union of each qualifying alert's [start − lead-in, end + tail], fetching whatever slots are missing. No per-alert cap.
  • Lead-in fetched retroactively at first fire, independent of any watch.
  • Tiers: full 5-minute capture for short-fused radar-driven warnings and convective watches; 15-minute, time-capped capture for long-duration precipitation alerts; none for non-precipitation alerts.
  • A reconciliation sweep over recently ended alerts, plus expected-versus-present frame accounting, so gaps from downtime or failed fetches are repaired and visible.

Open decisions: the tier lists, the lead-in and tail lengths (proposed 60 and 30 minutes), acceptance of IEM as the source (different colour table from NOAA's image), and whether this lands in 2.0.0 or 2.1. Not measured: how far behind real time IEM's newest frame runs.

This will get its own issue once the decisions are made.

Merged via #209 (`00e3c8e`). CI run 259 green in 6m23s, both test tiers. Not yet released: `main` is ahead of `v2.0.0-rc6` by this fix only. Leaving this open until it is confirmed on a deployed build (frames arriving every 5 minutes). ## The cap question has grown into a redesign, pending a decision @rbrooks asked (2026-10-13) to capture once per location and share frames across alerts, to make sure nothing is dropped under a Severe Thunderstorm or Tornado Warning, and to guarantee the lead-in. A proposal is out for @rbrooks's decision; nothing is built. The finding that shapes it: **Iowa State's archive serves the national NEXRAD reflectivity mosaic at 5-minute steps for arbitrary past times**, with the same bbox request shape used for NOAA today: ``` https://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0q-t.cgi?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap &LAYERS=nexrad-n0q-wmst&SRS=EPSG:4326&BBOX=<lon-2.5>,<lat-2.5>,<lon+2.5>,<lat+2.5> &WIDTH=600&HEIGHT=600&FORMAT=image/png&TRANSPARENT=true&TIME=<ISO8601 UTC> ``` Tested for the Wichita bbox: 2026-10-13 21:00Z and 21:05Z (today's central Kansas storms visible), 2026-09-21 01:30Z, and 2025-10-13 21:00Z all returned 600×600 transparent PNGs in under half a second, 55–190 KB each. Proposed shape: - Frames keyed by `(location_id, valid_at)` on a 5-minute grid rather than by alert, so overlapping alerts share one image. - Capture as an idempotent "fill the window" job: the union of each qualifying alert's `[start − lead-in, end + tail]`, fetching whatever slots are missing. No per-alert cap. - Lead-in fetched retroactively at first fire, independent of any watch. - Tiers: full 5-minute capture for short-fused radar-driven warnings and convective watches; 15-minute, time-capped capture for long-duration precipitation alerts; none for non-precipitation alerts. - A reconciliation sweep over recently ended alerts, plus expected-versus-present frame accounting, so gaps from downtime or failed fetches are repaired and visible. Open decisions: the tier lists, the lead-in and tail lengths (proposed 60 and 30 minutes), acceptance of IEM as the source (different colour table from NOAA's image), and whether this lands in 2.0.0 or 2.1. Not measured: how far behind real time IEM's newest frame runs. This will get its own issue once the decisions are made.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/WeatherBot#208
No description provided.