[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 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
2026-03-24 22:34:43 +01:00
parent a5e443a224
commit c93e855f0d
2 changed files with 68 additions and 38 deletions
@@ -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();
@@ -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 {