From 0836091bbc9afe533ad28e7ffd8fd8d8b58f2954 Mon Sep 17 00:00:00 2001 From: Stan Grams Date: Wed, 4 Mar 2026 19:45:17 +0100 Subject: [PATCH] [fix](trx-client): preserve ft8 history on reload Co-authored-by: OpenAI Codex Signed-off-by: Stan Grams --- src/trx-client/src/main.rs | 53 +++++++++++-------- .../trx-frontend-http/assets/web/app.js | 9 +++- .../assets/web/plugins/ft8.js | 23 ++++++-- .../trx-frontend-http/assets/web/style.css | 6 ++- 4 files changed, 62 insertions(+), 29 deletions(-) diff --git a/src/trx-client/src/main.rs b/src/trx-client/src/main.rs index 6860db0..fc2e3ed 100644 --- a/src/trx-client/src/main.rs +++ b/src/trx-client/src/main.rs @@ -286,6 +286,8 @@ async fn async_init() -> DynResult { })); // Audio streaming setup + let mut pending_audio_client = None; + let mut pending_audio_bridge = None; if cfg.frontends.audio.enabled { let (rx_audio_tx, _) = broadcast::channel::(256); let (tx_audio_tx, tx_audio_rx) = mpsc::channel::(64); @@ -304,7 +306,7 @@ async fn async_init() -> DynResult { let audio_rig_ports: HashMap = cfg.frontends.audio.rig_ports.clone(); let audio_shutdown_rx = shutdown_rx.clone(); - task_handles.push(tokio::spawn(audio_client::run_audio_client( + pending_audio_client = Some(tokio::spawn(audio_client::run_audio_client( remote_host, cfg.frontends.audio.server_port, audio_rig_ports, @@ -318,26 +320,7 @@ async fn async_init() -> DynResult { ))); if cfg.frontends.audio.bridge.enabled { - info!("Audio bridge enabled (local virtual-device integration)"); - task_handles.push(audio_bridge::spawn_audio_bridge( - cfg.frontends.audio.bridge.clone(), - frontend_runtime - .audio_rx - .as_ref() - .expect("audio rx must be set") - .clone(), - frontend_runtime - .audio_tx - .as_ref() - .expect("audio tx must be set") - .clone(), - frontend_runtime - .audio_info - .as_ref() - .expect("audio info must be set") - .clone(), - shutdown_rx.clone(), - )); + pending_audio_bridge = Some(cfg.frontends.audio.bridge.clone()); } } else { info!("Audio disabled in config, decode will not be available"); @@ -404,6 +387,34 @@ async fn async_init() -> DynResult { )?; } + // Start the audio connection only after frontends are running so decode + // subscribers can capture the server's initial history replay. + if let Some(handle) = pending_audio_client { + task_handles.push(handle); + } + if let Some(bridge_cfg) = pending_audio_bridge { + info!("Audio bridge enabled (local virtual-device integration)"); + task_handles.push(audio_bridge::spawn_audio_bridge( + bridge_cfg, + frontend_runtime_ctx + .audio_rx + .as_ref() + .expect("audio rx must be set") + .clone(), + frontend_runtime_ctx + .audio_tx + .as_ref() + .expect("audio tx must be set") + .clone(), + frontend_runtime_ctx + .audio_info + .as_ref() + .expect("audio info must be set") + .clone(), + shutdown_rx.clone(), + )); + } + Ok(AppState { shutdown_tx, task_handles, diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js index 7c2c7f3..5791b75 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js @@ -3877,8 +3877,15 @@ function initAprsMap() { function sizeAprsMapToViewport() { const mapEl = document.getElementById("aprs-map"); if (!mapEl) return; - const mapRect = mapEl.getBoundingClientRect(); const stage = mapStageEl(); + if (mapIsFullscreen() && stage) { + const stageHeight = stage.clientHeight || stage.getBoundingClientRect().height; + const target = Math.max(260, Math.floor(stageHeight)); + mapEl.style.height = `${target}px`; + if (aprsMap) aprsMap.invalidateSize(); + return; + } + const mapRect = mapEl.getBoundingClientRect(); const width = mapEl.clientWidth || mapRect.width; const footer = document.querySelector(".footer"); let bottom = mapIsFullscreen() && stage diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/ft8.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/ft8.js index a49cc99..3f4223b 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/ft8.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/ft8.js @@ -10,6 +10,16 @@ const FT8_PERIOD_SECONDS = 15; let ft8FilterText = ""; let ft8MessageHistory = []; +function normalizeFt8DisplayFreqHz(freqHz) { + const rawHz = Number(freqHz); + if (!Number.isFinite(rawHz)) return null; + const baseHz = Number.isFinite(window.ft8BaseHz) ? Number(window.ft8BaseHz) : null; + if (Number.isFinite(baseHz) && baseHz > 0 && rawHz >= 0 && rawHz < 100000) { + return baseHz + rawHz; + } + return rawHz; +} + function fmtTime(tsMs) { if (!tsMs) return "--:--:--"; return new Date(tsMs).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" }); @@ -33,7 +43,8 @@ function renderFt8Row(msg) { row.dataset.storedFreqHz = Number.isFinite(msg.freq_hz) ? String(msg.freq_hz) : ""; const snr = Number.isFinite(msg.snr_db) ? msg.snr_db.toFixed(1) : "--"; const dt = Number.isFinite(msg.dt_s) ? msg.dt_s.toFixed(2) : "--"; - const freq = Number.isFinite(msg.freq_hz) ? msg.freq_hz.toFixed(0) : "--"; + const displayFreqHz = normalizeFt8DisplayFreqHz(msg.freq_hz); + const freq = Number.isFinite(displayFreqHz) ? displayFreqHz.toFixed(0) : "--"; const renderedMessage = renderFt8Message(rawMessage); row.innerHTML = `${fmtTime(msg.ts_ms)}${snr}${dt}${freq}${renderedMessage}`; applyFt8FilterToRow(row); @@ -51,8 +62,9 @@ function addFt8Message(msg) { } function ft8BarRfText(msg) { - if (!Number.isFinite(msg.freq_hz)) return null; - return `${msg.freq_hz.toFixed(0)} Hz`; + const displayFreqHz = normalizeFt8DisplayFreqHz(msg.freq_hz); + if (!Number.isFinite(displayFreqHz)) return null; + return `${displayFreqHz.toFixed(0)} Hz`; } function updateFt8Bar() { @@ -173,8 +185,9 @@ function updateFt8RowRf(row) { const freqEl = row.querySelector(".ft8-freq"); if (!freqEl) return; const storedFreqHz = row.dataset.storedFreqHz ? Number(row.dataset.storedFreqHz) : NaN; - if (Number.isFinite(storedFreqHz)) { - freqEl.textContent = storedFreqHz.toFixed(0); + const displayFreqHz = normalizeFt8DisplayFreqHz(storedFreqHz); + if (Number.isFinite(displayFreqHz)) { + freqEl.textContent = displayFreqHz.toFixed(0); } else { freqEl.textContent = "--"; } diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css index f16c311..aa95496 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css @@ -1132,12 +1132,14 @@ small { color: var(--text-muted); } #map-stage:fullscreen, #map-stage:-webkit-full-screen { background: var(--bg); - padding: 0.75rem; + width: 100%; + height: 100%; + padding: 0; box-sizing: border-box; } #map-stage:fullscreen #aprs-map, #map-stage:-webkit-full-screen #aprs-map { - border-radius: 8px; + border-radius: 0; } .aprs-controls { display: flex; gap: 0.6rem; align-items: center; margin-bottom: 0.75rem; } .aprs-summary {