[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);
|
if (msg.type === "wspr" && window.onServerWspr) window.onServerWspr(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
const DECODE_HISTORY_MAX_BATCH = 12;
|
function dispatchDecodeBatch(batch) {
|
||||||
const DECODE_HISTORY_SLICE_BUDGET_MS = 6;
|
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) {
|
function scheduleDecodeHistoryDrainStep(callback) {
|
||||||
if (typeof callback !== "function") return;
|
if (typeof callback !== "function") return;
|
||||||
@@ -7343,9 +7379,19 @@ function drainDecodeHistory(buffer, index, onDone, onProgress) {
|
|||||||
: 0;
|
: 0;
|
||||||
let nextIndex = index;
|
let nextIndex = index;
|
||||||
while (nextIndex < buffer.length) {
|
while (nextIndex < buffer.length) {
|
||||||
dispatchDecodeMessage(buffer[nextIndex]);
|
const batchStart = nextIndex;
|
||||||
|
const batchType = String(buffer[nextIndex]?.type || "");
|
||||||
nextIndex += 1;
|
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 (startedAt > 0 && (performance.now() - startedAt) >= DECODE_HISTORY_SLICE_BUDGET_MS) break;
|
||||||
}
|
}
|
||||||
if (typeof onProgress === "function") {
|
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() {
|
window.pruneAisHistoryView = function() {
|
||||||
pruneAisMessageHistory();
|
pruneAisMessageHistory();
|
||||||
updateAisBar();
|
updateAisBar();
|
||||||
@@ -381,20 +428,7 @@ if (aisFilterInput) {
|
|||||||
|
|
||||||
window.onServerAis = function(msg) {
|
window.onServerAis = function(msg) {
|
||||||
if (aisStatus) aisStatus.textContent = aisPaused ? "Paused" : "Receiving";
|
if (aisStatus) aisStatus.textContent = aisPaused ? "Paused" : "Receiving";
|
||||||
addAisMessage({
|
addAisMessage(normalizeServerAisMessage(msg));
|
||||||
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,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
updateAisSummary();
|
updateAisSummary();
|
||||||
|
|||||||
@@ -400,6 +400,53 @@ function addAprsPacket(pkt) {
|
|||||||
scheduleAprsHistoryRender();
|
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 () => {
|
document.getElementById("aprs-clear-btn").addEventListener("click", async () => {
|
||||||
try {
|
try {
|
||||||
await postPath("/clear_aprs_decode");
|
await postPath("/clear_aprs_decode");
|
||||||
@@ -462,21 +509,7 @@ if (aprsFilterInput) {
|
|||||||
// --- Server-side APRS decode handler ---
|
// --- Server-side APRS decode handler ---
|
||||||
window.onServerAprs = function(pkt) {
|
window.onServerAprs = function(pkt) {
|
||||||
aprsStatus.textContent = aprsPaused ? "Paused" : "Receiving";
|
aprsStatus.textContent = aprsPaused ? "Paused" : "Receiving";
|
||||||
addAprsPacket({
|
addAprsPacket(normalizeServerAprsPacket(pkt));
|
||||||
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,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
renderAprsHistory();
|
renderAprsHistory();
|
||||||
|
|||||||
@@ -114,6 +114,59 @@ function addFt8Message(msg) {
|
|||||||
scheduleFt8HistoryRender();
|
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() {
|
window.pruneFt8HistoryView = function() {
|
||||||
pruneFt8MessageHistory();
|
pruneFt8MessageHistory();
|
||||||
updateFt8Bar();
|
updateFt8Bar();
|
||||||
@@ -385,28 +438,15 @@ document.getElementById("ft8-clear-btn").addEventListener("click", async () => {
|
|||||||
// --- Server-side FT8 decode handler ---
|
// --- Server-side FT8 decode handler ---
|
||||||
window.onServerFt8 = function(msg) {
|
window.onServerFt8 = function(msg) {
|
||||||
ft8Status.textContent = ft8Paused ? "Paused" : "Receiving";
|
ft8Status.textContent = ft8Paused ? "Paused" : "Receiving";
|
||||||
const raw = (msg.message || "").toString();
|
const next = normalizeServerFt8Message(msg);
|
||||||
const locatorDetails = ft8ExtractLocatorDetails(raw);
|
if (next.grids.length > 0 && window.ft8MapAddLocator) {
|
||||||
const grids = locatorDetails.length > 0
|
window.ft8MapAddLocator(next.raw, next.grids, "ft8", next.station, {
|
||||||
? 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, {
|
|
||||||
...msg,
|
...msg,
|
||||||
freq_hz: rfHz,
|
freq_hz: next.rfHz,
|
||||||
locator_details: locatorDetails,
|
locator_details: next.locatorDetails,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
addFt8Message({
|
addFt8Message(next.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,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
updateFt8PauseUi();
|
updateFt8PauseUi();
|
||||||
|
|||||||
@@ -352,6 +352,47 @@ function addHfAprsPacket(pkt) {
|
|||||||
scheduleHfAprsHistoryRender();
|
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 () => {
|
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); }
|
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 ---
|
// --- Server-side HF APRS decode handler ---
|
||||||
window.onServerHfAprs = function(pkt) {
|
window.onServerHfAprs = function(pkt) {
|
||||||
if (hfAprsStatus) hfAprsStatus.textContent = hfAprsPaused ? "Paused" : "Receiving";
|
if (hfAprsStatus) hfAprsStatus.textContent = hfAprsPaused ? "Paused" : "Receiving";
|
||||||
addHfAprsPacket({
|
addHfAprsPacket(normalizeServerHfAprsPacket(pkt));
|
||||||
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,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
renderHfAprsHistory();
|
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) {
|
if (vdesClearBtn) {
|
||||||
vdesClearBtn.addEventListener("click", async () => {
|
vdesClearBtn.addEventListener("click", async () => {
|
||||||
try {
|
try {
|
||||||
@@ -305,33 +363,10 @@ if (vdesFilterInput) {
|
|||||||
|
|
||||||
window.onServerVdes = function(msg) {
|
window.onServerVdes = function(msg) {
|
||||||
if (vdesStatus) vdesStatus.textContent = vdesPaused ? "Paused" : "Receiving";
|
if (vdesStatus) vdesStatus.textContent = vdesPaused ? "Paused" : "Receiving";
|
||||||
addVdesMessage({
|
const next = normalizeServerVdesMessage(msg);
|
||||||
message_type: msg.message_type,
|
addVdesMessage(next);
|
||||||
bit_len: msg.bit_len,
|
if (next.lat != null && next.lon != null && window.vdesMapAddPoint) {
|
||||||
raw_bytes: msg.raw_bytes,
|
window.vdesMapAddPoint(next);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -94,6 +94,56 @@ function addWsprMessage(msg) {
|
|||||||
scheduleWsprHistoryRender();
|
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() {
|
window.pruneWsprHistoryView = function() {
|
||||||
pruneWsprMessageHistory();
|
pruneWsprMessageHistory();
|
||||||
renderWsprHistory();
|
renderWsprHistory();
|
||||||
@@ -252,27 +302,14 @@ document.getElementById("wspr-clear-btn").addEventListener("click", async () =>
|
|||||||
|
|
||||||
window.onServerWspr = function(msg) {
|
window.onServerWspr = function(msg) {
|
||||||
wsprStatus.textContent = wsprPaused ? "Paused" : "Receiving";
|
wsprStatus.textContent = wsprPaused ? "Paused" : "Receiving";
|
||||||
const raw = (msg.message || "").toString();
|
const next = normalizeServerWsprMessage(msg);
|
||||||
const grids = extractAllGrids(raw);
|
if (next.grids.length > 0 && window.ft8MapAddLocator) {
|
||||||
const station = extractLikelyCallsign(raw);
|
window.ft8MapAddLocator(next.raw, next.grids, "wspr", next.station, {
|
||||||
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, {
|
|
||||||
...msg,
|
...msg,
|
||||||
freq_hz: rfHz,
|
freq_hz: next.rfHz,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
addWsprMessage({
|
addWsprMessage(next.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,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
updateWsprPauseUi();
|
updateWsprPauseUi();
|
||||||
|
|||||||
Reference in New Issue
Block a user