OFDM(直交周波数分割多重)の原理と実装を解説

現代のワイヤレス通信(Wi-Fi、4G LTE、5G NR)の根幹を支える技術が、OFDM(Orthogonal Frequency Division Multiplexing:直交周波数分割多重)です。OFDMは、高速データを多数の低速サブキャリアに分割して並列伝送することで、マルチパス伝搬によるシンボル間干渉(ISI)を効率的に克服します。

OFDMの理解には、フーリエ変換、直交性、巡回畳み込みといった数学的概念が不可欠です。本記事では、これらの数学的背景を省略せず導出し、Pythonによるフルシミュレーションまで行います。

本記事の内容

  • マルチパス伝搬とシンボル間干渉(ISI)の問題
  • OFDMの基本原理と直交サブキャリア
  • サブキャリアの直交性の数学的証明
  • IFFT/FFTによる変復調の導出
  • サイクリックプレフィックス(CP)の役割と巡回畳み込みの等価性
  • PAPR問題
  • PythonによるOFDM送受信のフルシミュレーション

前提知識

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

マルチパス伝搬とISI

マルチパス伝搬とは

無線通信において、送信信号は建物や地面などで反射・回折し、複数の経路(パス)を通って受信機に到達します。これをマルチパス伝搬と呼びます。各パスの遅延時間が異なるため、受信信号は送信信号の時間的に遅れたコピーの重ね合わせになります。

チャネルのインパルス応答を $h(\tau)$ とすると、受信信号 $r(t)$ は送信信号 $s(t)$ との畳み込みで表されます。

$$ r(t) = \int_{0}^{\tau_{\max}} h(\tau) s(t – \tau) \, d\tau + n(t) $$

ここで $\tau_{\max}$ は最大遅延拡がり(maximum delay spread)、$n(t)$ は加法性ガウス雑音です。

シンボル間干渉(ISI)

シングルキャリア伝送において、シンボル長 $T_s$ が遅延拡がり $\tau_{\max}$ と同程度またはそれ以下のとき、あるシンボルの遅延成分が次のシンボルの受信タイミングに重なります。これがシンボル間干渉(Inter-Symbol Interference: ISI)です。

データレートを上げるとシンボル長 $T_s$ は短くなり、ISIの影響が深刻化します。例えば、データレート $R = 1/T_s = 10\,\text{Mbps}$ では $T_s = 100\,\text{ns}$ ですが、屋内環境の遅延拡がりが $\tau_{\max} \approx 50\,\text{ns}$ 程度あると、ISIは無視できません。

OFDMのアイデア

OFDMの基本的なアイデアは、高速なシリアルデータストリームを $N$ 個の低速なパラレルストリームに分割し、それぞれを異なるサブキャリアで伝送することです。これにより、各サブキャリアのシンボル長が

$$ T_{\text{OFDM}} = N \cdot T_s $$

と大幅に長くなり、$T_{\text{OFDM}} \gg \tau_{\max}$ を達成できます。結果として、各サブキャリアではISIを無視できるほど小さくできます。

OFDMの基本原理

サブキャリアの構成

OFDM信号は $N$ 個のサブキャリアの重ね合わせで構成されます。第 $k$ 番目のサブキャリアの周波数は

$$ f_k = f_0 + k \Delta f, \quad k = 0, 1, \dots, N-1 $$

です。ここで $f_0$ は最低周波数、$\Delta f$ はサブキャリア間隔です。1つのOFDMシンボル区間 $[0, T]$ において、各サブキャリアにデータシンボル $X_k$(複素数)を載せた送信信号は

$$ s(t) = \sum_{k=0}^{N-1} X_k \, e^{j 2\pi f_k t}, \quad 0 \leq t \leq T $$

と表されます。

サブキャリア間隔と直交性

サブキャリア間隔を $\Delta f = 1/T$ と設定します。このとき、任意の2つのサブキャリア $k$ と $l$ について、1シンボル区間での内積が直交条件を満たします。

サブキャリアの直交性の証明

2つのサブキャリアの直交性を厳密に証明します。サブキャリア $k$ と $l$ の基底関数を

$$ \phi_k(t) = e^{j 2\pi f_k t}, \quad \phi_l(t) = e^{j 2\pi f_l t} $$

