diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js index d9189a0..4bd3d2f 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js @@ -3986,7 +3986,7 @@ const locatorMarkers = new Map(); const decodeContactPaths = new Map(); let selectedMapQsoKey = null; const mapMarkers = new Set(); -const DEFAULT_MAP_SOURCE_FILTER = { ais: true, vdes: true, aprs: true, bookmark: false, ft8: true, wspr: true }; +const DEFAULT_MAP_SOURCE_FILTER = { ais: true, vdes: true, aprs: true, bookmark: false, ft8: true, ft4: true, wspr: true }; const mapFilter = { ...DEFAULT_MAP_SOURCE_FILTER }; const mapLocatorFilter = { phase: "band", bands: new Set() }; let mapSearchFilter = ""; @@ -4163,7 +4163,7 @@ function ensureVdesMarker(key, entry) { } function ensureDecodeLocatorMarker(entry) { - if (!aprsMap || !entry || entry.marker || !entry.grid || (entry.sourceType !== "ft8" && entry.sourceType !== "wspr")) return; + if (!aprsMap || !entry || entry.marker || !entry.grid || (entry.sourceType !== "ft8" && entry.sourceType !== "ft4" && entry.sourceType !== "wspr")) return; const bounds = maidenheadToBounds(entry.grid); if (!bounds) return; const count = Math.max(entry.stationDetails?.size || 0, entry.stations?.size || 0, 1); @@ -4245,7 +4245,7 @@ function pruneAisEntry(key, entry, cutoffMs) { function pruneLocatorEntry(key, entry, cutoffMs) { const canRenderMap = !!aprsMap && !decodeHistoryReplayActive; - if (!entry || (entry.sourceType !== "ft8" && entry.sourceType !== "wspr")) return true; + if (!entry || (entry.sourceType !== "ft8" && entry.sourceType !== "ft4" && entry.sourceType !== "wspr")) return true; if (!(entry.allStationDetails instanceof Map)) { entry.allStationDetails = entry.stationDetails instanceof Map ? new Map(entry.stationDetails) @@ -4330,6 +4330,7 @@ function pruneMapHistory() { function locatorSourceLabel(type) { if (type === "bookmark") return "Bookmarks"; if (type === "wspr") return "WSPR"; + if (type === "ft4") return "FT4"; return "FT8"; } @@ -4345,7 +4346,7 @@ function locatorFilterColor(type) { const light = lightTheme ? 42 : 56; const hue = type === "bookmark" ? hues.bookmark - : (type === "wspr" ? hues.wspr : hues.ft8); + : (type === "wspr" ? hues.wspr : (type === "ft4" ? hues.ft4 : hues.ft8)); return `hsl(${hue.toFixed(1)} ${sat}% ${light}%)`; } @@ -4470,6 +4471,7 @@ function locatorThemeHues() { return { bookmark: wrapHue(baseHue), ft8: wrapHue(peakHue), + ft4: wrapHue(peakHue + 30), wspr: wrapHue((waveHue + baseHue) / 2), bandBase: wrapHue((baseHue * 0.65) + (peakHue * 0.35)), }; @@ -4519,6 +4521,7 @@ function locatorHueForEntry(entry) { } if (entry?.sourceType === "bookmark") return hues.bookmark; if (entry?.sourceType === "wspr") return hues.wspr; + if (entry?.sourceType === "ft4") return hues.ft4; return hues.ft8; } @@ -4785,7 +4788,7 @@ function setSelectedLocatorMarker(marker) { function isLocatorOverlay(marker) { const type = marker?.__trxType; - return type === "bookmark" || type === "ft8" || type === "wspr"; + return type === "bookmark" || type === "ft8" || type === "ft4" || type === "wspr"; } function sendLocatorOverlayToBack(marker) { @@ -4916,7 +4919,7 @@ function rebuildMapLocatorFilters() { for (const entry of locatorMarkers.values()) { const sourceType = entry?.sourceType; if (!sourceType) continue; - if ((sourceType === "ft8" || sourceType === "wspr") && !entry?.visibleInHistoryWindow) continue; + if ((sourceType === "ft8" || sourceType === "ft4" || sourceType === "wspr") && !entry?.visibleInHistoryWindow) continue; availableSources.add(sourceType); const meta = entry?.bandMeta instanceof Map ? entry.bandMeta : new Map(); for (const [label, hz] of meta.entries()) { @@ -4944,7 +4947,7 @@ function rebuildMapLocatorFilters() { if (!bandMap.has(key)) mapLocatorFilter.bands.delete(key); } - const sourceItems = ["ais", "vdes", "aprs", "bookmark", "ft8", "wspr"] + const sourceItems = ["ais", "vdes", "aprs", "bookmark", "ft8", "ft4", "wspr"] .filter((key) => availableSources.has(key)) .map((key) => ({ key, @@ -4986,7 +4989,7 @@ function markerPassesLocatorFilters(marker) { function markerSearchText(marker) { const type = marker?.__trxType; - if (type === "bookmark" || type === "ft8" || type === "wspr") { + if (type === "bookmark" || type === "ft8" || type === "ft4" || type === "wspr") { const entry = locatorEntryForMarker(marker); const parts = []; if (entry?.grid) parts.push(entry.grid); @@ -5132,7 +5135,7 @@ window.clearMapMarkersByType = function(type) { return; } - if (type === "ft8" || type === "wspr") { + if (type === "ft8" || type === "ft4" || type === "wspr") { const prefix = `${type}:`; for (const [key, entry] of locatorMarkers.entries()) { if (!key.startsWith(prefix)) continue; @@ -5379,7 +5382,7 @@ function initAprsMap() { return; } - if (marker.__trxType === "bookmark" || marker.__trxType === "ft8" || marker.__trxType === "wspr") { + if (marker.__trxType === "bookmark" || marker.__trxType === "ft8" || marker.__trxType === "ft4" || marker.__trxType === "wspr") { const center = locatorMarkerCenter(marker); if (center) { setSelectedLocatorMarker(marker); @@ -5618,10 +5621,10 @@ window.navigateToMapLocator = function(grid, preferredType = null) { sizeAprsMapToViewport(); if (!aprsMap) return false; - const pref = preferredType === "wspr" ? "wspr" : (preferredType === "ft8" ? "ft8" : null); + const pref = preferredType === "wspr" ? "wspr" : (preferredType === "ft4" ? "ft4" : (preferredType === "ft8" ? "ft8" : null)); const keys = pref - ? [`${pref}:${normalizedGrid}`, `ft8:${normalizedGrid}`, `wspr:${normalizedGrid}`, `bookmark:${normalizedGrid}`] - : [`ft8:${normalizedGrid}`, `wspr:${normalizedGrid}`, `bookmark:${normalizedGrid}`]; + ? [`${pref}:${normalizedGrid}`, `ft8:${normalizedGrid}`, `ft4:${normalizedGrid}`, `wspr:${normalizedGrid}`, `bookmark:${normalizedGrid}`] + : [`ft8:${normalizedGrid}`, `ft4:${normalizedGrid}`, `wspr:${normalizedGrid}`, `bookmark:${normalizedGrid}`]; let entry = null; for (const key of keys) { entry = locatorMarkers.get(key); @@ -6160,6 +6163,7 @@ function applyMapFilter() { (type === "vdes" && mapFilter.vdes) || (type === "aprs" && mapFilter.aprs) || (type === "ft8" && mapFilter.ft8) || + (type === "ft4" && mapFilter.ft4) || (type === "wspr" && mapFilter.wspr) ); const onMap = aprsMap.hasLayer(marker); @@ -6271,7 +6275,7 @@ function rebuildDecodeContactPaths() { const stationLocators = new Map(); const directedMessages = []; for (const entry of locatorMarkers.values()) { - if (!entry || (entry.sourceType !== "ft8" && entry.sourceType !== "wspr")) continue; + if (!entry || (entry.sourceType !== "ft8" && entry.sourceType !== "ft4" && entry.sourceType !== "wspr")) continue; const grid = String(entry.grid || "").trim().toUpperCase(); if (!grid || !(entry.stationDetails instanceof Map)) continue; for (const detail of entry.stationDetails.values()) { @@ -6520,7 +6524,7 @@ window.syncBookmarkMapLocators = function(bookmarks) { window.ft8MapAddLocator = function(message, grids, type = "ft8", station = null, details = null) { if (!Array.isArray(grids) || grids.length === 0) return; - const markerType = type === "wspr" ? "wspr" : "ft8"; + const markerType = type === "wspr" ? "wspr" : (type === "ft4" ? "ft4" : "ft8"); const unique = [...new Set(grids.map((g) => String(g).toUpperCase()))]; const stationId = station && String(station).trim() ? String(station).trim().toUpperCase() : ""; const locatorDetails = new Map(); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/ft4.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/ft4.js index 7890970..9cd97dd 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/ft4.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/ft4.js @@ -132,7 +132,7 @@ window.onServerFt4Batch = function(messages) { for (const msg of messages) { const next = normalizeServerFt4Message(msg); if (next.grids.length > 0 && window.ft8MapAddLocator) { - window.ft8MapAddLocator(next.raw, next.grids, "ft8", next.station, { ...msg, freq_hz: next.rfHz, locator_details: next.locatorDetails }); + window.ft8MapAddLocator(next.raw, next.grids, "ft4", next.station, { ...msg, freq_hz: next.rfHz, locator_details: next.locatorDetails }); } next.history._tsMs = Number.isFinite(next.history?.ts_ms) ? Number(next.history.ts_ms) : Date.now(); normalized.push(next.history); @@ -183,7 +183,7 @@ window.onServerFt4 = function(msg) { if (ft4Status) ft4Status.textContent = ft4Paused ? "Paused" : "Receiving"; const next = normalizeServerFt4Message(msg); if (next.grids.length > 0 && window.ft8MapAddLocator) { - window.ft8MapAddLocator(next.raw, next.grids, "ft8", next.station, { ...msg, freq_hz: next.rfHz, locator_details: next.locatorDetails }); + window.ft8MapAddLocator(next.raw, next.grids, "ft4", next.station, { ...msg, freq_hz: next.rfHz, locator_details: next.locatorDetails }); } addFt4Message(next.history); };