[fix](trx-frontend-http): put HF APRS on the map, under its own source
The HF APRS list never plotted anything. Its plugin ships in the map plugin group and its packets carry positions, but nothing ever handed one to the map, so a station heard on 30 m appeared in the panel and nowhere else — and unlike the replay gaps around it, no reload brought it back. Plot it, and not as more VHF APRS. HF is a different band and a different path, and lumping the two together would leave no way to tell them apart or to look at one without the other, so it goes on as a source of its own: its own colour, its own chip in the map's Show filter, its own entry in the source legend, and its own clear. Station entries are keyed by source and callsign rather than callsign alone, so a station worked on both bands keeps a marker for each while the popups, the search text and the tracks still show the callsign as heard. decode-flow feeds an HF beacon alongside the VHF one and checks all of it: both reach the map under their own sources, the Show row offers HF APRS next to APRS, and turning that chip off takes the HF station off the map while the VHF one stays. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SyX26FCpMQxiBoC7r5K1A7 Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -149,17 +149,32 @@ function resetHfAprsHistoryView() {
|
||||
if (hfAprsPacketsEl) hfAprsPacketsEl.innerHTML = "";
|
||||
hfAprsPacketHistory = [];
|
||||
renderHfAprsHistory();
|
||||
hfAprsWindow.clearMapMarkersByType?.("hf_aprs");
|
||||
}
|
||||
function pruneHfAprsHistoryView() {
|
||||
pruneHfAprsPacketHistory();
|
||||
renderHfAprsHistory();
|
||||
}
|
||||
function plotHfAprsPacket(pkt) {
|
||||
if (pkt.lat == null || pkt.lon == null || !hfAprsWindow.aprsMapAddStation) return;
|
||||
hfAprsWindow.aprsMapAddStation(
|
||||
pkt.srcCall ?? "",
|
||||
pkt.lat,
|
||||
pkt.lon,
|
||||
pkt.info ?? "",
|
||||
pkt.symbolTable,
|
||||
pkt.symbolCode,
|
||||
pkt,
|
||||
"hf_aprs"
|
||||
);
|
||||
}
|
||||
function addHfAprsPacket(pkt) {
|
||||
const tsMs = Number.isFinite(pkt.ts_ms) ? Number(pkt.ts_ms) : Date.now();
|
||||
pkt._tsMs = tsMs;
|
||||
pkt._ts = new Date(tsMs).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" });
|
||||
hfAprsPacketHistory.unshift(pkt);
|
||||
pruneHfAprsPacketHistory();
|
||||
plotHfAprsPacket(pkt);
|
||||
scheduleHfAprsHistoryRender();
|
||||
}
|
||||
function normalizeServerHfAprsPacket(pkt) {
|
||||
@@ -174,6 +189,7 @@ function onServerHfAprsBatch(packets) {
|
||||
const tsMs = Number.isFinite(next.ts_ms) ? Number(next.ts_ms) : Date.now();
|
||||
next._tsMs = tsMs;
|
||||
next._ts = new Date(tsMs).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" });
|
||||
plotHfAprsPacket(next);
|
||||
normalized.push(next);
|
||||
}
|
||||
normalized.reverse();
|
||||
@@ -248,5 +264,9 @@ window.trxPluginRuntime.registerDecoder({
|
||||
restore: onServerHfAprsBatch,
|
||||
reset: resetHfAprsHistoryView,
|
||||
prune: pruneHfAprsHistoryView,
|
||||
rerender: renderHfAprsHistory
|
||||
rerender: renderHfAprsHistory,
|
||||
// Oldest first, so station tracks are rebuilt in the order they happened.
|
||||
syncMap: () => {
|
||||
for (const entry of [...hfAprsPacketHistory].reverse()) plotHfAprsPacket(entry);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -60,7 +60,7 @@ var mapWindow = window;
|
||||
const decodeContactPaths = /* @__PURE__ */ new Map();
|
||||
let selectedMapQsoKey = null;
|
||||
const mapMarkers = /* @__PURE__ */ new Set();
|
||||
const DEFAULT_MAP_SOURCE_FILTER = { ais: true, vdes: true, aprs: true, bookmark: false, ft8: true, ft4: true, ft2: true, wspr: true, sat: false };
|
||||
const DEFAULT_MAP_SOURCE_FILTER = { ais: true, vdes: true, aprs: true, hf_aprs: true, bookmark: false, ft8: true, ft4: true, ft2: true, wspr: true, sat: false };
|
||||
const mapFilter = { ...DEFAULT_MAP_SOURCE_FILTER };
|
||||
const MAP_FILTER_ALL_KEY = "__all";
|
||||
const mapLocatorFilter = { phase: "band", bands: /* @__PURE__ */ new Set() };
|
||||
@@ -128,6 +128,12 @@ var mapWindow = window;
|
||||
}
|
||||
return trimmed;
|
||||
}
|
||||
function aprsEntrySource(entry) {
|
||||
return entry?.type === "hf_aprs" ? "hf_aprs" : "aprs";
|
||||
}
|
||||
function aprsStationKey(call, source) {
|
||||
return source === "aprs" ? call : `${source}:${call}`;
|
||||
}
|
||||
function refreshAprsTrack(call, entry) {
|
||||
if (!entry) return;
|
||||
if (!Array.isArray(entry.trackPoints) || entry.trackPoints.length < 2) {
|
||||
@@ -149,7 +155,7 @@ var mapWindow = window;
|
||||
lineJoin: "round",
|
||||
interactive: false
|
||||
});
|
||||
track.__trxType = "aprs";
|
||||
track.__trxType = aprsEntrySource(entry);
|
||||
track._aprsCall = call;
|
||||
entry.track = track;
|
||||
}
|
||||
@@ -381,6 +387,7 @@ var mapWindow = window;
|
||||
}
|
||||
function mapSourceLabel(type) {
|
||||
if (type === "bookmark") return "Bookmarks";
|
||||
if (type === "hf_aprs") return "HF APRS";
|
||||
return String(type || "").toUpperCase();
|
||||
}
|
||||
function locatorFilterColor(type) {
|
||||
@@ -396,6 +403,7 @@ var mapWindow = window;
|
||||
if (type === "vdes") return "#a78bfa";
|
||||
if (type === "sat") return "#f59e0b";
|
||||
if (type === "aprs") return "#00d17f";
|
||||
if (type === "hf_aprs") return "#fb7185";
|
||||
return locatorFilterColor(type);
|
||||
}
|
||||
function bandForHz(hz) {
|
||||
@@ -931,10 +939,8 @@ var mapWindow = window;
|
||||
}
|
||||
}
|
||||
for (const entry of stationMarkers.values()) {
|
||||
if (entry?.type === "aprs" && entry?.visibleInHistoryWindow) {
|
||||
availableSources.add("aprs");
|
||||
break;
|
||||
}
|
||||
if (!entry?.visibleInHistoryWindow) continue;
|
||||
availableSources.add(aprsEntrySource(entry));
|
||||
}
|
||||
const bandMap = /* @__PURE__ */ new Map();
|
||||
for (const entry of locatorMarkers.values()) {
|
||||
@@ -966,7 +972,7 @@ var mapWindow = window;
|
||||
for (const key of Array.from(mapLocatorFilter.bands)) {
|
||||
if (!bandMap.has(key)) mapLocatorFilter.bands.delete(key);
|
||||
}
|
||||
const sourceItems = ["ais", "vdes", "aprs", "bookmark", "ft8", "ft4", "ft2", "wspr"].filter((key) => availableSources.has(key)).map((key) => ({
|
||||
const sourceItems = ["ais", "vdes", "aprs", "hf_aprs", "bookmark", "ft8", "ft4", "ft2", "wspr"].filter((key) => availableSources.has(key)).map((key) => ({
|
||||
key,
|
||||
label: mapSourceLabel(key),
|
||||
color: mapSourceColor(key),
|
||||
@@ -1028,9 +1034,10 @@ var mapWindow = window;
|
||||
}
|
||||
return parts.join(" ").toLowerCase();
|
||||
}
|
||||
if (type === "aprs") {
|
||||
const call = marker?._aprsCall ? String(marker._aprsCall) : "";
|
||||
const entry = stationMarkers.get(call);
|
||||
if (type === "aprs" || type === "hf_aprs") {
|
||||
const key = marker?._aprsCall ? String(marker._aprsCall) : "";
|
||||
const entry = stationMarkers.get(key);
|
||||
const call = entry?.call ?? key;
|
||||
const info = entry?.info ? String(entry.info) : "";
|
||||
const pktRaw = entry?.pkt?.raw ? String(entry.pkt.raw) : "";
|
||||
return `${call} ${info} ${pktRaw}`.toLowerCase();
|
||||
@@ -1200,9 +1207,10 @@ var mapWindow = window;
|
||||
}
|
||||
};
|
||||
mapWindow.clearMapMarkersByType = function(type) {
|
||||
if (type === "aprs") {
|
||||
if (type === "aprs" || type === "hf_aprs") {
|
||||
selectedAprsTrackCall = null;
|
||||
stationMarkers.forEach((entry) => {
|
||||
stationMarkers.forEach((entry, key) => {
|
||||
if (aprsEntrySource(entry) !== type) return;
|
||||
if (entry && entry.marker) {
|
||||
if (aprsMap && aprsMap.hasLayer(entry.marker)) entry.marker.removeFrom(aprsMap);
|
||||
mapMarkers.delete(entry.marker);
|
||||
@@ -1211,8 +1219,8 @@ var mapWindow = window;
|
||||
if (aprsMap && aprsMap.hasLayer(entry.track)) entry.track.removeFrom(aprsMap);
|
||||
mapMarkers.delete(entry.track);
|
||||
}
|
||||
stationMarkers.delete(key);
|
||||
});
|
||||
stationMarkers.clear();
|
||||
return;
|
||||
}
|
||||
if (type === "ais") {
|
||||
@@ -1448,13 +1456,15 @@ var mapWindow = window;
|
||||
if (!ll) return;
|
||||
const entry = stationMarkers.get(marker._aprsCall);
|
||||
if (!entry) return;
|
||||
e.popup.setContent(buildAprsPopupHtml(marker._aprsCall, ll.lat, ll.lng, entry.info || "", entry.pkt));
|
||||
const source = aprsEntrySource(entry);
|
||||
const call = entry.call ?? String(marker._aprsCall);
|
||||
e.popup.setContent(buildAprsPopupHtml(call, ll.lat, ll.lng, entry.info || "", entry.pkt));
|
||||
refreshAprsTrack(String(marker._aprsCall), entry);
|
||||
if (entry.track && aprsMap && mapFilter.aprs && !aprsMap.hasLayer(entry.track)) {
|
||||
if (entry.track && aprsMap && mapFilter[source] && !aprsMap.hasLayer(entry.track)) {
|
||||
entry.track.addTo(aprsMap);
|
||||
}
|
||||
selectedAprsTrackCall = String(marker._aprsCall);
|
||||
setMapRadioPathTo(ll.lat, ll.lng, mapSourceColor("aprs"), "aprs-radio-path", marker.__trxRigIds);
|
||||
setMapRadioPathTo(ll.lat, ll.lng, mapSourceColor(source), "aprs-radio-path", marker.__trxRigIds);
|
||||
return;
|
||||
}
|
||||
if (marker._aisMmsi) {
|
||||
@@ -1848,28 +1858,33 @@ var mapWindow = window;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function _aprsAddMarkerToMap(call, entry) {
|
||||
function _aprsAddMarkerToMap(key, entry) {
|
||||
if (!aprsMap || entry.lat == null || entry.lon == null) return;
|
||||
refreshAprsTrack(call, entry);
|
||||
refreshAprsTrack(key, entry);
|
||||
const source = aprsEntrySource(entry);
|
||||
const call = entry.call ?? key;
|
||||
const icon = aprsSymbolIcon(entry.symbolTable ?? "", entry.symbolCode ?? "");
|
||||
const popupContent = buildAprsPopupHtml(call, entry.lat, entry.lon, entry.info || "", entry.pkt);
|
||||
const color = mapSourceColor(source);
|
||||
const marker = icon ? L.marker([entry.lat, entry.lon], { icon }).addTo(aprsMap).bindPopup(popupContent) : L.circleMarker([entry.lat, entry.lon], {
|
||||
radius: 6,
|
||||
color: "#00d17f",
|
||||
fillColor: "#00d17f",
|
||||
color,
|
||||
fillColor: color,
|
||||
fillOpacity: 0.8
|
||||
}).addTo(aprsMap).bindPopup(popupContent);
|
||||
marker.__trxType = "aprs";
|
||||
marker.__trxType = source;
|
||||
marker.__trxRigIds = entry.rigIds || /* @__PURE__ */ new Set();
|
||||
marker._aprsCall = call;
|
||||
marker._aprsCall = key;
|
||||
entry.marker = marker;
|
||||
mapMarkers.add(marker);
|
||||
}
|
||||
mapWindow.aprsMapAddStation = function(call, lat, lon, info, symbolTable, symbolCode, pkt) {
|
||||
mapWindow.aprsMapAddStation = function(call, lat, lon, info, symbolTable, symbolCode, pkt, source) {
|
||||
const nextPoint = [lat, lon];
|
||||
const tsMs = Number.isFinite(pkt?._tsMs) ? Number(pkt._tsMs) : Date.now();
|
||||
const msgRigId = pkt?.rig_id || T.lastActiveRigId;
|
||||
const existing = stationMarkers.get(call);
|
||||
const stationSource = source === "hf_aprs" ? "hf_aprs" : "aprs";
|
||||
const key = aprsStationKey(call, stationSource);
|
||||
const existing = stationMarkers.get(key);
|
||||
if (existing) {
|
||||
existing.pkt = pkt;
|
||||
existing.lat = lat;
|
||||
@@ -1888,7 +1903,8 @@ var mapWindow = window;
|
||||
} else if (prevPoint) {
|
||||
prevPoint.tsMs = tsMs;
|
||||
}
|
||||
pruneAprsEntry(call, existing, mapHistoryCutoffMs());
|
||||
existing.call = call;
|
||||
pruneAprsEntry(key, existing, mapHistoryCutoffMs());
|
||||
if (aprsMap && existing.marker && !T.decodeHistoryReplayActive) {
|
||||
existing.marker.setLatLng([lat, lon]);
|
||||
existing.marker.setPopupContent(buildAprsPopupHtml(call, lat, lon, info, pkt));
|
||||
@@ -1899,7 +1915,8 @@ var mapWindow = window;
|
||||
track: null,
|
||||
trackHistory: [{ lat, lon, tsMs }],
|
||||
trackPoints: [nextPoint],
|
||||
type: "aprs",
|
||||
type: stationSource,
|
||||
call,
|
||||
pkt,
|
||||
lat,
|
||||
lon,
|
||||
@@ -1908,9 +1925,9 @@ var mapWindow = window;
|
||||
symbolCode,
|
||||
rigIds: new Set(msgRigId ? [msgRigId] : [])
|
||||
};
|
||||
stationMarkers.set(call, entry);
|
||||
pruneAprsEntry(call, entry, mapHistoryCutoffMs());
|
||||
if (entry.visibleInHistoryWindow) ensureAprsMarker(call, entry);
|
||||
stationMarkers.set(key, entry);
|
||||
pruneAprsEntry(key, entry, mapHistoryCutoffMs());
|
||||
if (entry.visibleInHistoryWindow) ensureAprsMarker(key, entry);
|
||||
if (aprsMap) scheduleDecodeMapMaintenance();
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user