とします。1シンボル区間 $[0, T]$ での内積を計算します。

$$ \begin{align} \langle \phi_k, \phi_l \rangle &= \frac{1}{T} \int_0^T \phi_k(t) \, \phi_l^*(t) \, dt \\ &= \frac{1}{T} \int_0^T e^{j 2\pi f_k t} \, e^{-j 2\pi f_l t} \, dt \\ &= \frac{1}{T} \int_0^T e^{j 2\pi (f_k – f_l) t} \, dt \end{align} $$

ここで $f_k – f_l = (k – l) \Delta f = (k – l)/T$ を代入します。

場合1: $k = l$ のとき

$$ \langle \phi_k, \phi_l \rangle = \frac{1}{T} \int_0^T e^{j \cdot 0} \, dt = \frac{1}{T} \int_0^T 1 \, dt = 1 $$

場合2: $k \neq l$ のとき

$$ \begin{align} \langle \phi_k, \phi_l \rangle &= \frac{1}{T} \int_0^T e^{j 2\pi \frac{(k-l)}{T} t} \, dt \\ &= \frac{1}{T} \left[ \frac{e^{j 2\pi \frac{(k-l)}{T} t}}{j 2\pi \frac{(k-l)}{T}} \right]_0^T \\ &= \frac{1}{T} \cdot \frac{T}{j 2\pi (k-l)} \left( e^{j 2\pi (k-l)} – e^0 \right) \\ &= \frac{1}{j 2\pi (k-l)} \left( e^{j 2\pi (k-l)} – 1 \right) \end{align} $$

$k – l$ は整数なので $e^{j 2\pi (k-l)} = \cos(2\pi(k-l)) + j\sin(2\pi(k-l)) = 1$ となります。したがって

$$ \langle \phi_k, \phi_l \rangle = \frac{1}{j 2\pi (k-l)} (1 – 1) = 0 $$

以上をまとめると

$$ \langle \phi_k, \phi_l \rangle = \frac{1}{T} \int_0^T e^{j 2\pi (f_k – f_l) t} \, dt = \delta_{kl} $$

ここで $\delta_{kl}$ はクロネッカーのデルタです。この直交性により、受信側では各サブキャリアのデータシンボルをサブキャリア間干渉(ICI)なしに独立に復元できます。

IFFT/FFTによる変復調

連続信号から離散信号へ

OFDM信号 $s(t)$ を1シンボル区間 $[0, T]$ 内で $N$ 点等間隔にサンプリングします。サンプリング時刻を $t_n = nT/N$($n = 0, 1, \dots, N-1$)とすると

$$ \begin{align} s(t_n) = s\left(\frac{nT}{N}\right) &= \sum_{k=0}^{N-1} X_k \, e^{j 2\pi f_k \cdot \frac{nT}{N}} \\ &= \sum_{k=0}^{N-1} X_k \, e^{j 2\pi (f_0 + k\Delta f) \cdot \frac{nT}{N}} \end{align} $$

ベースバンドを考えて $f_0 = 0$ とし、$\Delta f = 1/T$ を代入すると

$$ \begin{align} s[n] &= \sum_{k=0}^{N-1} X_k \, e^{j 2\pi \frac{k}{T} \cdot \frac{nT}{N}} \\ &= \sum_{k=0}^{N-1} X_k \, e^{j 2\pi kn / N} \end{align} $$

IFFT との関係

$N$ 点の逆離散フーリエ変換(IDFT)は次のように定義されます。

$$ x[n] = \frac{1}{N} \sum_{k=0}^{N-1} X_k \, e^{j 2\pi kn / N}, \quad n = 0, 1, \dots, N-1 $$

上の結果と比較すると、$s[n] = N \cdot x[n]$ という関係があります。つまり、OFDM変調は本質的にIDFT(逆離散フーリエ変換)そのものです。定数倍 $N$ は正規化の違いに過ぎないため、実用上は

$$ s[n] = \text{IFFT}\{X_0, X_1, \dots, X_{N-1}\} $$

と表せます。高速な計算のために、IDFT を FFT アルゴリズム(IFFT)で実装します。計算量は $O(N^2)$ から $O(N \log N)$ に削減されます。

FFTによる復調

受信側では、受信信号のサンプル $r[n]$ に対してDFT(FFT)を適用してデータシンボルを復元します。

