Go-based FM stereo transmitter with RDS, Windows-first and cross-platform
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

16KB

fm-rds-tx — DSP Signal Chain & Konfiguration

Übersicht

fm-rds-tx ist ein broadcast-konformer FM-Stereo-MPX-Encoder mit RDS für PlutoSDR/SoapySDR. Die DSP-Kette folgt dem Industriestandard (Omnia, Orban, Stereo Tool) mit Clip-Filter-Clip- Architektur und ITU-R BS.412 MPX Power Limiting.


Signalkette

┌─────────────────────────────────────────────────────────────────┐
│  AUDIO INPUT                                                     │
│  S16LE Stereo PCM via stdin (ffmpeg) oder interner Tongenerator  │
└──────────────────────────┬──────────────────────────────────────┘
                           │
                           ▼
┌──────────────────────────────────────────────────────────────────┐
│  STAGE 1: Pre-Emphasis + Band-Limiting (pro Kanal L/R)           │
│                                                                   │
│  Audio × gain ──→ Pre-Emphasis (50µs EU / 75µs US)               │
│    ──→ 15kHz LPF (8th-order Chebyshev Type I, 0.5dB Ripple)     │
│    ──→ 19kHz Notch (Q=15, double-cascade)                        │
│                                                                   │
│  Frequenzantwort (verifiziert):                                   │
│    10kHz: +0.1dB (flat)     15kHz: -0.2dB                        │
│    17kHz: -21dB             18.5kHz: -40dB                        │
│    19kHz: -155dB (tot)      22kHz: -51dB                          │
└──────────────────────────┬──────────────────────────────────────┘
                           │
                           ▼
┌──────────────────────────────────────────────────────────────────┐
│  STAGE 2: Drive + Kompression + Clip₁                            │
│                                                                   │
│  × OutputDrive                                                    │
│    ──→ StereoLimiter (5ms Attack / 200ms Release, ceiling)       │
│    ──→ HardClip₁ (ceiling)                                       │
│                                                                   │
│  Der Limiter komprimiert die Dynamik (bringt Average hoch).      │
│  Der Clip fängt Peaks die der Limiter's Attack verpasst.         │
│  "Slow-to-fast Progression" — Broadcast-Standard.                │
└──────────────────────────┬──────────────────────────────────────┘
                           │
                           ▼
┌──────────────────────────────────────────────────────────────────┐
│  STAGE 3: Cleanup LPF + Clip₂ (Overshoot-Kompensator)           │
│                                                                   │
│  ──→ 15kHz LPF (8th-order Chebyshev, identisch zu Stage 1)      │
│  ──→ HardClip₂ (ceiling)                                         │
│                                                                   │
│  Der zweite LPF-Pass entfernt Clip₁-Harmonische.                │
│  Clip₂ fängt die LPF-Overshoots (IIR-Filter erzeugen diese).   │
│  Doppelter LPF-Pass verdoppelt die Guard-Band-Dämpfung.         │
└──────────────────────────┬──────────────────────────────────────┘
                           │
                           ▼
┌──────────────────────────────────────────────────────────────────┐
│  STAGE 4: Stereo-Encode                                          │
│                                                                   │
│  L/R ──→ Mono: (L+R)/2         (0–15kHz Baseband)               │
│      ──→ Stereo: (L-R)/2 × sin(38kHz)  (23–53kHz DSB-SC)       │
│      ──→ Pilot: sin(19kHz)     (phase-locked, kohärent)         │
│      ──→ RDS Carrier: sin(57kHz) (3× Pilot-Phase, kohärent)     │
└──────────────────────────┬──────────────────────────────────────┘
                           │
                           ▼
┌──────────────────────────────────────────────────────────────────┐
│  STAGE 5: Composite Clip + Schutzfilter                          │
│                                                                   │
│  Audio-MPX (Mono + Stereo-Sub)                                   │
│    ──→ HardClip₃ (ceiling)     — finale Deviations-Kontrolle    │
│    ──→ 19kHz Notch (Q=10, double) — Clip-Harmonische bei Pilot  │
│    ──→ 57kHz Notch (Q=10, double) — Clip-Harmonische bei RDS    │
│                                                                   │
│  Guard Bands (total, 2× LPF + Notches):                         │
│    19kHz: >-80dB broadband, >-90dB exakt                         │
│    57kHz: >-100dB                                                 │
│    (Omnia 11 Spezifikation: >80dB — wir sind on par)            │
└──────────────────────────┬──────────────────────────────────────┘
                           │
                           ▼
