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.
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.

## 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.
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 main2026-08-06 00:21:26 +02:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Follows #43, which added the
trx-sstvdecoder crate on its own. This wires it into the stack: audio the server already has in, a picture in the browser out.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, ansstv_decode_enabledflag on the rig state, two audio message types, andSstv/SstvProgressonDecodedMessage.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,.pngonly.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, andverify-generatedall 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.