AM変調の原理をPythonでわかりやすく解説する

AM変調はアナログ変調方式の1つで、信号を搬送波の振幅に乗せて情報を伝達する方式です。AMラジオ放送で広く使われてきた歴史のある変調方式です。

本記事の内容

  • AM変調の数学的定式化
  • 変調指数と過変調
  • 周波数スペクトルの解析
  • 包絡線検波による復調
  • Pythonでの完全な実装

前提知識

この記事を読む前に、以下の記事を読んでおくと理解が深まります。

AM変調の数学的定式化

信号波(ベースバンド信号)

送信したい情報を含む信号を $m(t)$ とします。簡単のため正弦波とすると、

$$ m(t) = A_m \cos(2\pi f_m t) $$

ここで $A_m$ は振幅、$f_m$ は信号の周波数です。

搬送波

搬送波は信号波より十分に高い周波数の正弦波です。

$$ c(t) = A_c \cos(2\pi f_c t) $$

AM変調信号

AM変調信号 $s(t)$ は次のように定義されます。

$$ s(t) = A_c [1 + m \cdot \hat{m}(t)] \cos(2\pi f_c t) $$

ここで $\hat{m}(t) = m(t)/A_m$ は正規化された信号波、$m = A_m / A_c$ は変調指数(modulation index)です。

正弦波の場合、

$$ s(t) = A_c [1 + m \cos(2\pi f_m t)] \cos(2\pi f_c t) $$

変調指数

変調指数 $m$ は変調の深さを制御するパラメータです。

$$ m = \frac{A_{\max} – A_{\min}}{A_{\max} + A_{\min}} $$

  • $0 < m < 1$: 正常な変調
  • $m = 1$: 100%変調(最大深度)
  • $m > 1$: 過変調(信号が歪む)

周波数スペクトル

三角関数の積の公式を使って展開します。

$$ \begin{align} s(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) \end{align} $$

スペクトルは3つの成分から構成されます。

成分 周波数 振幅
搬送波 $f_c$ $A_c$
上側帯波(USB) $f_c + f_m$ $mA_c/2$
下側帯波(LSB) $f_c – f_m$ $mA_c/2$

電力効率

AM変調の電力効率は次のように計算されます。

$$ \eta = \frac{P_{\text{sideband}}}{P_{\text{total}}} = \frac{m^2/2}{1 + m^2/2} $$

$m = 1$(100%変調)でも効率は33%にとどまります。これはAM変調の大きな欠点です。

包絡線検波(復調)

AM信号の包絡線(振幅の変化)を抽出することで、元の信号を復調できます。

$$ \text{envelope}(t) = A_c [1 + m \cdot \hat{m}(t)] $$

包絡線検波はダイオードとRCフィルタで簡単に実装できます。これがAMラジオの受信方式です。

Pythonでの実装

import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import hilbert

# パラメータ
fs = 100000    # サンプリング周波数 [Hz]
T = 0.1        # 信号長 [s]
t = np.linspace(0, T, int(fs * T))
fc = 10000     # 搬送波周波数 [Hz]
fm = 500       # 信号波周波数 [Hz]
Ac = 1.0       # 搬送波振幅

# === AM変調 ===
fig, axes = plt.subplots(3, 2, figsize=(14, 14))