┌──────────────────────────────────────────────────────────────────┐
│  STAGE 6: BS.412 MPX Power Limiter (optional)                    │
│                                                                   │
│  ──→ × BS412 Gain                                                │
│                                                                   │
│  Rolling 60-Sekunden RMS-Messung auf dem Audio-Composite.        │
│  Langsamer Gain-Regler (2s Attack / 5s Release).                 │
│  Zieht Pilot+RDS-Power automatisch vom Budget ab.               │
│  Pflicht in CH, DE, NL, FR für lizenzierte FM-Sender.            │
│  ~5dB Lautheitsverlust bei 0 dBr Threshold.                     │
└──────────────────────────┬──────────────────────────────────────┘
                           │
                           ▼
┌──────────────────────────────────────────────────────────────────┐
│  STAGE 7: Pilot + RDS Injection (fixe Amplitude)                 │
│                                                                   │
│  composite = audioMPX                                             │
│            + pilotLevel × sin(19kHz)     — IMMER 9%              │
│            + rdsInjection × rdsWaveform  — IMMER 4%              │
│                                                                   │
│  Pilot und RDS werden NIE geclippt, NIE gefiltert, NIE vom      │
│  BS.412-Limiter berührt. Konstante Amplitude, immer.             │
│                                                                   │
│  Peak Composite = ceiling + pilotLevel + rdsInjection ≈ 113%    │
│  (Standard-Broadcast-Praxis — Pilot/RDS werden von den meisten  │
│   Regulierungsbehörden aus dem Modulationslimit ausgenommen)     │
└──────────────────────────┬──────────────────────────────────────┘
                           │
                           ▼
┌──────────────────────────────────────────────────────────────────┐
│  STAGE 8: FM-Modulation                                          │
│                                                                   │
│  Split-Rate: Composite @ 228kHz ──→ FMUpsampler ──→ IQ @ 2.28MHz│
│  maxDeviation × mpxGain = effektive Deviation                    │
│  composite=1.0 → ±75kHz Deviation (bei mpxGain=1.0)             │
│                                                                   │
│  Ausgabe: IQ-Samples (float32) an PlutoSDR via libiio            │
└──────────────────────────────────────────────────────────────────┘

Konfiguration

Audio

Parameter Typ Default Beschreibung
audio.gain float 1.0 Eingangsverstärkung vor Pre-Emphasis. 1.0 = unity.
audio.inputPath string "” WAV-Datei als Quelle (leer = stdin oder Tongenerator)

Empfehlung: gain: 1.0. Pegel-Kontrolle über outputDrive.

FM — Audio-Processing

Parameter Typ Default Bereich Beschreibung
outputDrive float 0.5 0–10 Eingangsverstärkung vor Limiter/Clip. Bestimmt wie aggressiv Limiter und nachfolgende Clip-Stufen arbeiten.
limiterEnabled bool true Aktiviert nur die StereoLimiter-Stufe (5ms/200ms) im L/R-Pfad. Die Hard-Clip-Stufen bleiben aktiv.
limiterCeiling float 1.0 0–2 Maximum-Amplitude für Audio L/R und Composite. 1.0 = ±75kHz.
preEmphasisTauUS float 50 0/50/75 Pre-Emphasis Zeitkonstante. 50µs = Europa/CH, 75µs = USA, 0 = aus.

outputDrive im Detail:

Der Drive bestimmt den Klangcharakter, nicht die Lautstärke (wenn BS.412 aktiv ist):

Drive Effekt Einsatz
1–2 Wenig Kompression, dynamisch, sauber Klassik, Jazz, Wortbeiträge
3–4 Moderate Kompression, ausgewogen Empfohlen für die meisten Formate
5–7 Aggressive Kompression, dichter Sound Pop/Rock-Formatradio
8–10 Maximale Dichte, hörbare Clip-Artefakte Nicht empfohlen

Empfehlung: outputDrive: 3.0 für sauberen, broadcast-fähigen Sound.

FM — Pilot & RDS

Parameter Typ Default Bereich Beschreibung
pilotLevel float 0.09 0–0.2 19kHz Pilot-Amplitude. Direkte Prozentangabe von ±75kHz. 0.09 = 9% = ITU-Standard.
rdsInjection float 0.04 0–0.15 RDS-Amplitude. Direkte Prozentangabe. 0.04 = 4%. Waveform ist unity-normalisiert.
stereoEnabled bool true Stereo-Encode an/aus. Aus = nur Mono (L+R)/2, kein Pilot.

Empfehlung: pilotLevel: 0.09, rdsInjection: 0.04. Nicht ändern ausser es gibt einen guten Grund.

Pilot und RDS sind unabhängig vom OutputDrive — sie bleiben immer bei der konfigurierten Amplitude, egal wie hart das Audio komprimiert wird.

FM — BS.412 (ITU-R MPX Power Limiter)

