Czym jest modulacja FM?
W modulacji częstotliwościowej (FM — Frequency Modulation) informacja jest zakodowana w częstotliwości chwilowej. Gdy sygnał audio rośnie — częstotliwość nośnej rośnie powyżej nominalnej. Gdy spada — częstotliwość maleje. Amplituda nośnej pozostaje stała.
Dlaczego FM brzmi lepiej niż AM?
- Odporność na zakłócenia — szumy atmosferyczne i interferencje wpływają na amplitudę sygnału, nie na częstotliwość. FM jest na nie naturalnie odporna
- Efekt capture — gdy dwa nadajniki nadają na tej samej częstotliwości, odbiornik FM „chwyta" silniejszy i całkowicie tłumi słabszy. W AM oba sygnały mieszają się i powstaje kakofonia
- Szerokie pasmo audio — 15 kHz pasma vs 5 kHz w AM. Słychać pełne brzmienie muzyki, nie tylko mowę
- Stereo — FM broadcast obsługuje stereo dzięki pilotowi 19 kHz i składowej L−R. AM jest monofoniczne
Parametry sygnału FM broadcast
| Wartość | |
|---|---|
| Zakres częstotliwości | 87,5 – 108 MHz |
| Modulacja | Wideband FM (WFM) |
| Dewiacja | ±75 kHz |
| Szerokość kanału | 200 kHz |
| Pilot stereo | 19 kHz subcarrier |
| RDS | 57 kHz subcarrier |
| De-emphasis | 50 µs (Europa) / 75 µs (USA) |
Maksymalne odchylenie częstotliwości od nominalnej to dewiacja — w europejskim FM broadcast wynosi ±75 kHz. Kanał zajmuje 200 kHz, co daje miejsce na sygnał audio, pilot stereo i dane RDS.
Widmo sygnału FM — co jest w środku
Sygnał FM broadcast to nie tylko dźwięk mono. Po demodulacji FM w paśmie podstawowym widać cztery składowe:
| Składowa | Częstotliwość | Opis |
|---|---|---|
| L+R (mono) | 0–15 kHz | sygnał monofoniczny — kompatybilny z każdym odbiornikiem |
| Pilot | 19 kHz | ton referencyjny — informuje odbiornik o transmisji stereo |
| L−R (stereo) | 23–53 kHz | różnica kanałów, modulowana wokół 2×pilot = 38 kHz |
| RDS | 57 kHz | dane tekstowe (nazwa stacji, PI code, typ programu), modulowane wokół 3×pilot |
Dlaczego suma i różnica?
Stereo FM wprowadzono w 1961 roku, gdy miliony odbiorników monofonicznych były już w użyciu. Kompatybilność wsteczna była warunkiem koniecznym — standard nie mógł wymagać wymiany odbiorników.
Gdyby nadawać kanały lewy i prawy jako dwa osobne sygnały
Odbiornik stereo demoduluje obie składowe i odtwarza oryginalne kanały z prostych przekształceń algebraicznych:
Konsekwencje tej decyzji są odczuwalne do dziś.
Jak działa demodulator FM
Demodulacja FM to proces odzyskiwania sygnału audio z modulowanej fali nośnej. W SDR składa się z dwóch części: toru analogowego w sprzęcie i cyfrowego w oprogramowaniu.
Tor analogowy
Poniższy diagram pokazuje przykładowy tor analogowy z podwójną przemianą częstotliwości (superheterodyna). To klasyczna architektura — RTL-SDR i Airspy działają podobnie — ale wcale nie jedyna możliwa. Prostsze układy SDR stosują tylko jedną przemianę lub konwertują sygnał bezpośrednio do baseband (direct conversion). Różnice wpływają na poziom szumów, zakres częstotliwości i poziom artefaktów (np. DC spike w centrum pasma przy direct conversion).
graph LR
A["Antena"] --> B["LNA\n(wzmacniacz\nniskoszumowy)"]
B --> C["Mikser 1\n+ heterodyna 1\n→ IF1"]
C --> D["Filtr IF1\n(ceramiczny)"]
D --> E["Mikser 2\n+ heterodyna 2\n→ IF2 / baseband"]
E --> F["ADC\n(8–12 bit)"]
F --> G["Próbki IQ\ndo komputera"]
Kluczowa granica: za ADC kończy się analogowe przetwarzanie sygnału — dalej wszystko dzieje się w oprogramowaniu.
Tor cyfrowy — łańcuch demodulacji
Łańcuch demodulacji — blok po bloku
Diagram powyżej pokazuje klasyczny demodulator kwadraturowy. Sygnał z ADC to strumień par próbek I i Q — razem tworzą sygnał zespolony
Celem demodulatora jest odzyskanie chwilowej częstotliwości sygnału — tempa zmian fazy. Tor I i tor Q rozchodzą się na dwie ścieżki. Każdy jest osobno różniczkowany — to daje
Po demodulatorze sygnał zawiera cały multipleks FM — mono, pilot 19 kHz, stereo i RDS. Filtr dolnoprzepustowy 0–15 kHz przepuszcza tylko składową mono, odcinając resztę. Bez niego ton pilota 19 kHz byłby słyszalny w głośniku.
De-emphasis kompensuje preemfazę nadajnika — nadajnik celowo wzmacnia wysokie częstotliwości, żeby przebić szum. Odbiornik stosuje odwrotny filtr (τ = 50 µs w Europie, 75 µs w USA), przywracając naturalny balans brzmienia.
Odbiornik FM w GNU Radio
GNU Radio pozwala zbudować działający odbiornik FM blok po bloku — każdy element łańcucha demodulacji z poprzedniej sekcji staje się osobnym blokiem w flowgraphu.
Flowgraph — ręczna demodulacja
Zamiast gotowego bloku WBFM Receive, rozbijamy demodulację na elementy składowe — dokładnie tak, jak na diagramie powyżej:
[Osmocom Source] → [Low Pass Filter] → [Quadrature Demod] → [Low Pass Filter] → [FM Deemphasis] → [Rational Resampler] → [Audio Sink]
Pierwszy filtr LPF odcina sąsiednie kanały FM i zmniejsza sample rate (decimacja ×10). Quadrature Demod to odpowiednik mnożników i sumatora z diagramu — oblicza chwilową częstotliwość z próbek IQ. Drugi filtr LPF (0–15 kHz) przepuszcza tylko mono, odcinając pilot i stereo. De-emphasis przywraca naturalny balans częstotliwości. Resampler dopasowuje sample rate do karty dźwiękowej.

