Project Plan - fm-rds-tx
1. Mission
Create a Go-based UKW/FM stereo transmitter with RDS that can run on standard Linux systems and, where feasible, on Raspberry Pi / SBC-class hardware.
Primary design goals:
- reliable CPU implementation first
- clean architecture for real-time DSP
- optional CUDA path for selected heavy DSP blocks
- practical control interfaces for audio, RDS, frequency, and TX settings
- portability across x86_64 Linux and ARM Linux
2. Non-negotiable constraints
Legal / operational
- Use only inside the licensed event window and within the license conditions.
- No assumption that software-only limiting of TX power is sufficient; real RF power control depends on hardware.
- Emissions, pilot level, deviation, RDS injection, harmonics/spurs, and filtering must be measured on real equipment.
- The software must support safe defaults and explicit operator confirmation for potentially risky RF settings.
Technical
- Go as the primary implementation language.
- Linux is the primary target.
- Raspberry Pi / ARM compatibility must be preserved for the CPU path.
- CUDA is optional and must be runtime-detectable, never mandatory.
3. High-level architecture
Audio In ─┐
├─> audio preprocessing ─> stereo encoder ─┐
RDS In ──┘ ├─> MPX generator ─> output backend ─> exciter / SDR / DAC
│
Config/API/CLI ───────────────────────────────────────┘
Core components:
- Audio ingest
- file / playlist input
- live PCM input
- optional network audio input later
- Audio preprocessing
- sample-rate conversion
- gain staging
- limiter / clipper (careful, broadcast chain quality matters)
- optional pre-emphasis
- Stereo encoder
- L+R baseband
- 19 kHz pilot
- L-R double-sideband suppressed-carrier at 38 kHz
- RDS encoder
- PS, RT, PI, PTY, TA/TP basics
- group scheduler
- 57 kHz subcarrier insertion
- MPX generator
- combine mono/stereo + pilot + RDS
- deviation / injection calibration hooks
- Output backend
- IQ output for SDR-based transmit chain
- composite/MPX output if supported by hardware
- test file output for offline analysis
- Control plane
- config file
- CLI
- HTTP API
- optional small web UI later
4. Proposed phased roadmap
Phase 0 - Research & requirements freeze
Deliverables:
- supported hardware/backend shortlist
- target sample rates and timing model
- legal/operational assumption document
- MVP feature freeze
Tasks:
- decide primary output mode:
- SDR IQ output
- direct MPX/composite output
- both
- shortlist hardware targets:
- Linux PC + SDR
- Raspberry Pi 4/5 + SDR
- other ARM SBC
- define minimum RDS feature set for MVP
- define exact operator parameters and safe ranges
Open question:
- which TX hardware/exciter are we driving?
- HackRF / Pluto / Lime / bladeRF / RTL-SDR not suitable for TX / custom DAC / external exciter input
Phase 1 - Repository bootstrap
Deliverables:
- initial Go module
- package layout
- build/test/lint scripts
- baseline docs
Suggested structure:
cmd/fmrtx/
internal/audio/
internal/dsp/
internal/stereo/
internal/rds/
internal/mpx/
internal/output/
internal/control/
internal/config/
internal/platform/
internal/telemetry/
docs/
examples/
scripts/
Tasks:
- initialize module
- define config schema
- add structured logging
- add benchmark harness for DSP blocks
- add deterministic test vectors where possible
Phase 2 - CPU MVP pipeline
Goal: produce valid offline MPX / IQ output from known inputs.
Deliverables:
- stereo encoder MVP
- RDS encoder MVP
- MPX combiner
- WAV/file output for verification
- offline spectrum / pilot / RDS validation tooling
Tasks:
- implement audio resampler path
- generate 19 kHz pilot with stable phase
- implement L+R and L-R paths
- implement 57 kHz RDS subcarrier generation
- implement basic group 0A / 2A support
- build verification scripts for:
- pilot amplitude
- stereo separation sanity
- RDS subcarrier presence
- modulation headroom checks
Acceptance criteria:
- generated composite can be decoded by reference analysis tools
- RDS PS and RadioText can be recovered reliably from recorded output
- CPU use acceptable on x86 Linux for real-time operation
Phase 3 - Real-time output backend
Goal: real-time transmit-capable software chain.
Deliverables:
- live audio input path
- real-time scheduler/buffering
- first real output backend
- underrun/overrun telemetry
Backend options to evaluate:
- SoapySDR backend for broad SDR support
- Vendor-native backend for selected hardware where latency/control is better
- Composite audio backend for hardware exciters that accept MPX input
Tasks:
- define backend abstraction
- implement timing-safe ring buffers
- implement frequency/config control surfaces
- expose operator-safe start/stop + status endpoints
Acceptance criteria:
- stable continuous real-time output for multi-hour tests
- no audible glitches under expected load
- backend reports lock/state/errors cleanly
Phase 4 - Broadcast chain quality work
Deliverables:
- gain staging and limiter improvements
- pre-emphasis options
- calibration tools
- monitoring/telemetry dashboards or endpoints
Tasks:
- tune audio processing to avoid ugly overmodulation
- add regional pre-emphasis selection if needed
- add composite level calibration workflow
- add spectrum snapshots / diagnostics hooks
Acceptance criteria:
- operator can set conservative legal operating point
- composite behavior measurable and repeatable
Phase 5 - CUDA acceleration (optional)
Goal: accelerate only the blocks that are actually worth offloading.
Candidates:
- FIR/filter banks
- resampling kernels
- vector mixing / waveform generation at higher rates
- possibly stereo/RDS composite generation if profiling justifies it
Rules:
- CPU path remains canonical and fully functional
- CUDA path selected at runtime
- identical output targets within defined tolerance
- no CUDA dependency on ARM/SBC builds
Tasks:
- benchmark CPU hotspots first
- define clean Go <-> native/CUDA boundary
- prototype one block at a time
- compare latency overhead vs throughput gain
Acceptance criteria:
- measurable real benefit on supported GPU hosts
- no regression for non-CUDA systems
Phase 6 - SBC / Raspberry Pi optimization
Deliverables:
- tuned CPU profile for ARM
- deployment docs
- systemd service example
Tasks:
- test on Pi 4/5 or equivalent ARM SBC
- reduce allocation churn and GC pressure
- use fixed-size buffers where practical
- document thermal/load expectations
Acceptance criteria:
- stable real-time operation on target SBC profile for chosen backend and sample rate
5. Interfaces to expose
Audio input
MVP:
- local WAV/PCM file
- local audio device or pipe input
Later:
- RTP/UDP
- Icecast/HTTP pull
- JACK/PipeWire integration
RDS input/control
MVP:
- static config for PI, PS, PTY
- dynamic RadioText update API
- TA/TP flags
Later:
- event/queue-based RDS scheduler
- external automation bridge
TX / RF controls
Important: actual supported parameters depend on hardware backend.
Expose where meaningful:
- center frequency
- sample rate
- output gain / drive level
- stereo enable/disable
- pilot level
- RDS injection level
- pre-emphasis mode
- backend device selection
Be careful with naming:
- use drive level / output gain in software
- do not promise true absolute RF power control unless hardware can measure/control it
6. Risks and unknowns
- Hardware backend choice dominates complexity
- the whole design changes depending on SDR vs composite exciter
- Raspberry Pi real-time headroom may be tight
- especially at higher sample rates and with extra DSP
- CUDA may be unnecessary for MVP
- profiling may show good CPU performance already
- Broadcast-quality audio processing is deeper than “just encode stereo”
- limiter/clipper/processing can become a whole subproject
- Regulatory and RF compliance live outside the codebase too
- filters, clocks, linearity, and measurement gear matter
7. Recommended MVP definition
Ship this first:
- Linux CLI daemon in Go
- CPU-only path
- stereo MPX generation
- basic RDS (PS + RT + PI + PTY)
- file output + one real hardware backend
- config file + HTTP status/control API
- test/analysis tooling for composite validation
Defer until proven necessary:
- CUDA
- fancy web UI
- advanced audio processing
- many hardware backends at once
8. Immediate next steps
- Decide the first output backend/hardware target.
- Freeze MVP feature list.
- Bootstrap the Go module and package skeleton.
- Implement offline composite generation first.
- Add verification tooling before real TX tests.
- Only then bring up live hardware output.
9. Suggested first milestone
Milestone A: Offline stereo + RDS composite generator
Definition of done:
- takes WAV input + simple RDS config
- emits composite/baseband output file
- reference tools can decode pilot + stereo + RDS correctly
- architecture is ready for later real-time backend insertion
This is the safest and smartest place to start.