# fm-rds-tx

Go-based FM stereo transmitter with RDS. Supports ADALM-Pluto (PlutoSDR) and any SoapySDR-compatible TX device.

## Status: v0.7.0-pre — hardware bring-up milestone

### What works
- Complete DSP chain: pre-emphasis → stereo encoding → RDS (IEC 62106) → MPX → limiter → FM modulation
- Real hardware TX via SoapySDR CGO binding (PlutoSDR tested)
- Continuous TX engine with Start/Stop/Stats
- IQ resampling (composite rate → device rate)
- HTTP control plane with /tx/start, /tx/stop, /runtime
- 82 passing tests including spectral verification

### Signal path
```
Audio Source → PreEmphasis(50µs) → StereoEncoder(19k+38k) → RDS(57k)
→ MPX Combiner → Limiter → FM Modulator(±75kHz)
→ IQ Resample(228k→528k) → SoapySDR → PlutoSDR RF
```

## Build

```powershell
# Without hardware (simulation/offline only):
go build ./cmd/fmrtx
go build ./cmd/offline

# With SoapySDR hardware support (requires PothosSDR installed):
go build -tags soapy ./cmd/fmrtx
```

## Usage

### List available SDR devices
```powershell
.\fmrtx.exe --list-devices
```

### Offline IQ file generation
```powershell
.\fmrtx.exe --dry-run --dry-output build/dryrun/frame.json
go run ./cmd/offline -duration 2s -output build/offline/composite.iqf32
```

### Real TX (PlutoSDR)
```powershell
# Start with manual TX control via HTTP:
.\fmrtx.exe --tx --config docs/config.plutosdr.json

# Start with auto-TX on launch:
.\fmrtx.exe --tx --tx-auto-start --config docs/config.plutosdr.json
```

### HTTP control
```
POST http://localhost:8088/tx/start    → start transmission
POST http://localhost:8088/tx/stop     → stop transmission
GET  http://localhost:8088/runtime     → engine + driver telemetry
GET  http://localhost:8088/status      → config status
GET  http://localhost:8088/config      → full config
POST http://localhost:8088/config      → patch config (freq, RDS, etc.)
GET  http://localhost:8088/dry-run     → dry-run summary
GET  http://localhost:8088/healthz     → health check
```

## PlutoSDR notes

- Device rate: 528 kHz (PlutoSDR minimum ~521 kHz)
- IQ format: CF32 (float32 interleaved I/Q)
- Gain range: 0–89 dB (`outputDrive` 0..1 maps to 0..89 dB)
- SoapySDR driver name: `plutosdr`
- Requires: PothosSDR or SoapySDR + SoapyPlutoSDR plugin installed

## Repository layout

```text
cmd/
  fmrtx/          main CLI (--tx, --dry-run, --simulate-tx, --list-devices)
  offline/        offline IQ file generator
internal/
  app/            TX engine (continuous chunk loop) + simulated transmit
  audio/          sample types, WAV loader, resampler, tone generator
  config/         config schema, validation, PI parsing
  control/        HTTP control plane (/tx/start, /tx/stop, /runtime)
  dryrun/         JSON dry-run summaries
  dsp/            oscillator, pre-emphasis, FM modulator, limiter, Goertzel, IQ resampler
  mpx/            MPX combiner
  offline/        offline composite generation (full DSP chain)
  output/         backend abstractions (file, dummy)
  platform/       SoapyDriver interface, SoapyBackend, SimulatedDriver
  platform/soapysdr/  CGO SoapySDR native binding (build tag: soapy)
  rds/            RDS encoder (IEC 62106, CRC, differential, group scheduler)
  stereo/         stereo encoder (19 kHz pilot, 38 kHz DSB-SC)
docs/
  config.sample.json                default config
  config.plutosdr.json              PlutoSDR-specific config
  pro-runtime-hardening-workboard.md  living workboard for pro runtime hardening
scripts/
examples/
```

## Planning / workboard

For the current pro-runtime-hardening track, see:

- `docs/pro-runtime-hardening-workboard.md`

This document is the detailed working board for status tracking, confirmed findings, open decisions, verification notes, and implementation progress.

## Legal note

This project is intended only for lawful use within relevant license and regulatory constraints.
RF output, deviation, filtering, and transmitted power must be validated with proper measurement equipment.
