Go-based FM stereo transmitter with RDS, Windows-first and cross-platform
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Jan Svabenik 93cdcab8d8 feat: add hardware TX mode with PlutoSDR and SoapySDR drivers 1 kuukausi sitten
..
NOTES.md docs: bootstrap fm rds transmitter project plan 1 kuukausi sitten
README.md feat: expose new DSP controls across config, control API, dry-run and docs 1 kuukausi sitten
config.plutosdr.json feat: add hardware TX mode with PlutoSDR and SoapySDR drivers 1 kuukausi sitten
config.sample.json refactor: remove unused input sample rate config 1 kuukausi sitten

README.md

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.

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

Available endpoints:

  • GET /healthz
  • GET /status
  • GET /dry-run
  • GET /config
  • POST /config

Current patchable runtime fields via POST /config:

  • frequencyMHz
  • outputDrive
  • toneLeftHz
  • toneRightHz
  • toneAmplitude
  • ps
  • radioText
  • preEmphasisUS
  • limiterEnabled
  • limiterCeiling

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.4.0-pre. See CHANGELOG.md and RELEASE.md.