Make limiterEnabled control the stereo limiter stage only while leaving hard clipping active, and update UI/docs to distinguish saved RadioText from the active on-air value exposed at runtime.
Add an explicit fm.watermarkEnabled switch, validate fm.stereoMode values, and only persist /config snapshots after a live update succeeds. Also separate evaluation-jingle licensing behavior from watermark handling.
Extend the RDS feature set with LPS and eRT support, add initial RDS2/logo-transfer wiring, update the control UI and refresh the PlutoSDR example configuration and reference docs.
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.
Address a set of production-facing edge cases discovered during bug hunting.
Included fixes:
- make FrameQueue close handling race-safe by replacing the TOCTOU close check with a dedicated close signal channel
- relax tone frequency validation when tone amplitude is zero, and default tone amplitude to 0 to avoid unintended test-tone output
- validate PI codes consistently whenever provided, and require a PI when RDS is enabled
- harden Icecast reconnect backoff against duration overflow
- prevent duplicate hard-reload goroutines from rapid repeated ingest-save requests
- clamp BS.412 power accumulation against negative float drift before sqrt to avoid NaN gain propagation
These changes focus on shutdown safety, config correctness, reconnect robustness, and long-running DSP stability.
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.
rejectBody() returns true when the request body is acceptable and false when a body must be rejected. The TX and fault-reset handlers treated the return value the wrong way around and returned early on valid empty POST requests. This prevented actions like /tx/stop from running in the normal no-body case.
Update the handlers to only abort when rejectBody() reports an actual rejection, so empty POST control actions proceed as intended.