FM(Frequency Modulation: 周波数変調)は、搬送波の周波数をベースバンド信号に応じて変化させる変調方式です。AM変調が振幅を変化させるのに対し、FM変調は周波数(位相)を変化させるため、振幅変動に起因する雑音に強いという大きな利点があります。
FMラジオ放送はその代表的な応用であり、AMラジオに比べて高い音質が得られることでよく知られています。また、衛星通信、テレメトリ、アナログ映像伝送など、幅広い分野で使用されてきました。
本記事の内容
- FM変調の数学的表現と瞬時周波数
- 変調指数の定義
- ベッセル関数展開によるFMスペクトルの導出
- カーソンの帯域幅則の導出
- 狭帯域FM(NBFM)と広帯域FM(WBFM)
- FM検波の原理
- Pythonでの実装とシミュレーション
前提知識
この記事を読む前に、以下の記事を読んでおくと理解が深まります。
FM変調の数学的表現
瞬時周波数と瞬時位相
FM変調を理解するには、まず瞬時周波数(instantaneous frequency)と瞬時位相(instantaneous phase)の関係を明確にする必要があります。
一般的な角度変調信号を次のように書きます。
$$ s(t) = A_c \cos\!\left[\theta(t)\right] $$
ここで $\theta(t)$ は瞬時位相です。瞬時角周波数 $\omega_i(t)$ は瞬時位相の時間微分として定義されます。
$$ \omega_i(t) = \frac{d\theta(t)}{dt} $$
瞬時周波数は $f_i(t) = \omega_i(t) / (2\pi)$ です。
逆に、瞬時位相は瞬時角周波数の積分で得られます。
$$ \theta(t) = \int_{-\infty}^{t} \omega_i(\tau) \, d\tau = 2\pi \int_{-\infty}^{t} f_i(\tau) \, d\tau $$
FM変調の定義
FM変調では、瞬時周波数をベースバンド信号 $x(t)$ に比例して変化させます。
$$ f_i(t) = f_c + k_f \, x(t) $$
ここで $f_c$ は搬送波周波数、$k_f$ は周波数感度(frequency sensitivity)で、単位は [Hz/V] です。$x(t)$ の振幅が1のとき、周波数偏移(frequency deviation)は $\Delta f = k_f$ となります。
瞬時位相を求めます。
$$ \theta(t) = 2\pi \int_{-\infty}^{t} f_i(\tau) \, d\tau = 2\pi \int_{-\infty}^{t} \left[ f_c + k_f \, x(\tau) \right] d\tau $$
$$ = 2\pi f_c t + 2\pi k_f \int_{-\infty}^{t} x(\tau) \, d\tau $$
したがって、FM変調信号は
$$ \boxed{ s(t) = A_c \cos\!\left[ 2\pi f_c t + 2\pi k_f \int_{-\infty}^{t} x(\tau) \, d\tau \right] } $$
搬送波の位相に、ベースバンド信号の積分が加わる形になっています。
正弦波変調の場合
$x(t) = \cos(2\pi f_m t)$(単一トーン)の場合を考えます。
$$ 2\pi k_f \int_{-\infty}^{t} \cos(2\pi f_m \tau) \, d\tau = 2\pi k_f \cdot \frac{\sin(2\pi f_m t)}{2\pi f_m} = \frac{k_f}{f_m} \sin(2\pi f_m t) $$
ここで変調指数(modulation index)$\beta$ を次のように定義します。
$$ \boxed{ \beta = \frac{\Delta f}{f_m} = \frac{k_f}{f_m} } $$
$\Delta f = k_f$ は最大周波数偏移です。
FM信号は次のように書けます。
$$ \boxed{ s(t) = A_c \cos\!\left[ 2\pi f_c t + \beta \sin(2\pi f_m t) \right] } $$
この式がFM変調の基本形です。AM変調と異なり、搬送波の振幅 $A_c$ は一定で、位相(したがって周波数)のみが変化します。
変調指数の物理的意味
変調指数 $\beta$ は、最大位相偏移をラジアン単位で表したものです。
$$ \beta = \frac{\Delta f}{f_m} = \frac{\text{最大周波数偏移}}{\text{変調信号の周波数}} $$
- $\beta \ll 1$: 狭帯域FM(NBFM) — 位相偏移が小さく、AMに近い振る舞い
- $\beta \gg 1$: 広帯域FM(WBFM) — 位相偏移が大きく、多数の側帯波が出現
FMラジオ放送では、$\Delta f = 75$ kHz、$f_m = 15$ kHz(可聴帯域の上限)なので、$\beta = 5$ であり、広帯域FMに分類されます。
ベッセル関数展開によるFMスペクトルの導出
導出の出発点
FM信号のスペクトルを求めるために、複素表現に変換します。
$$ s(t) = A_c \, \text{Re}\!\left[ e^{j(2\pi f_c t + \beta \sin(2\pi f_m t))} \right] $$
$$ = A_c \, \text{Re}\!\left[ e^{j2\pi f_c t} \cdot e^{j\beta \sin(2\pi f_m t)} \right] $$
重要なのは $e^{j\beta \sin(2\pi f_m t)}$ の部分です。これをフーリエ級数に展開する必要があります。
ヤコビ-アンガー展開
次の恒等式が知られています(ヤコビ-アンガー(Jacobi-Anger)展開)。
$$ e^{j\beta \sin\phi} = \sum_{n=-\infty}^{\infty} J_n(\beta) \, e^{jn\phi} $$
ここで $J_n(\beta)$ は第1種ベッセル関数(Bessel function of the first kind)の $n$ 次です。
この展開の導出を簡潔に示します。$e^{j\beta \sin\phi}$ は $\phi$ に関して周期 $2\pi$ の関数ですから、フーリエ級数に展開できます。
$$ e^{j\beta \sin\phi} = \sum_{n=-\infty}^{\infty} c_n \, e^{jn\phi} $$
フーリエ係数は、
$$ c_n = \frac{1}{2\pi} \int_{-\pi}^{\pi} e^{j\beta \sin\phi} e^{-jn\phi} \, d\phi = \frac{1}{2\pi} \int_{-\pi}^{\pi} e^{j(\beta \sin\phi – n\phi)} \, d\phi $$
この積分は第1種ベッセル関数の積分表現そのものです。
$$ J_n(\beta) = \frac{1}{2\pi} \int_{-\pi}^{\pi} e^{j(\beta \sin\phi – n\phi)} \, d\phi $$
したがって $c_n = J_n(\beta)$ であり、ヤコビ-アンガー展開が成り立ちます。
FM信号のスペクトル
$\phi = 2\pi f_m t$ とおくと、
$$ s(t) = A_c \, \text{Re}\!\left[ e^{j2\pi f_c t} \sum_{n=-\infty}^{\infty} J_n(\beta) \, e^{jn \cdot 2\pi f_m t} \right] $$
$$ = A_c \sum_{n=-\infty}^{\infty} J_n(\beta) \cos\!\left[ 2\pi(f_c + n f_m) t \right] $$
$$ \boxed{ s(t) = A_c \sum_{n=-\infty}^{\infty} J_n(\beta) \cos\!\left[ 2\pi(f_c + n f_m) t \right] } $$
これがFM信号のスペクトル表現です。周波数スペクトルは $f = f_c + n f_m$($n = 0, \pm 1, \pm 2, \ldots$)に離散的なスペクトル線を持ち、各スペクトル線の振幅は $A_c J_n(\beta)$ で与えられます。
ベッセル関数の性質
ベッセル関数 $J_n(\beta)$ には以下の重要な性質があります。
対称性: $J_{-n}(\beta) = (-1)^n J_n(\beta)$
エネルギー保存: $\sum_{n=-\infty}^{\infty} J_n^2(\beta) = 1$
これは、FM信号の全電力が $A_c^2/2$ で一定であることを意味します。変調指数 $\beta$ を変えても全電力は変わらず、スペクトル成分間のエネルギー配分が変化するだけです。
近似($\beta \ll 1$ のとき):
$$ J_0(\beta) \approx 1, \quad J_1(\beta) \approx \frac{\beta}{2}, \quad J_n(\beta) \approx 0 \quad (|n| \geq 2) $$
この場合、搬送波と1次側帯波のみが残り、狭帯域FM(NBFM)となります。
カーソンの帯域幅則の導出
有意な側帯波の数
理論的にはFM信号のスペクトルは無限個の側帯波を持ちますが、実用上は $|J_n(\beta)|$ がある閾値以下のスペクトル線は無視できます。
一般に $|J_n(\beta)| < \epsilon$(例えば $\epsilon = 0.01$)となる $n$ のスペクトル線は無視できるとすると、有意な側帯波の数は近似的に $\beta + 1$ から $\beta + 2$ 程度です。
カーソンの帯域幅則
カーソンの帯域幅則(Carson’s bandwidth rule)は、FM信号の占有帯域幅の実用的な近似式です。
FM信号の帯域幅を、全電力の約98%を含む帯域として定義します。有意な側帯波の次数が $\pm(\beta + 1)$ 程度であることから、
$$ B_{\text{FM}} \approx 2(\beta + 1) f_m = 2\left(\frac{\Delta f}{f_m} + 1\right) f_m = 2(\Delta f + f_m) $$
$$ \boxed{ B_{\text{FM}} \approx 2(\Delta f + f_m) = 2 f_m (\beta + 1) } $$
この式の解釈は明快です。
- $\beta \gg 1$(WBFM): $B_{\text{FM}} \approx 2\Delta f$ — 帯域幅は周波数偏移の2倍で決まる
- $\beta \ll 1$(NBFM): $B_{\text{FM}} \approx 2f_m$ — 帯域幅はAMとほぼ同じ $2W$
帯域幅の検証
FMラジオ放送の場合: $\Delta f = 75$ kHz, $f_m = 15$ kHz, $\beta = 5$
$$ B_{\text{FM}} = 2(75 + 15) = 180 \text{ kHz} $$
実際のFMラジオの割当帯域幅は200 kHz(ガードバンド含む)であり、カーソンの帯域幅則とよく一致します。
狭帯域FM(NBFM)と広帯域FM(WBFM)
NBFM($\beta \ll 1$)
$\beta \ll 1$ のとき、ベッセル関数の近似を用いると、
$$ s(t) \approx A_c J_0(\beta) \cos(2\pi f_c t) + A_c J_1(\beta) \cos\!\left[2\pi(f_c + f_m)t\right] – A_c J_1(\beta) \cos\!\left[2\pi(f_c – f_m)t\right] $$
$J_0(\beta) \approx 1$, $J_1(\beta) \approx \beta/2$ を代入すると、
$$ s(t) \approx A_c \cos(2\pi f_c t) + \frac{A_c \beta}{2} \cos\!\left[2\pi(f_c + f_m)t\right] – \frac{A_c \beta}{2} \cos\!\left[2\pi(f_c – f_m)t\right] $$
AM信号 $s_{\text{AM}}(t) = A_c \cos(2\pi f_c t) + \frac{mA_c}{2}\cos[2\pi(f_c + f_m)t] + \frac{mA_c}{2}\cos[2\pi(f_c – f_m)t]$ と比較すると、NBFMはAMとほぼ同じスペクトル構造を持ちますが、下側帯波の符号が反転しています。
この符号の違いは、時間波形のベクトル図(フェーザ)で見ると、AMでは側帯波が振幅方向に変動するのに対し、NBFMでは位相方向に変動することを意味します。
WBFM($\beta \gg 1$)
$\beta$ が大きくなると有意なスペクトル線の数が増え、帯域幅が広がります。しかし、その代わりに雑音耐性が向上します(FM閾値効果)。
FMの最大の利点は、帯域幅と信号対雑音比(SNR)のトレードオフが可能であることです。$\beta$ を大きくして帯域幅を広げれば、復調後のSNRが改善されます。具体的には、
$$ \text{SNR}_{\text{FM}} \propto \beta^2 \cdot \text{SNR}_{\text{受信}} $$
これは帯域幅とSNRのトレードオフであり、シャノンの通信容量の定理
$$ C = B \log_2\!\left(1 + \text{SNR}\right) $$
と本質的に関連しています。
FM検波の原理
周波数弁別器
最も直感的なFM検波方式は周波数弁別器(Frequency Discriminator)です。原理は以下の2段階です。
ステップ1: 微分器
FM信号を時間微分します。
$$ \frac{ds(t)}{dt} = -A_c \left[2\pi f_c + 2\pi k_f x(t)\right] \sin\!\left[2\pi f_c t + 2\pi k_f \int x(\tau) d\tau\right] $$
この信号の瞬時振幅は $A_c[2\pi f_c + 2\pi k_f x(t)]$ であり、瞬時周波数に比例した包絡線を持ちます。つまり、FM信号が微分によりAM-FM信号に変換されています。
ステップ2: 包絡線検波
AM-FM信号の包絡線を包絡線検波で抽出すると、
$$ e(t) = A_c \left| 2\pi f_c + 2\pi k_f x(t) \right| \approx 2\pi A_c f_c + 2\pi A_c k_f x(t) $$
($k_f x(t) \ll f_c$ の条件下で絶対値を外せます。)
DC成分 $2\pi A_c f_c$ を除去すると、元のベースバンド信号 $x(t)$ に比例した出力が得られます。
PLL検波
PLL(Phase Locked Loop: 位相同期ループ)を用いたFM検波は、より高性能な方式です。
PLLは入力信号の位相に追従するVCO(電圧制御発振器)を持ちます。VCOの制御電圧がFM信号の瞬時周波数偏移に比例するため、この制御電圧を取り出すことで復調が完了します。
PLLの動作を数式で表すと、VCOの出力位相 $\theta_{\text{VCO}}(t)$ が入力位相 $\theta(t) = 2\pi f_c t + \beta\sin(2\pi f_m t)$ に追従するとき、位相誤差信号は
$$ e(t) = \theta(t) – \theta_{\text{VCO}}(t) \approx 0 $$
PLLがロック状態であれば、VCOの制御電圧 $v(t)$ は
$$ v(t) \propto \frac{d\theta(t)}{dt} – 2\pi f_c = 2\pi k_f x(t) \propto x(t) $$
となり、ベースバンド信号が復元されます。
Pythonでの実装
FM変調信号の生成と時間波形
import numpy as np
import matplotlib.pyplot as plt
# パラメータ設定
fs = 500000 # サンプリング周波数 [Hz]
T = 0.02 # 信号長 [s]
t = np.arange(0, T, 1/fs)
fc = 50000 # 搬送波周波数 [Hz]
fm = 2000 # 変調信号周波数 [Hz]
Ac = 1.0 # 搬送波振幅
# ベースバンド信号
x = np.cos(2 * np.pi * fm * t)
# 各変調指数でのFM信号
beta_list = [0.2, 1.0, 3.0, 10.0]
fig, axes = plt.subplots(len(beta_list), 2, figsize=(16, 14))
for i, beta in enumerate(beta_list):
delta_f = beta * fm # 周波数偏移
kf = delta_f # 周波数感度
# FM変調信号
# 位相の積分: ∫x(τ)dτ = sin(2πfm*t)/(2πfm)
phase_integral = np.cumsum(x) / fs # 数値積分
s_fm = Ac * np.cos(2 * np.pi * fc * t + 2 * np.pi * kf * phase_integral)
# 瞬時周波数の可視化
inst_freq = fc + kf * x
# 時間波形
axes[i, 0].plot(t * 1000, s_fm, 'b-', linewidth=0.5)
axes[i, 0].set_xlabel('Time [ms]')
axes[i, 0].set_ylabel('Amplitude')
axes[i, 0].set_title(f'FM Signal (β = {beta}, Δf = {delta_f:.0f} Hz)')
axes[i, 0].set_xlim(0, 3)
axes[i, 0].grid(True, alpha=0.3)
# 瞬時周波数
axes[i, 1].plot(t * 1000, inst_freq / 1000, 'r-', linewidth=1.5)
axes[i, 1].axhline(y=fc/1000, color='gray', linestyle='--', alpha=0.5)
axes[i, 1].set_xlabel('Time [ms]')
axes[i, 1].set_ylabel('Instantaneous Freq [kHz]')
axes[i, 1].set_title(f'Instantaneous Frequency (β = {beta})')
axes[i, 1].set_xlim(0, 3)
axes[i, 1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
変調指数 $\beta$ が大きくなると、周波数の振れ幅が大きくなり、時間波形もより複雑になることが確認できます。
ベッセル関数によるFMスペクトルの計算
import numpy as np
import matplotlib.pyplot as plt
from scipy.special import jv # 第1種ベッセル関数
# ベッセル関数の可視化
beta_range = np.linspace(0, 12, 500)
n_max = 8
fig, axes = plt.subplots(1, 2, figsize=(16, 6))
# ベッセル関数 J_n(β) の値
for n in range(n_max):
axes[0].plot(beta_range, jv(n, beta_range), linewidth=1.5,
label=f'$J_{n}(\\beta)$')
axes[0].set_xlabel('Modulation Index β')
axes[0].set_ylabel('$J_n(β)$')
axes[0].set_title('Bessel Functions of the First Kind')
axes[0].legend(loc='upper right', fontsize=9)
axes[0].grid(True, alpha=0.3)
axes[0].set_xlim(0, 12)
axes[0].set_ylim(-0.5, 1.1)
# エネルギー保存の確認
n_terms = 50
for beta_val in [0.5, 2.0, 5.0, 10.0]:
n_arr = np.arange(-n_terms, n_terms + 1)
jn_vals = jv(n_arr, beta_val)
cumsum = np.cumsum(jn_vals**2)
axes[1].plot(n_arr, cumsum, label=f'β = {beta_val}')
axes[1].axhline(y=1.0, color='k', linestyle='--', alpha=0.5,
label='Total = 1')
axes[1].set_xlabel('Summation index n')
axes[1].set_ylabel('$\\sum J_n^2(β)$')
axes[1].set_title('Energy Conservation: $\\sum J_n^2(β) = 1$')
axes[1].legend()
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
ベッセル関数の値が $\beta$ によって大きく変化し、エネルギーが各スペクトル成分に再配分される様子がわかります。
変調指数ごとのスペクトル比較
import numpy as np
import matplotlib.pyplot as plt
from scipy.special import jv
# パラメータ
fm = 1000 # 変調周波数 [Hz]
n_max = 20 # ベッセル関数の次数
beta_list = [0.2, 0.5, 1.0, 2.0, 5.0, 10.0]
fig, axes = plt.subplots(2, 3, figsize=(18, 10))
axes = axes.flatten()
for idx, beta in enumerate(beta_list):
n_arr = np.arange(-n_max, n_max + 1)
jn_vals = jv(n_arr, beta)
# スペクトル線
freq_offsets = n_arr * fm / 1000 # kHz単位
# 棒グラフ
colors = ['red' if abs(j) > 0.01 else 'lightgray' for j in jn_vals]
axes[idx].bar(freq_offsets, np.abs(jn_vals), width=0.08,
color=colors, edgecolor='none')
# カーソンの帯域幅
bw_carson = 2 * (beta + 1) * fm / 1000 # kHz
axes[idx].axvline(x=bw_carson/2, color='green', linestyle='--',
linewidth=2, alpha=0.7)
axes[idx].axvline(x=-bw_carson/2, color='green', linestyle='--',
linewidth=2, alpha=0.7, label=f'Carson BW = {bw_carson:.1f} kHz')
axes[idx].set_xlabel('Frequency offset from carrier [kHz]')
axes[idx].set_ylabel('|$J_n$(β)|')
axes[idx].set_title(f'β = {beta} (Δf = {beta*fm:.0f} Hz)')
axes[idx].legend(fontsize=8, loc='upper right')
axes[idx].grid(True, alpha=0.3)
axes[idx].set_ylim(0, 1.1)
axes[idx].set_xlim(-15, 15)
plt.suptitle('FM Spectrum vs Modulation Index (Bessel Function Expansion)',
fontsize=14)
plt.tight_layout()
plt.show()
# カーソンの帯域幅の数値確認
print("=== カーソンの帯域幅則 ===")
print(f"{'β':>6} {'Δf [Hz]':>10} {'BW [Hz]':>10} {'BW/2fm':>8}")
for beta in beta_list:
delta_f = beta * fm
bw = 2 * (delta_f + fm)
print(f"{beta:6.1f} {delta_f:10.0f} {bw:10.0f} {bw/(2*fm):8.1f}")
$\beta$ が小さいときは搬送波と1次側帯波のみが有意ですが、$\beta$ が大きくなるにつれて多数の側帯波が出現し、カーソンの帯域幅(緑の破線)内にほとんどの電力が収まっていることが確認できます。
FFTによるFMスペクトルとの比較
import numpy as np
import matplotlib.pyplot as plt
from scipy.special import jv
# パラメータ
fs = 500000
T = 0.1
t = np.arange(0, T, 1/fs)
fc = 50000
fm = 2000
Ac = 1.0
beta = 5.0
delta_f = beta * fm
kf = delta_f
# FM信号生成
x = np.cos(2 * np.pi * fm * t)
phase_integral = np.cumsum(x) / fs
s_fm = Ac * np.cos(2 * np.pi * fc * t + 2 * np.pi * kf * phase_integral)
# FFTスペクトル
N_fft = len(t)
freq = np.fft.fftfreq(N_fft, 1/fs)
S = np.abs(np.fft.fft(s_fm)) / N_fft
pos_mask = freq >= 0
fig, axes = plt.subplots(2, 1, figsize=(14, 10))
# FFTスペクトル
axes[0].plot(freq[pos_mask] / 1000, 2 * S[pos_mask], 'b-', linewidth=0.8)
axes[0].set_xlabel('Frequency [kHz]')
axes[0].set_ylabel('Magnitude')
axes[0].set_title(f'FM Spectrum (FFT) — β = {beta}, Δf = {delta_f} Hz, fm = {fm} Hz')
axes[0].set_xlim((fc - 20*fm)/1000, (fc + 20*fm)/1000)
axes[0].grid(True, alpha=0.3)
# カーソンの帯域幅
bw = 2 * (delta_f + fm)
axes[0].axvline(x=(fc - bw/2)/1000, color='red', linestyle='--',
linewidth=2, label=f'Carson BW = {bw/1000:.0f} kHz')
axes[0].axvline(x=(fc + bw/2)/1000, color='red', linestyle='--',
linewidth=2)
axes[0].legend(fontsize=11)
# ベッセル関数による理論スペクトルとの比較
n_arr = np.arange(-20, 21)
freq_theory = fc + n_arr * fm
jn_theory = np.abs(jv(n_arr, beta)) * Ac / 2 # 片側スペクトルの振幅
axes[1].stem(freq_theory / 1000, jn_theory, linefmt='r-', markerfmt='ro',
basefmt='k-', label='Bessel Theory')
# FFTスペクトルを重ねて表示
axes[1].plot(freq[pos_mask] / 1000, 2 * S[pos_mask], 'b-', linewidth=0.8,
alpha=0.5, label='FFT')
axes[1].set_xlabel('Frequency [kHz]')
axes[1].set_ylabel('Magnitude')
axes[1].set_title('FFT vs Bessel Function Theory')
axes[1].set_xlim((fc - 15*fm)/1000, (fc + 15*fm)/1000)
axes[1].legend(fontsize=11)
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
FFTで得られたスペクトルとベッセル関数による理論予測が非常によく一致していることが確認できます。
FM検波(微分器+包絡線検波)のシミュレーション
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import hilbert, butter, filtfilt
# パラメータ
fs = 500000
T = 0.02
t = np.arange(0, T, 1/fs)
fc = 50000
fm = 2000
Ac = 1.0
beta = 5.0
delta_f = beta * fm
kf = delta_f
# 複合ベースバンド信号
x = 0.7 * np.cos(2 * np.pi * 1000 * t) + 0.3 * np.cos(2 * np.pi * 3000 * t)
x = x / np.max(np.abs(x)) # 正規化
# FM変調
phase_integral = np.cumsum(x) / fs
s_fm = Ac * np.cos(2 * np.pi * fc * t + 2 * np.pi * kf * phase_integral)
# 雑音付加(SNR = 30 dB)
snr_dB = 30
signal_power = np.mean(s_fm**2)
noise_power = signal_power / (10**(snr_dB / 10))
noise = np.sqrt(noise_power) * np.random.randn(len(t))
s_noisy = s_fm + noise
# FM検波(微分器 + 包絡線検波)
# 1. 微分(差分近似)
ds = np.diff(s_noisy) * fs
ds = np.append(ds, ds[-1]) # 長さを合わせる
# 2. 包絡線検波(ヒルベルト変換)
envelope = np.abs(hilbert(ds))
# 3. ローパスフィルタでベースバンド抽出
b, a = butter(5, 5000 / (fs / 2), btype='low')
demod = filtfilt(b, a, envelope)
demod_ac = demod - np.mean(demod)
# 正規化(元信号と比較しやすいように)
demod_norm = demod_ac / np.max(np.abs(demod_ac)) * np.max(np.abs(x))
# 可視化
fig, axes = plt.subplots(4, 1, figsize=(14, 14))
axes[0].plot(t * 1000, x, 'b-', linewidth=1.5)
axes[0].set_xlabel('Time [ms]')
axes[0].set_ylabel('Amplitude')
axes[0].set_title('Original Baseband Signal')
axes[0].set_xlim(0, 10)
axes[0].grid(True, alpha=0.3)
axes[1].plot(t * 1000, s_noisy, 'b-', linewidth=0.3, alpha=0.5)
axes[1].set_xlabel('Time [ms]')
axes[1].set_ylabel('Amplitude')
axes[1].set_title(f'Noisy FM Signal (β = {beta}, SNR = {snr_dB} dB)')
axes[1].set_xlim(0, 10)
axes[1].grid(True, alpha=0.3)
axes[2].plot(t * 1000, envelope / 1e5, 'r-', linewidth=0.5, alpha=0.7)
axes[2].set_xlabel('Time [ms]')
axes[2].set_ylabel('Envelope (arb.)')
axes[2].set_title('After Differentiation + Envelope Detection')
axes[2].set_xlim(0, 10)
axes[2].grid(True, alpha=0.3)
axes[3].plot(t * 1000, x, 'b-', linewidth=1.5, label='Original')
axes[3].plot(t * 1000, demod_norm, 'r-', linewidth=1.5, alpha=0.7,
label='Demodulated')
axes[3].set_xlabel('Time [ms]')
axes[3].set_ylabel('Amplitude')
axes[3].set_title('Demodulation Result')
axes[3].set_xlim(0, 10)
axes[3].legend()
axes[3].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
FM検波により元のベースバンド信号が復元されていることが確認できます。微分器+包絡線検波というシンプルな構成で実現可能です。
まとめ
本記事では、FM変調の理論とカーソンの帯域幅則について解説しました。
- FM変調信号は $s(t) = A_c \cos[2\pi f_c t + \beta \sin(2\pi f_m t)]$ で表される
- 変調指数 $\beta = \Delta f / f_m$ は最大位相偏移をラジアンで表したものである
- ベッセル関数展開により、FM信号のスペクトルは $f_c + n f_m$ に離散的なスペクトル線を持つ
- カーソンの帯域幅則 $B \approx 2(\Delta f + f_m) = 2f_m(\beta + 1)$ は実用的な帯域幅の近似式である
- 狭帯域FM($\beta \ll 1$)はAMに似た構造、広帯域FM($\beta \gg 1$)は帯域幅とSNRのトレードオフが可能
- FM検波は微分器+包絡線検波やPLL検波で実現できる
- FM信号の全電力は変調指数によらず一定($A_c^2 / 2$)であり、エネルギーがスペクトル成分間で再配分される
次のステップとして、以下の記事も参考にしてください。