Commit Graph

1340 Commits

Author SHA1 Message Date
sjg b12d93fb3c [feat](trx-server): fast per-rig meter broadcast at 30 Hz
Adds a per-rig meter broadcast channel on RigHandle and threads it
through run_rig_task. SDR meter tick drops from 100 ms to 33 ms; every
tick publishes a MeterUpdate while RigState is only updated on
>=0.25 dB deltas so rigctl/JSON-TCP frontends keep working without
amplifying state churn. Listener handles SubscribeMeter by converting
the TCP connection into a one-way JSON-line stream; TCP_NODELAY is
enabled on every accepted socket for low-latency frame delivery.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-19 19:50:06 +02:00
sjg 894b7c57be [feat](trx-protocol): add SubscribeMeter command and MeterUpdate DTO
Adds a dedicated one-way JSON-line stream for high-rate signal-strength
samples so meter updates bypass full-RigState diffing in the control path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-19 19:49:57 +02:00
sjg 6bd06d7872 [test](trx-reporting): expand PSKReporter tests with spot conversion, maidenhead, and helpers
Add tests for decoded_to_spot (FT8, WSPR, CW rejection), maidenhead
grid computation for known cities, callsign/locator validation, padding,
string encoding, and directed FT8 message parsing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-08 22:01:28 +02:00
sjg 7cb2b84d94 [test](trx-decode-log): add unit tests for config, template resolution, and write round-trip
Cover disabled config, template date token substitution, logger
initialization, and JSON Lines write+read verification for FT8 and
APRS payloads.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-08 21:58:01 +02:00
sjg cee84aa904 [test](trx-aprs): add unit tests for CRC, AX.25, APRS parsing, and demodulator
Cover CRC-16-CCITT, AX.25 address decoding, frame parsing, APRS
position formats (uncompressed, compressed, timestamped), packet type
detection, HDLC bit-to-byte conversion, and demodulator reset.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-08 21:56:46 +02:00
sjg 79053cdc5b [fix](trx-frontend-http): add missing context app_data in test_toggle_ft8_decode
The toggle_ft8_decode handler requires FrontendRuntimeContext for
multi-rig state resolution, but the test did not register it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-08 21:54:42 +02:00
sjg 53dfe72143 [fix](trx-wefax): validate row widths and log dimensions before PNG save
Sync docs to Wiki / wiki (push) Has been cancelled
png::Writer::write_image_data only validates the total byte count,
so if individual scan lines were pushed at the wrong width the total
could still match and the resulting PNG would be silently skewed.
Explicitly check each row against pixels_per_line before encoding
and bail with a descriptive error if any row disagrees.

Also log the final file path, dimensions, and byte size at debug
level so corrupted-image reports have something concrete to look at.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 00:27:46 +02:00
sjg 77c9b52ac3 [feat](trx-server): run fast S-meter tick on CAT rigs too
Extend the between-poll meter refresh that was previously SDR-only
to also run on CAT backends. CAT rigs now poll the S-meter every
150 ms (SDR remains at 100 ms), so the frontend bar moves in near
real-time instead of updating only on the 500 ms full-state poll.

The fast path calls get_signal_strength_db() first (SDR), then
falls back to the coarse get_signal_strength() + map_signal_strength
path for CAT rigs. It is skipped while powered off, transmitting,
or while a full poll is paused after a CAT write.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 21:35:02 +02:00
sjg bb45153ede [feat](trx-frontend-http): disable mode-incompatible decoders on bookmark apply
When a bookmark switches to a mode that a currently-enabled toggle
decoder doesn't support (e.g. moving from DIG to FM while FT8 is on),
turn the incompatible decoder off. Previously bmApply only touched
decoders that were compatible with the new mode, leaving stale
decoders running against modulation they can't handle.