Parameter Typ Default Beschreibung
bs412Enabled bool false Aktiviert den BS.412 MPX Power Limiter. Pflicht in CH, DE, NL, FR.
bs412ThresholdDBr float 0 Power-Limit in dBr. 0 = Standard. +3 = relaxiert.

Was BS.412 macht: Begrenzt die durchschnittliche MPX-Leistung über ein rollendes 60-Sekunden-Fenster. Reduziert die Audio-Amplitude langsam wenn die Power den Threshold überschreitet. Pilot und RDS werden automatisch vom Power-Budget abgezogen.

Effekt auf den Sound:

  • ~5dB Lautheitsverlust bei 0 dBr mit aggressiver Kompression
  • Weniger Verlust bei dynamischerem Material
  • OutputDrive beeinflusst bei aktivem BS.412 nur den Klangcharakter, nicht die Lautheit

Empfehlung: bs412Enabled: true, bs412ThresholdDBr: 0 für BAKOM-Compliance.

FM — Hardware-Kalibrierung

Parameter Typ Default Bereich Beschreibung
mpxGain float 1.0 0.1–5 Skaliert die FM-Deviation (nicht den Composite!). Kompensiert DAC/SDR-Hardware-Faktoren.
maxDeviationHz float 75000 0–150000 Maximale FM-Deviation in Hz. 75000 = Standard.
compositeRateHz int 228000 Interne DSP-Sample-Rate. 228000 = 12×19kHz (optimal für Pilot-Kohärenz).

MpxTool-Kalibrierung:

  1. mpxGain: 1.0 setzen (keine Skalierung)
  2. MpxTool Ref Level so einstellen dass Pilot Level = 9.0% anzeigt
  3. Für PlutoSDR typisch: Ref Level ca. -7.5 dBFS
  4. Einmal kalibrieren, nie wieder anfassen

Empfehlung: mpxGain: 1.0, maxDeviationHz: 75000. Kalibrierung über MpxTool Ref Level.

RDS

Parameter Typ Default Beschreibung
rds.enabled bool true RDS an/aus
rds.pi string “1234” Programme Identification (4-stellig hex). Muss mit BAKOM-Zuteilung übereinstimmen.
rds.ps string “FMRTX” Programme Service Name (max 8 Zeichen). Stationsname auf dem Display.
rds.radioText string "” Radio Text (max 64 Zeichen). Scrolltext auf dem Display.
rds.pty int 0 Programme Type. 0=undefined, 1=News, 3=Info, 10=Pop, 15=Other Music, etc.

Backend

Parameter Typ Default Beschreibung
backend.kind string “file” "pluto" für PlutoSDR, "soapy" für SoapySDR, "file" für Dateiausgabe
backend.device string "” Device-String. PlutoSDR: "usb:" oder "ip:192.168.2.1"
backend.deviceSampleRateHz float 0 SDR-Device-Rate. 2280000 = 10× compositeRate (optimal).

Referenz-Konfiguration (BAKOM-konform, PlutoSDR)

{
  "audio": {
    "gain": 1.0
  },
  "rds": {
    "enabled": true,
    "pi": "BEEF",
    "ps": "RADIO-ZH",
    "radioText": "Ihr Zürcher Kurzradio",
    "pty": 0
  },
  "fm": {
    "frequencyMHz": 100.0,
    "stereoEnabled": true,
    "pilotLevel": 0.09,
    "rdsInjection": 0.04,
    "preEmphasisTauUS": 50,
    "outputDrive": 3.0,
    "limiterEnabled": true,
    "limiterCeiling": 1.0,
    "bs412Enabled": true,
    "bs412ThresholdDBr": 0,
    "mpxGain": 1.0,
    "compositeRateHz": 228000,
    "maxDeviationHz": 75000,
    "fmModulationEnabled": true
  },
  "backend": {
    "kind": "pluto",
    "device": "usb:",
    "deviceSampleRateHz": 2280000
  },
  "control": {
    "listenAddress": "127.0.0.1:8088"
  }
}

Audio-Streaming (Produktionsbetrieb)

ffmpeg -i http://stream-url/stream -f s16le -ar 44100 -ac 2 - | fmrtx.exe --tx --tx-auto-start --audio-stdin --audio-rate 44100 --config config.json

Hinweis: Unter Windows cmd.exe verwenden, nicht PowerShell (korrumpiert die Binary-Pipe).


Verifizierte Messwerte (MpxTool, PlutoSDR @ 100MHz)

Parameter Messung Soll
Pilot Level 9.0% 9% ✓
RDS Injection 3.4% 4% (≈, BPSK-Mittelung)
MPX Peak 105–110% 100–113% ✓
Guard Band 19kHz >-80dB >-80dB (Omnia 11: >80dB) ✓
Audio Bandwidth flat bis 15kHz 15kHz ✓