# fm-rds-tx docs ## Build & Test ### Root CLI - `go test ./...` - `go run ./cmd/fmrtx -print-config` - `go run ./cmd/fmrtx -config docs/config.sample.json` - `go run ./cmd/fmrtx --dry-run --dry-output build/dryrun/frame.json` - `go run ./cmd/fmrtx --simulate-tx --simulate-output build/sim/simulated-soapy.iqf32 --simulate-duration 250ms` - `go run ./cmd/offline -duration 500ms -output build/offline/composite.iqf32` ### Audio source modes Current no-hardware sources: - generated stereo tones via config - 16-bit PCM WAV file input via `audio.inputPath` (robust chunk-scanning loader) - linear-interpolation sample-rate conversion for WAV sources - transparent tone fallback if the configured WAV source cannot be loaded ### Tone configuration The current no-hardware source can be parameterized via config: - `audio.toneLeftHz` - `audio.toneRightHz` - `audio.toneAmplitude` ### DSP chain The full signal chain from audio input to IQ output: 1. **Audio ingest** — tone generator or WAV file with linear-interpolation resampler 2. **Gain staging** — configurable audio gain 3. **Pre-emphasis** — first-order IIR high-shelf filter, configurable τ (50 µs EU / 75 µs US / 0 = off) 4. **Stereo encoder** — L+R mono, L-R on stateful 38 kHz DSB-SC subcarrier, phase-coherent 19 kHz pilot 5. **RDS encoder** — standards-grade group framing (0A/2A), CRC-10 per IEC 62106, differential encoding, 57 kHz BPSK subcarrier, group scheduler cycling PS and RadioText 6. **MPX combiner** — configurable gains for mono, stereo, pilot, and RDS components 7. **Output drive** — configurable output level scaling 8. **MPX limiter** — smooth attack/release peak limiter with hard-clip safety net 9. **FM modulator** — composite-to-IQ via phase integration, configurable ±75 kHz deviation, unit-magnitude IQ output ### Pre-emphasis FM broadcast requires pre-emphasis to boost high frequencies before transmission. The receiver applies complementary de-emphasis to restore flat response while reducing noise. - Europe/World: τ = 50 µs (`preEmphasisUS: 50`) - North America/South Korea: τ = 75 µs (`preEmphasisUS: 75`) - Disabled: `preEmphasisUS: 0` ### FM modulation modes - `fmModulationEnabled: true` — output is baseband FM-modulated IQ (I² + Q² = 1). This is what SDR transmitters expect. - `fmModulationEnabled: false` — output is raw composite MPX (I = composite, Q = 0). Useful for analysis or composite exciters. ### Split-rate mode (Pluto / HackRF) When `deviceSampleRateHz > compositeRateHz` (e.g. Pluto at 2.28 MHz, composite at 228 kHz), the engine automatically activates split-rate mode: 1. DSP chain (stereo, RDS, limiter) runs at `compositeRateHz` (228 kHz) 2. `FMUpsampler` performs FM modulation + phase-domain interpolation to `deviceSampleRateHz` 3. Hardware receives IQ at device rate This halves CPU load compared to running all DSP at device rate. Log output confirms the active mode: ``` engine: split-rate mode — DSP@228000Hz → upsample@2280000Hz (ratio 10.00) ``` When rates are equal (e.g. LimeSDR at 228 kHz), same-rate mode is used: ``` engine: same-rate mode — DSP@228000Hz ``` ### Limiter The MPX limiter prevents overmodulation by applying smooth gain reduction when the composite signal exceeds the configured ceiling. A hard clipper acts as a safety net after the limiter. - `limiterEnabled: true/false` - `limiterCeiling: 1.0` (max composite level before FM modulation) ### HTTP control surface Full API documentation: **[docs/API.md](API.md)** All major TX parameters are hot-reloadable via `POST /config` during live transmission — frequency, stereo/mono, RDS text, output drive, pilot/RDS levels, limiter. Changes take effect within 50–88ms without stopping the stream. Available endpoints: `/healthz`, `/status`, `/runtime`, `/config` (GET/POST), `/dry-run`, `/tx/start`, `/tx/stop` Control-plane HTTP server is configured with 5s read, 10s write, and 60s idle timeouts plus a 1 MiB header limit to reduce slow-client abuse. ### Internal DSP module - `cd internal` - `go test ./...` ### Examples module - `cd examples` - `go test ./...` - `go run ./soapy_simulated` ## Dry run The dry-run mode generates a synthetic, hardware-free frame summary based on the current config. It reports the active source label, pre-emphasis setting, limiter state, and FM modulation mode. The HTTP control plane also exposes `GET /dry-run` for quick inspection. ## Simulated transmit `--simulate-tx` runs the offline generator through the Soapy-oriented simulated backend path and writes an IQ-style artifact to disk. ## Offline generation `cmd/offline` generates a deterministic no-hardware IQ/composite file using the full DSP chain (pre-emphasis, stereo encoding, RDS, limiter, FM modulation). ## Release posture Current honest release posture: **pre-v1**. Recommended milestone tag: `v0.9.0`. See `CHANGELOG.md` and `RELEASE.md`.