$$ \hat{X}_k = \frac{1}{N} \sum_{n=0}^{N-1} r[n] \, e^{-j 2\pi kn / N}, \quad k = 0, 1, \dots, N-1 $$

直交性により、チャネルの影響がなければ $\hat{X}_k = X_k$ が成り立ちます。

OFDMシステムのブロック図

OFDMの送受信処理の全体像は以下のようになります。

送信側: 1. シリアルデータをパラレルに変換(S/P変換) 2. 各パラレルストリームを変調シンボル $X_k$ にマッピング(例: QPSK, 16-QAM) 3. IFFT で時間領域信号 $s[n]$ を生成 4. サイクリックプレフィックス(CP)を付加 5. D/A変換、アップコンバート、送信

受信側: 1. 受信、ダウンコンバート、A/D変換 2. CPを除去 3. FFT で周波数領域信号 $\hat{X}_k$ を復元 4. パラレルをシリアルに変換(P/S変換) 5. 各サブキャリアのシンボルを判定(デマッピング)

サイクリックプレフィックス(CP)

CPの定義

サイクリックプレフィックス(Cyclic Prefix: CP)とは、OFDMシンボルの末尾部分をコピーして先頭に付加するガード区間のことです。

$N$ サンプルのOFDMシンボル $s[0], s[1], \dots, s[N-1]$ に対して、CPの長さを $N_{\text{CP}}$ サンプルとすると、CP付きOFDMシンボルは

$$ s_{\text{CP}}[n] = s[n + N – N_{\text{CP}}], \quad n = -N_{\text{CP}}, \dots, -1, 0, 1, \dots, N-1 $$

つまり $s[N – N_{\text{CP}}], \dots, s[N-1], s[0], s[1], \dots, s[N-1]$ の合計 $N + N_{\text{CP}}$ サンプルが1つのOFDMシンボルになります。

CPの長さの条件

CPの長さは、チャネルの最大遅延拡がり $\tau_{\max}$ をカバーする必要があります。

$$ N_{\text{CP}} \geq \lceil \tau_{\max} / T_s \rceil $$

ここで $T_s = T/N$ はサンプリング間隔です。この条件を満たせば、ISIを完全に除去できます。

巡回畳み込みの等価性

CPが果たす最も重要な役割は、線形畳み込みを巡回畳み込みに変換することです。これにより、周波数領域での乗算による等化が可能になります。

チャネルのインパルス応答を $h[n]$($n = 0, 1, \dots, L-1$、$L \leq N_{\text{CP}}$)とすると、送信信号 $s[n]$ を通過した受信信号は線形畳み込み

$$ r[n] = \sum_{m=0}^{L-1} h[m] \, s[n – m] $$

です。CP除去後の受信信号($n = 0, 1, \dots, N-1$)について考えます。$n – m < 0$ のとき、CP があるため $s[n - m] = s[n - m + N]$ が成り立ちます。これは $s[n]$ の周期 $N$ の周期的拡張です。

したがって、CP除去後の受信信号は $N$ 点巡回畳み込み

$$ r[n] = \sum_{m=0}^{L-1} h[m] \, s[(n – m) \bmod N] = (h \circledast s)[n] $$

と等価になります。$\circledast$ は巡回畳み込みを表します。

巡回畳み込みの離散フーリエ変換は、各成分の積になります(巡回畳み込み定理)。

$$ \text{DFT}\{h \circledast s\} = H[k] \cdot S[k] $$

ここで $H[k] = \text{DFT}\{h[n]\}$ はチャネルの周波数応答、$S[k] = X_k$ はデータシンボルです。したがって、FFT復調後の受信シンボルは

$$ R[k] = H[k] \cdot X_k + W[k] $$

という非常にシンプルな形になります。$W[k]$ は雑音の周波数成分です。各サブキャリアで $H[k]$ による1タップ等化を行えば、データシンボルを復元できます。

$$ \hat{X}_k = \frac{R[k]}{H[k]} $$

これがOFDMの最大の利点です。マルチパスチャネルの等化が、周波数領域での単純な割り算に帰着します。

PAPR問題

PAPRの定義

OFDMの欠点として、ピーク対平均電力比(Peak-to-Average Power Ratio: PAPR)が高いという問題があります。PAPRは次のように定義されます。