# 変調指数を変えたAM信号
for idx, m in enumerate([0.5, 1.0, 1.5]):
    signal = np.cos(2 * np.pi * fm * t)
    am = Ac * (1 + m * signal) * np.cos(2 * np.pi * fc * t)
    envelope = Ac * (1 + m * signal)

    axes[idx, 0].plot(t * 1000, am, 'b-', linewidth=0.3)
    axes[idx, 0].plot(t * 1000, envelope, 'r-', linewidth=1.5, label='Envelope')
    axes[idx, 0].plot(t * 1000, -envelope, 'r-', linewidth=1.5)
    axes[idx, 0].set_xlabel('Time [ms]')
    axes[idx, 0].set_ylabel('Amplitude')
    axes[idx, 0].set_title(f'AM Signal (m = {m})')
    axes[idx, 0].set_xlim(0, 10)
    axes[idx, 0].legend()

    # スペクトル
    freq = np.fft.fftfreq(len(t), 1/fs)
    am_fft = np.abs(np.fft.fft(am)) / len(t)
    axes[idx, 1].plot(freq[:len(freq)//2], 2*am_fft[:len(freq)//2], 'b-')
    axes[idx, 1].set_xlabel('Frequency [Hz]')
    axes[idx, 1].set_ylabel('Magnitude')
    axes[idx, 1].set_title(f'Spectrum (m = {m})')
    axes[idx, 1].set_xlim(fc - 5*fm, fc + 5*fm)

    if m <= 1:
        status = "正常"
    else:
        status = "過変調"
    axes[idx, 1].text(0.02, 0.95, status, transform=axes[idx, 1].transAxes,
                      fontsize=12, color='red' if m > 1 else 'green',
                      verticalalignment='top')

plt.tight_layout()
plt.show()

# === 復調(包絡線検波) ===
m = 0.8
signal = np.cos(2 * np.pi * fm * t) + \
         0.5 * np.cos(2 * np.pi * 2 * fm * t)  # 2つの周波数成分
signal = signal / np.max(np.abs(signal))  # 正規化

am_signal = Ac * (1 + m * signal) * np.cos(2 * np.pi * fc * t)

# ヒルベルト変換による包絡線検波
analytic = hilbert(am_signal)
envelope_detected = np.abs(analytic)

# DCオフセット除去と正規化
demodulated = (envelope_detected - Ac) / (m * Ac)

fig, axes = plt.subplots(3, 1, figsize=(14, 10))

# 元の信号
axes[0].plot(t * 1000, signal, 'b-', linewidth=1.5)
axes[0].set_title('Original Signal')
axes[0].set_xlabel('Time [ms]')
axes[0].set_ylabel('Amplitude')
axes[0].set_xlim(0, 20)
axes[0].grid(True)

# AM変調信号
axes[1].plot(t * 1000, am_signal, 'g-', linewidth=0.3)
axes[1].plot(t * 1000, envelope_detected, 'r-', linewidth=1.5, label='Detected envelope')
axes[1].set_title('AM Modulated Signal + Envelope Detection')
axes[1].set_xlabel('Time [ms]')
axes[1].set_ylabel('Amplitude')
axes[1].set_xlim(0, 20)
axes[1].legend()
axes[1].grid(True)

# 復調信号
axes[2].plot(t * 1000, signal, 'b-', linewidth=1, alpha=0.5, label='Original')
axes[2].plot(t * 1000, demodulated, 'r-', linewidth=1.5, label='Demodulated')
axes[2].set_title('Demodulated Signal')
axes[2].set_xlabel('Time [ms]')
axes[2].set_ylabel('Amplitude')
axes[2].set_xlim(0, 20)
axes[2].legend()
axes[2].grid(True)

plt.tight_layout()
plt.show()

# 電力効率の計算
print("=== AM変調の電力効率 ===")
for m_val in [0.25, 0.5, 0.75, 1.0]:
    eta = m_val**2 / 2 / (1 + m_val**2 / 2)
    print(f"  m = {m_val:.2f}: 電力効率 = {eta*100:.1f}%")

まとめ

本記事では、AM変調の原理について解説しました。

  • AM変調は搬送波の振幅を信号に応じて変化させる方式である
  • 変調信号のスペクトルは搬送波成分とUSB・LSBの3成分からなる
  • 変調指数 $m > 1$ では過変調となり、包絡線検波で正しく復調できなくなる
  • 電力効率は $m=1$ でも33%と低い
  • 包絡線検波は簡単な回路で実現できるため、AMラジオで広く使われてきた

次のステップとして、以下の記事も参考にしてください。