From c93e855f0de911fd7973b7f2da2abf8c0f360ef5 Mon Sep 17 00:00:00 2001 From: Stan Grams Date: Tue, 24 Mar 2026 22:34:43 +0100 Subject: [PATCH] [fix](trx-frontend-http): draw radio paths from actual receiver, improve mobile spectrum controls Radio paths now originate from the rig that decoded the message rather than the currently selected rig. Bookmark locators no longer draw radio paths. Rig switch no longer tears down decode pipeline since it is rig-independent. Mobile spectrum controls use flex-wrap for better layout. Co-Authored-By: Claude Opus 4.6 Signed-off-by: Stan Grams --- .../trx-frontend-http/assets/web/app.js | 46 ++++++++++---- .../trx-frontend-http/assets/web/style.css | 60 +++++++++++-------- 2 files changed, 68 insertions(+), 38 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 ffce0f6..706d687 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 @@ -3490,8 +3490,7 @@ async function switchRigFromSelect(selectEl) { if (typeof setSchedulerRig === "function") setSchedulerRig(lastActiveRigId); if (typeof setBackgroundDecodeRig === "function") setBackgroundDecodeRig(lastActiveRigId); if (typeof bmFetch === "function") bmFetch(document.getElementById("bm-category-filter")?.value || ""); - // Reconnect decode SSE so history is re-fetched with the new rig filter. - connectDecode(); + // Decode SSE and history are rig-independent — no reconnect needed. // Switch this session's rig and reconnect SSE to the new rig's // state channel. try { @@ -5026,13 +5025,30 @@ function syncDecodeContactPathVisibility() { updateMapPathsAnimationClass(); } -function setMapRadioPathTo(lat, lon, color, className = "aprs-radio-path") { +function _resolveReceiverLocation(rigIds) { + // Try to find location from the specific rig(s) that decoded this message + if (rigIds && rigIds.size) { + for (const rid of rigIds) { + const rig = serverRigs.find(r => r.remote === rid); + if (rig && rig.latitude != null && rig.longitude != null) { + return [rig.latitude, rig.longitude]; + } + } + } + // Fall back to active rig location + if (serverLat != null && serverLon != null) return [serverLat, serverLon]; + return null; +} + +function setMapRadioPathTo(lat, lon, color, className = "aprs-radio-path", rigIds) { clearMapRadioPath(); - if (!mapP2pRadioPathsEnabled || serverLat == null || serverLon == null || !Number.isFinite(lat) || !Number.isFinite(lon) || !aprsMap) { + if (!mapP2pRadioPathsEnabled || !Number.isFinite(lat) || !Number.isFinite(lon) || !aprsMap) { return; } + const src = _resolveReceiverLocation(rigIds); + if (!src) return; aprsRadioPath = L.polyline( - [[serverLat, serverLon], [lat, lon]], + [src, [lat, lon]], { color, opacity: 0.85, weight: 2, interactive: false, className } ).addTo(aprsMap); } @@ -5675,7 +5691,7 @@ function initAprsMap() { entry.track.addTo(aprsMap); } selectedAprsTrackCall = String(marker._aprsCall); - setMapRadioPathTo(ll.lat, ll.lng, mapSourceColor("aprs"), "aprs-radio-path"); + setMapRadioPathTo(ll.lat, ll.lng, mapSourceColor("aprs"), "aprs-radio-path", marker.__trxRigIds); return; } @@ -5687,7 +5703,7 @@ function initAprsMap() { refreshAisTrack(String(marker._aisMmsi), entry); selectedAisTrackMmsi = String(marker._aisMmsi); syncSelectedAisTrackVisibility(); - setMapRadioPathTo(ll.lat, ll.lng, mapSourceColor("ais"), "aprs-radio-path"); + setMapRadioPathTo(ll.lat, ll.lng, mapSourceColor("ais"), "aprs-radio-path", marker.__trxRigIds); return; } @@ -5696,18 +5712,20 @@ function initAprsMap() { const entry = vdesMarkers.get(String(marker._vdesKey)); if (!entry || !entry.msg) return; e.popup.setContent(buildVdesPopupHtml(entry.msg)); - setMapRadioPathTo(ll.lat, ll.lng, mapSourceColor("vdes"), "aprs-radio-path"); + setMapRadioPathTo(ll.lat, ll.lng, mapSourceColor("vdes"), "aprs-radio-path", marker.__trxRigIds); return; } - if (marker.__trxType === "bookmark" || marker.__trxType === "ft8" || marker.__trxType === "ft4" || marker.__trxType === "ft2" || marker.__trxType === "wspr") { + if (marker.__trxType === "ft8" || marker.__trxType === "ft4" || marker.__trxType === "ft2" || marker.__trxType === "wspr") { const center = locatorMarkerCenter(marker); if (center) { setSelectedLocatorMarker(marker); const lEntry = locatorEntryForMarker(marker); const lColor = lEntry ? locatorStyleForEntry(lEntry, locatorEntryCount(lEntry)).color : locatorFilterColor(marker.__trxType); - setMapRadioPathTo(center.lat, center.lon, lColor, "locator-radio-path"); + setMapRadioPathTo(center.lat, center.lon, lColor, "locator-radio-path", marker.__trxRigIds); } + } else if (marker.__trxType === "bookmark") { + setSelectedLocatorMarker(marker); } }); @@ -5978,9 +5996,11 @@ window.navigateToMapLocator = function(grid, preferredType = null) { if (center) { const targetZoom = Math.max(aprsMap.getZoom() || 0, 7); aprsMap.setView([center.lat, center.lon], targetZoom); - const fEntry = locatorEntryForMarker(marker); - const fColor = fEntry ? locatorStyleForEntry(fEntry, locatorEntryCount(fEntry)).color : locatorFilterColor(marker?.__trxType); - setMapRadioPathTo(center.lat, center.lon, fColor, "locator-radio-path"); + if (marker.__trxType !== "bookmark") { + const fEntry = locatorEntryForMarker(marker); + const fColor = fEntry ? locatorStyleForEntry(fEntry, locatorEntryCount(fEntry)).color : locatorFilterColor(marker?.__trxType); + setMapRadioPathTo(center.lat, center.lon, fColor, "locator-radio-path", marker.__trxRigIds); + } } setSelectedLocatorMarker(marker); if (typeof marker.openPopup === "function") marker.openPopup(); 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 341aff7..87a3ee9 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 @@ -2606,16 +2606,44 @@ button:focus-visible, input:focus-visible, select:focus-visible { .signal-measure #sig-result { width: 100%; } - #spectrum-controls, + #spectrum-controls { + flex-direction: column; + align-items: stretch; + gap: 0.45rem; + padding-top: 0.45rem; + } #spectrum-bw-row, #spectrum-level-row { - display: grid; - grid-template-columns: minmax(0, 1fr); - justify-content: stretch; + display: flex; + flex-wrap: wrap; + gap: 0.35rem 0.4rem; } - #spectrum-controls { - gap: 0.5rem; - padding-top: 0.45rem; + #spectrum-bw-label, + #spectrum-floor-label, + #spectrum-peak-hold-label { + flex: 1 1 100%; + justify-content: space-between; + } + #spectrum-gamma-label { + flex: 1 1 100%; + justify-content: space-between; + } + #spectrum-gamma-input { + flex: 1 1 auto; + min-width: 3rem; + } + #spectrum-bw-input, + #spectrum-floor-input, + #overview-peak-hold { + flex: 1 1 auto; + min-width: 3rem; + box-sizing: border-box; + } + #spectrum-bw-set-btn, + #spectrum-bw-auto-btn, + #spectrum-bw-sweet-btn, + #spectrum-auto-btn { + flex: 1 1 auto; } .spectrum-edge-shift { width: 0.95rem; @@ -2630,24 +2658,6 @@ button:focus-visible, input:focus-visible, select:focus-visible { .spectrum-edge-shift-right { right: 0.2rem; } - #spectrum-bw-row, - #spectrum-level-row { - gap: 0.4rem; - } - #spectrum-bw-label, - #spectrum-floor-label, - #spectrum-gamma-label, - #spectrum-peak-hold-label { - width: 100%; - justify-content: space-between; - } - #spectrum-bw-input, - #spectrum-floor-input, - #overview-peak-hold { - width: 100%; - min-width: 0; - box-sizing: border-box; - } .aprs-controls, .ft8-controls, .cw-controls {