Add experimental single-sideband and vestigial-sideband stereo encoding
modes alongside the standard DSB-SC stereo subcarrier path.
- add encoder mode selection (DSB, SSB, VSB)
- add Hilbert-transform based quadrature path for SSB generation
- add vestigial split with low-band DSB and high-band SSB handling
- wire the mode through config, control, engine, generator, and fmrtx
- add test coverage for the SSB path
The default mode remains the standard DSB stereo encoder.
The decoder took ~70s for a 20-minute recording. Profiling revealed the
bottleneck was not the 6400-candidate cycle-offset search, but the
cepstrum filter's naive O(N²) DCT calling math.Cos() in the inner loop:
55458 STFT frames × 2 passes × 256² × math.Cos() = 7.27 billion calls
At ~20ns per call: ~145 seconds (dominated total runtime)
Fixes:
1. Precomputed cosine table: compute 256×256 = 65536 cosine values
once, then use table lookups in the inner loop. Eliminates all
math.Cos() calls from the per-frame processing.
2. Parallel cycle-offset search: 5 goroutines (one per rep offset),
each searching 1280 cycle offsets independently. The rep offsets
are fully independent — no shared state, no synchronization needed
until the final result merge.
3. Precomputed center-frame lists: instead of checking f%timeRep for
every frame in every candidate test, precompute which frames are
center frames for each rep offset. Eliminates per-frame branching.
4. Float64 PN chip arrays: convert int8 PN chips to float64 once at
startup. Eliminates int8→float64 conversion in the hot inner loop
(204 conversions × 11000 frames × 6400 candidates = 14.4 billion
avoided conversions).
Performance (20-minute recording, 55458 STFT frames):
Before: 70s (math.Cos dominated)
After: 11.5s (6x faster)
Unit test (round-trip): 20s → 1.4s (14x faster)
Note: attempted coarse/fine search (testing every 10th group offset,
then refining) but abandoned — the chi-squared metric peak is too
narrow and the coarse step missed the true peak, causing false
positives. The full 6400-candidate brute-force search is kept for
correctness; the speedup comes entirely from eliminating per-operation
overhead, not from reducing the number of operations.
Add an STFT watermark path inspired by Kirovski & Malvar, including the frequency-domain embedder/decoder, FFT support, and round-trip coverage. Wire the generator and CLI tools to use the new analysis/synthesis flow for watermark experiments on the watermark-rework branch.
Wire tone frequency, tone amplitude, and audio gain through the live control path so the UI's live-update behavior matches the engine.
This changes the generator live params to carry tone and gain values, propagates them through Engine.UpdateConfig and txBridge.UpdateConfig, and extends the control-plane patch types accordingly.
It also refines the control API behavior:
- avoid holding the server config mutex across tx.UpdateConfig()
- report live=true only when a request contains at least one genuinely live-applicable field
Together these fixes align the UI semantics with the actual runtime behavior and remove a lock hazard in the config update path.
Add a lock-free stdin PCM ingest path, streaming resampler, stereo-linked limiting and pre-MPX audio filtering, plus the engine/control wiring needed to drive live audio into TX mode. Also document the ingest API and include a helper batch script for piping ffmpeg audio into fmrtx.
All major TX parameters are now hot-swappable during transmission:
- DSP params (drive, stereo, pilot, RDS levels, limiter) via
atomic.Pointer[LiveParams], loaded once per chunk (~50ms)
- RDS text (PS, RT) via atomic.Value in encoder, applied at
RDS group boundaries (~88ms)
- TX frequency via driver.Tune(), applied between chunks
Zero locks in DSP path. HTTP handler writes atomics, run loop
reads them. Validation happens before store.
New: SoapyDriver.Tune() for live frequency changes.
New: LiveConfigUpdate/LivePatch types for type-safe patching.
New: 4 engine tests for live DSP/freq/RDS updates + validation.
Clarify that outputDrive controls only the composite signal path while PlutoSDR hardware gain stays fixed at 0 dB. Relax outputDrive validation to allow stronger composite drive during hardware tuning and update the Pluto example config accordingly.
Introduce CLI TX mode, hardware driver selection, IQ resampling to device rate, and platform-specific PlutoSDR/SoapySDR integrations. Update engine pacing for blocking hardware writes and refresh docs/release notes for the hardware-ready v0.6.0-pre milestone.