# 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`.