$$ \text{PAPR} = \frac{\max_{0 \leq n \leq N-1} |s[n]|^2}{E\left[|s[n]|^2\right]} $$

$N$ 個のサブキャリアの信号が同位相で加算された場合、ピーク電力は平均電力の $N$ 倍に達します。

$$ \text{PAPR}_{\max} = N $$

デシベル表記では $10 \log_{10} N$ dB となります。例えば $N = 1024$ の場合、$\text{PAPR}_{\max} = 30\,\text{dB}$ です。

PAPRが問題となる理由

高いPAPRは電力増幅器(PA)の線形動作範囲を超えやすく、信号の歪みを引き起こします。PAの電力効率は入力バックオフに依存するため、PAPRが高いと電力効率が低下します。

PAPR低減技術

代表的なPAPR低減技術には以下があります。

  1. クリッピング: ピークを閾値で制限する(帯域外放射が増加)
  2. SLM(Selected Mapping): 位相回転系列の候補から最もPAPRが低いものを選択
  3. PTS(Partial Transmit Sequence): サブブロックの位相を最適化
  4. DFT-s-OFDM: DFTプリコーディングによりシングルキャリアに近い特性を実現(5G NR の上りリンクで採用)

Pythonによるフルシミュレーション

OFDMシステムの一連の処理をPythonで実装します。QPSK変調、IFFTによるOFDM変調、CP付加、マルチパスチャネル通過、CP除去、FFT復調、チャネル等化、BER計算を行います。

import numpy as np
import matplotlib.pyplot as plt

# ===== パラメータ設定 =====
N = 64                   # サブキャリア数(FFTサイズ)
N_cp = 16                # サイクリックプレフィックス長
N_symbols = 1000         # OFDMシンボル数
modulation_order = 4     # QPSK
SNR_dB_list = np.arange(0, 26, 2)  # SNR範囲 [dB]

# マルチパスチャネル(指数減衰プロファイル)
channel_taps = 10
h = np.exp(-np.arange(channel_taps) / 3.0)
h = h / np.sqrt(np.sum(np.abs(h)**2))  # 電力正規化

# ===== QPSK変復調関数 =====
def qpsk_mod(bits):
    """ビット列をQPSKシンボルに変換"""
    # 2ビットずつ取り出してQPSKシンボルを生成
    symbols = (1 - 2*bits[0::2]) + 1j*(1 - 2*bits[1::2])
    return symbols / np.sqrt(2)

def qpsk_demod(symbols):
    """QPSKシンボルをビット列に復元"""
    bits = np.zeros(2 * len(symbols), dtype=int)
    bits[0::2] = (np.real(symbols) < 0).astype(int)
    bits[1::2] = (np.imag(symbols) < 0).astype(int)
    return bits

# ===== OFDM送受信関数 =====
def ofdm_transmit(data_symbols, N, N_cp):
    """IFFT変調 + CP付加"""
    # IFFT変調
    time_signal = np.fft.ifft(data_symbols, N)
    # サイクリックプレフィックス付加
    cp = time_signal[-N_cp:]
    return np.concatenate([cp, time_signal])

def ofdm_receive(rx_signal, N, N_cp):
    """CP除去 + FFT復調"""
    # CP除去
    rx_no_cp = rx_signal[N_cp:N_cp + N]
    # FFT復調
    return np.fft.fft(rx_no_cp, N)

def add_awgn(signal, snr_db):
    """AWGN雑音を付加"""
    snr_linear = 10**(snr_db / 10)
    signal_power = np.mean(np.abs(signal)**2)
    noise_power = signal_power / snr_linear
    noise = np.sqrt(noise_power / 2) * (
        np.random.randn(len(signal)) + 1j * np.random.randn(len(signal))
    )
    return signal + noise

def multipath_channel(signal, h):
    """マルチパスチャネル通過(線形畳み込み)"""
    return np.convolve(signal, h)

# ===== BERシミュレーション =====
np.random.seed(42)
ber_results = []