Compatible decoders keep their existing behaviour: if the bookmark
specifies a decoder set, toggles are driven to match it; otherwise
they're left as-is.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 20:55:50 +02:00
sjg ede5e75dca [fix](trx-wefax): finalize and fsync PNG file before reporting path
Relying on Drop to write the PNG IEND trailer and flush BufWriter
silently swallows I/O errors, which can leave truncated/corrupted
image files on disk. Explicitly call writer.finish() to surface
encoding errors and sync_all() the File so bytes are durable before
WefaxEvent::Complete is emitted with the path (the frontend may read
the file immediately). The intermediate BufWriter is dropped since
we already buffer all rows and write them in a single call.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 20:42:39 +02:00
sjg d487711237 [feat](trx-wefax): add continuous slant correction via line cross-correlation
Track sample-clock drift between transmitter and receiver by
cross-correlating each new scan line against the previous one at
shifts of ±6 samples. The best-matching shift nudges the slicer's
extraction cursor, keeping adjacent lines aligned and removing the
diagonal skew that would otherwise accumulate over an 800-line image.

A small correlation-peak deadband prefers d=0 on quiet lines, and a
minimum-variance guard skips flat reference lines where drift
estimation is meaningless. Enabled by default via
WefaxConfig::slant_correction.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 20:42:31 +02:00
sjg 4fb9d3b2f9 [feat](trx-frontend-http): add inline audio player to recorder history
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 20:17:25 +02:00
sjg 9c4f93f951 [fix](trx-wefax): add phasing timeout fallback to receiving
PhasingDetector is strict (needs 10 phasing lines with low pulse-
position variance). On real-world signals with noise, tuning error,
or non-standard phasing, it can fail to converge — leaving the state
machine wedged in Phasing forever after a successful APT start
detection.

Add a ~30 s timeout: if phasing alignment doesn't lock after
PHASING_TIMEOUT_LINES worth of samples, fall through to Receiving
with phase_offset=0 and verified=false. The correlation verifier
then decides whether the content is real imagery (commit, eventually
save) or not (drop, back to Idle). The image will be horizontally
misaligned since we never locked phase, but it's better than a
stuck state that produces nothing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-04 20:05:59 +02:00
sjg 76f9953695 [refactor](trx-wefax): verify unverified auto-starts via line correlation
Previously, the variance-based auto-start entered State::Receiving
directly and committed to saving whatever came out, relying on a
100-line minimum as a crude filter. This let any sustained tone or
noise burst allocate an image buffer and emit state events.

Replace that filter with real verification. Each entry into Receiving
is now tagged verified (phasing-driven) or unverified (variance
auto-start). Unverified receptions must produce 5 consecutive lines
of r >= 0.5 correlation within the first 40 lines to commit. Otherwise
the buffered content is dropped silently and the decoder returns to
Idle — no image saved, no history entry, no carrier-lost event.

The carrier-loss watchdog is now gated on verified==true so it can
only ever finalize genuine captures. Phasing-driven receptions (APT
start tone + phasing pulses) enter verified and don't wait on the
correlation streak.

The 100-line minimum in finalize_image is removed — verification is
a cleaner semantic gate. A very short but genuinely phasing-validated
capture will now save.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-04 19:49:22 +02:00
sjg b12112d035 [fix](trx-wefax): discard short images from false auto-start
The variance-based auto-start in Idle state is permissive and fires on
any sustained modulated audio — tones, beeps, noise bursts. When that
happens mid-transmission, the decoder enters Receiving, the correlation
watchdog trips after exactly 31 lines (1 seed + 30 low-correlation),
and we end up saving a sliver of garbage to disk and the history.

Gate finalize_image() on a 100-line minimum. A real WEFAX chart is
hundreds of lines; anything shorter is almost certainly a false
auto-start and gets dropped silently (with a debug log). This doesn't
change start-tone / phasing-driven captures, only filters out the
noise-triggered entries.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-04 19:46:50 +02:00
sjg 84e50789d2 [fix](trx-wefax): auto-finalize image when carrier is lost
WEFAX images were only saved to disk and recorded in history when an APT
stop tone was detected or the decoder was explicitly reset. If the
transmission broke (carrier dropout, tuning drift, noise masking the
stop tone), the decoder stayed in Receiving state forever and the
partial image was never flushed.

Add a line-to-line Pearson correlation watchdog modelled on fldigi's
wefax automatic stop: real imagery has highly correlated adjacent scan
lines, while noise does not. After 30 consecutive low-correlation lines
(~15s at 120 LPM, ~30s at 60 LPM) the decoder finalizes the image,
emits WefaxEvent::Complete, and returns to Idle — so partial
transmissions show up in the web UI history like completed ones.

