[feat](trx-rs): add FT4 decoder support
Reuse the existing ft8_lib C library (FTX_PROTOCOL_FT4) and FT8 decoder infrastructure to add FT4 decoding across the full stack. Changes: - trx-ft8: add protocol param to ft8_decoder_create; add Ft8Decoder::new_ft4() - trx-core: DecodedMessage::Ft4 variant, AUDIO_MSG_FT4_DECODE (0x14), ft4_decode_enabled/ft4_decode_reset_seq state, SetFt4DecodeEnabled/ ResetFt4Decoder commands, protocol mapping - trx-server: DecoderHistories::ft4, run_ft4_decoder (7.5s slots via now*2/15), run_background_ft4_decoder, history push/replay, decoder task spawn - trx-frontend-http: ft4_history in FrontendRuntimeContext, toggle/clear endpoints, /ft4.js route, bookmark/scheduler/background decode support, DecodeHistoryPayload ft4 field - web: ft4.js plugin (7.5s period timer, reuses FT8 CSS/map infra), FT4 subtab in index.html, app.js dispatch (onServerFt4/Batch, restoreFt4History), decode-history-worker HISTORY_GROUP_KEYS Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
This commit is contained in:
@@ -214,11 +214,13 @@ function applyAuthRestrictions() {
|
||||
"ais-clear-btn",
|
||||
"vdes-clear-btn",
|
||||
"ft8-decode-toggle-btn",
|
||||
"ft4-decode-toggle-btn",
|
||||
"wspr-decode-toggle-btn",
|
||||
"hf-aprs-decode-toggle-btn",
|
||||
"cw-auto",
|
||||
"aprs-clear-btn",
|
||||
"ft8-clear-btn",
|
||||
"ft4-clear-btn",
|
||||
"wspr-clear-btn",
|
||||
"cw-clear-btn"
|
||||
];
|
||||
@@ -2847,6 +2849,13 @@ function render(update) {
|
||||
ft8ToggleBtn.style.borderColor = ft8On ? "#00d17f" : "";
|
||||
ft8ToggleBtn.style.color = ft8On ? "#00d17f" : "";
|
||||
}
|
||||
const ft4ToggleBtn = document.getElementById("ft4-decode-toggle-btn");
|
||||
if (ft4ToggleBtn) {
|
||||
const ft4On = !!update.ft4_decode_enabled;
|
||||
ft4ToggleBtn.textContent = ft4On ? "Disable FT4" : "Enable FT4";
|
||||
ft4ToggleBtn.style.borderColor = ft4On ? "#00d17f" : "";
|
||||
ft4ToggleBtn.style.color = ft4On ? "#00d17f" : "";
|
||||
}
|
||||
const wsprToggleBtn = document.getElementById("wspr-decode-toggle-btn");
|
||||
if (wsprToggleBtn) {
|
||||
const wsprOn = !!update.wspr_decode_enabled;
|
||||
@@ -7312,6 +7321,7 @@ function updateDecodeStatus(text) {
|
||||
const aprs = document.getElementById("aprs-status");
|
||||
const cw = document.getElementById("cw-status");
|
||||
const ft8 = document.getElementById("ft8-status");
|
||||
const ft4 = document.getElementById("ft4-status");
|
||||
setModeBoundDecodeStatus(ais, ["AIS"], "Select AIS mode to decode", text);
|
||||
const vdesText = text === "Connected, listening for packets" ? "Connected, listening for bursts" : text;
|
||||
setModeBoundDecodeStatus(vdes, ["VDES"], "Select VDES mode to decode", vdesText);
|
||||
@@ -7319,6 +7329,7 @@ function updateDecodeStatus(text) {
|
||||
const cwText = text === "Connected, listening for packets" ? "Connected, listening for CW" : text;
|
||||
setModeBoundDecodeStatus(cw, ["CW", "CWR"], "Select CW mode to decode", cwText);
|
||||
if (ft8 && ft8.textContent !== "Receiving") ft8.textContent = text;
|
||||
if (ft4 && ft4.textContent !== "Receiving") ft4.textContent = text;
|
||||
}
|
||||
function dispatchDecodeMessage(msg) {
|
||||
if (msg.type === "ais" && window.onServerAis) window.onServerAis(msg);
|
||||
@@ -7327,6 +7338,7 @@ function dispatchDecodeMessage(msg) {
|
||||
if (msg.type === "hf_aprs" && window.onServerHfAprs) window.onServerHfAprs(msg);
|
||||
if (msg.type === "cw" && window.onServerCw) window.onServerCw(msg);
|
||||
if (msg.type === "ft8" && window.onServerFt8) window.onServerFt8(msg);
|
||||
if (msg.type === "ft4" && window.onServerFt4) window.onServerFt4(msg);
|
||||
if (msg.type === "wspr" && window.onServerWspr) window.onServerWspr(msg);
|
||||
}
|
||||
|
||||
@@ -7355,6 +7367,10 @@ function dispatchDecodeBatch(batch) {
|
||||
window.onServerFt8Batch(batch);
|
||||
return;
|
||||
}
|
||||
if (type === "ft4" && window.onServerFt4Batch) {
|
||||
window.onServerFt4Batch(batch);
|
||||
return;
|
||||
}
|
||||
if (type === "wspr" && window.onServerWsprBatch) {
|
||||
window.onServerWsprBatch(batch);
|
||||
return;
|
||||
@@ -7425,6 +7441,10 @@ function restoreDecodeHistoryGroup(kind, messages) {
|
||||
window.restoreFt8History(messages);
|
||||
return;
|
||||
}
|
||||
if (kind === "ft4" && window.restoreFt4History) {
|
||||
window.restoreFt4History(messages);
|
||||
return;
|
||||
}
|
||||
if (kind === "wspr" && window.restoreWsprHistory) {
|
||||
window.restoreWsprHistory(messages);
|
||||
return;
|
||||
@@ -7441,6 +7461,7 @@ function connectDecode() {
|
||||
if (window.resetAprsHistoryView) window.resetAprsHistoryView();
|
||||
if (window.resetCwHistoryView) window.resetCwHistoryView();
|
||||
if (window.resetFt8HistoryView) window.resetFt8HistoryView();
|
||||
if (window.resetFt4HistoryView) window.resetFt4HistoryView();
|
||||
if (window.resetWsprHistoryView) window.resetWsprHistoryView();
|
||||
|
||||
// Buffer live messages until history fetch settles so history always appears
|
||||
@@ -7512,7 +7533,7 @@ function connectDecode() {
|
||||
|
||||
function totalDecodeHistoryMessages(groups) {
|
||||
if (!groups || typeof groups !== "object") return 0;
|
||||
return ["ais", "vdes", "aprs", "hf_aprs", "cw", "ft8", "wspr"]
|
||||
return ["ais", "vdes", "aprs", "hf_aprs", "cw", "ft8", "ft4", "wspr"]
|
||||
.reduce((sum, key) => sum + (Array.isArray(groups[key]) ? groups[key].length : 0), 0);
|
||||
}
|
||||
|
||||
@@ -7523,7 +7544,7 @@ function connectDecode() {
|
||||
setDecodeHistoryReplayActive(true);
|
||||
updateHistoryReplayOverlay();
|
||||
}
|
||||
for (const kind of ["ais", "vdes", "aprs", "hf_aprs", "cw", "ft8", "wspr"]) {
|
||||
for (const kind of ["ais", "vdes", "aprs", "hf_aprs", "cw", "ft8", "ft4", "wspr"]) {
|
||||
const messages = groups && Array.isArray(groups[kind]) ? groups[kind] : [];
|
||||
if (messages.length === 0) continue;
|
||||
for (let index = 0; index < messages.length; index += DECODE_HISTORY_WORKER_GROUP_LIMIT) {
|
||||
|
||||
Reference in New Issue
Block a user