for snr_db in SNR_dB_list:
    total_bits = 0
    error_bits = 0

    for _ in range(N_symbols):
        # 送信ビット生成
        tx_bits = np.random.randint(0, 2, N * 2)  # QPSK: 2bit/symbol
        # QPSK変調
        tx_symbols = qpsk_mod(tx_bits)
        # OFDM変調(IFFT + CP付加)
        tx_signal = ofdm_transmit(tx_symbols, N, N_cp)
        # マルチパスチャネル通過
        rx_channel = multipath_channel(tx_signal, h)
        # AWGN雑音付加
        rx_signal = add_awgn(rx_channel, snr_db)
        # OFDM復調(CP除去 + FFT)
        rx_freq = ofdm_receive(rx_signal, N, N_cp)
        # チャネル等化(ゼロフォーシング)
        H = np.fft.fft(h, N)
        rx_eq = rx_freq / H
        # QPSK復調
        rx_bits = qpsk_demod(rx_eq)
        # BER計算
        error_bits += np.sum(tx_bits != rx_bits)
        total_bits += len(tx_bits)

    ber = error_bits / total_bits
    ber_results.append(ber)
    print(f"SNR = {snr_db:2d} dB, BER = {ber:.6f}")

ber_results = np.array(ber_results)
# ===== 結果のプロット =====
fig, axes = plt.subplots(2, 2, figsize=(14, 10))

# (1) BER曲線
ax1 = axes[0, 0]
ax1.semilogy(SNR_dB_list, ber_results, 'bo-', label='OFDM-QPSK (Multipath)')
# AWGN理論値(QPSK)
from scipy.special import erfc
ber_theory = 0.5 * erfc(np.sqrt(10**(SNR_dB_list / 10)))
ax1.semilogy(SNR_dB_list, ber_theory, 'r--', label='QPSK (AWGN Theory)')
ax1.set_xlabel('SNR [dB]')
ax1.set_ylabel('BER')
ax1.set_title('BER Performance of OFDM-QPSK')
ax1.legend()
ax1.grid(True)
ax1.set_ylim([1e-6, 1])

# (2) OFDMシンボルの時間波形
ax2 = axes[0, 1]
tx_bits_demo = np.random.randint(0, 2, N * 2)
tx_sym_demo = qpsk_mod(tx_bits_demo)
tx_ofdm_demo = ofdm_transmit(tx_sym_demo, N, N_cp)
ax2.plot(np.real(tx_ofdm_demo), label='Real part')
ax2.plot(np.imag(tx_ofdm_demo), label='Imag part', alpha=0.7)
ax2.axvline(x=N_cp, color='red', linestyle='--', label='CP boundary')
ax2.set_xlabel('Sample index')
ax2.set_ylabel('Amplitude')
ax2.set_title('OFDM Symbol (Time Domain)')
ax2.legend()
ax2.grid(True)

# (3) チャネルの周波数応答
ax3 = axes[1, 0]
H_plot = np.fft.fft(h, N)
freq_axis = np.arange(N) / N
ax3.plot(freq_axis, 20 * np.log10(np.abs(H_plot)), 'g-')
ax3.set_xlabel('Normalized Frequency')
ax3.set_ylabel('|H(f)| [dB]')
ax3.set_title('Channel Frequency Response')
ax3.grid(True)

# (4) 受信コンスタレーション(等化前後)
ax4 = axes[1, 1]
# 等化前
tx_bits_const = np.random.randint(0, 2, N * 2)
tx_sym_const = qpsk_mod(tx_bits_const)
tx_sig_const = ofdm_transmit(tx_sym_const, N, N_cp)
rx_ch_const = multipath_channel(tx_sig_const, h)
rx_noisy = add_awgn(rx_ch_const, 20)
rx_freq_const = ofdm_receive(rx_noisy, N, N_cp)
rx_eq_const = rx_freq_const / np.fft.fft(h, N)
ax4.scatter(np.real(rx_freq_const), np.imag(rx_freq_const),
            s=5, alpha=0.5, label='Before EQ')
ax4.scatter(np.real(rx_eq_const), np.imag(rx_eq_const),
            s=5, alpha=0.5, label='After EQ')
ax4.set_xlabel('In-phase')
ax4.set_ylabel('Quadrature')
ax4.set_title('Constellation (SNR=20dB)')
ax4.legend()
ax4.grid(True)
ax4.set_aspect('equal')

plt.tight_layout()
plt.savefig('ofdm_simulation.png', dpi=150, bbox_inches='tight')
plt.show()

