import { aprsAgeText, aprsCategoryLabel, aprsHexBytes, aprsPacketCategory, collapseAprsDuplicates, normalizeAprsPacket, renderAprsInfo, renderLocalAprsSymbol } from "./chunk-M2I6DH4X.js"; import { hostCore, hostState } from "./chunk-KL66PICH.js"; // src/plugins/hf-aprs.ts var hfAprsWindow = window; var escapeHfAprsHtml = (input) => hostCore.escapeMapHtml(input); var hfAprsStatus = document.getElementById("hf-aprs-status"); var hfAprsPacketsEl = document.getElementById("hf-aprs-packets"); var hfAprsFilterInput = document.getElementById("hf-aprs-filter"); var hfAprsOnlyPosBtn = document.getElementById("hf-aprs-only-pos-btn"); var hfAprsHideCrcBtn = document.getElementById("hf-aprs-hide-crc-btn"); var hfAprsCollapseDupBtn = document.getElementById("hf-aprs-collapse-dup-btn"); var hfAprsTotalCountEl = document.getElementById("hf-aprs-total-count"); var hfAprsVisibleCountEl = document.getElementById("hf-aprs-visible-count"); var hfAprsLatestSeenEl = document.getElementById("hf-aprs-latest-seen"); var hfAprsFilterText = ""; var hfAprsPacketHistory = []; var hfAprsOnlyPos = false; var hfAprsHideCrc = false; var hfAprsCollapseDup = false; var hfAprsTypeFilter = "all"; function currentHfAprsHistoryRetentionMs() { return typeof hfAprsWindow.getDecodeHistoryRetentionMs === "function" ? hfAprsWindow.getDecodeHistoryRetentionMs() : 24 * 60 * 60 * 1e3; } function pruneHfAprsPacketHistory() { const cutoffMs = Date.now() - currentHfAprsHistoryRetentionMs(); hfAprsPacketHistory = hfAprsPacketHistory.filter((pkt) => (pkt._tsMs ?? 0) >= cutoffMs); } function scheduleHfAprsHistoryRender() { if (typeof hfAprsWindow.trxScheduleUiFrameJob === "function") { hfAprsWindow.trxScheduleUiFrameJob("hf-aprs-history", () => { renderHfAprsHistory(); }); return; } renderHfAprsHistory(); } function hfAprsDistanceText(pkt) { if (hostState.serverLat == null || hostState.serverLon == null || pkt.lat == null || pkt.lon == null) return ""; const distKm = hostCore.haversineKm(hostState.serverLat, hostState.serverLon, pkt.lat, pkt.lon); if (!Number.isFinite(distKm)) return ""; if (distKm < 1) return `${Math.round(distKm * 1e3)} m from TRX`; return `${distKm.toFixed(1)} km from TRX`; } function hfAprsFilterMatch(pkt) { if (hfAprsOnlyPos && (pkt.lat == null || pkt.lon == null)) return false; if (hfAprsHideCrc && !pkt.crcOk) return false; if (hfAprsTypeFilter !== "all" && aprsPacketCategory(pkt) !== hfAprsTypeFilter) return false; if (!hfAprsFilterText) return true; const haystack = [ pkt.srcCall, pkt.destCall, pkt.path, pkt.info, pkt.type, pkt.lat != null ? pkt.lat.toFixed(4) : "", pkt.lon != null ? pkt.lon.toFixed(4) : "", aprsPacketCategory(pkt) ].filter(Boolean).join(" ").toUpperCase(); return haystack.includes(hfAprsFilterText); } function hfAprsVisiblePackets() { const packets = hfAprsCollapseDup ? collapseHfAprsDuplicates(hfAprsPacketHistory) : hfAprsPacketHistory; return packets.filter(hfAprsFilterMatch); } var collapseHfAprsDuplicates = collapseAprsDuplicates; function updateHfAprsSummary() { const visible = hfAprsVisiblePackets(); if (hfAprsTotalCountEl) { hfAprsTotalCountEl.textContent = `${hfAprsPacketHistory.length} total`; } if (hfAprsVisibleCountEl) { hfAprsVisibleCountEl.textContent = `${visible.length} shown`; } if (hfAprsLatestSeenEl) { const latest = hfAprsPacketHistory[0]; if (!latest) { hfAprsLatestSeenEl.textContent = "No packets yet"; } else { hfAprsLatestSeenEl.textContent = `${latest.srcCall} ${aprsAgeText(latest._tsMs)}`; } } } function updateHfAprsChipState() { document.querySelectorAll("[id^='hf-aprs-type-']").forEach((btn) => { btn.classList.toggle("active", btn.id === `hf-aprs-type-${hfAprsTypeFilter}`); }); hfAprsOnlyPosBtn?.classList.toggle("active", hfAprsOnlyPos); hfAprsHideCrcBtn?.classList.toggle("active", hfAprsHideCrc); hfAprsCollapseDupBtn?.classList.toggle("active", hfAprsCollapseDup); } function renderHfAprsRow(pkt, isFresh) { const row = document.createElement("div"); row.className = "aprs-packet"; if (!pkt.crcOk) row.classList.add("aprs-packet-crc"); if (isFresh) row.classList.add("aprs-packet-new"); const ts = pkt._ts || (/* @__PURE__ */ new Date()).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" }); const age = aprsAgeText(pkt._tsMs); const category = aprsPacketCategory(pkt); const categoryLabel = aprsCategoryLabel(category); const categoryClass = `aprs-badge aprs-badge-type aprs-badge-type-${category}`; const pathBadge = pkt.path ? `${escapeHfAprsHtml(pkt.path)}` : ""; const crcBadge = pkt.crcOk ? "" : 'CRC Fail'; const hfBadge = 'HF'; const symbolHtml = renderLocalAprsSymbol(pkt, escapeHfAprsHtml); const posLink = pkt.lat != null && pkt.lon != null ? `${pkt.lat.toFixed(4)}, ${pkt.lon.toFixed(4)}` : ""; const distance = hfAprsDistanceText(pkt); const qrzHref = `https://qrzcq.com/call/${encodeURIComponent(pkt.srcCall || "")}`; row.innerHTML = `
${ts}` + hfBadge + symbolHtml + `${escapeHfAprsHtml(pkt.srcCall ?? "")}>${escapeHfAprsHtml(pkt.destCall || "")}${escapeHfAprsHtml(categoryLabel)}` + pathBadge + crcBadge + `
${escapeHfAprsHtml(age)}` + (distance ? `${escapeHfAprsHtml(distance)}` : "") + `${escapeHfAprsHtml(pkt.type || "--")}
${renderAprsInfo(pkt)}` + (posLink ? `${posLink}` : "") + `
` + (pkt.lat != null && pkt.lon != null ? `` : "") + (pkt.lat != null && pkt.lon != null ? `` : "") + `QRZ
Details
Source${escapeHfAprsHtml(pkt.srcCall || "--")}Destination${escapeHfAprsHtml(pkt.destCall || "--")}Type${escapeHfAprsHtml(pkt.type || "--")}Path${escapeHfAprsHtml(pkt.path || "--")}Age${escapeHfAprsHtml(age)}CRC${pkt.crcOk ? "OK" : "Failed"}Position${pkt.lat != null && pkt.lon != null ? `${pkt.lat.toFixed(5)}, ${pkt.lon.toFixed(5)}` : "--"}Info${escapeHfAprsHtml(pkt.info || "--")}Info Bytes${escapeHfAprsHtml(aprsHexBytes(pkt.info_bytes))}
`; row.querySelectorAll("[data-aprs-map]").forEach((el) => { el.addEventListener("click", (evt) => { evt.preventDefault(); const raw = el.dataset.aprsMap ?? ""; const [lat, lon] = raw.split(",").map(Number); if (hfAprsWindow.navigateToAprsMap && typeof lat === "number" && typeof lon === "number" && Number.isFinite(lat) && Number.isFinite(lon)) { hfAprsWindow.navigateToAprsMap(lat, lon); } }); }); const copyBtn = row.querySelector("[data-aprs-copy]"); if (copyBtn) { copyBtn.addEventListener("click", () => { void (async () => { const raw = copyBtn.dataset.aprsCopy ?? ""; try { const clipboard = Reflect.get(navigator, "clipboard"); if (clipboard) { await clipboard.writeText(raw); hostCore.showHint("Coordinates copied", 1200); } } catch { hostCore.showHint("Copy failed", 1500); } })(); }); } return row; } function renderHfAprsHistory() { pruneHfAprsPacketHistory(); if (!hfAprsPacketsEl) { updateHfAprsSummary(); updateHfAprsChipState(); return; } const visible = hfAprsVisiblePackets(); const fragment = document.createDocumentFragment(); for (const [index, packet] of visible.entries()) { fragment.appendChild(renderHfAprsRow(packet, index === 0)); } hfAprsPacketsEl.replaceChildren(fragment); updateHfAprsSummary(); updateHfAprsChipState(); } function resetHfAprsHistoryView() { if (hfAprsPacketsEl) hfAprsPacketsEl.innerHTML = ""; hfAprsPacketHistory = []; renderHfAprsHistory(); } function pruneHfAprsHistoryView() { pruneHfAprsPacketHistory(); renderHfAprsHistory(); } 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(); scheduleHfAprsHistoryRender(); } function normalizeServerHfAprsPacket(pkt) { return normalizeAprsPacket(pkt, hfAprsWindow.getDecodeRigMeta?.() ?? null); } function onServerHfAprsBatch(packets) { if (!Array.isArray(packets) || packets.length === 0) return; if (hfAprsStatus) hfAprsStatus.textContent = "Receiving"; const normalized = []; for (const pkt of packets) { const next = normalizeServerHfAprsPacket(pkt); 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" }); normalized.push(next); } normalized.reverse(); hfAprsPacketHistory = normalized.concat(hfAprsPacketHistory); pruneHfAprsPacketHistory(); scheduleHfAprsHistoryRender(); } var hfAprsDecodeToggleBtn = document.getElementById("hf-aprs-decode-toggle-btn"); hfAprsDecodeToggleBtn?.addEventListener("click", () => { void (async () => { try { await hfAprsWindow.takeSchedulerControlForDecoderDisable?.(hfAprsDecodeToggleBtn); await hostCore.postPath("/toggle_hf_aprs_decode"); } catch (e) { console.error("HF APRS toggle failed", e); } })(); }); document.getElementById("settings-clear-hf-aprs-history")?.addEventListener("click", () => { void (async () => { if (!await hfAprsWindow.trxUi.confirm({ title: "Clear HF APRS history?", message: "All stored HF APRS packets will be permanently removed.", confirmLabel: "Clear history" })) return; try { await hostCore.postPath("/clear_hf_aprs_decode"); resetHfAprsHistoryView(); } catch (e) { console.error("HF APRS history clear failed", e); } })(); }); if (hfAprsOnlyPosBtn) { hfAprsOnlyPosBtn.addEventListener("click", () => { hfAprsOnlyPos = !hfAprsOnlyPos; renderHfAprsHistory(); }); } if (hfAprsHideCrcBtn) { hfAprsHideCrcBtn.addEventListener("click", () => { hfAprsHideCrc = !hfAprsHideCrc; renderHfAprsHistory(); }); } if (hfAprsCollapseDupBtn) { hfAprsCollapseDupBtn.addEventListener("click", () => { hfAprsCollapseDup = !hfAprsCollapseDup; renderHfAprsHistory(); }); } ["all", "position", "message", "weather", "telemetry", "other"].forEach((type) => { const btn = document.getElementById(`hf-aprs-type-${type}`); if (!btn) return; btn.addEventListener("click", () => { hfAprsTypeFilter = type; renderHfAprsHistory(); }); }); if (hfAprsFilterInput) { hfAprsFilterInput.addEventListener("input", () => { hfAprsFilterText = hfAprsFilterInput.value.trim().toUpperCase(); renderHfAprsHistory(); }); } function onServerHfAprs(pkt) { if (hfAprsStatus) hfAprsStatus.textContent = "Receiving"; addHfAprsPacket(normalizeServerHfAprsPacket(pkt)); } renderHfAprsHistory(); window.trxPluginRuntime.registerDecoder({ id: "hf_aprs", onMessage: onServerHfAprs, onBatch: onServerHfAprsBatch, restore: onServerHfAprsBatch, reset: resetHfAprsHistoryView, prune: pruneHfAprsHistoryView });