Flat lines with near-zero variance are treated as "undefined" and
leave the counter unchanged, so solid black/white image bands don't
falsely reset or trip the watchdog.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-04 19:07:19 +02:00
sjg 2a1c97a8dd [fix](trx-frontend-http): fix map-core.js crash when cached scripts race app.js
Dynamic scripts (map-core.js etc.) are effectively async and can execute
before the defer'd app.js that creates window.trx. When map-core.js is
served from browser cache it loads instantly and crashes on
`const T = window.trx` (undefined), preventing window.trx.map from
ever being set — the map never initialises and Ctrl+R is needed.

Move eager plugin loading from the inline script to app.js, triggered
via window.loadEagerPlugins() after window.trx is fully populated.
This guarantees the namespace exists before any plugin script runs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-04 15:44:44 +02:00
sjg 71650c0e70 [style](trx-frontend-http): use card overlay for map loading indicator
Replace the plain centered text with the same overlay card style used
by the connection-lost screen (decode-history-overlay + content-overlay).
Toggle visibility via the is-hidden class for a smooth fade transition.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-04 15:34:10 +02:00
sjg aadfa90a60 [fix](trx-frontend-http): fix map not loading without manual page refresh
map-core.js is loaded as a dynamic script (effectively async) while
leaflet.js is a static <script defer>. When the user clicks the map tab,
Leaflet may not have executed yet, causing initAprsMap() to silently
bail on the `typeof L === "undefined"` guard with no retry. Introduce
_initMapWhenReady() that polls at 100ms intervals until both Leaflet
and map-core.js are available, showing the loading message in the
meantime. Also update autoInitIfVisible() for direct /map navigation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-04 15:16:20 +02:00
sjg 806a66b8c6 [fix](trx-frontend-http): fix about tab data and sub-tab navigation broken by deferred templates
About-tab element refs were cached at script load time but the elements
live inside a <template> that hasn't been cloned yet, so all refs were
null. Convert to lazy resolution via _resolveAboutEls() called on first
about-tab render. Also extract _wireSubTabBar() so sub-tab click
listeners are attached after template cloning (the about Client sub-tab
was unreachable). Decoder status elements use the same lazy pattern.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-04 15:04:09 +02:00
sjg bed49ce13b [fix](trx-frontend-http): fix map and statistics tabs requiring refresh to show data
Move template cloning into navigateToTab() so deferred <template>
content is materialized before any tab-specific initialization runs.
Previously the document-level template cloner fired after navigateToTab
due to event propagation order, causing initAprsMap() and
scheduleStatsRender() to target elements that did not yet exist. Also
defer statistics control wiring until the first render so event
listeners are attached after the template is cloned.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-04 13:48:08 +02:00
sjg 81427cd87b [fix](trx-frontend-http): align recorder download and remove button heights
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-04 13:12:40 +02:00
sjg 1a88bde406 [style](trx-rs): apply cargo fmt formatting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-04 13:06:39 +02:00
sjg 9f29876afc [fix](trx-frontend-http): show map loading message, sync active rig marker, align recorder actions with bookmarks
- Display "Loading map…" placeholder on first map tab click while
  map-core.js is still loading; hide it once the module initializes.
- Sync receiver marker highlight when switching rigs so the map
  reflects the currently active rig immediately.
- Add "Actions" header to recorder files table and match button
  sizing to bookmarks table style.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-04 13:04:42 +02:00
