Receive SSTV pictures end to end #44

Merged
sjg merged 1 commits from feat/sstv-wiring into main 2026-08-06 00:21:26 +02:00
Owner

Follows #43, which added the trx-sstv decoder crate on its own. This wires it into the stack: audio the server already has in, a picture in the browser out.

panel

Server

A decoder task alongside the WEFAX one, running whenever SSTV decoding is enabled and the rig is in a mode SSTV is sent in (USB/LSB on HF, FM on VHF). A finished picture is written to the cache as a PNG and sent on as a message; rows go out as they decode, so a client watches two minutes of Martin M1 fill in rather than waiting for it.

Pictures join the decode history, are replayed to a client that connects later, and survive a restart. The stored message drops its base64 payload — the picture is on disk, and a megabyte per entry is not what a history is for.

Protocol

SetSstvDecodeEnabled / ResetSstvDecoder, an sstv_decode_enabled flag on the rig state, two audio message types, and Sstv / SstvProgress on DecodedMessage.

Client

Pictures land in their own history, and the PNG the server sent is written to the local cache so /sstv-images/ can serve it back. That endpoint and the WEFAX one now share their filename checks instead of each carrying a copy — no separators, no parent references, .png only.

Web UI

An SSTV sub-tab beside WEFAX: a live canvas that rows paint into at the line number they carry (not in arrival order — a dropped line would otherwise shear everything below it), a card for the last picture, and a filterable history linking to the files. Rows below the one arriving are grey rather than black, because not-yet-received is a different thing from received-as-black.

Pictures are not spots, so neither they nor their progress updates reach the decode statistics. That exclusion list had been growing by hand for LRPT and WEFAX; it is now one named set.

Decoder crate

Gains what the server needed to hand a picture on: to_png, to_png_base64, save_png, with UTC file-name stamps that sort.

Testing

Panel behaviour is tested against the plugin runtime: rows painting at their own line numbers, a completed picture linked by file name alone with no server path in the page, a cut-off picture reported as partial, clearing, and the toggle following the rig state.

Full workspace test suite, cargo fmt --check, clippy, frontend typecheck, lint, 47 unit tests, all four browser suites, and verify-generated all pass.

Not covered: nothing here has met a real off-air transmission — the end-to-end path is exercised with synthesised messages, and the decoder itself with synthesised audio. The first real signal is still the test that matters.

Follows #43, which added the `trx-sstv` decoder crate on its own. This wires it into the stack: audio the server already has in, a picture in the browser out. ![panel](receiving) ## Server A decoder task alongside the WEFAX one, running whenever SSTV decoding is enabled and the rig is in a mode SSTV is sent in (USB/LSB on HF, FM on VHF). A finished picture is written to the cache as a PNG and sent on as a message; rows go out as they decode, so a client watches two minutes of Martin M1 fill in rather than waiting for it. Pictures join the decode history, are replayed to a client that connects later, and survive a restart. The stored message drops its base64 payload — the picture is on disk, and a megabyte per entry is not what a history is for. ## Protocol `SetSstvDecodeEnabled` / `ResetSstvDecoder`, an `sstv_decode_enabled` flag on the rig state, two audio message types, and `Sstv` / `SstvProgress` on `DecodedMessage`. ## Client Pictures land in their own history, and the PNG the server sent is written to the local cache so `/sstv-images/` can serve it back. That endpoint and the WEFAX one now share their filename checks instead of each carrying a copy — no separators, no parent references, `.png` only. ## Web UI An SSTV sub-tab beside WEFAX: a live canvas that rows paint into **at the line number they carry** (not in arrival order — a dropped line would otherwise shear everything below it), a card for the last picture, and a filterable history linking to the files. Rows below the one arriving are grey rather than black, because not-yet-received is a different thing from received-as-black. Pictures are not spots, so neither they nor their progress updates reach the decode statistics. That exclusion list had been growing by hand for LRPT and WEFAX; it is now one named set. ## Decoder crate Gains what the server needed to hand a picture on: `to_png`, `to_png_base64`, `save_png`, with UTC file-name stamps that sort. ## Testing Panel behaviour is tested against the plugin runtime: rows painting at their own line numbers, a completed picture linked by file name alone with no server path in the page, a cut-off picture reported as partial, clearing, and the toggle following the rig state. Full workspace test suite, `cargo fmt --check`, clippy, frontend typecheck, lint, 47 unit tests, all four browser suites, and `verify-generated` all pass. **Not covered:** nothing here has met a real off-air transmission — the end-to-end path is exercised with synthesised messages, and the decoder itself with synthesised audio. The first real signal is still the test that matters.
sjg added 1 commit 2026-08-06 00:15:29 +02:00
[feat](trx-rs): receive SSTV pictures end to end
CI / lint (pull_request) Successful in 2m16s
CI / frontend (pull_request) Successful in 4m12s
CI / reuse (pull_request) Successful in 2s
CI / lint (push) Successful in 2m15s
CI / test (pull_request) Successful in 9m37s
CI / test (push) Successful in 7m36s
CI / frontend (push) Failing after 31s
CI / reuse (push) Successful in 3s
18107ce07e
Wires the SSTV decoder into the stack, from the audio the server already
has to a panel in the browser that shows the picture arriving.

Server: a decoder task alongside the WEFAX one, running whenever the
decoder is enabled and the rig is in a mode SSTV is sent in.  A finished
picture is written to the cache as a PNG and sent on as a message; the
rows are sent as they decode, so a client can watch two minutes of
Martin M1 fill in rather than waiting for it.  Pictures join the decode
history, are replayed to a client that connects later, and survive a
restart.

Protocol: SetSstvDecodeEnabled and ResetSstvDecoder, a sstv_decode
_enabled flag in the rig state, two audio message types, and Sstv and
SstvProgress on DecodedMessage.  The history stores the message without
its base64 payload -- the picture is already on disk, and a megabyte per
entry is not what a history is for.

Client: pictures land in their own history, and the PNG the server sent
is written to the local cache so /sstv-images/ can serve it back.  That
endpoint and the WEFAX one now share their filename checks rather than
each carrying a copy: no separators, no parent references, .png only.

Web UI: an SSTV sub-tab beside WEFAX, with a live canvas the rows paint
into at the line number they carry, a card for the last picture, and a
filterable history with links to the files.  Rows below the one arriving
are grey rather than black -- not yet received is a different thing from
received as black.  A picture is not a spot, so neither pictures nor
their progress updates reach the decode statistics; that exclusion list
had grown by hand for LRPT and WEFAX and is now one named set.

The decoder crate gains what the server needed to hand a picture on:
to_png, to_png_base64 and save_png, with file names stamped in UTC so
they sort.

Panel behaviour is tested with the plugin runtime: rows painting at
their own line numbers rather than in arrival order, a completed picture
linked by file name alone with no server path in the page, a cut-off
picture reported as partial, clearing, and the toggle following the rig
state.

Signed-off-by: Stan Grams <sjg@haxx.space>
sjg merged commit 18107ce07e into main 2026-08-06 00:21:26 +02:00
sjg deleted branch feat/sstv-wiring 2026-08-06 00:21:27 +02:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sjg/trx-rs#44