Plik projektu GNU Radio
Gotowy flowgraph do otwarcia w GNU Radio Companion — wszystkie parametry (sample rate, gain, decimacja, de-emphasis) są ustawione w pliku:
options:
parameters:
author: robertolechowski.com
catch_exceptions: 'True'
category: '[GRC Hier Blocks]'
cmake_opt: ''
comment: 'Demodulator FM mono — reczna demodulacja IQ'
copyright: ''
description: 'SDR #3 — odbior FM broadcast z rozbitym torem demodulacji'
gen_cmake: 'On'
gen_linking: dynamic
generate_options: qt_gui
hier_block_src_path: '.:'
id: fm_demod_manual
max_nouts: '0'
output_language: python
placement: (0,0)
qt_qss_theme: ''
realtime_scheduling: ''
run: 'True'
run_command: '{python} -u {filename}'
run_options: prompt
sizing_mode: fixed
thread_safe_setters: ''
title: 'FM Demod Manual (mono)'
window_size: (1000,1000)
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [8, 8]
rotation: 0
state: enabled
blocks:
- name: samp_rate
id: variable
parameters:
comment: ''
value: 2.4e6
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [8, 160]
rotation: 0
state: enabled
- name: freq
id: variable
parameters:
comment: ''
value: 98e6
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [8, 240]
rotation: 0
state: enabled
- name: decimation
id: variable
parameters:
comment: ''
value: '10'
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [8, 320]
rotation: 0
state: enabled
- name: quad_rate
id: variable
parameters:
comment: ''
value: samp_rate / decimation
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [8, 400]
rotation: 0
state: enabled
- name: audio_rate
id: variable
parameters:
comment: ''
value: '48000'
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [8, 480]
rotation: 0
state: enabled
- name: max_dev
id: variable
parameters:
comment: ''
value: 75e3
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [8, 560]
rotation: 0
state: enabled
# --- Source ---
- name: osmosdr_source
id: osmosdr_source
parameters:
args: rtl=0
sample_rate: samp_rate
freq: freq
gain: '30'
if_gain: '20'
bb_gain: '20'
ant: ''
bandwidth: '0'
comment: ''
corr: '0'
dc_offset_mode: '0'
freq_corr: '0'
iq_balance_mode: '0'
maxoutbuf: '0'
minoutbuf: '0'
nchan: '1'
num_mboards: '1'
type: fc32
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [56, 700]
rotation: 0
state: enabled
# --- LPF kanalowy (100 kHz + decimacja x10) ---
- name: lpf_channel
id: low_pass_filter
parameters:
beta: '6.76'
comment: 'LPF kanalowy — odcina sasiednie kanaly FM'
cutoff_freq: 100e3
decim: decimation
gain: '1'
interp: '1'
maxoutbuf: '0'
minoutbuf: '0'
samp_rate: samp_rate
type: fir_filter_ccf
width: 25e3
win: window.WIN_HAMMING
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [320, 700]
rotation: 0
state: enabled
# --- Quadrature Demod ---
- name: quad_demod
id: analog_quadrature_demod_cf
parameters:
comment: 'Demodulator kwadraturowy — serce toru FM'
gain: quad_rate / (2 * math.pi * max_dev)
maxoutbuf: '0'
minoutbuf: '0'
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [584, 740]
rotation: 0
state: enabled
# --- LPF audio (0-15 kHz) ---
- name: lpf_audio
id: low_pass_filter
parameters:
beta: '6.76'
comment: 'LPF audio — przepuszcza mono 0-15 kHz, odcina pilot i stereo'
cutoff_freq: 15e3
decim: '1'
gain: '1'
interp: '1'
maxoutbuf: '0'
minoutbuf: '0'
samp_rate: quad_rate
type: fir_filter_fff
width: 3e3
win: window.WIN_HAMMING
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [56, 900]
rotation: 0
state: enabled
# --- FM De-emphasis ---
- name: fm_deemph
id: analog_fm_deemph
parameters:
comment: 'De-emphasis tau=50us (Europa)'
maxoutbuf: '0'
minoutbuf: '0'
samp_rate: quad_rate
tau: 50e-6
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [320, 920]
rotation: 0
state: enabled
# --- Rational Resampler (240k -> 48k = /5) ---
- name: resampler
id: rational_resampler_xxx
parameters:
comment: 'Resampler 240 kHz -> 48 kHz'
decim: '5'
fbw: '0'
interp: '1'
maxoutbuf: '0'
minoutbuf: '0'
taps: '[]'
type: fff
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [584, 920]
rotation: 0
state: enabled
# --- Audio Sink ---
- name: audio_sink
id: audio_sink
parameters:
comment: ''
device_name: ''
num_inputs: '1'
ok_to_block: 'True'
samp_rate: audio_rate
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [800, 932]
rotation: 0
state: enabled
connections:
- [osmosdr_source, '0', lpf_channel, '0']
- [lpf_channel, '0', quad_demod, '0']
- [quad_demod, '0', lpf_audio, '0']
- [lpf_audio, '0', fm_deemph, '0']
- [fm_deemph, '0', resampler, '0']
- [resampler, '0', audio_sink, '0']
metadata:
file_format: 1
grc_version: 3.10.9.2
Uruchomienie:
gnuradio-companion sdr_03_img/fm_demod_manual.grc
# lub wygeneruj skrypt i uruchom:
grcc fm_demod_manual.grc && python3 fm_demod_manual.py