sjg 02ed6d918c [fix](trx-frontend-http): redirect to login when navigating to protected tabs unauthenticated, right-align recorder table buttons
Show the auth gate instead of silently blocking navigation to non-main
tabs when not logged in. Also fix recorder file table layout so the file
column takes full width and action buttons are right-aligned.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-04 12:54:11 +02:00
sjg 7e15c0b5e4 [fix](trx-frontend-http): widen recorder filter input and narrow sort picker
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-04 12:48:34 +02:00
sjg 8b840dbf9e [fix](trx-frontend-http): improve recorder filter input and normalize file action button sizes
Widen the recordings filter input (remove max-width cap, set min-width),
add search icon placeholder, use type=search for native clear button.
Also fix download/remove button size mismatch with explicit height.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-04 12:42:35 +02:00
sjg e1554d95c0 [fix](trx-frontend-http): eliminate bookmark chip wobble by using compositor-friendly transforms
Batch offsetWidth reads before writes to prevent layout thrashing, and
position chips via transform instead of left to avoid sub-pixel jitter.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-04 12:36:08 +02:00
sjg 13aceb35c6 [fix](trx-frontend-http): stop reloading shared decode history on rig switch
The decode SSE stream and history endpoint are unfiltered and carry data
for all rigs. Reconnecting them on rig switch needlessly tore down the
entire decode state and re-fetched identical data. Also removed the
FT8/FT4/FT2/WSPR history table clearing since that data is shared.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-04 12:22:35 +02:00
sjg 0d85b7880c [fix](trx-frontend-http): align recorder download/remove buttons with general button styling
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-04 12:19:57 +02:00
sjg 79068e55af [fix](trx-frontend-http): buffer decode messages until map-data plugins are ready
Eagerly load map-data plugins (AIS, APRS, VDES, HF-APRS) on startup and
buffer any decode history or live SSE messages that arrive before plugin
handlers register. Each plugin drains its pending buffer on init.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-04 12:08:18 +02:00
sjg f978812090 [feat](trx-frontend-http): add exclusive flag to scheduler entries
When a schedule entry has `exclusive: true`, the scheduler stays on that
entry's bookmark for the entire time window without interleaving with
other overlapping entries. Useful for WEFAX and satellite passes where
switching away mid-reception would lose data.

Backend: first exclusive active entry wins outright in timespan_active_entry.
Frontend: "Excl." checkbox in inline edit disables interleave input;
interleave status shows exclusive entry as sole active entry.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-04 08:07:20 +02:00
sjg 036442d0ed [feat](trx-wefax): save images as YYYY-MM-DD_HH-MM-SS-freq_kHz_MODE.png
Include rig dial frequency and mode in WEFAX image filenames, matching
fldigi's approach of capturing tuning at save time. Images are saved to
~/.cache/trx-rs/wefax/. Server passes current rig state to the decoder
via set_tuning() before each processing block.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-04 08:00:21 +02:00
sjg 69e00a8245 [fix](trx-wefax): remove idle phasing fallback detection
Phasing-only signals (no APT start tone) should not trigger image
decoding. Only APT start tones and signal-level variance detection
can start reception.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-04 07:54:31 +02:00
sjg e467ba0537 [fix](trx-frontend-http): fix WEFAX toggle button and bookmark decoder wiring
Per-entry caching in _ensureDecoderToggles prevents stale guard from
blocking re-scan. Direct syncWefaxToggle path ensures dataset.enabled
stays current for bookmark prefill.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-04 07:54:26 +02:00
sjg 0fcd45f1ba [fix](trx-wefax): auto-save in-progress image on decoder reset
decoder.reset() now finalises and saves any partially-received image
before returning to Idle. The server emits the completion event so the
image appears in the frontend history and is persisted to disk.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-04 07:24:20 +02:00
sjg e5d8533a74 [feat](trx-wefax): auto-detect active signal and show live decode
Add signal-level detection that monitors luminance variance to auto-start
receiving when tuning in mid-image (~3s of sustained modulated signal),
matching fldigi's "strong image signal" detection. Reduce APT sustain
to 1.0s (2 windows) matching fldigi. Emit initial "Idle — scanning"
state event so the frontend shows the decoder is processing audio.
Add tracing instrumentation for luminance stats and tone analysis.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-03 23:13:07 +02:00
sjg 832cf2429d [fix](trx-frontend-http): rebuild decoder checkboxes when bookmark form opens
Dynamic plugin scripts can execute before deferred app.js, causing
bookmarks.js to miss the onDecoderRegistryReady callback and never
build decoder checkboxes. Rebuild from the registry each time the
form opens so checkboxes always reflect the current registry state.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-03 22:51:38 +02:00
sjg 2035e0cd6f [feat](trx-wefax): show decoder state transitions in frontend
Emit WefaxProgress events with a state label on each decoder state
transition (APT Start, Phasing, Receiving) so the frontend can display
the current decoder phase instead of just "listening for packets".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-03 22:48:10 +02:00
sjg a01609212e [fix](trx-wefax): detect APT tones from demodulated luminance, not raw audio
APT start/stop signals are not audio-frequency tones — they are
black↔white transition rates in the FM-demodulated output (300, 675,
450 transitions/s). The Goertzel detector was running on the raw ~1900 Hz
carrier where no energy exists at those frequencies, so APT detection
never fired on real HF WEFAX signals.

