Add .gitignore entry for data/subghz/captures/ to prevent large
IQ recording files from being committed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Digital voice is intermittent — dsd-fme only outputs PCM during active
voice transmissions. Without input, ffmpeg never wrote the WAV header
and the browser got an empty response. Add an audio bridge thread that
feeds 100ms silence chunks during voice gaps so ffmpeg always has input
and the browser receives a continuous WAV stream. Add auto-reconnect
on the frontend if the audio stream drops while the decoder is running.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Stream decoded digital voice audio to the browser via ffmpeg pipeline
(dsd-fme 8kHz PCM → ffmpeg → 44.1kHz WAV → chunked HTTP). Persist
frequency/protocol/gain/ppm settings in localStorage so they survive
page navigation. Add bookmark system for saving and recalling frequencies.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
dsd-fme remapped several flags from classic DSD: -fp is ProVoice (not
P25), -fi is NXDN48 (not D-Star), -fv doesn't exist. This caused P25
to trigger ProVoice decoding and D-Star to trigger NXDN48. Corrected
flag table and added C4FM modulation hints for better sync reliability.
Also fixes: device panel showing "DMR" regardless of protocol, signal
activity status flip-flopping between LISTENING and IDLE, and rtl_fm
squelch chopping the bitstream mid-frame. Adds PPM correction and
relax CRC controls for fine-tuning on marginal signals.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When the backend has an active DMR session but the frontend lost track
(page refresh, broken flags causing silent running), clicking Start
returned 409 with no recovery path. Now the frontend resyncs on
"Already running" responses and checks backend status on tab activation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reverts IQ pipeline and removes syncWaterfallToFrequency calls from
pager, sensor, rtlamr, DMR, SSTV, and SSTV general modes. Waterfall
is now exclusive to listening post mode.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The dmrRawOutput div was rendering garbled box-drawing characters from
the dsd-fme ASCII art banner below the signal activity canvas. Remove
the div and filter banner lines (box-drawing chars, version info) in
the parser so they never become events.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The stream thread used a blocking readline() with no timeout, so once
DSD finished outputting its startup banner there were no more events
until actual signal activity. The frontend decayed to zero and appeared
dead. If DSD crashed, the synthesizer state never transitioned to
'stopped' so there was no visual or textual indication of failure.
- Use select() with 1s timeout on DSD stderr to avoid indefinite block
- Send heartbeat events every 3s while decoder is alive but idle
- Detect DSD crashes: capture exit code and remaining stderr, send as
'crashed' status with details and show notification to user
- Frontend properly transitions synthesizer to 'stopped' on process
death (was only happening on user-initiated stop)
- Increase idle breathing amplitude so LISTENING state is clearly
visible (0.12 +/- 0.06 vs old 0.05 +/- 0.035)
- Release device reservation on crash, not just user stop
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
DMR was missing checkDeviceAvailability/reserveDevice/releaseDevice
calls that other modes (SSTV, listening post) use, so the device
dropdown showed device 0 as available even when another process held
it. Also detect USB claim errors from rtl_fm and surface a clear
message telling the user to pick a different device.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
getBoundingClientRect on the canvas itself (sized via CSS width:100%)
instead of parentElement with arbitrary offset, preventing zero-width
canvas when flex layout timing varies.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Event-driven spring-physics bar visualization reacting to SSE events
(sync/call/voice) with HSL color coding and center-outward ripple effects.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- DMR/P25 digital voice decoder mode with DSD-FME integration
- WebSDR mode with KiwiSDR audio proxy and websocket-client support
- Listening post waterfall/spectrogram visualization and audio streaming
- Dockerfile updates for mbelib and DSD-FME build dependencies
- New tests for DMR, WebSDR, KiwiSDR, waterfall, and signal guess API
- Chart.js date adapter for time-scale axes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>