Fix DMR decoder signal activity and parsing for dsd-fme compatibility

The DSD stderr parser had regex ordering bugs that swallowed voice and
call events as bare slot events, and only matched classic dsd output
format (not dsd-fme). Unmatched lines were silently dropped, leaving
the signal activity panel with nothing to display.

- Reorder regex checks: TG/Src before voice before slot
- Support dsd-fme comma-separated format (TG: x, Src: y)
- Make bare slot regex strict (only standalone "Slot N" lines)
- Forward unmatched DSD lines as raw events for diagnostics
- Add LISTENING state to signal activity panel for raw output
- Show raw decoder output text below synthesizer canvas
- Fix test mocks for find_dsd() tuple return value

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Smittix
2026-02-07 12:41:15 +00:00
parent d8c5491200
commit 3b205db329
4 changed files with 108 additions and 33 deletions

View File

@@ -146,6 +146,11 @@ function handleDmrMessage(msg) {
if (mainCountEl) mainCountEl.textContent = dmrCallCount;
// Update current call display
const slotInfo = msg.slot != null ? `
<div style="display: flex; justify-content: space-between; margin-bottom: 4px;">
<span style="color: var(--text-muted);">Slot</span>
<span style="color: var(--accent-orange); font-family: var(--font-mono);">${msg.slot}</span>
</div>` : '';
const callEl = document.getElementById('dmrCurrentCall');
if (callEl) {
callEl.innerHTML = `
@@ -156,7 +161,7 @@ function handleDmrMessage(msg) {
<div style="display: flex; justify-content: space-between; margin-bottom: 4px;">
<span style="color: var(--text-muted);">Source ID</span>
<span style="color: var(--accent-cyan); font-family: var(--font-mono);">${msg.source_id}</span>
</div>
</div>${slotInfo}
<div style="display: flex; justify-content: space-between;">
<span style="color: var(--text-muted);">Time</span>
<span style="color: var(--text-primary);">${msg.timestamp}</span>
@@ -176,6 +181,10 @@ function handleDmrMessage(msg) {
} else if (msg.type === 'slot') {
// Update slot info in current call
} else if (msg.type === 'raw') {
// Raw DSD output — update last line display for diagnostics
const rawEl = document.getElementById('dmrRawOutput');
if (rawEl) rawEl.textContent = msg.text || '';
} else if (msg.type === 'status') {
const statusEl = document.getElementById('dmrStatus');
if (statusEl) {
@@ -399,6 +408,10 @@ function dmrSynthPulse(type) {
dmrEventType = 'voice';
} else if (type === 'slot' || type === 'nac') {
dmrActivityTarget = Math.max(dmrActivityTarget, 0.5);
} else if (type === 'raw') {
// Any DSD output means the decoder is alive and processing
dmrActivityTarget = Math.max(dmrActivityTarget, 0.25);
if (dmrEventType === 'idle') dmrEventType = 'raw';
}
// keepalive and status don't change visuals
@@ -412,6 +425,7 @@ function updateDmrSynthStatus() {
const labels = {
stopped: 'STOPPED',
idle: 'IDLE',
raw: 'LISTENING',
sync: 'SYNC',
call: 'CALL',
voice: 'VOICE'
@@ -419,6 +433,7 @@ function updateDmrSynthStatus() {
const colors = {
stopped: 'var(--text-muted)',
idle: 'var(--text-muted)',
raw: '#607d8b',
sync: '#00e5ff',
call: '#4caf50',
voice: '#ff9800'