# Changelog ## v0.5.0-pre Full review implementation: HW-integration readiness, unity signal path, TX engine, spectral verification. ### Architecture changes - All signal sources (pilot, RDS, stereo) now output unity-normalized signals (peak ±1.0) - Combiner gains are direct config values — no magic-number normalization (`/0.1`, `/0.05` eliminated) - Pre-emphasis moved from composite rate to audio input rate (correct signal path, efficient on SBC) - RDS encoder: `NextSample()` zero-allocation hot path, fixed-size `[104]uint8` bit buffer - PI validation moved to `config.Validate()` — fail-loud, no silent 0x1234 fallback ### New: TX Engine (`internal/app/engine.go`) - Continuous chunk-based generation loop with configurable chunk duration - Explicit `Start()`/`Stop()` — TX default is OFF - Atomic underrun/overrun counters, chunk/sample stats - Context-based cancellation with clean drain ### New: Extended SoapyDriver interface - `Start(ctx)`, `Stop(ctx)`, `Capabilities(ctx)`, `Stats()` added to driver contract - `SimulatedDriver` implements full interface with atomic runtime counters - `DeviceCaps` struct for hardware capability reporting - `RuntimeStats` struct for live telemetry ### New: Rate adaptation - `backend.deviceSampleRateHz` separates SDR device rate from internal composite rate - `Config.EffectiveDeviceRate()` resolves to device rate or falls back to composite rate ### New: Control plane upgrade - `POST /tx/start` — explicit TX start (requires `TXController`) - `POST /tx/stop` — explicit TX stop - `GET /runtime` — live engine + driver telemetry - `TXController` interface for decoupled engine control ### New: Spectral verification - Goertzel algorithm (`dsp.GoertzelEnergy`, `dsp.BandEnergy`) - Blackbox tests verify 19 kHz pilot, 38 kHz stereo, 57 kHz RDS energy presence - Blackbox tests verify suppression when stereo/RDS disabled ### New: Operator truth tests - `rds.enabled=false` → verified no 57 kHz energy - `fmModulationEnabled=false` → verified Q=0 - `limiterEnabled=false` → verified higher peaks than with limiter - `stereoEnabled=false` → verified no pilot/stereo energy ### Config changes - `preEmphasisUS` → `preEmphasisTauUS` (unambiguous: microseconds, not region) - `audio.sampleRate` → `audio.inputSampleRate` (clear: input source rate) - `backend.deviceSampleRateHz` added (0 = same as compositeRateHz) - `rds.pi` validated at load time; empty/invalid = hard error - `rds.pty` range-checked (0-31) ## v0.4.0-pre [previous changelog entries]