シミュレーション結果の考察

  1. BER曲線: OFDM-QPSKのBERは、チャネル等化により理論的なAWGN-QPSK曲線に近づきます。ゼロフォーシング等化のため、チャネルのヌル($|H[k]| \approx 0$)付近では雑音が増幅され、理論値からの劣化が生じます。

  2. 時間波形: CP部分(赤い破線の左側)がOFDMシンボル末尾のコピーであることが確認できます。時間波形はガウス的で、中心極限定理によりPAPRが高くなることが視覚的にわかります。

  3. チャネル周波数応答: マルチパスチャネルにより周波数選択性フェージングが発生し、特定の周波数で大きな減衰が生じます。OFDMではサブキャリアごとに等化できるため、この問題に対処できます。

  4. コンスタレーション: 等化前は円状に広がっていた受信点が、等化後はQPSKの4つの信号点に集約されることが確認できます。

# ===== PAPR分析 =====
# PAPRのCCDF(相補累積分布関数)を計算
N_papr = 10000  # OFDMシンボル数
papr_values = []

for _ in range(N_papr):
    bits = np.random.randint(0, 2, N * 2)
    symbols = qpsk_mod(bits)
    # IFFT変調(4倍オーバーサンプリングでピークを正確に捕捉)
    N_os = 4 * N
    freq_data = np.zeros(N_os, dtype=complex)
    freq_data[:N//2] = symbols[:N//2]
    freq_data[N_os - N//2:] = symbols[N//2:]
    time_signal = np.fft.ifft(freq_data, N_os)
    # PAPR計算
    peak_power = np.max(np.abs(time_signal)**2)
    avg_power = np.mean(np.abs(time_signal)**2)
    papr_db = 10 * np.log10(peak_power / avg_power)
    papr_values.append(papr_db)

papr_values = np.array(papr_values)

# CCDF プロット
plt.figure(figsize=(8, 5))
papr_sorted = np.sort(papr_values)
ccdf = 1 - np.arange(1, len(papr_sorted) + 1) / len(papr_sorted)
plt.semilogy(papr_sorted, ccdf, 'b-', label=f'OFDM (N={N}, QPSK)')
# 理論的な上界: 1 - (1 - exp(-x))^N
papr_th = np.linspace(0, 14, 200)
ccdf_th = 1 - (1 - np.exp(-10**(papr_th/10)))**N
plt.semilogy(papr_th, ccdf_th, 'r--', label='Theoretical upper bound')
plt.xlabel('PAPR [dB]')
plt.ylabel('CCDF = Pr(PAPR > x)')
plt.title('CCDF of PAPR for OFDM')
plt.legend()
plt.grid(True)
plt.xlim([0, 14])
plt.ylim([1e-4, 1])
plt.savefig('ofdm_papr.png', dpi=150, bbox_inches='tight')
plt.show()

print(f"PAPR の平均値: {np.mean(papr_values):.2f} dB")
print(f"PAPR の99パーセンタイル: {np.percentile(papr_values, 99):.2f} dB")

まとめ

本記事では、OFDM(直交周波数分割多重)について解説しました。

  • マルチパス伝搬とISI: 高速伝送では遅延拡がりによるISIが深刻な問題となる。OFDMはシンボル長を長くすることでISIを回避する
  • 直交サブキャリア: サブキャリア間隔 $\Delta f = 1/T$ とすることで、$\frac{1}{T}\int_0^T e^{j2\pi(f_k – f_l)t}dt = \delta_{kl}$ が成立し、サブキャリア間干渉なく独立に復調できる
  • IFFT/FFT: OFDM変調はIFFT、復調はFFTで効率的に実装でき、計算量は $O(N\log N)$
  • サイクリックプレフィックス: CPにより線形畳み込みが巡回畳み込みに変換され、周波数領域での1タップ等化 $\hat{X}_k = R[k]/H[k]$ が可能になる
  • PAPR: OFDM信号のPAPRは最大 $N$ ($10\log_{10}N$ dB)に達し、電力増幅器の効率低下を招く

OFDMは Wi-Fi(IEEE 802.11a/g/n/ac/ax)、4G LTE、5G NR など現代の無線通信規格で広く採用されています。

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