From abae110ecd84f8b14b6fc27493ecae8f3aa7bfe5 Mon Sep 17 00:00:00 2001 From: Stan Grams Date: Sat, 14 Mar 2026 17:56:28 +0100 Subject: [PATCH] [fix](trx-frontend-http): batch decode history replay Replay decode history in decoder-specific batches instead of feeding every message through the single-message path. This reduces per-message array churn and UI scheduling during large history loads while keeping the existing live decode behavior unchanged. Co-authored-by: OpenAI Codex Signed-off-by: Stan Grams --- .../trx-frontend-http/assets/web/app.js | 54 ++++++++++- .../assets/web/plugins/ais.js | 62 ++++++++++--- .../assets/web/plugins/aprs.js | 63 +++++++++---- .../assets/web/plugins/ft8.js | 78 ++++++++++++---- .../assets/web/plugins/hf-aprs.js | 57 ++++++++---- .../assets/web/plugins/vdes.js | 89 +++++++++++++------ .../assets/web/plugins/wspr.js | 73 +++++++++++---- 7 files changed, 364 insertions(+), 112 deletions(-) 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 8716cab..e7ea3ab 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 @@ -7325,8 +7325,44 @@ function dispatchDecodeMessage(msg) { if (msg.type === "wspr" && window.onServerWspr) window.onServerWspr(msg); } -const DECODE_HISTORY_MAX_BATCH = 12; -const DECODE_HISTORY_SLICE_BUDGET_MS = 6; +function dispatchDecodeBatch(batch) { + if (!Array.isArray(batch) || batch.length === 0) return; + const type = String(batch[0]?.type || ""); + const uniformType = batch.every((msg) => String(msg?.type || "") === type); + if (uniformType) { + if (type === "ais" && window.onServerAisBatch) { + window.onServerAisBatch(batch); + return; + } + if (type === "vdes" && window.onServerVdesBatch) { + window.onServerVdesBatch(batch); + return; + } + if (type === "aprs" && window.onServerAprsBatch) { + window.onServerAprsBatch(batch); + return; + } + if (type === "hf_aprs" && window.onServerHfAprsBatch) { + window.onServerHfAprsBatch(batch); + return; + } + if (type === "ft8" && window.onServerFt8Batch) { + window.onServerFt8Batch(batch); + return; + } + if (type === "wspr" && window.onServerWsprBatch) { + window.onServerWsprBatch(batch); + return; + } + } + for (const msg of batch) { + dispatchDecodeMessage(msg); + } +} + +const DECODE_HISTORY_MAX_BATCH = 256; +const DECODE_HISTORY_TYPE_BATCH_LIMIT = 192; +const DECODE_HISTORY_SLICE_BUDGET_MS = 10; function scheduleDecodeHistoryDrainStep(callback) { if (typeof callback !== "function") return; @@ -7343,9 +7379,19 @@ function drainDecodeHistory(buffer, index, onDone, onProgress) { : 0; let nextIndex = index; while (nextIndex < buffer.length) { - dispatchDecodeMessage(buffer[nextIndex]); + const batchStart = nextIndex; + const batchType = String(buffer[nextIndex]?.type || ""); nextIndex += 1; - if (nextIndex - index >= DECODE_HISTORY_MAX_BATCH) break; + while ( + nextIndex < buffer.length + && (nextIndex - batchStart) < DECODE_HISTORY_TYPE_BATCH_LIMIT + && (nextIndex - index) < DECODE_HISTORY_MAX_BATCH + && String(buffer[nextIndex]?.type || "") === batchType + ) { + nextIndex += 1; + } + dispatchDecodeBatch(buffer.slice(batchStart, nextIndex)); + if ((nextIndex - index) >= DECODE_HISTORY_MAX_BATCH) break; if (startedAt > 0 && (performance.now() - startedAt) >= DECODE_HISTORY_SLICE_BUDGET_MS) break; } if (typeof onProgress === "function") { diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/ais.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/ais.js index e42fa05..455933a 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/ais.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/ais.js @@ -343,6 +343,53 @@ function addAisMessage(msg) { } } +function normalizeServerAisMessage(msg) { + return { + channel: msg.channel, + message_type: msg.message_type, + mmsi: msg.mmsi, + lat: msg.lat, + lon: msg.lon, + sog_knots: msg.sog_knots, + cog_deg: msg.cog_deg, + heading_deg: msg.heading_deg, + vessel_name: msg.vessel_name, + callsign: msg.callsign, + destination: msg.destination, + ts_ms: msg.ts_ms, + }; +} + +window.onServerAisBatch = function(messages) { + if (!Array.isArray(messages) || messages.length === 0) return; + if (aisStatus) aisStatus.textContent = aisPaused ? "Paused" : "Receiving"; + const normalized = []; + for (const msg of messages) { + const next = normalizeServerAisMessage(msg); + 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", + }); + if (next.lat != null && next.lon != null && window.aisMapAddVessel) { + window.aisMapAddVessel(next); + } + normalized.push(next); + } + normalized.reverse(); + aisMessageHistory = normalized.concat(aisMessageHistory); + pruneAisMessageHistory(); + scheduleAisBarUpdate(); + if (aisPaused) { + aisBufferedWhilePaused += messages.length; + updateAisSummary(); + return; + } + scheduleAisHistoryRender(); +}; + window.pruneAisHistoryView = function() { pruneAisMessageHistory(); updateAisBar(); @@ -381,20 +428,7 @@ if (aisFilterInput) { window.onServerAis = function(msg) { if (aisStatus) aisStatus.textContent = aisPaused ? "Paused" : "Receiving"; - addAisMessage({ - channel: msg.channel, - message_type: msg.message_type, - mmsi: msg.mmsi, - lat: msg.lat, - lon: msg.lon, - sog_knots: msg.sog_knots, - cog_deg: msg.cog_deg, - heading_deg: msg.heading_deg, - vessel_name: msg.vessel_name, - callsign: msg.callsign, - destination: msg.destination, - ts_ms: msg.ts_ms, - }); + addAisMessage(normalizeServerAisMessage(msg)); }; updateAisSummary(); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/aprs.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/aprs.js index 0158a3c..44802dc 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/aprs.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/aprs.js @@ -400,6 +400,53 @@ function addAprsPacket(pkt) { scheduleAprsHistoryRender(); } +function normalizeServerAprsPacket(pkt) { + return { + receiver: window.getDecodeRigMeta ? window.getDecodeRigMeta() : null, + srcCall: pkt.src_call, + destCall: pkt.dest_call, + path: pkt.path, + info: pkt.info, + info_bytes: pkt.info_bytes, + type: pkt.packet_type, + crcOk: pkt.crc_ok, + ts_ms: pkt.ts_ms, + lat: pkt.lat, + lon: pkt.lon, + symbolTable: pkt.symbol_table, + symbolCode: pkt.symbol_code, + }; +} + +window.onServerAprsBatch = function(packets) { + if (!Array.isArray(packets) || packets.length === 0) return; + aprsStatus.textContent = aprsPaused ? "Paused" : "Receiving"; + const normalized = []; + let hasCrcOk = false; + for (const pkt of packets) { + const next = normalizeServerAprsPacket(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" }); + if (next.lat != null && next.lon != null && window.aprsMapAddStation) { + window.aprsMapAddStation(next.srcCall, next.lat, next.lon, next.info, next.symbolTable, next.symbolCode, next); + } + if (next.crcOk) hasCrcOk = true; + normalized.push(next); + } + normalized.reverse(); + aprsPacketHistory = normalized.concat(aprsPacketHistory); + pruneAprsPacketHistory(); + if (hasCrcOk) scheduleAprsBarUpdate(); + if (aprsPaused) { + aprsBufferedWhilePaused += packets.length; + updateAprsSummary(); + updateAprsChipState(); + return; + } + scheduleAprsHistoryRender(); +}; + document.getElementById("aprs-clear-btn").addEventListener("click", async () => { try { await postPath("/clear_aprs_decode"); @@ -462,21 +509,7 @@ if (aprsFilterInput) { // --- Server-side APRS decode handler --- window.onServerAprs = function(pkt) { aprsStatus.textContent = aprsPaused ? "Paused" : "Receiving"; - addAprsPacket({ - receiver: window.getDecodeRigMeta ? window.getDecodeRigMeta() : null, - srcCall: pkt.src_call, - destCall: pkt.dest_call, - path: pkt.path, - info: pkt.info, - info_bytes: pkt.info_bytes, - type: pkt.packet_type, - crcOk: pkt.crc_ok, - ts_ms: pkt.ts_ms, - lat: pkt.lat, - lon: pkt.lon, - symbolTable: pkt.symbol_table, - symbolCode: pkt.symbol_code, - }); + addAprsPacket(normalizeServerAprsPacket(pkt)); }; renderAprsHistory(); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/ft8.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/ft8.js index e3c5511..df139cc 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/ft8.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/ft8.js @@ -114,6 +114,59 @@ function addFt8Message(msg) { scheduleFt8HistoryRender(); } +function normalizeServerFt8Message(msg) { + const raw = (msg.message || "").toString(); + const locatorDetails = ft8ExtractLocatorDetails(raw); + const grids = locatorDetails.length > 0 + ? locatorDetails.map((detail) => detail.grid) + : ft8ExtractAllGrids(raw); + const station = ft8ExtractLikelyCallsign(raw); + const rfHz = normalizeFt8DisplayFreqHz(msg.freq_hz); + return { + raw, + grids, + station, + rfHz, + locatorDetails, + history: { + receiver: window.getDecodeRigMeta ? window.getDecodeRigMeta() : null, + ts_ms: msg.ts_ms, + snr_db: msg.snr_db, + dt_s: msg.dt_s, + freq_hz: Number.isFinite(rfHz) ? rfHz : msg.freq_hz, + message: msg.message, + }, + }; +} + +window.onServerFt8Batch = function(messages) { + if (!Array.isArray(messages) || messages.length === 0) return; + ft8Status.textContent = ft8Paused ? "Paused" : "Receiving"; + const normalized = []; + for (const msg of messages) { + const next = normalizeServerFt8Message(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, + }); + } + next.history._tsMs = Number.isFinite(next.history?.ts_ms) ? Number(next.history.ts_ms) : Date.now(); + normalized.push(next.history); + } + normalized.reverse(); + ft8MessageHistory = normalized.concat(ft8MessageHistory); + pruneFt8MessageHistory(); + scheduleFt8BarUpdate(); + if (ft8Paused) { + ft8BufferedWhilePaused += messages.length; + updateFt8PauseUi(); + return; + } + scheduleFt8HistoryRender(); +}; + window.pruneFt8HistoryView = function() { pruneFt8MessageHistory(); updateFt8Bar(); @@ -385,28 +438,15 @@ document.getElementById("ft8-clear-btn").addEventListener("click", async () => { // --- Server-side FT8 decode handler --- window.onServerFt8 = function(msg) { ft8Status.textContent = ft8Paused ? "Paused" : "Receiving"; - const raw = (msg.message || "").toString(); - const locatorDetails = ft8ExtractLocatorDetails(raw); - const grids = locatorDetails.length > 0 - ? locatorDetails.map((detail) => detail.grid) - : ft8ExtractAllGrids(raw); - const station = ft8ExtractLikelyCallsign(raw); - const rfHz = normalizeFt8DisplayFreqHz(msg.freq_hz); - if (grids.length > 0 && window.ft8MapAddLocator) { - window.ft8MapAddLocator(raw, grids, "ft8", station, { + const next = normalizeServerFt8Message(msg); + if (next.grids.length > 0 && window.ft8MapAddLocator) { + window.ft8MapAddLocator(next.raw, next.grids, "ft8", next.station, { ...msg, - freq_hz: rfHz, - locator_details: locatorDetails, + freq_hz: next.rfHz, + locator_details: next.locatorDetails, }); } - addFt8Message({ - receiver: window.getDecodeRigMeta ? window.getDecodeRigMeta() : null, - ts_ms: msg.ts_ms, - snr_db: msg.snr_db, - dt_s: msg.dt_s, - freq_hz: Number.isFinite(rfHz) ? rfHz : msg.freq_hz, - message: msg.message, - }); + addFt8Message(next.history); }; updateFt8PauseUi(); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/hf-aprs.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/hf-aprs.js index 51d5a86..4f22091 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/hf-aprs.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/hf-aprs.js @@ -352,6 +352,47 @@ function addHfAprsPacket(pkt) { scheduleHfAprsHistoryRender(); } +function normalizeServerHfAprsPacket(pkt) { + return { + receiver: window.getDecodeRigMeta ? window.getDecodeRigMeta() : null, + srcCall: pkt.src_call, + destCall: pkt.dest_call, + path: pkt.path, + info: pkt.info, + info_bytes: pkt.info_bytes, + type: pkt.packet_type, + crcOk: pkt.crc_ok, + ts_ms: pkt.ts_ms, + lat: pkt.lat, + lon: pkt.lon, + symbolTable: pkt.symbol_table, + symbolCode: pkt.symbol_code, + }; +} + +window.onServerHfAprsBatch = function(packets) { + if (!Array.isArray(packets) || packets.length === 0) return; + if (hfAprsStatus) hfAprsStatus.textContent = hfAprsPaused ? "Paused" : "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(); + if (hfAprsPaused) { + hfAprsBufferedWhilePaused += packets.length; + updateHfAprsSummary(); + updateHfAprsChipState(); + return; + } + scheduleHfAprsHistoryRender(); +}; + document.getElementById("hf-aprs-decode-toggle-btn")?.addEventListener("click", async () => { try { await postPath("/toggle_hf_aprs_decode"); } catch (e) { console.error("HF APRS toggle failed", e); } }); @@ -418,21 +459,7 @@ if (hfAprsFilterInput) { // --- Server-side HF APRS decode handler --- window.onServerHfAprs = function(pkt) { if (hfAprsStatus) hfAprsStatus.textContent = hfAprsPaused ? "Paused" : "Receiving"; - addHfAprsPacket({ - receiver: window.getDecodeRigMeta ? window.getDecodeRigMeta() : null, - srcCall: pkt.src_call, - destCall: pkt.dest_call, - path: pkt.path, - info: pkt.info, - info_bytes: pkt.info_bytes, - type: pkt.packet_type, - crcOk: pkt.crc_ok, - ts_ms: pkt.ts_ms, - lat: pkt.lat, - lon: pkt.lon, - symbolTable: pkt.symbol_table, - symbolCode: pkt.symbol_code, - }); + addHfAprsPacket(normalizeServerHfAprsPacket(pkt)); }; renderHfAprsHistory(); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/vdes.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/vdes.js index 027b253..a32f0ef 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/vdes.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/vdes.js @@ -273,6 +273,64 @@ function addVdesMessage(msg) { } } +function normalizeServerVdesMessage(msg) { + return { + message_type: msg.message_type, + bit_len: msg.bit_len, + raw_bytes: msg.raw_bytes, + lat: msg.lat, + lon: msg.lon, + vessel_name: msg.vessel_name, + callsign: msg.callsign, + destination: msg.destination, + message_label: msg.message_label, + session_id: msg.session_id, + source_id: msg.source_id, + destination_id: msg.destination_id, + data_count: msg.data_count, + asm_identifier: msg.asm_identifier, + ack_nack_mask: msg.ack_nack_mask, + channel_quality: msg.channel_quality, + payload_preview: msg.payload_preview, + link_id: msg.link_id, + sync_score: msg.sync_score, + sync_errors: msg.sync_errors, + phase_rotation: msg.phase_rotation, + fec_state: msg.fec_state, + ts_ms: msg.ts_ms, + }; +} + +window.onServerVdesBatch = function(messages) { + if (!Array.isArray(messages) || messages.length === 0) return; + if (vdesStatus) vdesStatus.textContent = vdesPaused ? "Paused" : "Receiving"; + const normalized = []; + for (const msg of messages) { + const next = normalizeServerVdesMessage(msg); + 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", + }); + if (next.lat != null && next.lon != null && window.vdesMapAddPoint) { + window.vdesMapAddPoint(next); + } + normalized.push(next); + } + normalized.reverse(); + vdesMessageHistory = normalized.concat(vdesMessageHistory); + pruneVdesMessageHistory(); + scheduleVdesBarUpdate(); + if (vdesPaused) { + vdesBufferedWhilePaused += messages.length; + updateVdesSummary(); + return; + } + scheduleVdesHistoryRender(); +}; + if (vdesClearBtn) { vdesClearBtn.addEventListener("click", async () => { try { @@ -305,33 +363,10 @@ if (vdesFilterInput) { window.onServerVdes = function(msg) { if (vdesStatus) vdesStatus.textContent = vdesPaused ? "Paused" : "Receiving"; - addVdesMessage({ - message_type: msg.message_type, - bit_len: msg.bit_len, - raw_bytes: msg.raw_bytes, - lat: msg.lat, - lon: msg.lon, - vessel_name: msg.vessel_name, - callsign: msg.callsign, - destination: msg.destination, - message_label: msg.message_label, - session_id: msg.session_id, - source_id: msg.source_id, - destination_id: msg.destination_id, - data_count: msg.data_count, - asm_identifier: msg.asm_identifier, - ack_nack_mask: msg.ack_nack_mask, - channel_quality: msg.channel_quality, - payload_preview: msg.payload_preview, - link_id: msg.link_id, - sync_score: msg.sync_score, - sync_errors: msg.sync_errors, - phase_rotation: msg.phase_rotation, - fec_state: msg.fec_state, - ts_ms: msg.ts_ms, - }); - if (msg.lat != null && msg.lon != null && window.vdesMapAddPoint) { - window.vdesMapAddPoint(msg); + const next = normalizeServerVdesMessage(msg); + addVdesMessage(next); + if (next.lat != null && next.lon != null && window.vdesMapAddPoint) { + window.vdesMapAddPoint(next); } }; diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/wspr.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/wspr.js index 709b30f..3b32914 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/wspr.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/wspr.js @@ -94,6 +94,56 @@ function addWsprMessage(msg) { scheduleWsprHistoryRender(); } +function normalizeServerWsprMessage(msg) { + const raw = (msg.message || "").toString(); + const grids = extractAllGrids(raw); + const station = extractLikelyCallsign(raw); + const baseHz = Number.isFinite(window.ft8BaseHz) ? Number(window.ft8BaseHz) : null; + const rfHz = Number.isFinite(msg.freq_hz) && Number.isFinite(baseHz) + ? (baseHz + Number(msg.freq_hz)) + : (Number.isFinite(msg.freq_hz) ? Number(msg.freq_hz) : null); + return { + raw, + grids, + station, + rfHz, + history: { + receiver: window.getDecodeRigMeta ? window.getDecodeRigMeta() : null, + ts_ms: msg.ts_ms, + snr_db: msg.snr_db, + dt_s: msg.dt_s, + freq_hz: msg.freq_hz, + message: raw, + }, + }; +} + +window.onServerWsprBatch = function(messages) { + if (!Array.isArray(messages) || messages.length === 0) return; + wsprStatus.textContent = wsprPaused ? "Paused" : "Receiving"; + const normalized = []; + for (const msg of messages) { + const next = normalizeServerWsprMessage(msg); + if (next.grids.length > 0 && window.ft8MapAddLocator) { + window.ft8MapAddLocator(next.raw, next.grids, "wspr", next.station, { + ...msg, + freq_hz: next.rfHz, + }); + } + next.history._tsMs = Number.isFinite(next.history?.ts_ms) ? Number(next.history.ts_ms) : Date.now(); + normalized.push(next.history); + } + normalized.reverse(); + wsprMessageHistory = normalized.concat(wsprMessageHistory); + pruneWsprMessageHistory(); + if (wsprPaused) { + wsprBufferedWhilePaused += messages.length; + updateWsprPauseUi(); + return; + } + scheduleWsprHistoryRender(); +}; + window.pruneWsprHistoryView = function() { pruneWsprMessageHistory(); renderWsprHistory(); @@ -252,27 +302,14 @@ document.getElementById("wspr-clear-btn").addEventListener("click", async () => window.onServerWspr = function(msg) { wsprStatus.textContent = wsprPaused ? "Paused" : "Receiving"; - const raw = (msg.message || "").toString(); - const grids = extractAllGrids(raw); - const station = extractLikelyCallsign(raw); - const baseHz = Number.isFinite(window.ft8BaseHz) ? Number(window.ft8BaseHz) : null; - const rfHz = Number.isFinite(msg.freq_hz) && Number.isFinite(baseHz) - ? (baseHz + Number(msg.freq_hz)) - : (Number.isFinite(msg.freq_hz) ? Number(msg.freq_hz) : null); - if (grids.length > 0 && window.ft8MapAddLocator) { - window.ft8MapAddLocator(raw, grids, "wspr", station, { + const next = normalizeServerWsprMessage(msg); + if (next.grids.length > 0 && window.ft8MapAddLocator) { + window.ft8MapAddLocator(next.raw, next.grids, "wspr", next.station, { ...msg, - freq_hz: rfHz, + freq_hz: next.rfHz, }); } - addWsprMessage({ - receiver: window.getDecodeRigMeta ? window.getDecodeRigMeta() : null, - ts_ms: msg.ts_ms, - snr_db: msg.snr_db, - dt_s: msg.dt_s, - freq_hz: msg.freq_hz, - message: raw, - }); + addWsprMessage(next.history); }; updateWsprPauseUi();