From 2035e0cd6fde6bf4704f70bf674ac2792dbfb402 Mon Sep 17 00:00:00 2001 From: Stan Grams Date: Fri, 3 Apr 2026 22:48:10 +0200 Subject: [PATCH] [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 Signed-off-by: Stan Grams --- src/decoders/trx-wefax/src/decoder.rs | 36 ++++++++++++++----- .../assets/web/plugins/wefax.js | 9 +++++ src/trx-core/src/decode.rs | 3 ++ 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/decoders/trx-wefax/src/decoder.rs b/src/decoders/trx-wefax/src/decoder.rs index 12f70eb..f5bf67a 100644 --- a/src/decoders/trx-wefax/src/decoder.rs +++ b/src/decoders/trx-wefax/src/decoder.rs @@ -114,13 +114,13 @@ impl WefaxDecoder { match tone { AptTone::Start576 => { self.idle_phasing = None; - self.transition_to_start_detected(576); + events.push(self.transition_to_start_detected(576)); got_start = true; break; } AptTone::Start288 => { self.idle_phasing = None; - self.transition_to_start_detected(288); + events.push(self.transition_to_start_detected(288)); got_start = true; break; } @@ -143,7 +143,7 @@ impl WefaxDecoder { .as_millis() as i64, ); self.idle_phasing = None; - self.transition_to_receiving(ioc, lpm, offset); + events.push(self.transition_to_receiving(ioc, lpm, offset)); } } } @@ -157,7 +157,7 @@ impl WefaxDecoder { .any(|r| matches!(r.tone, Some(AptTone::Start576 | AptTone::Start288))); if !still_start { - self.transition_to_phasing(ioc); + events.push(self.transition_to_phasing(ioc)); } } @@ -173,7 +173,7 @@ impl WefaxDecoder { if let Some(ref mut phasing) = self.phasing { if let Some(offset) = phasing.process(&luminance) { - self.transition_to_receiving(ioc, lpm, offset); + events.push(self.transition_to_receiving(ioc, lpm, offset)); } } } @@ -214,6 +214,7 @@ impl WefaxDecoder { ioc, pixels_per_line: WefaxConfig::pixels_per_line(ioc), line_data: Some(b64), + state: None, }, line_data, )); @@ -255,7 +256,22 @@ impl WefaxDecoder { ) } - fn transition_to_start_detected(&mut self, ioc: u16) { + fn state_event(&self, label: &str, ioc: u16, lpm: u16) -> WefaxEvent { + WefaxEvent::Progress( + WefaxProgress { + rig_id: None, + line_count: 0, + lpm, + ioc, + pixels_per_line: WefaxConfig::pixels_per_line(ioc), + line_data: None, + state: Some(label.to_string()), + }, + Vec::new(), + ) + } + + fn transition_to_start_detected(&mut self, ioc: u16) -> WefaxEvent { let ioc = self.config.ioc.unwrap_or(ioc); self.state = State::StartDetected { ioc }; self.reception_start_ms = Some( @@ -264,22 +280,26 @@ impl WefaxDecoder { .unwrap_or_default() .as_millis() as i64, ); + let lpm = self.config.lpm.unwrap_or(120); + self.state_event(&format!("APT Start {}", ioc), ioc, lpm) } - fn transition_to_phasing(&mut self, ioc: u16) { + fn transition_to_phasing(&mut self, ioc: u16) -> WefaxEvent { let lpm = self.config.lpm.unwrap_or(120); // Default 120 LPM. self.tone_detector.reset(); self.phasing = Some(PhasingDetector::new(lpm, INTERNAL_RATE)); self.demodulator.reset(); self.state = State::Phasing { ioc, lpm }; + self.state_event("Phasing", ioc, lpm) } - fn transition_to_receiving(&mut self, ioc: u16, lpm: u16, phase_offset: usize) { + fn transition_to_receiving(&mut self, ioc: u16, lpm: u16, phase_offset: usize) -> WefaxEvent { let ppl = WefaxConfig::pixels_per_line(ioc) as usize; self.slicer = Some(LineSlicer::new(lpm, ioc, INTERNAL_RATE, phase_offset)); self.image = Some(ImageAssembler::new(ppl)); self.tone_detector.reset(); self.state = State::Receiving { ioc, lpm }; + self.state_event("Receiving", ioc, lpm) } fn transition_to_idle(&mut self) { diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/wefax.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/wefax.js index e34b12f..51a4ef4 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/wefax.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/wefax.js @@ -249,6 +249,15 @@ function addWefaxImage(msg) { // ── SSE event handlers (public API) ───────────────────────────────── window.onServerWefaxProgress = function (msg) { + // State-only update (no image data): show decoder state in status. + if (msg.state && !msg.line_data) { + if (wefaxDom.status) { + wefaxDom.status.textContent = msg.state; + wefaxDom.status.style.color = 'var(--text-accent)'; + } + return; + } + if (msg.line_count <= 1 || !wefaxLiveCtx) { resetLiveCanvas(msg.pixels_per_line || 1809); } diff --git a/src/trx-core/src/decode.rs b/src/trx-core/src/decode.rs index 7a4e15f..deb6e90 100644 --- a/src/trx-core/src/decode.rs +++ b/src/trx-core/src/decode.rs @@ -311,4 +311,7 @@ pub struct WefaxProgress { /// Base64-encoded greyscale line data (one row of pixels). #[serde(skip_serializing_if = "Option::is_none")] pub line_data: Option, + /// Decoder state label (e.g. "APT Start 576", "Phasing", "Receiving"). + #[serde(skip_serializing_if = "Option::is_none")] + pub state: Option, }