[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 <codex@openai.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -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") {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user