[debug](trx-backend-soapysdr,trx-frontend-http): add RDS diagnostics

Add server-side debug log when RDS data is decoded (PI, PS, PTY).
Extend the RDS panel with active mode, frame counter, and a raw JSON
dump of the last spectrum frame (bins excluded) to help diagnose why
RDS remains absent from the SSE stream.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
2026-02-28 09:31:24 +01:00
parent 88e0be0cdd
commit 855d21fd8a
4 changed files with 25 additions and 3 deletions
@@ -350,6 +350,7 @@ function syncTopBarAccess() {
let overviewDrawPending = false; let overviewDrawPending = false;
let lastSpectrumData = null; let lastSpectrumData = null;
let rdsFrameCount = 0;
let lastControl; let lastControl;
let lastTxEn = null; let lastTxEn = null;
let lastHasTx = true; let lastHasTx = true;
@@ -3099,6 +3100,7 @@ function startSpectrumStreaming() {
} }
try { try {
lastSpectrumData = JSON.parse(evt.data); lastSpectrumData = JSON.parse(evt.data);
rdsFrameCount++;
pushOverviewWaterfallFrame(lastSpectrumData); pushOverviewWaterfallFrame(lastSpectrumData);
refreshCenterFreqDisplay(); refreshCenterFreqDisplay();
scheduleSpectrumDraw(); scheduleSpectrumDraw();
@@ -3125,6 +3127,7 @@ function stopSpectrumStreaming() {
} }
spectrumDrawPending = false; spectrumDrawPending = false;
lastSpectrumData = null; lastSpectrumData = null;
rdsFrameCount = 0;
overviewWaterfallRows = []; overviewWaterfallRows = [];
overviewWaterfallPushCount = 0; overviewWaterfallPushCount = 0;
_wfResetOffscreen(); _wfResetOffscreen();
@@ -3155,6 +3158,7 @@ function updateRdsPsOverlay(rds) {
// RDS debug panel // RDS debug panel
const statusEl = document.getElementById("rds-status"); const statusEl = document.getElementById("rds-status");
const modeEl = document.getElementById("rds-mode");
const piEl = document.getElementById("rds-pi"); const piEl = document.getElementById("rds-pi");
const psEl = document.getElementById("rds-ps"); const psEl = document.getElementById("rds-ps");
const ptyEl = document.getElementById("rds-pty"); const ptyEl = document.getElementById("rds-pty");
@@ -3162,6 +3166,11 @@ function updateRdsPsOverlay(rds) {
const rawEl = document.getElementById("rds-raw"); const rawEl = document.getElementById("rds-raw");
if (!statusEl) return; if (!statusEl) return;
// Always show the current mode, frame counter, and a sanitised spectrum snapshot
if (modeEl) modeEl.textContent = document.getElementById("mode")?.value || "--";
const framesEl = document.getElementById("rds-frames");
if (framesEl) framesEl.textContent = String(rdsFrameCount);
if (!rds) { if (!rds) {
statusEl.textContent = "No signal"; statusEl.textContent = "No signal";
statusEl.className = "rds-value rds-no-signal"; statusEl.className = "rds-value rds-no-signal";
@@ -3169,7 +3178,10 @@ function updateRdsPsOverlay(rds) {
psEl.textContent = "--"; psEl.textContent = "--";
ptyEl.textContent = "--"; ptyEl.textContent = "--";
ptyNameEl.textContent = "--"; ptyNameEl.textContent = "--";
rawEl.textContent = "--"; if (rawEl && lastSpectrumData) {
const { bins: _b, ...rest } = lastSpectrumData;
rawEl.textContent = JSON.stringify(rest, null, 2);
}
return; return;
} }
@@ -3344,7 +3356,7 @@ function drawSpectrum(data) {
ctx.stroke(); ctx.stroke();
ctx.restore(); ctx.restore();
// Peak markers for easier snap-tune targeting. // ── Peak markers for easier snap-tune targeting ──────────────────────────
const markerPeaks = visibleSpectrumPeakIndices(data); const markerPeaks = visibleSpectrumPeakIndices(data);
if (markerPeaks.length > 0) { if (markerPeaks.length > 0) {
ctx.save(); ctx.save();
@@ -274,12 +274,14 @@
<div id="subtab-rds" class="sub-tab-panel" style="display:none;"> <div id="subtab-rds" class="sub-tab-panel" style="display:none;">
<div class="rds-grid"> <div class="rds-grid">
<div class="rds-field"><span class="rds-label">Status</span><span id="rds-status" class="rds-value rds-no-signal">No signal</span></div> <div class="rds-field"><span class="rds-label">Status</span><span id="rds-status" class="rds-value rds-no-signal">No signal</span></div>
<div class="rds-field"><span class="rds-label">Active mode</span><span id="rds-mode" class="rds-value">--</span></div>
<div class="rds-field"><span class="rds-label">Frames received</span><span id="rds-frames" class="rds-value">0</span></div>
<div class="rds-field"><span class="rds-label">PI</span><span id="rds-pi" class="rds-value">--</span></div> <div class="rds-field"><span class="rds-label">PI</span><span id="rds-pi" class="rds-value">--</span></div>
<div class="rds-field"><span class="rds-label">PS</span><span id="rds-ps" class="rds-value rds-ps">--</span></div> <div class="rds-field"><span class="rds-label">PS</span><span id="rds-ps" class="rds-value rds-ps">--</span></div>
<div class="rds-field"><span class="rds-label">PTY</span><span id="rds-pty" class="rds-value">--</span></div> <div class="rds-field"><span class="rds-label">PTY</span><span id="rds-pty" class="rds-value">--</span></div>
<div class="rds-field"><span class="rds-label">PTY Name</span><span id="rds-pty-name" class="rds-value">--</span></div> <div class="rds-field"><span class="rds-label">PTY Name</span><span id="rds-pty-name" class="rds-value">--</span></div>
</div> </div>
<div class="rds-raw-label">Raw JSON</div> <div class="rds-raw-label">Raw JSON (last spectrum frame)</div>
<pre id="rds-raw" class="rds-raw">--</pre> <pre id="rds-raw" class="rds-raw">--</pre>
</div> </div>
<div id="subtab-map" class="sub-tab-panel" style="display:none;"> <div id="subtab-map" class="sub-tab-panel" style="display:none;">
@@ -123,6 +123,11 @@ body {
input.status-input, select.status-input { width: 100%; padding: 0.45rem 0.5rem; font-size: 1rem; border: 1px solid var(--border-light); border-radius: 6px; background: var(--input-bg); color: var(--text); } input.status-input, select.status-input { width: 100%; padding: 0.45rem 0.5rem; font-size: 1rem; border: 1px solid var(--border-light); border-radius: 6px; background: var(--input-bg); color: var(--text); }
#mode { height: var(--control-height); } #mode { height: var(--control-height); }
#freq { font-family: 'DSEG14 Classic', monospace; font-size: 2rem; padding: 0.5rem 0.6rem; letter-spacing: 0.05em; text-align: center; } #freq { font-family: 'DSEG14 Classic', monospace; font-size: 2rem; padding: 0.5rem 0.6rem; letter-spacing: 0.05em; text-align: center; }
#freq:disabled,
#center-freq:disabled {
opacity: 1;
-webkit-text-fill-color: currentColor;
}
#center-freq { color: var(--wavelength-fg); } #center-freq { color: var(--wavelength-fg); }
.controls-row { .controls-row {
display: grid; display: grid;
@@ -460,6 +460,9 @@ impl RigCat for SoapySdrRig {
.channel_dsps .channel_dsps
.get(self.primary_channel_idx) .get(self.primary_channel_idx)
.and_then(|dsp| dsp.lock().ok().and_then(|d| d.rds_data())); .and_then(|dsp| dsp.lock().ok().and_then(|d| d.rds_data()));
if let Some(ref r) = rds {
tracing::debug!("RDS: pi={:?} ps={:?} pty={:?}({})", r.pi, r.program_service, r.pty, r.pty_name.as_deref().unwrap_or("?"));
}
Some(SpectrumData { Some(SpectrumData {
bins, bins,
center_hz: self.center_hz.max(0) as u64, center_hz: self.center_hz.max(0) as u64,