瀏覽代碼

docs: add API reference for live TX control and hot reload

Document the HTTP API surface in detail, including runtime control, live config updates, split-rate behaviour, and the current hot-reload semantics for on-air parameter changes.
tags/v0.9.0
Jan Svabenik 1 月之前
父節點
當前提交
66ecde05de
共有 2 個文件被更改,包括 245 次插入18 次删除
  1. +220
    -0
      docs/API.md
  2. +25
    -18
      docs/README.md

+ 220
- 0
docs/API.md 查看文件

@@ -0,0 +1,220 @@
# fm-rds-tx HTTP Control API

Base URL: `http://{listenAddress}` (default `127.0.0.1:8088`)

---

## Endpoints

### `GET /healthz`

Health check.

**Response:**
```json
{"ok": true}
```

---

### `GET /status`

Current transmitter status (read-only snapshot).

**Response:**
```json
{
"service": "fm-rds-tx",
"backend": "pluto",
"frequencyMHz": 100.0,
"stereoEnabled": true,
"rdsEnabled": true,
"preEmphasisTauUS": 50,
"limiterEnabled": true,
"fmModulationEnabled": true
}
```

---

### `GET /runtime`

Live engine and driver telemetry. Only populated when TX is active.

**Response:**
```json
{
"engine": {
"state": "running",
"chunksProduced": 12345,
"totalSamples": 1408950000,
"underruns": 0,
"lastError": "",
"uptimeSeconds": 3614.2
},
"driver": {
"txEnabled": true,
"streamActive": true,
"framesWritten": 12345,
"samplesWritten": 1408950000,
"underruns": 0,
"effectiveSampleRateHz": 2280000
}
}
```

---

### `GET /config`

Full current configuration (all fields, including non-patchable).

**Response:** Complete `Config` JSON object.

---

### `POST /config`

**Live parameter update.** Changes are applied to the running TX engine immediately — no restart required. Only include fields you want to change (PATCH semantics).

**Request body:** JSON with any subset of patchable fields.

**Response:**
```json
{"ok": true, "live": true}
```

`"live": true` = changes were forwarded to the running engine.
`"live": false` = engine not active, changes saved for next start.

#### Patchable fields — DSP (applied within ~50ms)

| Field | Type | Range | Description |
|---|---|---|---|
| `frequencyMHz` | float | 65–110 | TX center frequency. Tunes hardware LO live. |
| `outputDrive` | float | 0–3 | Composite output level multiplier. |
| `stereoEnabled` | bool | | Enable/disable stereo (pilot + 38kHz subcarrier). |
| `pilotLevel` | float | 0–0.2 | 19 kHz pilot injection level. |
| `rdsInjection` | float | 0–0.15 | 57 kHz RDS subcarrier injection level. |
| `rdsEnabled` | bool | | Enable/disable RDS subcarrier. |
| `limiterEnabled` | bool | | Enable/disable MPX peak limiter. |
| `limiterCeiling` | float | 0–2 | Limiter ceiling (max composite amplitude). |

#### Patchable fields — RDS text (applied within ~88ms)

| Field | Type | Max length | Description |
|---|---|---|---|
| `ps` | string | 8 chars | Program Service name (station name on receiver display). |
| `radioText` | string | 64 chars | RadioText message (scrolling text on receiver). |

When `radioText` is updated, the RDS A/B flag toggles automatically per spec, signaling receivers to refresh their display.

#### Patchable fields — other (saved, not live-applied)

| Field | Type | Description |
|---|---|---|
| `toneLeftHz` | float | Left tone frequency (test generator). |
| `toneRightHz` | float | Right tone frequency (test generator). |
| `toneAmplitude` | float | Test tone amplitude (0–1). |
| `preEmphasisTauUS` | float | Pre-emphasis time constant. **Requires restart.** |

#### Examples

```bash
# Tune to 99.5 MHz
curl -X POST localhost:8088/config -d '{"frequencyMHz": 99.5}'

# Switch to mono
curl -X POST localhost:8088/config -d '{"stereoEnabled": false}'

# Update now-playing text
curl -X POST localhost:8088/config \
-d '{"ps": "MYRADIO", "radioText": "Artist - Song Title"}'

# Reduce power + disable limiter
curl -X POST localhost:8088/config \
-d '{"outputDrive": 0.8, "limiterEnabled": false}'

# Full update
curl -X POST localhost:8088/config -d '{
"frequencyMHz": 101.3,
"outputDrive": 2.2,
"stereoEnabled": true,
"pilotLevel": 0.041,
"rdsInjection": 0.021,
"rdsEnabled": true,
"limiterEnabled": true,
"limiterCeiling": 1.0,
"ps": "PIRATE",
"radioText": "Broadcasting from the attic"
}'
```

#### Error handling

Invalid values return `400 Bad Request` with a descriptive message:
```bash
curl -X POST localhost:8088/config -d '{"frequencyMHz": 200}'
# → 400: frequencyMHz out of range (65-110)
```

---

### `POST /tx/start`

Start transmission. Requires `--tx` mode with hardware.

**Response:**
```json
{"ok": true, "action": "started"}
```

**Errors:**
- `405` if not POST
- `503` if no TX controller (not in `--tx` mode)
- `409` if already running

---

### `POST /tx/stop`

Stop transmission.

**Response:**
```json
{"ok": true, "action": "stopped"}
```

---

### `GET /dry-run`

Generate a synthetic frame summary without hardware. Useful for config verification.

**Response:** `FrameSummary` JSON with mode, rates, source info, preview samples.

---

## Live update architecture

All live updates are **lock-free** in the DSP path:

| What | Mechanism | Latency |
|---|---|---|
| DSP params | `atomic.Pointer[LiveParams]` loaded once per chunk | ≤ 50ms |
| RDS text | `atomic.Value` in encoder, read at group boundary | ≤ 88ms |
| TX frequency | `atomic.Pointer` in engine, `driver.Tune()` between chunks | ≤ 50ms |

No mutex, no channel, no allocation in the real-time path. The HTTP goroutine writes atomics, the DSP goroutine reads them.

## Parameters that require restart

These cannot be hot-reloaded (they affect DSP pipeline structure):

- `compositeRateHz` — changes sample rate of entire DSP chain
- `deviceSampleRateHz` — changes hardware rate / upsampler ratio
- `maxDeviationHz` — changes FM modulator scaling
- `preEmphasisTauUS` — changes filter coefficients
- `rds.pi` / `rds.pty` — rarely change, baked into encoder init
- `audio.inputPath` — audio source selection
- `backend.kind` / `backend.device` — hardware selection

+ 25
- 18
docs/README.md 查看文件

@@ -52,6 +52,26 @@ FM broadcast requires pre-emphasis to boost high frequencies before transmission
- `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.
@@ -61,24 +81,11 @@ The MPX limiter prevents overmodulation by applying smooth gain reduction when t

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

### Internal DSP module
- `cd internal`


Loading…
取消
儲存