import { aprsAgeText, aprsPacketCategory, collapseAprsDuplicates, normalizeAprsPacket, renderAprsPacketRow } from "./chunk-YAWVT7YC.js"; import { hostCore, hostState } from "./chunk-KL66PICH.js"; // src/plugins/hf-aprs.ts var hfAprsWindow = window; 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) { return renderAprsPacketRow(pkt, { fresh: isFresh, badge: "HF", distance: hfAprsDistanceText(pkt), onMap: (lat, lon) => { hfAprsWindow.navigateToAprsMap?.(lat, lon); }, onCopy: (text) => { void copyHfAprsCoords(text); } }); } async function copyHfAprsCoords(text) { try { const clipboard = Reflect.get(navigator, "clipboard"); if (!clipboard) return; await clipboard.writeText(text); hostCore.showHint("Coordinates copied", 1200); } catch { hostCore.showHint("Copy failed", 1500); } } 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 });