1本の高速道路を多数の車が共有するように、1つの通信路を複数のユーザーやデータストリームが共有する仕組みが多重化(multiplexing) です。高速道路の場合は車線を分けたり(空間分割)、時間帯を分けたりして共有しますが、通信の世界では周波数、時間、符号という3つの「次元」を使って通信路を分割します。
この多重化技術がなければ、1つの通信路で1つの通話しかできず、世界中の何十億もの通信デバイスが同時に動作することは不可能です。電話網、テレビ放送、インターネット、携帯電話 — あらゆる通信インフラの根底に多重化技術があります。
多重化技術を理解すると、次のような技術体系が見通せるようになります:
- 4G/5Gモバイル通信: OFDMAはFDMの発展形であり、SC-FDMAと合わせて上り・下りの多元接続を実現
- 光ファイバー通信: WDM(波長分割多重)はFDMの光版であり、1本のファイバーで数十Tbpsの伝送を可能にする
- デジタルオーディオ・映像: TDMはISDN、SONET/SDH、デジタル電話交換の基盤
本記事の内容
- 多重化と多元接続の違い
- FDM(周波数分割多重)の原理とガードバンド
- TDM(時分割多重)の原理とフレーム同期
- CDM(符号分割多重)とウォルシュ符号の直交性
- OFDMA / SC-FDMA の概要と4G/5Gとの関係
- Pythonによるシミュレーション: FDM・TDMの動作と直交性の検証
前提知識
この記事を読む前に、以下の記事を読んでおくと理解が深まります。
多重化と多元接続の違い
「多重化」と「多元接続」はしばしば混同されますが、厳密には異なる概念です。
多重化(Multiplexing) は、1つの物理的な伝送媒体に複数の信号を同時に載せる技術です。典型的には1対1の通信路(ポイント・ツー・ポイント)で使われ、送信側と受信側が制御を統括します。例えば、電話局間の長距離回線に数千本の電話回線を束ねるのが多重化です。
多元接続(Multiple Access) は、複数の独立した送信者が1つの共有メディアにアクセスするための方式です。各送信者は分散的に動作し、互いの干渉を避ける(あるいは管理する)仕組みが必要です。携帯電話の基地局と複数の端末の通信がこれに当たります。
| 観点 | 多重化 | 多元接続 |
|---|---|---|
| 典型的なトポロジ | ポイント・ツー・ポイント | ポイント・ツー・マルチポイント |
| 制御 | 集中管理 | 分散/半分散 |
| 例 | FDM, TDM | FDMA, TDMA, CDMA |
| 同期 | 容易(一箇所で管理) | 困難(分散同期が必要) |
技術的な基本原理は同じで、名前に -M(Multiplexing)が付くか -MA(Multiple Access)が付くかの違いです。本記事では基本原理の解説として「多重化」の用語を中心に使いますが、多元接続への拡張も随時触れます。
この区別を踏まえた上で、最も歴史の古い多重化方式であるFDMから見ていきましょう。
FDM(周波数分割多重)
直感的な理解
FDM(Frequency Division Multiplexing)は、利用可能な周波数帯域を複数の狭い帯域に分割し、各ユーザー(チャンネル)にそれぞれ異なる帯域を割り当てる方式です。
ラジオ放送がFDMの最もわかりやすい例です。AM放送では530〜1710 kHzの帯域を9 kHzまたは10 kHz幅のチャンネルに分割し、各放送局が異なるチャンネルを使います。リスナーは選局ダイヤルで特定の周波数帯域を選ぶことで、目的の放送局を聴取できます。
数学的表現
$N$ 個のチャンネルが帯域幅 $W$ の信号 $m_k(t)$($k = 1, 2, \ldots, N$)をそれぞれ搬送波周波数 $f_k$ で変調して送信する場合、FDM信号は:
$$ \begin{equation} s_{\text{FDM}}(t) = \sum_{k=1}^{N} m_k(t) \cos(2\pi f_k t) \end{equation} $$
隣接チャンネルの搬送波周波数間隔は:
$$ \Delta f = f_{k+1} – f_k \geq W + W_g $$
ここで $W_g$ はガードバンドの帯域幅です。
ガードバンドの必要性
理想的なフィルタ(矩形の周波数応答)が実現できれば $W_g = 0$ とできますが、実際のフィルタには遷移帯域(ロールオフ)があり、隣接チャンネル間に空白の帯域(ガードバンド)を設ける必要があります。
ガードバンドは帯域の無駄ですが、隣接チャンネル干渉(Adjacent Channel Interference: ACI) を防ぐために不可欠です。帯域効率は:
$$ \eta_{\text{FDM}} = \frac{N \cdot W}{N \cdot (W + W_g)} = \frac{W}{W + W_g} $$
フィルタの急峻さが高いほど $W_g$ を小さくでき、帯域効率が向上します。
FDMの長所と短所
長所: – 原理が単純で歴史が長い(アナログ電話網で実績) – 各チャンネルが独立しており、1つの障害が他に波及しにくい – 連続的な伝送に適している(会話、音楽など)
短所: – ガードバンドによる帯域の無駄 – 非線形歪みによる相互変調干渉(IM歪み)に注意が必要 – チャンネル数の変更が柔軟にできない(フィルタの再設計が必要)
FDMが「周波数」という次元で通信路を分割するのに対し、次に見るTDMは「時間」という次元を使います。
TDM(時分割多重)
直感的な理解
TDM(Time Division Multiplexing)は、時間を細かいスロットに分割し、各ユーザーに順番にスロットを割り当てる方式です。
高速道路の料金所を想像してください。複数のレーン(FDM的な空間分割)の代わりに、1つのレーンを時間で区切って「この5秒間はAさん、次の5秒間はBさん」と順番に通過させるイメージです。各ユーザーは自分のスロット期間中は通信路の全帯域幅を独占できます。
数学的表現
$N$ 個のチャンネルの信号を $T_f$(フレーム周期)ごとに多重化する場合:
- フレームを $N$ 個のタイムスロット(各幅 $T_s = T_f / N$)に分割
- チャンネル $k$ は第 $k$ スロットに割り当て
- 各スロットでチャンネル $k$ のサンプルを送信
TDM信号は:
$$ \begin{equation} s_{\text{TDM}}(t) = \sum_{k=1}^{N} m_k(t) \cdot p(t – kT_s) \end{equation} $$
ここで $p(t)$ はスロット期間の矩形パルスで、各フレームで繰り返されます。
フレーム同期
TDMの正しい動作には、受信側が「今どのスロットがどのチャンネルか」を正確に知っている必要があります。これをフレーム同期と呼びます。
フレーム同期は通常、フレームの先頭に挿入された同期ワード(sync word) を受信側が検出することで実現します。同期ワードは、データとして現れにくい特殊なビットパターン(バーカー符号など)が使われます。
ナイキストのサンプリング定理との関係
TDMがアナログ信号にも適用できるのは、ナイキストのサンプリング定理のおかげです。帯域幅 $W$ の信号は $2W$ [サンプル/秒] でサンプリングすれば完全に復元できます。したがって、$N$ チャンネルの帯域幅 $W$ 信号をTDMするには:
$$ f_s = N \times 2W \quad [\text{サンプル/秒}] $$
のサンプリングレート(送信側のクロック速度)が必要です。
例: デジタル電話(T1回線) – 24チャンネル × 8 kHz サンプリング = 192,000 サンプル/秒 – 各サンプル8ビット + フレーム同期1ビット = $24 \times 8 + 1 = 193$ ビット/フレーム – ビットレート: $193 \times 8000 = 1.544$ Mbps
これがT1(DS-1)回線の有名な1.544 Mbpsの由来です。
ガードタイム
FDMにガードバンドがあるように、TDMにはガードタイムがあります。伝搬遅延のばらつきや送信タイミングの誤差を吸収するため、隣接スロット間に短い空白時間を設けます。
多元接続(TDMA)の場合、各端末から基地局までの距離が異なるため伝搬遅延に差が生じ、タイムスロットの境界が曖昧になるリスクがあります。ガードタイムとタイミングアドバンス(基地局が各端末に送信タイミングの補正値を指示する仕組み)で対処します。
TDMの長所と短所
長所: – デジタル信号処理との相性が良い – チャンネル数の変更が比較的柔軟(スロット割り当ての変更) – 全帯域幅を各スロットで利用できるため、バースト的な通信に適している
短所: – 厳密な時間同期が必要 – ガードタイムによる時間の無駄 – 長い伝搬遅延がある環境(衛星通信など)では効率が下がる
FDMが周波数を、TDMが時間を分割するのに対して、第3の方法であるCDMは「符号」という新しい次元を使います。これはスペクトル拡散の技術から生まれた方式です。
CDM(符号分割多重)
直感的な理解
CDM(Code Division Multiplexing)では、全てのユーザーが同じ周波数帯域を同じ時間に使いますが、各ユーザーに異なる拡散符号を割り当てることで信号を区別します。
混雑したパーティーでの会話に例えると、FDMは「異なる言語で話す」(英語、フランス語、日本語…)、TDMは「交代で話す」に相当します。CDMは「全員が同じ言語で同時に話すが、各人が独特のアクセントや話し方を持っていて、聞き手が特定の話し方だけを聞き分けられる」ようなものです。
ウォルシュ符号による直交CDM
CDMで最も理想的なのは、各ユーザーの拡散符号が互いに完全に直交する場合です。ウォルシュ符号(Walsh code) は、この直交性を保証する符号の代表例です。
ウォルシュ符号はアダマール行列から生成されます。$N$ 次のアダマール行列 $\bm{H}_N$ は再帰的に定義されます:
$$ \bm{H}_1 = [1] $$
$$ \begin{equation} \bm{H}_{2N} = \begin{bmatrix} \bm{H}_N & \bm{H}_N \\ \bm{H}_N & -\bm{H}_N \end{bmatrix} \end{equation} $$
$N = 4$ のとき:
$$ \bm{H}_4 = \begin{bmatrix} 1 & 1 & 1 & 1 \\ 1 & -1 & 1 & -1 \\ 1 & 1 & -1 & -1 \\ 1 & -1 & -1 & 1 \end{bmatrix} $$
各行がウォルシュ符号であり、異なる行の内積はゼロです:
$$ \bm{w}_i \cdot \bm{w}_j = \sum_{n=0}^{N-1} w_i(n) w_j(n) = \begin{cases} N & (i = j) \\ 0 & (i \neq j) \end{cases} $$
この直交性を確認しましょう。$\bm{H}_4$ の第1行 $\bm{w}_1 = [1, -1, 1, -1]$ と第2行 $\bm{w}_2 = [1, 1, -1, -1]$ の内積は:
$$ \bm{w}_1 \cdot \bm{w}_2 = (1)(1) + (-1)(1) + (1)(-1) + (-1)(-1) = 1 – 1 – 1 + 1 = 0 $$
確かに直交しています。
CDMの送受信モデル
$K$ 人のユーザーが長さ $N$ のウォルシュ符号 $\bm{w}_k$ を使う場合:
送信(ユーザー $k$): $$ s_k(t) = d_k \cdot \bm{w}_k $$
ここで $d_k \in \{-1, +1\}$ はデータビットです。
多重化(全ユーザーの合成): $$ s(t) = \sum_{k=1}^{K} d_k \cdot \bm{w}_k $$
受信(ユーザー $k$ の復調): $$ \hat{d}_k = \frac{1}{N} \bm{w}_k \cdot s(t) = \frac{1}{N} \bm{w}_k \cdot \sum_{j=1}^{K} d_j \bm{w}_j = \frac{1}{N} \sum_{j=1}^{K} d_j (\bm{w}_k \cdot \bm{w}_j) = d_k $$
ウォルシュ符号の直交性により、ユーザー $k$ のデータ $d_k$ だけが正確に取り出され、他ユーザーの信号は完全にキャンセルされます。
CDMの長所と短所
長所: – 全ユーザーが全帯域・全時間を共有(柔軟なリソース利用) – ソフトハンドオーバー(基地局の切替えが滑らか)に適する – 帯域拡散による耐干渉性、秘匿性 – ユーザー数の緩やかなグレースフルデグラデーション
短所: – 近遠問題(DS-CDMAの場合、パワーコントロールが必須) – 同期が不完全な場合、ウォルシュ符号の直交性が崩れる – 多ユーザー環境でMAI(多元接続干渉)が累積 – 受信機の複雑さが FDM/TDM より高い
3つの基本的な多重化方式を概観したところで、これらを統一的な「直交性」の観点から比較してみましょう。
FDM・TDM・CDMの統一的理解 — 直交性
信号空間としての多重化
3つの多重化方式はすべて、信号の直交性という共通原理に基づいています。直交な信号集合を使ってチャンネルを分離するという点で、本質的に等価です。
数学的に言えば、$N$ 個の信号 $\phi_k(t)$($k = 1, \ldots, N$)が直交であるとは:
$$ \int_0^T \phi_i(t) \phi_j^*(t) \, dt = \begin{cases} E_k & (i = j) \\ 0 & (i \neq j) \end{cases} $$
各方式はこの直交基底を異なる方法で構成しています:
FDM: 異なる周波数の正弦波が直交基底 $$ \phi_k(t) = \cos(2\pi f_k t), \quad \int_0^T \cos(2\pi f_i t)\cos(2\pi f_j t)\,dt \approx 0 \quad (f_i \neq f_j) $$
TDM: 異なる時間スロットの矩形パルスが直交基底 $$ \phi_k(t) = p(t – kT_s), \quad \int_0^T p(t – iT_s)p(t – jT_s)\,dt = 0 \quad (i \neq j) $$
CDM: 異なる拡散符号が直交基底 $$ \phi_k(t) = w_k(t), \quad \sum_n w_i(n) w_j(n) = 0 \quad (i \neq j) $$
時間-周波数平面での可視化
各方式がどのように通信リソース(時間×周波数)を分割するかを整理すると:
| 方式 | 1ユーザーの占有パターン |
|---|---|
| FDM | 狭い帯域 × 全時間 |
| TDM | 全帯域 × 短いスロット |
| CDM | 全帯域 × 全時間(符号で分離) |
FDMは周波数軸を縦に短冊切り、TDMは時間軸を横に短冊切り、CDMは全面を重ねて符号で分離 — これが3方式の幾何学的な対比です。
この統一的な理解を踏まえて、現代の通信で広く使われるOFDMAについて触れましょう。OFDMAはFDMの発展形であり、直交性の概念をさらに洗練させた方式です。
OFDMA / SC-FDMA — 4G/5Gの多元接続
OFDM: ガードバンド不要のFDM
通常のFDMでは、隣接チャンネル間にガードバンドが必要でした。OFDM(Orthogonal Frequency Division Multiplexing:直交周波数分割多重) は、サブキャリアの間隔を $\Delta f = 1/T_s$($T_s$ はOFDMシンボル長)に正確に設定することで、サブキャリア間の直交性を保証し、ガードバンドをゼロにできます。
$k$ 番目のサブキャリアと $l$ 番目のサブキャリアの直交性は:
$$ \int_0^{T_s} e^{j2\pi k \Delta f \, t} \cdot e^{-j2\pi l \Delta f \, t} \, dt = \int_0^{T_s} e^{j2\pi (k-l) \Delta f \, t} \, dt $$
$\Delta f = 1/T_s$ を代入すると:
$$ = \int_0^{T_s} e^{j2\pi (k-l) t/T_s} \, dt = \begin{cases} T_s & (k = l) \\ 0 & (k \neq l) \end{cases} $$
$k \neq l$ のとき、積分範囲がちょうど $e^{j\theta}$ の整数周期分に対応するため、積分がゼロになります。これにより、スペクトルが重なり合っていても互いに干渉しません。
OFDMは離散フーリエ変換(DFT/IDFT)で効率的に実装できます。送信側ではIDFTで時間信号を生成し、受信側ではDFTで各サブキャリアのデータを復元します。FFTアルゴリズムにより $O(N\log N)$ の計算量で実現でき、ハードウェア実装も効率的です。
OFDMAとSC-FDMA
OFDMを多元接続に拡張したのがOFDMA(OFDM Access) です。利用可能なサブキャリアをユーザーごとに動的に割り当てます。
- LTEダウンリンク: OFDMA(各ユーザーに異なるサブキャリア群を割り当て)
- LTEアップリンク: SC-FDMA(Single Carrier FDMA)
SC-FDMAはOFDMAの変形で、送信信号のPAPR(ピーク対平均電力比)を低く抑えられるため、端末側の電力増幅器の効率が改善されます。バッテリー駆動の端末にとって、これは重要な利点です。
5G NRのサブキャリア構成
5G NR(New Radio)では、柔軟なサブキャリア間隔(ヌメロロジー)を導入しています:
| ヌメロロジー ($\mu$) | サブキャリア間隔 | 主な用途 |
|---|---|---|
| 0 | 15 kHz | Sub-6 GHz(LTE互換) |
| 1 | 30 kHz | Sub-6 GHz |
| 2 | 60 kHz | Sub-6 / mmWave |
| 3 | 120 kHz | mmWave |
| 4 | 240 kHz | mmWave(SSブロック用) |
サブキャリア間隔を広げるとシンボル長が短くなり、ドップラー耐性や低遅延が求められるユースケースに対応できます。
OFDMAの理論を理解したところで、Pythonで各多重化方式を実装し、動作を確認しましょう。
Pythonシミュレーション: FDM
FDMの基本動作として、3チャンネルの信号を周波数多重し、復調する過程を実装します。
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import butter, filtfilt
# パラメータ
fs = 10000 # サンプリング周波数 [Hz]
T = 0.5 # 信号長 [s]
t = np.arange(0, T, 1/fs)
N = len(t)
# 3チャンネルのメッセージ信号(異なる周波数)
m1 = np.cos(2 * np.pi * 10 * t) # 10 Hz 正弦波
m2 = 0.5 * (np.sign(np.cos(2 * np.pi * 8 * t))) # 8 Hz 矩形波(近似)
m3 = np.cos(2 * np.pi * 15 * t) + 0.3 * np.cos(2 * np.pi * 5 * t) # 複合波
# 搬送波周波数(十分に離す: ガードバンドあり)
fc1, fc2, fc3 = 500, 800, 1100 # Hz
guard_band = 100 # Hz
# FDM変調
s1 = m1 * np.cos(2 * np.pi * fc1 * t)
s2 = m2 * np.cos(2 * np.pi * fc2 * t)
s3 = m3 * np.cos(2 * np.pi * fc3 * t)
s_fdm = s1 + s2 + s3
# スペクトル
freqs = np.fft.rfftfreq(N, 1/fs)
S_fdm = np.abs(np.fft.rfft(s_fdm)) / N * 2
# FDM復調(帯域通過フィルタ + 復調)
def bandpass_demod(signal, fc, bw, fs, t):
"""帯域通過フィルタ → 搬送波乗算 → ローパスフィルタ"""
# 帯域通過フィルタ
low = (fc - bw) / (fs/2)
high = (fc + bw) / (fs/2)
b_bp, a_bp = butter(4, [low, high], btype='band')
filtered = filtfilt(b_bp, a_bp, signal)
# 搬送波乗算(同期検波)
demod = 2 * filtered * np.cos(2 * np.pi * fc * t)
# ローパスフィルタ
b_lp, a_lp = butter(4, bw / (fs/2), btype='low')
recovered = filtfilt(b_lp, a_lp, demod)
return recovered
bw = 80 # 帯域通過フィルタの半幅 [Hz]
m1_recv = bandpass_demod(s_fdm, fc1, bw, fs, t)
m2_recv = bandpass_demod(s_fdm, fc2, bw, fs, t)
m3_recv = bandpass_demod(s_fdm, fc3, bw, fs, t)
# プロット
fig, axes = plt.subplots(4, 2, figsize=(14, 14))
# 左列: 送信側
axes[0, 0].plot(t[:1000], m1[:1000], 'b-', linewidth=1.5)
axes[0, 0].set_title('Ch1: Message signal (10 Hz sine)')
axes[0, 0].set_ylabel('Amplitude')
axes[0, 0].grid(True, alpha=0.3)
axes[1, 0].plot(t[:1000], m2[:1000], 'g-', linewidth=1.5)
axes[1, 0].set_title('Ch2: Message signal (8 Hz square)')
axes[1, 0].set_ylabel('Amplitude')
axes[1, 0].grid(True, alpha=0.3)
axes[2, 0].plot(t[:1000], m3[:1000], 'r-', linewidth=1.5)
axes[2, 0].set_title('Ch3: Message signal (composite)')
axes[2, 0].set_ylabel('Amplitude')
axes[2, 0].grid(True, alpha=0.3)
# FDMスペクトル
axes[3, 0].plot(freqs, 20*np.log10(S_fdm + 1e-10), 'k-', linewidth=0.8)
axes[3, 0].set_xlim([0, 1500])
axes[3, 0].set_ylim([-60, 0])
axes[3, 0].set_title('FDM Composite Spectrum')
axes[3, 0].set_xlabel('Frequency [Hz]')
axes[3, 0].set_ylabel('Magnitude [dB]')
for fc, label in zip([fc1, fc2, fc3], ['Ch1', 'Ch2', 'Ch3']):
axes[3, 0].axvline(x=fc, color='r', linestyle='--', alpha=0.5)
axes[3, 0].text(fc, -5, label, ha='center', fontsize=9)
axes[3, 0].grid(True, alpha=0.3)
# 右列: 受信側(復調後)
axes[0, 1].plot(t[:1000], m1_recv[:1000], 'b-', linewidth=1.5, label='Recovered')
axes[0, 1].plot(t[:1000], m1[:1000], 'k--', linewidth=1, alpha=0.5, label='Original')
axes[0, 1].set_title('Ch1: Recovered')
axes[0, 1].set_ylabel('Amplitude')
axes[0, 1].legend()
axes[0, 1].grid(True, alpha=0.3)
axes[1, 1].plot(t[:1000], m2_recv[:1000], 'g-', linewidth=1.5, label='Recovered')
axes[1, 1].plot(t[:1000], m2[:1000], 'k--', linewidth=1, alpha=0.5, label='Original')
axes[1, 1].set_title('Ch2: Recovered')
axes[1, 1].set_ylabel('Amplitude')
axes[1, 1].legend()
axes[1, 1].grid(True, alpha=0.3)
axes[2, 1].plot(t[:1000], m3_recv[:1000], 'r-', linewidth=1.5, label='Recovered')
axes[2, 1].plot(t[:1000], m3[:1000], 'k--', linewidth=1, alpha=0.5, label='Original')
axes[2, 1].set_title('Ch3: Recovered')
axes[2, 1].set_ylabel('Amplitude')
axes[2, 1].legend()
axes[2, 1].grid(True, alpha=0.3)
# 復調精度
for idx, (orig, recv, name) in enumerate(zip([m1, m2, m3], [m1_recv, m2_recv, m3_recv], ['Ch1', 'Ch2', 'Ch3'])):
mid = slice(1000, 4000)
corr = np.corrcoef(orig[mid], recv[mid])[0, 1]
axes[idx, 1].text(0.02, 0.05, f'Correlation: {corr:.4f}', transform=axes[idx, 1].transAxes, fontsize=9)
axes[3, 1].axis('off')
axes[3, 1].text(0.1, 0.5, 'FDM: Each channel occupies\na separate frequency band.\nBPF + coherent detection\nrecovers the original signals.',
transform=axes[3, 1].transAxes, fontsize=11, va='center')
plt.tight_layout()
plt.savefig('fdm_demo.png', dpi=150, bbox_inches='tight')
plt.show()
FDMシミュレーションの結果から、周波数多重の動作が確認できます:
- 3チャンネルの信号がスペクトル上で明確に分離されている — 500 Hz, 800 Hz, 1100 Hz にそれぞれのチャンネルが配置され、ガードバンド(300 Hz間隔 – 2×80 Hz帯域 = 140 Hz)により干渉が防がれています。
- 帯域通過フィルタと同期検波により各チャンネルが正確に復元されている — 相関係数が1に近く、元のメッセージ信号とほぼ一致しています。
- 矩形波(Ch2)も復元できているが、高調波がフィルタでカットされるため完全な矩形にはならない — これはフィルタの帯域幅制限による波形の鈍りであり、FDMの原理的な限界ではなく帯域幅の制約です。
次に、TDMの動作をシミュレーションします。
Pythonシミュレーション: TDM
import numpy as np
import matplotlib.pyplot as plt
# パラメータ
N_channels = 4 # チャンネル数
N_frames = 20 # フレーム数
samples_per_slot = 50 # スロットあたりサンプル数
guard_samples = 5 # ガードタイム(サンプル数)
slot_total = samples_per_slot + guard_samples
frame_length = N_channels * slot_total
# 各チャンネルのデータ信号
np.random.seed(42)
channels = []
channel_labels = ['Voice A', 'Voice B', 'Data C', 'Video D']
channel_colors = ['blue', 'green', 'red', 'purple']
for ch in range(N_channels):
freq = 3 + ch * 2 # 異なる周波数
t_ch = np.arange(N_frames * samples_per_slot) / 1000
signal = np.sin(2 * np.pi * freq * t_ch) + 0.3 * np.random.randn(len(t_ch))
channels.append(signal)
# TDM多重化
tdm_signal = np.zeros(N_frames * frame_length)
for frame in range(N_frames):
for ch in range(N_channels):
src_start = frame * samples_per_slot
src_end = src_start + samples_per_slot
dst_start = frame * frame_length + ch * slot_total
dst_end = dst_start + samples_per_slot
tdm_signal[dst_start:dst_end] = channels[ch][src_start:src_end]
# ガードタイムはゼロ(デフォルト)
# TDM分離
recovered = [np.zeros(N_frames * samples_per_slot) for _ in range(N_channels)]
for frame in range(N_frames):
for ch in range(N_channels):
src_start = frame * frame_length + ch * slot_total
src_end = src_start + samples_per_slot
dst_start = frame * samples_per_slot
dst_end = dst_start + samples_per_slot
recovered[ch][dst_start:dst_end] = tdm_signal[src_start:src_end]
# プロット
fig, axes = plt.subplots(3, 1, figsize=(14, 10))
# 各チャンネルの元信号(一部)
for ch in range(N_channels):
t_plot = np.arange(3 * samples_per_slot) / 1000
axes[0].plot(t_plot, channels[ch][:3*samples_per_slot],
color=channel_colors[ch], linewidth=1.5, label=channel_labels[ch])
axes[0].set_title('Original Channel Signals (first 3 frames)')
axes[0].set_ylabel('Amplitude')
axes[0].legend(loc='upper right')
axes[0].grid(True, alpha=0.3)
# TDM多重化信号(3フレーム分)
t_tdm = np.arange(3 * frame_length)
tdm_3frames = tdm_signal[:3 * frame_length]
# カラーリング
for frame in range(3):
for ch in range(N_channels):
start = frame * frame_length + ch * slot_total
end = start + samples_per_slot
t_slot = np.arange(start, end)
axes[1].fill_between(t_slot, -2, 2, alpha=0.1, color=channel_colors[ch])
axes[1].plot(t_slot, tdm_signal[start:end],
color=channel_colors[ch], linewidth=1)
# ガードタイムを表示
for ch in range(N_channels):
guard_start = frame * frame_length + ch * slot_total + samples_per_slot
guard_end = guard_start + guard_samples
axes[1].axvspan(guard_start, guard_end, alpha=0.3, color='gray')
axes[1].set_title(f'TDM Signal (3 frames, {N_channels} slots/frame, guard time = {guard_samples} samples)')
axes[1].set_ylabel('Amplitude')
axes[1].set_xlabel('Sample index')
axes[1].grid(True, alpha=0.3)
# 復元信号
for ch in range(N_channels):
t_recv = np.arange(3 * samples_per_slot) / 1000
axes[2].plot(t_recv, recovered[ch][:3*samples_per_slot],
color=channel_colors[ch], linewidth=1.5, label=channel_labels[ch])
axes[2].set_title('Recovered Channel Signals')
axes[2].set_ylabel('Amplitude')
axes[2].set_xlabel('Time [s]')
axes[2].legend(loc='upper right')
axes[2].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('tdm_demo.png', dpi=150, bbox_inches='tight')
plt.show()
# 復元精度
print("=== TDM復元精度 ===")
for ch in range(N_channels):
mse = np.mean((channels[ch] - recovered[ch])**2)
print(f" {channel_labels[ch]}: MSE = {mse:.2e}")
TDMシミュレーションの結果から、時分割多重の動作が明確に確認できます:
- TDM信号では4チャンネルが時間的に交互に配置されている — カラーリングにより、各スロットがどのチャンネルに対応するかが視覚的にわかります。灰色の領域がガードタイムです。
- 各フレームの構造が規則的に繰り返される — フレーム同期が正しく行われていれば、受信側は各スロットの境界を正確に特定してチャンネルを分離できます。
- 復元信号は元の信号と完全に一致(MSE ≈ 0) — TDMでは各スロットの信号をそのまま取り出すだけなので、理想的な同期環境では情報の損失がありません。
最後に、CDMの直交性を検証するシミュレーションを行います。
Pythonシミュレーション: CDMの直交性検証
ウォルシュ符号を使ったCDMで、複数ユーザーの信号を多重化し、直交性により正しく分離できることを確認します。
import numpy as np
import matplotlib.pyplot as plt
def hadamard_matrix(n):
"""n次アダマール行列を生成(nは2のべき乗)"""
if n == 1:
return np.array([[1]])
H_half = hadamard_matrix(n // 2)
return np.block([[H_half, H_half], [H_half, -H_half]])
# パラメータ
N = 8 # ウォルシュ符号長(= 最大ユーザー数)
K = 4 # アクティブユーザー数
N_bits = 10 # 各ユーザーのビット数
np.random.seed(42)
# ウォルシュ符号
H = hadamard_matrix(N)
walsh_codes = H[1:K+1] # 行0はオール1なので避ける
# 各ユーザーのデータ
data = 2 * np.random.randint(0, 2, (K, N_bits)) - 1
# CDM送信: 各ビットをウォルシュ符号で拡散して加算
cdm_signal = np.zeros((N_bits, N))
for bit_idx in range(N_bits):
for user in range(K):
cdm_signal[bit_idx] += data[user, bit_idx] * walsh_codes[user]
# AWGN雑音を付加
snr_db = 10
noise_power = K * N * 10**(-snr_db/10) # K*Nは信号電力の目安
noise = np.sqrt(noise_power/2) * np.random.randn(N_bits, N)
cdm_received = cdm_signal + noise
# CDM復調: ウォルシュ符号との相関
decoded = np.zeros((K, N_bits), dtype=int)
correlation_values = np.zeros((K, N_bits))
for user in range(K):
for bit_idx in range(N_bits):
corr = np.dot(cdm_received[bit_idx], walsh_codes[user]) / N
correlation_values[user, bit_idx] = corr
decoded[user, bit_idx] = 1 if corr > 0 else -1
# 結果表示
fig, axes = plt.subplots(3, 1, figsize=(14, 10))
# ウォルシュ符号の直交性マトリクス
cross_corr = np.zeros((K, K))
for i in range(K):
for j in range(K):
cross_corr[i, j] = np.dot(walsh_codes[i], walsh_codes[j]) / N
im = axes[0].imshow(cross_corr, cmap='RdBu_r', vmin=-1, vmax=1, aspect='auto')
axes[0].set_title(f'Walsh Code Cross-Correlation Matrix ({K} users, length {N})')
axes[0].set_xlabel('User index')
axes[0].set_ylabel('User index')
for i in range(K):
for j in range(K):
axes[0].text(j, i, f'{cross_corr[i,j]:.1f}', ha='center', va='center', fontsize=12)
plt.colorbar(im, ax=axes[0])
# CDM合成信号の1ビット分
bit_show = 0
chip_indices = np.arange(N)
bar_width = 0.35
for user in range(K):
axes[1].bar(chip_indices + user*bar_width/K, data[user, bit_show] * walsh_codes[user],
width=bar_width/K, label=f'User {user+1} (d={data[user,bit_show]:+d})',
alpha=0.7)
axes[1].bar(chip_indices + 0.5, cdm_signal[bit_show], width=0.15, color='black',
label='CDM sum', alpha=0.8)
axes[1].set_title(f'CDM Signal Composition (bit index = {bit_show})')
axes[1].set_xlabel('Chip index')
axes[1].set_ylabel('Amplitude')
axes[1].legend(loc='upper right', fontsize=8)
axes[1].grid(True, alpha=0.3)
# 復調結果の比較
user_labels = [f'User {i+1}' for i in range(K)]
x_pos = np.arange(N_bits)
bar_w = 0.8 / K
for user in range(K):
offset = (user - K/2 + 0.5) * bar_w
colors = ['green' if d == o else 'red' for d, o in zip(decoded[user], data[user])]
axes[2].bar(x_pos + offset, correlation_values[user], width=bar_w,
color=colors, alpha=0.6, edgecolor='black', linewidth=0.5)
axes[2].axhline(y=0, color='k', linewidth=0.5)
axes[2].set_title(f'Correlation Values after Despreading (SNR = {snr_db} dB, green=correct, red=error)')
axes[2].set_xlabel('Bit index')
axes[2].set_ylabel('Correlation')
axes[2].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('cdm_orthogonality.png', dpi=150, bbox_inches='tight')
plt.show()
# BER計算
total_errors = np.sum(decoded != data)
total_bits = K * N_bits
print(f"=== CDM復調結果 ===")
print(f"ユーザー数: {K}, 符号長: {N}, SNR: {snr_db} dB")
print(f"総ビット数: {total_bits}, エラー数: {total_errors}, BER: {total_errors/total_bits:.4f}")
for user in range(K):
errors = np.sum(decoded[user] != data[user])
print(f" User {user+1}: 送信 {data[user]}, 復調 {decoded[user]}, エラー: {errors}")
CDMシミュレーションの結果から、符号分割多重の核心が確認できます:
- 相互相関マトリクス: 対角要素が1.0(自己相関 = 完全一致)、非対角要素が0.0(相互相関 = ゼロ)です。これがウォルシュ符号の完全な直交性を示しています。
- CDM合成信号: 4ユーザーの拡散信号が重ね合わされた合成信号では、個々のユーザーの信号を目視で区別することはできません。しかし、正しいウォルシュ符号との相関を取ることで、各ユーザーのデータが分離されます。
- 復調相関値: 各ユーザーの逆拡散後の相関値がビットごとに表示されています。正しいデータの符号(+1 or -1)と相関値の符号が一致していれば正しく復調されたことを意味します(緑色)。SNR = 10 dBの雑音環境でも、直交性の力で正確な復調が実現されています。
まとめ
本記事では、通信路を複数のユーザーやデータストリームで共有するための3つの基本的な多重化方式について解説しました。
- FDM は周波数帯域を分割し、各チャンネルに異なる帯域を割り当てる。ガードバンドが必要だが原理が単純
- TDM は時間をスロットに分割し、各チャンネルに順番にスロットを割り当てる。フレーム同期が重要
- CDM は直交符号(ウォルシュ符号など)で信号を拡散し、全ユーザーが同じ帯域・時間を共有する
- 3方式はすべて信号の直交性という共通原理に基づいており、直交基底の構成方法が異なるだけ
- OFDMA はFDMの発展形であり、サブキャリアの直交性を $\Delta f = 1/T_s$ で保証することでガードバンドを不要にした
- 4G/5Gでは OFDMA(下り)/ SC-FDMA(上り)が標準的な多元接続方式
多重化技術は通信工学の最も基本的なインフラ技術であり、無線通信、光通信、ネットワーク技術のすべてに関わっています。
次のステップとして、以下の記事も参考にしてください。