[fix](trx-frontend-http): filter ft8 and refresh rf values
Co-authored-by: OpenAI <assistant@openai.com> Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
This commit is contained in:
@@ -241,6 +241,9 @@ function render(update) {
|
|||||||
freqEl.value = formatFreq(update.status.freq.hz);
|
freqEl.value = formatFreq(update.status.freq.hz);
|
||||||
}
|
}
|
||||||
window.ft8BaseHz = update.status.freq.hz;
|
window.ft8BaseHz = update.status.freq.hz;
|
||||||
|
if (window.updateFt8RfDisplay) {
|
||||||
|
window.updateFt8RfDisplay();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!modeDirty && update.status && update.status.mode) {
|
if (!modeDirty && update.status && update.status.mode) {
|
||||||
const mode = normalizeMode(update.status.mode);
|
const mode = normalizeMode(update.status.mode);
|
||||||
@@ -785,21 +788,33 @@ window.aprsMapAddStation = function(call, lat, lon, info, symbolTable, symbolCod
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function maidenheadToLatLon(grid) {
|
function maidenheadToBounds(grid) {
|
||||||
if (!grid || grid.length < 4) return null;
|
if (!grid || grid.length < 4) return null;
|
||||||
const g = grid.toUpperCase();
|
const g = grid.toUpperCase();
|
||||||
const A = "A".charCodeAt(0);
|
const A = "A".charCodeAt(0);
|
||||||
const lon = (g.charCodeAt(0) - A) * 20 - 180 + (parseInt(g[2], 10) * 2) + 1;
|
const fieldLon = (g.charCodeAt(0) - A) * 20 - 180;
|
||||||
const lat = (g.charCodeAt(1) - A) * 10 - 90 + (parseInt(g[3], 10) * 1) + 0.5;
|
const fieldLat = (g.charCodeAt(1) - A) * 10 - 90;
|
||||||
let lonAdj = lon;
|
const squareLon = parseInt(g[2], 10) * 2;
|
||||||
let latAdj = lat;
|
const squareLat = parseInt(g[3], 10) * 1;
|
||||||
|
|
||||||
|
let lon = fieldLon + squareLon;
|
||||||
|
let lat = fieldLat + squareLat;
|
||||||
|
let lonSpan = 2;
|
||||||
|
let latSpan = 1;
|
||||||
|
|
||||||
if (g.length >= 6) {
|
if (g.length >= 6) {
|
||||||
const subLon = (g.charCodeAt(4) - A) * (5 / 60);
|
const subLon = (g.charCodeAt(4) - A) * (5 / 60);
|
||||||
const subLat = (g.charCodeAt(5) - A) * (2.5 / 60);
|
const subLat = (g.charCodeAt(5) - A) * (2.5 / 60);
|
||||||
lonAdj += subLon - (5 / 120);
|
lon += subLon;
|
||||||
latAdj += subLat - (2.5 / 120);
|
lat += subLat;
|
||||||
|
lonSpan = 5 / 60;
|
||||||
|
latSpan = 2.5 / 60;
|
||||||
}
|
}
|
||||||
return { lat: latAdj, lon: lonAdj };
|
|
||||||
|
return [
|
||||||
|
[lat, lon],
|
||||||
|
[lat + latSpan, lon + lonSpan],
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyMapFilter() {
|
function applyMapFilter() {
|
||||||
@@ -816,11 +831,14 @@ function applyMapFilter() {
|
|||||||
window.ft8MapAddLocator = function(message, grid) {
|
window.ft8MapAddLocator = function(message, grid) {
|
||||||
if (!aprsMap) initAprsMap();
|
if (!aprsMap) initAprsMap();
|
||||||
if (!aprsMap) return;
|
if (!aprsMap) return;
|
||||||
const loc = maidenheadToLatLon(grid);
|
const bounds = maidenheadToBounds(grid);
|
||||||
if (!loc) return;
|
if (!bounds) return;
|
||||||
const popupContent = `<b>${grid}</b><br>${message}`;
|
const popupContent = `<b>${grid}</b><br>${message}`;
|
||||||
const marker = L.circleMarker([loc.lat, loc.lon], {
|
const marker = L.rectangle(bounds, {
|
||||||
radius: 5, color: "#ffb020", fillColor: "#ffb020", fillOpacity: 0.85
|
color: "#ffb020",
|
||||||
|
weight: 1,
|
||||||
|
fillColor: "#ffb020",
|
||||||
|
fillOpacity: 0.25,
|
||||||
}).addTo(aprsMap).bindPopup(popupContent);
|
}).addTo(aprsMap).bindPopup(popupContent);
|
||||||
marker.__trxType = "ft8";
|
marker.__trxType = "ft8";
|
||||||
mapMarkers.add(marker);
|
mapMarkers.add(marker);
|
||||||
|
|||||||
@@ -165,6 +165,7 @@
|
|||||||
<div class="ft8-controls">
|
<div class="ft8-controls">
|
||||||
<button id="ft8-decode-toggle-btn" type="button">Enable FT8</button>
|
<button id="ft8-decode-toggle-btn" type="button">Enable FT8</button>
|
||||||
<button id="ft8-clear-btn" type="button">Clear</button>
|
<button id="ft8-clear-btn" type="button">Clear</button>
|
||||||
|
<input id="ft8-filter" class="ft8-filter" type="text" placeholder="Filter (e.g. CQ, DL4)" />
|
||||||
<small id="ft8-status" style="color:var(--text-muted);">Waiting for server decode</small>
|
<small id="ft8-status" style="color:var(--text-muted);">Waiting for server decode</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="ft8-header">
|
<div class="ft8-header">
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
// --- FT8 Decoder Plugin (server-side decode) ---
|
// --- FT8 Decoder Plugin (server-side decode) ---
|
||||||
const ft8Status = document.getElementById("ft8-status");
|
const ft8Status = document.getElementById("ft8-status");
|
||||||
const ft8MessagesEl = document.getElementById("ft8-messages");
|
const ft8MessagesEl = document.getElementById("ft8-messages");
|
||||||
|
const ft8FilterInput = document.getElementById("ft8-filter");
|
||||||
const FT8_MAX_MESSAGES = 200;
|
const FT8_MAX_MESSAGES = 200;
|
||||||
|
let ft8FilterText = "";
|
||||||
|
|
||||||
function fmtTime(tsMs) {
|
function fmtTime(tsMs) {
|
||||||
if (!tsMs) return "--:--:--";
|
if (!tsMs) return "--:--:--";
|
||||||
@@ -11,13 +13,17 @@ function fmtTime(tsMs) {
|
|||||||
function renderFt8Row(msg) {
|
function renderFt8Row(msg) {
|
||||||
const row = document.createElement("div");
|
const row = document.createElement("div");
|
||||||
row.className = "ft8-row";
|
row.className = "ft8-row";
|
||||||
|
const rawMessage = (msg.message || "").toString();
|
||||||
|
row.dataset.message = rawMessage.toUpperCase();
|
||||||
|
row.dataset.offsetHz = Number.isFinite(msg.freq_hz) ? String(msg.freq_hz) : "";
|
||||||
const snr = Number.isFinite(msg.snr_db) ? msg.snr_db.toFixed(1) : "--";
|
const snr = Number.isFinite(msg.snr_db) ? msg.snr_db.toFixed(1) : "--";
|
||||||
const dt = Number.isFinite(msg.dt_s) ? msg.dt_s.toFixed(2) : "--";
|
const dt = Number.isFinite(msg.dt_s) ? msg.dt_s.toFixed(2) : "--";
|
||||||
const baseHz = Number.isFinite(window.ft8BaseHz) ? window.ft8BaseHz : null;
|
const baseHz = Number.isFinite(window.ft8BaseHz) ? window.ft8BaseHz : null;
|
||||||
const rfHz = Number.isFinite(msg.freq_hz) && Number.isFinite(baseHz) ? (baseHz + msg.freq_hz) : null;
|
const rfHz = Number.isFinite(msg.freq_hz) && Number.isFinite(baseHz) ? (baseHz + msg.freq_hz) : null;
|
||||||
const freq = Number.isFinite(rfHz) ? rfHz.toFixed(0) : "--";
|
const freq = Number.isFinite(rfHz) ? rfHz.toFixed(0) : "--";
|
||||||
const renderedMessage = renderFt8Message(msg.message || "");
|
const renderedMessage = renderFt8Message(rawMessage);
|
||||||
row.innerHTML = `<span class="ft8-time">${fmtTime(msg.ts_ms)}</span><span class="ft8-snr">${snr}</span><span class="ft8-dt">${dt}</span><span class="ft8-freq">${freq}</span><span class="ft8-msg">${renderedMessage}</span>`;
|
row.innerHTML = `<span class="ft8-time">${fmtTime(msg.ts_ms)}</span><span class="ft8-snr">${snr}</span><span class="ft8-dt">${dt}</span><span class="ft8-freq">${freq}</span><span class="ft8-msg">${renderedMessage}</span>`;
|
||||||
|
applyFt8FilterToRow(row);
|
||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,6 +87,44 @@ function isAlphaNum(ch) {
|
|||||||
return /[A-Za-z0-9]/.test(ch);
|
return /[A-Za-z0-9]/.test(ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyFt8FilterToRow(row) {
|
||||||
|
if (!ft8FilterText) {
|
||||||
|
row.style.display = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const message = row.dataset.message || "";
|
||||||
|
row.style.display = message.includes(ft8FilterText) ? "" : "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyFt8FilterToAll() {
|
||||||
|
const rows = ft8MessagesEl.querySelectorAll(".ft8-row");
|
||||||
|
rows.forEach((row) => applyFt8FilterToRow(row));
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateFt8RowRf(row) {
|
||||||
|
const freqEl = row.querySelector(".ft8-freq");
|
||||||
|
if (!freqEl) return;
|
||||||
|
const baseHz = Number.isFinite(window.ft8BaseHz) ? window.ft8BaseHz : null;
|
||||||
|
const offset = row.dataset.offsetHz ? Number(row.dataset.offsetHz) : NaN;
|
||||||
|
if (Number.isFinite(baseHz) && Number.isFinite(offset)) {
|
||||||
|
freqEl.textContent = (baseHz + offset).toFixed(0);
|
||||||
|
} else {
|
||||||
|
freqEl.textContent = "--";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.updateFt8RfDisplay = function() {
|
||||||
|
const rows = ft8MessagesEl.querySelectorAll(".ft8-row");
|
||||||
|
rows.forEach((row) => updateFt8RowRf(row));
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ft8FilterInput) {
|
||||||
|
ft8FilterInput.addEventListener("input", () => {
|
||||||
|
ft8FilterText = ft8FilterInput.value.trim().toUpperCase();
|
||||||
|
applyFt8FilterToAll();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById("ft8-decode-toggle-btn").addEventListener("click", async () => {
|
document.getElementById("ft8-decode-toggle-btn").addEventListener("click", async () => {
|
||||||
try { await postPath("/toggle_ft8_decode"); } catch (e) { console.error("FT8 toggle failed", e); }
|
try { await postPath("/toggle_ft8_decode"); } catch (e) { console.error("FT8 toggle failed", e); }
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -224,6 +224,7 @@ small { color: var(--text-muted); }
|
|||||||
.aprs-pos:hover { text-decoration: underline; }
|
.aprs-pos:hover { text-decoration: underline; }
|
||||||
.aprs-byte { color: var(--accent-yellow); background: rgba(255, 214, 0, 0.12); border: 1px solid rgba(255, 214, 0, 0.25); border-radius: 4px; padding: 0 0.2rem; margin: 0 0.1rem; font-size: 0.78em; }
|
.aprs-byte { color: var(--accent-yellow); background: rgba(255, 214, 0, 0.12); border: 1px solid rgba(255, 214, 0, 0.25); border-radius: 4px; padding: 0 0.2rem; margin: 0 0.1rem; font-size: 0.78em; }
|
||||||
.ft8-controls { display: flex; gap: 0.6rem; align-items: center; margin-bottom: 0.75rem; }
|
.ft8-controls { display: flex; gap: 0.6rem; align-items: center; margin-bottom: 0.75rem; }
|
||||||
|
.ft8-filter { flex: 1; min-width: 10rem; }
|
||||||
.ft8-header { display: flex; gap: 0.6rem; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted); border-bottom: 1px solid var(--border); padding: 0 0 0.35rem 0; margin-bottom: 0.35rem; }
|
.ft8-header { display: flex; gap: 0.6rem; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted); border-bottom: 1px solid var(--border); padding: 0 0 0.35rem 0; margin-bottom: 0.35rem; }
|
||||||
#ft8-messages { max-height: 360px; overflow-y: auto; border: 1px solid var(--border-light); border-radius: 6px; background: var(--input-bg); font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 0.85rem; padding: 0.35rem 0.5rem; }
|
#ft8-messages { max-height: 360px; overflow-y: auto; border: 1px solid var(--border-light); border-radius: 6px; background: var(--input-bg); font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 0.85rem; padding: 0.35rem 0.5rem; }
|
||||||
.ft8-row { display: flex; gap: 0.6rem; line-height: 1.4; border-bottom: 1px solid var(--border); padding: 0.25rem 0; }
|
.ft8-row { display: flex; gap: 0.6rem; line-height: 1.4; border-bottom: 1px solid var(--border); padding: 0.25rem 0; }
|
||||||
|
|||||||
Reference in New Issue
Block a user