[feat](trx-frontend-http): add strongest decoded signal summary to map
Add a new "Strongest decoded signal" section below the existing "Longest decode paths" section on the map tab, showing the top 5 stations with the highest SNR across the current map history window. Reuses existing map-qso-card styling with SNR displayed in place of distance. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
This commit is contained in:
@@ -4789,6 +4789,7 @@ function syncDecodeContactPathVisibility() {
|
||||
ensureDecodeContactPathRendered(entry);
|
||||
}
|
||||
renderMapQsoSummary();
|
||||
renderMapSignalSummary();
|
||||
updateMapPathsAnimationClass();
|
||||
}
|
||||
|
||||
@@ -6491,6 +6492,115 @@ function renderMapQsoSummary() {
|
||||
listEl.replaceChildren(fragment);
|
||||
}
|
||||
|
||||
function renderMapSignalSummary() {
|
||||
const listEl = document.getElementById("map-signal-summary-list");
|
||||
if (!listEl) return;
|
||||
|
||||
const bestByStation = new Map();
|
||||
for (const entry of locatorMarkers.values()) {
|
||||
if (!entry || (entry.sourceType !== "ft8" && entry.sourceType !== "ft4" && entry.sourceType !== "ft2" && entry.sourceType !== "wspr")) continue;
|
||||
if (!(entry.stationDetails instanceof Map)) continue;
|
||||
for (const detail of entry.stationDetails.values()) {
|
||||
if (!Number.isFinite(detail?.snr_db)) continue;
|
||||
const station = String(detail?.source || detail?.station || "").trim().toUpperCase();
|
||||
if (!station) continue;
|
||||
const snrDb = Number(detail.snr_db);
|
||||
const tsMs = Number.isFinite(detail?.ts_ms) ? Number(detail.ts_ms) : 0;
|
||||
const prev = bestByStation.get(station);
|
||||
if (!prev || snrDb > prev.snrDb || (snrDb === prev.snrDb && tsMs > prev.tsMs)) {
|
||||
bestByStation.set(station, {
|
||||
station,
|
||||
snrDb,
|
||||
tsMs,
|
||||
grid: entry.grid,
|
||||
sourceType: entry.sourceType,
|
||||
bandLabel: bandForHz(Number(detail?.freq_hz))?.label || null,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const entries = Array.from(bestByStation.values())
|
||||
.sort((a, b) => {
|
||||
const delta = b.snrDb - a.snrDb;
|
||||
if (Math.abs(delta) > 0.001) return delta;
|
||||
return b.tsMs - a.tsMs;
|
||||
})
|
||||
.slice(0, MAP_QSO_SUMMARY_LIMIT);
|
||||
|
||||
if (entries.length === 0) {
|
||||
const empty = document.createElement("div");
|
||||
empty.className = "map-qso-summary-empty";
|
||||
empty.textContent = "No decoded signals with SNR data in the current map history.";
|
||||
listEl.replaceChildren(empty);
|
||||
return;
|
||||
}
|
||||
|
||||
const fragment = document.createDocumentFragment();
|
||||
entries.forEach((entry, index) => {
|
||||
const card = document.createElement("button");
|
||||
card.type = "button";
|
||||
card.className = "map-qso-card";
|
||||
|
||||
const head = document.createElement("div");
|
||||
head.className = "map-qso-card-head";
|
||||
|
||||
const rank = document.createElement("span");
|
||||
rank.className = "map-qso-card-rank";
|
||||
rank.textContent = `#${index + 1}`;
|
||||
head.appendChild(rank);
|
||||
|
||||
const snr = document.createElement("span");
|
||||
snr.className = "map-qso-card-distance";
|
||||
snr.textContent = `${entry.snrDb >= 0 ? "+" : ""}${entry.snrDb.toFixed(0)} dB`;
|
||||
head.appendChild(snr);
|
||||
|
||||
const body = document.createElement("div");
|
||||
body.className = "map-qso-card-body";
|
||||
|
||||
const pair = document.createElement("div");
|
||||
pair.className = "map-qso-card-pair";
|
||||
pair.textContent = entry.station;
|
||||
body.appendChild(pair);
|
||||
|
||||
const meta = document.createElement("div");
|
||||
meta.className = "map-qso-card-meta";
|
||||
|
||||
const sourceType = document.createElement("span");
|
||||
sourceType.className = "map-qso-card-pill";
|
||||
sourceType.textContent = String(entry.sourceType || "ft8").toUpperCase();
|
||||
meta.appendChild(sourceType);
|
||||
|
||||
if (entry.bandLabel) {
|
||||
const band = document.createElement("span");
|
||||
band.className = "map-qso-card-pill map-qso-card-band";
|
||||
band.style.setProperty("--band-color", locatorBandChipColor(entry.bandLabel));
|
||||
band.textContent = entry.bandLabel;
|
||||
meta.appendChild(band);
|
||||
}
|
||||
|
||||
const ageText = formatTimeAgo(Number(entry.tsMs));
|
||||
if (ageText) {
|
||||
const age = document.createElement("span");
|
||||
age.className = "map-qso-card-pill";
|
||||
age.textContent = ageText;
|
||||
meta.appendChild(age);
|
||||
}
|
||||
|
||||
body.appendChild(meta);
|
||||
|
||||
const grids = document.createElement("div");
|
||||
grids.className = "map-qso-card-grids";
|
||||
grids.textContent = entry.grid || "--";
|
||||
body.appendChild(grids);
|
||||
|
||||
card.appendChild(head);
|
||||
card.appendChild(body);
|
||||
fragment.appendChild(card);
|
||||
});
|
||||
listEl.replaceChildren(fragment);
|
||||
}
|
||||
|
||||
function buildBookmarkLocatorPopupHtml(grid, bookmarks) {
|
||||
const list = Array.isArray(bookmarks) ? bookmarks : [];
|
||||
const rows = list
|
||||
|
||||
@@ -782,6 +782,15 @@
|
||||
</div>
|
||||
<div id="map-qso-summary-list" class="map-qso-summary-list"></div>
|
||||
</section>
|
||||
<section class="map-qso-summary" aria-labelledby="map-signal-summary-title">
|
||||
<div class="map-qso-summary-head">
|
||||
<div>
|
||||
<div id="map-signal-summary-title" class="map-qso-summary-title">Strongest decoded signal</div>
|
||||
<div class="map-qso-summary-subtitle">Top 5 strongest signals in the current map history</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="map-signal-summary-list" class="map-qso-summary-list"></div>
|
||||
</section>
|
||||
</div>
|
||||
<div id="tab-settings" class="tab-panel" style="display:none;">
|
||||
<div class="sub-tab-bar">
|
||||
|
||||
Reference in New Issue
Block a user