Skip to content

Send a frame every interval, empty or not - #16

Closed
Babissimo wants to merge 1 commit into
mainfrom
feat/send-empty-frames
Closed

Babissimo wants to merge 1 commit into
mainfrom
feat/send-empty-frames

Conversation

@Babissimo

Copy link
Copy Markdown
Contributor

Why

retina-server marks its aircraft feed dirty once per frame received, in
services/tasks/frame_loop.py, whatever the frame carried. That flag gates the
1 Hz websocket broadcast to the map, so the socket is fed by frames arriving,
not by aircraft existing.

Skipping empty frames made the fleet's silence indistinguishable from a dead
connection. With nothing in any node's beam the broadcast stops, and after 12 s
the map client's watchdog force-closes a healthy websocket and falls back to
HTTP polling, showing POLL in the toolbar. It shows up on an empty world, a
stopped fleet, and a local stack before the fleet comes up.

Real nodes already send empty frames: delay/doppler/snr/adsb_hex carry
only a max_length in the v1 contract, so an all-empty frame is valid and the
hardware sends one per CPI. This removes a behavioural difference between the
synthetic fleet and the hardware it stands in for, which is why POLL meant
something different on testmap than on map.retina.fm.

What

Drops the if frame.get("delay") guard in run_simulation_loop. Two tests
drive one pass of the loop against a stub connection and a world in which
nothing is ever in a beam.

Cost

The send rate is now the configured rate rather than whatever fraction of nodes
happened to see something, so staging runs at the 50 fps its compose file
already describes rather than below it. Empty frames reach the server's archive
buffer like any other; there is no empty-frame fast path in
process_one_frame, though every stage it runs iterates over empty lists.

Verification

pytest: 213 passed. pre-commit run --all-files clean. Both new tests were
confirmed to fail with the guard restored.

Needs a submodule pointer bump in retina-server before staging picks it up.

🤖 Generated with Claude Code

The server marks its aircraft feed dirty once per frame received, whatever the
frame carried, and that dirty flag is what drives the 1 Hz websocket broadcast
to the map. Skipping empty frames therefore made the fleet's silence look like
a dead socket: with nothing in any node's beam the broadcast stops, and after
12 s the client's watchdog force-closes a healthy connection and drops the map
to HTTP polling.

Real nodes already send empty frames (an all-empty detection array is a valid
v1 frame), so this also removes a behavioural difference between the synthetic
fleet and the hardware it stands in for, which is what made the POLL badge mean
different things on testmap and on the production map.

Cost: the send rate is now the configured one rather than whatever fraction of
nodes happened to see something, so staging runs at the 50 fps its compose file
already describes, and empty frames reach the server's archive buffer like any
other.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Babissimo Babissimo closed this Sep 16, 2026
@Babissimo
Babissimo deleted the feat/send-empty-frames branch September 16, 2026 11:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant