From 176b3e306c9f8f093a668c33570136dc69b937a9 Mon Sep 17 00:00:00 2001 From: Stanislaw Grams Date: Mon, 9 Feb 2026 23:38:50 +0100 Subject: [PATCH] [fix](trx-frontend-http): filter ft8 and refresh rf values Co-authored-by: OpenAI Signed-off-by: Stanislaw Grams --- .../trx-frontend-http/assets/web/app.js | 42 ++++++++++++----- .../trx-frontend-http/assets/web/index.html | 1 + .../assets/web/plugins/ft8.js | 46 ++++++++++++++++++- .../trx-frontend-http/assets/web/style.css | 1 + 4 files changed, 77 insertions(+), 13 deletions(-) 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 b478397..aec85e1 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 @@ -241,6 +241,9 @@ function render(update) { freqEl.value = formatFreq(update.status.freq.hz); } window.ft8BaseHz = update.status.freq.hz; + if (window.updateFt8RfDisplay) { + window.updateFt8RfDisplay(); + } } if (!modeDirty && update.status && update.status.mode) { const mode = normalizeMode(update.status.mode); @@ -785,21 +788,33 @@ window.aprsMapAddStation = function(call, lat, lon, info, symbolTable, symbolCod } }; -function maidenheadToLatLon(grid) { +function maidenheadToBounds(grid) { if (!grid || grid.length < 4) return null; const g = grid.toUpperCase(); const A = "A".charCodeAt(0); - const lon = (g.charCodeAt(0) - A) * 20 - 180 + (parseInt(g[2], 10) * 2) + 1; - const lat = (g.charCodeAt(1) - A) * 10 - 90 + (parseInt(g[3], 10) * 1) + 0.5; - let lonAdj = lon; - let latAdj = lat; + const fieldLon = (g.charCodeAt(0) - A) * 20 - 180; + const fieldLat = (g.charCodeAt(1) - A) * 10 - 90; + const squareLon = parseInt(g[2], 10) * 2; + const squareLat = parseInt(g[3], 10) * 1; + + let lon = fieldLon + squareLon; + let lat = fieldLat + squareLat; + let lonSpan = 2; + let latSpan = 1; + if (g.length >= 6) { const subLon = (g.charCodeAt(4) - A) * (5 / 60); const subLat = (g.charCodeAt(5) - A) * (2.5 / 60); - lonAdj += subLon - (5 / 120); - latAdj += subLat - (2.5 / 120); + lon += subLon; + lat += subLat; + lonSpan = 5 / 60; + latSpan = 2.5 / 60; } - return { lat: latAdj, lon: lonAdj }; + + return [ + [lat, lon], + [lat + latSpan, lon + lonSpan], + ]; } function applyMapFilter() { @@ -816,11 +831,14 @@ function applyMapFilter() { window.ft8MapAddLocator = function(message, grid) { if (!aprsMap) initAprsMap(); if (!aprsMap) return; - const loc = maidenheadToLatLon(grid); - if (!loc) return; + const bounds = maidenheadToBounds(grid); + if (!bounds) return; const popupContent = `${grid}
${message}`; - const marker = L.circleMarker([loc.lat, loc.lon], { - radius: 5, color: "#ffb020", fillColor: "#ffb020", fillOpacity: 0.85 + const marker = L.rectangle(bounds, { + color: "#ffb020", + weight: 1, + fillColor: "#ffb020", + fillOpacity: 0.25, }).addTo(aprsMap).bindPopup(popupContent); marker.__trxType = "ft8"; mapMarkers.add(marker); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html index 4a1e62c..5e7b800 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html @@ -165,6 +165,7 @@
+ Waiting for server decode
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 61a6f1c..962edb8 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 @@ -1,7 +1,9 @@ // --- FT8 Decoder Plugin (server-side decode) --- const ft8Status = document.getElementById("ft8-status"); const ft8MessagesEl = document.getElementById("ft8-messages"); +const ft8FilterInput = document.getElementById("ft8-filter"); const FT8_MAX_MESSAGES = 200; +let ft8FilterText = ""; function fmtTime(tsMs) { if (!tsMs) return "--:--:--"; @@ -11,13 +13,17 @@ function fmtTime(tsMs) { function renderFt8Row(msg) { const row = document.createElement("div"); row.className = "ft8-row"; + const rawMessage = (msg.message || "").toString(); + row.dataset.message = rawMessage.toUpperCase(); + row.dataset.offsetHz = 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 baseHz = Number.isFinite(window.ft8BaseHz) ? window.ft8BaseHz : null; const rfHz = Number.isFinite(msg.freq_hz) && Number.isFinite(baseHz) ? (baseHz + msg.freq_hz) : null; const freq = Number.isFinite(rfHz) ? rfHz.toFixed(0) : "--"; - const renderedMessage = renderFt8Message(msg.message || ""); + const renderedMessage = renderFt8Message(rawMessage); row.innerHTML = `${fmtTime(msg.ts_ms)}${snr}${dt}${freq}${renderedMessage}`; + applyFt8FilterToRow(row); return row; } @@ -81,6 +87,44 @@ function isAlphaNum(ch) { return /[A-Za-z0-9]/.test(ch); } +function applyFt8FilterToRow(row) { + if (!ft8FilterText) { + row.style.display = ""; + return; + } + const message = row.dataset.message || ""; + row.style.display = message.includes(ft8FilterText) ? "" : "none"; +} + +function applyFt8FilterToAll() { + const rows = ft8MessagesEl.querySelectorAll(".ft8-row"); + rows.forEach((row) => applyFt8FilterToRow(row)); +} + +function updateFt8RowRf(row) { + const freqEl = row.querySelector(".ft8-freq"); + if (!freqEl) return; + const baseHz = Number.isFinite(window.ft8BaseHz) ? window.ft8BaseHz : null; + const offset = row.dataset.offsetHz ? Number(row.dataset.offsetHz) : NaN; + if (Number.isFinite(baseHz) && Number.isFinite(offset)) { + freqEl.textContent = (baseHz + offset).toFixed(0); + } else { + freqEl.textContent = "--"; + } +} + +window.updateFt8RfDisplay = function() { + const rows = ft8MessagesEl.querySelectorAll(".ft8-row"); + rows.forEach((row) => updateFt8RowRf(row)); +}; + +if (ft8FilterInput) { + ft8FilterInput.addEventListener("input", () => { + ft8FilterText = ft8FilterInput.value.trim().toUpperCase(); + applyFt8FilterToAll(); + }); +} + document.getElementById("ft8-decode-toggle-btn").addEventListener("click", async () => { try { await postPath("/toggle_ft8_decode"); } catch (e) { console.error("FT8 toggle failed", e); } }); 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 4994259..1d4a3c7 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 @@ -224,6 +224,7 @@ small { color: var(--text-muted); } .aprs-pos:hover { text-decoration: underline; } .aprs-byte { color: var(--accent-yellow); background: rgba(255, 214, 0, 0.12); border: 1px solid rgba(255, 214, 0, 0.25); border-radius: 4px; padding: 0 0.2rem; margin: 0 0.1rem; font-size: 0.78em; } .ft8-controls { display: flex; gap: 0.6rem; align-items: center; margin-bottom: 0.75rem; } +.ft8-filter { flex: 1; min-width: 10rem; } .ft8-header { display: flex; gap: 0.6rem; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted); border-bottom: 1px solid var(--border); padding: 0 0 0.35rem 0; margin-bottom: 0.35rem; } #ft8-messages { max-height: 360px; overflow-y: auto; border: 1px solid var(--border-light); border-radius: 6px; background: var(--input-bg); font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 0.85rem; padding: 0.35rem 0.5rem; } .ft8-row { display: flex; gap: 0.6rem; line-height: 1.4; border-bottom: 1px solid var(--border); padding: 0.25rem 0; }