Replace the Goertzel approach with transition-counting on demodulated
luminance (matching fldigi's decode_apt), and swap the processing order
so FM demodulation runs before APT detection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-03 22:44:27 +02:00
sjg 96dcbbe8f1 [fix](trx-frontend-http): fix bookmark decoder wiring
Two issues prevented bookmark decoder toggles from working:

1. bmPrefillFromStatus() did not prefill decoder checkboxes from the
   current toggle button state, so bookmarks were saved with an empty
   decoders array even when decoders were active.

2. The bookmark apply code fetched /status without the remote parameter,
   comparing against the wrong rig's decoder state in multi-rig setups.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-03 22:30:48 +02:00
sjg 0a8e004a62 [fix](trx-wefax): fallback phasing detection for mid-transmission tune-in
When tuning into a WEFAX station after the APT start tone has already
passed, the decoder stayed in Idle forever. Add an idle_phasing detector
that continuously runs phasing detection on demodulated luminance while
in Idle state, allowing the decoder to lock onto ongoing transmissions
without requiring the 300/675 Hz start tone.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-03 22:30:43 +02:00
sjg ce5b55386c [fix](trx-frontend-http): fix misaligned text in recorder Download/Remove buttons
Normalize button styling between <a> and <button> elements by using
inline-flex with centered alignment instead of inline-block. Add
align-items to the container and box-sizing to the buttons.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-03 22:13:50 +02:00
sjg feb249cc0f [fix](trx-frontend-http): resolve per-rig state in decoder toggle endpoints
All decoder toggle endpoints (APRS, HF-APRS, CW, FT8, FT4, FT2, WSPR,
LRPT, WEFAX) read the enabled flag from the global default state watch
instead of the target rig's state. When controlling a non-active rig the
toggle reads the wrong rig's flag and sends the wrong enable/disable
value, causing the button to have no effect or invert the state.

Add resolve_rig_state() helper that looks up the per-rig watch via
context.rig_state_rx() and falls back to the global default, matching
the pattern already used by the /status endpoint.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-03 22:13:44 +02:00
sjg 919b6c5885 [fix](trx-frontend-http): route vchan commands to correct rig in background decode
BackgroundDecodeManager.send_audio_cmd used the global active_rig_id()
to route virtual channel commands. During a rig switch, Remove commands
for the old rig's channels were sent to the new rig's audio pipeline,
leaving orphaned virtual channels on the previous rig's server.

Replace send_audio_cmd with send_audio_cmd_to_rig that takes an explicit
rig_id, derived from the channel's own rig_id field. Both Remove and
SubscribeBackground commands now reach the correct rig.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-03 22:04:11 +02:00
sjg 42259d3c0d [fix](trx-wefax): block-based DSP for realtime decode performance
Replace per-sample circular-buffer processing with block-based linear
buffers in the FM discriminator and polyphase resampler. This eliminates
modular indexing in FIR inner loops, enabling compiler auto-vectorisation.
Also fix O(n²) drain pattern in the line slicer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-03 21:50:25 +02:00
sjg ced77464f9 [fix](trx-rs): use cache_dir for recordings and decode logs
Move default output directories from $XDG_DATA_HOME to $XDG_CACHE_HOME
so all runtime data lives under ~/.cache/trx-rs/ consistently.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-03 19:33:11 +02:00
sjg c8a5e15b3b [feat](trx-frontend-http): add Live/History views to WEFAX tab
Restructure the WEFAX tab to match the SAT/LRPT pattern with a
view switcher bar. Live view shows decoder description, live canvas,
and latest image card. History view adds a filterable, sortable table
of all decoded images with Clear All button.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-03 19:26:18 +02:00
sjg f0621078ce [fix](trx-wefax): allow WEFAX decoder to run in DIG mode
DIG mode provides the same SSB audio as USB, so WEFAX reception works
there. Added DIG to both the decoder registry active_modes and the
server-side mode gate.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-03 19:13:51 +02:00
sjg 462d7494fb [fix](trx-frontend-http): remove duplicate id on WEFAX tab button
The WEFAX button had id="subtab-wefax" which duplicated the panel's id,
causing querySelector to match the button instead of the panel on click.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-04-03 18:52:27 +02:00