あなたが友人と混雑したカフェで会話しているところを想像してください。周囲は騒がしく、他のテーブルの会話が飛び交っています。通常の会話では、この騒音に負けてしまうかもしれません。しかし、もし友人と事前に「暗号のような特別なルール」を共有していて、それを使えば周囲の騒音の中から自分たちのメッセージだけを取り出せるとしたら? これがスペクトル拡散通信の基本的な発想です。
スペクトル拡散(Spread Spectrum)通信 は、情報信号を元の帯域幅よりもはるかに広い帯域に意図的に「拡散」させて送信し、受信側で同じ拡散符号を使って元の帯域に「逆拡散」して復元する方式です。一見すると帯域幅の無駄遣いに思えますが、この方式は驚くほど強力な利点をもたらします。
スペクトル拡散通信を理解すると、次のような技術に直結します:
- CDMA(符号分割多元接続): 3G携帯電話(W-CDMA)や GPS の基盤技術
- Wi-Fi / Bluetooth: IEEE 802.11b は DS-SS、Bluetooth は FH-SS を採用
- 軍事通信: ジャミング耐性と低被探知性を活かした秘匿通信
本記事の内容
- スペクトル拡散の基本原理と処理利得
- DS-SS(直接拡散)の送受信モデルと逆拡散のメカニズム
- FH-SS(周波数ホッピング)の送受信モデルとホッピングパターン
- DS-SS vs FH-SS の比較(帯域幅、同期、耐干渉性)
- Pythonによる両方式の並列シミュレーション
前提知識
この記事を読む前に、以下の記事を読んでおくと理解が深まります。
なお、上記の既存記事はそれぞれDS-SSとFH-SSの個別の詳細を扱っています。本記事は両方式を統一的な観点から比較・概観することに焦点を当てています。
スペクトル拡散の基本原理
「拡散」とは何か — 周波数領域での直感
通常の通信方式では、情報信号の帯域幅はなるべく狭く保ちます。帯域幅が狭いほど、限られた周波数資源の中でより多くのユーザーが通信できるからです。
スペクトル拡散はこの常識に反して、情報信号を元の帯域幅の数十〜数千倍に「薄く広げて」送信します。イメージとしては、同じ量の絵の具をキャンバス一面に薄く塗り広げるようなものです。薄くなった分、雑音フロアの下に埋もれてしまうこともありますが、受信側で正しい拡散符号を使って「集め直す」と、元の信号レベルに復元できます。
この「拡散 → 伝送 → 逆拡散」のプロセスを数式で表現しましょう。
シャノンの定理との関係
スペクトル拡散の有用性は、シャノンの通信路容量の定理:
$$ C = B \log_2\left(1 + \frac{S}{N}\right) $$
から理解できます。ここで $C$ は通信路容量 [bps]、$B$ は帯域幅 [Hz]、$S/N$ は信号対雑音比です。
この式は、帯域幅 $B$ を増やせば、$S/N$ が低くても同じ通信路容量 $C$ を維持できることを示しています。$S/N \ll 1$ のとき、$\log_2(1 + S/N) \approx S/(N \ln 2)$ と近似できるので:
$$ C \approx \frac{B \cdot S}{N \ln 2} $$
つまり、帯域幅を10倍に広げれば、$S/N$ が10分の1(信号電力が雑音の1/10)でも同じ情報量を伝送できます。スペクトル拡散は、この帯域幅と $S/N$ のトレードオフを積極的に活用する方式なのです。
処理利得(Processing Gain)
拡散前の情報信号の帯域幅を $W_{\text{info}}$、拡散後の帯域幅を $W_{\text{ss}}$ とすると、処理利得(Processing Gain: PG) は:
$$ \begin{equation} G_p = \frac{W_{\text{ss}}}{W_{\text{info}}} \end{equation} $$
デシベルでは $G_p [\text{dB}] = 10\log_{10}(W_{\text{ss}} / W_{\text{info}})$ です。
DS-SSの場合、拡散後の帯域幅はチップレートで決まり、チップレート $R_c$ と情報ビットレート $R_b$ の比が処理利得になります:
$$ G_p = \frac{R_c}{R_b} = N_c \quad (\text{1ビットあたりのチップ数}) $$
処理利得は「逆拡散によって得られるSNRの改善量」を表します。たとえば $G_p = 1000$(30 dB)なら、受信時に信号が雑音の1/100(-20 dB)に埋もれていても、逆拡散後に10 dB(信号が雑音の10倍)のSNRが得られます。
ジャミングマージン
軍事通信で重要な指標がジャミングマージン(Jamming Margin) です:
$$ \begin{equation} M_j = G_p – L_{\text{sys}} – \left(\frac{S}{N}\right)_{\text{out,req}} \quad [\text{dB}] \end{equation} $$
ここで $L_{\text{sys}}$ はシステム損失、$(S/N)_{\text{out,req}}$ は復調に必要な最小SNRです。ジャミングマージンが大きいほど、強力なジャミング信号に対して耐性が高くなります。
スペクトル拡散の基本概念が理解できたところで、具体的な2つの方式 — DS-SSとFH-SS — の仕組みを詳しく見ていきましょう。まずはDS-SSからです。
DS-SS(直接拡散)の送受信モデル
拡散の仕組み
DS-SS(Direct Sequence Spread Spectrum)は、情報ビットに疑似雑音符号(PN符号: Pseudo-Noise code) を直接乗算して帯域を拡散します。
送信プロセスを数式で表すと:
- 情報ビット列 $d(t) \in \{-1, +1\}$(ビット幅 $T_b$)
- PN符号 $c(t) \in \{-1, +1\}$(チップ幅 $T_c$、$T_c \ll T_b$)
- 拡散信号: $s_{\text{bb}}(t) = d(t) \cdot c(t)$
- 搬送波に載せて送信: $s(t) = d(t) \cdot c(t) \cdot \cos(2\pi f_c t)$
1ビットの持続時間 $T_b$ の間に $N_c = T_b / T_c$ 個のチップが含まれます。情報ビットが拡散符号で「細かく刻まれる」ことで、スペクトルが $R_c = 1/T_c$ の帯域幅に広がります。
なぜ広がるのか — スペクトルの変化
情報ビット $d(t)$ のスペクトルは、sinc関数の形でヌル間帯域幅が $2R_b = 2/T_b$ です。PN符号 $c(t)$ のスペクトルも同様にsinc関数ですが、チップレート $R_c$ で決まるヌル間帯域幅は $2R_c = 2/T_c$ です。
$d(t) \cdot c(t)$ の乗算は、周波数領域では畳み込みになります。しかし、$d(t)$ は $c(t)$ よりはるかにゆっくり変化する($R_b \ll R_c$)ため、拡散後の信号のスペクトルはPN符号のスペクトルにほぼ支配されます。つまり:
$$ \text{拡散前の帯域幅} \approx 2R_b \quad \xrightarrow{\text{拡散}} \quad \text{拡散後の帯域幅} \approx 2R_c = 2 N_c R_b $$
帯域幅が $N_c$ 倍に広がり、同時にスペクトル密度(単位周波数あたりのパワー)は $1/N_c$ に低下します。トータルの信号パワーは変わりませんが、広い帯域に薄く分散されます。
逆拡散 — なぜ元に戻るのか
受信側では、送信側と同じPN符号 $c(t)$ を受信信号に乗算します(タイミング同期済みと仮定):
$$ r(t) \cdot c(t) = [d(t) \cdot c(t) + n(t)] \cdot c(t) = d(t) \cdot c^2(t) + n(t) \cdot c(t) $$
ここで鍵となるのは、PN符号の自己相関特性です。$c(t) \in \{-1, +1\}$ なので:
$$ c^2(t) = 1 \quad (\text{常に}) $$
したがって:
$$ r(t) \cdot c(t) = d(t) + n(t) \cdot c(t) $$
第1項: 情報信号 $d(t)$ が元の帯域幅に復元される(逆拡散)。
第2項: 雑音 $n(t)$ は逆にPN符号で拡散され、広帯域に分散されます。ローパスフィルタ(帯域幅 $R_b$)で積分すると、雑音電力は $1/N_c$ に低減します。
これが処理利得 $G_p = N_c$ の物理的な意味です。逆拡散は情報信号を集約し、雑音を分散させる — この非対称な効果がスペクトル拡散の核心です。
他ユーザーの信号(MAI)
CDMA方式では、複数のユーザーが異なるPN符号で同じ帯域を共有します。ユーザー $k$ の信号 $d_k(t) \cdot c_k(t)$ を受信したとき、ユーザー1の逆拡散を行うと:
$$ d_k(t) \cdot c_k(t) \cdot c_1(t) $$
$c_k(t) \neq c_1(t)$ のとき、$c_k(t) \cdot c_1(t)$ は拡散されたままの広帯域信号であり、積分後の寄与は小さくなります。これが理想的な場合ですが、実際には多元接続干渉(MAI: Multiple Access Interference) として残り、CDMAの容量を制限する主要因となります。
DS-SSの仕組みが理解できたところで、もう一つの主要方式であるFH-SSに進みましょう。DS-SSが「帯域を広げる」のに対して、FH-SSは「周波数を飛び回る」という全く異なるアプローチを取ります。
FH-SS(周波数ホッピング)の送受信モデル
ホッピングの仕組み
FH-SS(Frequency Hopping Spread Spectrum)は、搬送波周波数を疑似ランダムなパターンで高速に切り替える方式です。ある瞬間は $f_1$ で送信し、次の瞬間は $f_7$ に飛び、その次は $f_3$ に飛ぶ、というように周波数を「ホッピング」させます。
日常的なアナロジーで言えば、DS-SSが「会話の内容を暗号化する」のに対して、FH-SSは「会話する部屋を次々と変える」ようなものです。盗聴者は正しい部屋の順番を知らなければ、断片的な情報しか得られません。
数学的表現
FH-SS信号は次のように表されます:
$$ \begin{equation} s_{\text{FH}}(t) = m(t) \cdot \cos\bigl(2\pi [f_c + f_h(t)] t + \theta(t)\bigr) \end{equation} $$
ここで $f_h(t)$ はPN符号によって制御されるホッピング周波数のオフセットで、離散的な値の集合 $\{f_{h,0}, f_{h,1}, \ldots, f_{h,M-1}\}$ から選ばれます。$M$ は利用可能な周波数スロットの数です。
ホッピング周期を $T_h$ とすると、1秒あたりのホッピング回数は $R_h = 1/T_h$ です。
低速FH vs 高速FH
情報シンボルレート $R_s$ とホッピングレート $R_h$ の関係で2つのカテゴリに分かれます:
低速FH(Slow FH-SS): $R_h < R_s$
1つのホッピング周期に複数のシンボルが含まれます。つまり、ある周波数に「しばらく留まって」複数シンボルを送信してから次の周波数に飛びます。実装が容易ですが、1つのホッピング周波数がジャミングされると複数シンボルが失われます。
高速FH(Fast FH-SS): $R_h > R_s$
1つのシンボルが複数のホッピング周期にまたがります。同じシンボルが異なる周波数で繰り返し送信されるため、周波数ダイバーシティ効果が得られます。仮に一部の周波数がジャミングされても、他の周波数での受信結果からシンボルを復元できます。ただし、ホッピングの高速化には高性能な周波数シンセサイザが必要です。
FH-SSの処理利得
FH-SSの処理利得は、ホッピング帯域幅 $W_{\text{ss}}$ と瞬時帯域幅 $W_{\text{inst}}$ の比で定義されます:
$$ G_p^{\text{FH}} = \frac{W_{\text{ss}}}{W_{\text{inst}}} \approx M $$
ここで $M$ は周波数スロット数です。直感的には、ジャマーがホッピング帯域全体を妨害するには各スロットに電力を分配する必要があり、1スロットあたりのジャミング電力が $1/M$ に低減するため、処理利得 $M$ が得られます。
FH-SSの耐ジャミング性
FH-SSの大きな利点は、パーシャルバンドジャミング(部分帯域ジャミング) に対する耐性です。ジャマーがホッピング帯域の一部だけを妨害する場合、被害を受けるのはその帯域にホッピングした時間だけです。
ジャマーが帯域全体の割合 $\rho$($0 < \rho \leq 1$)を妨害する場合、ホッピングが均等であれば影響を受ける確率は $\rho$ です。誤り訂正符号を組み合わせることで、$\rho$ が一定以下なら妨害されたシンボルを復元できます。
DS-SSとFH-SSの個別の仕組みが理解できたところで、両者を正面から比較してみましょう。
DS-SS vs FH-SS — 統一的な比較
拡散の方法論の違い
| 観点 | DS-SS | FH-SS |
|---|---|---|
| 拡散の仕組み | 符号乗算で帯域を広げる | 搬送波周波数をランダムに切替える |
| 瞬時帯域幅 | 広い($W_{\text{ss}}$ 全体) | 狭い(1スロット分) |
| スペクトルの形 | 常に広帯域(雑音状) | 瞬間的には狭帯域だが、時間平均で広帯域 |
| 拡散符号の役割 | ビットに直接乗算 | ホッピングパターンの制御 |
性能比較
| 指標 | DS-SS | FH-SS |
|---|---|---|
| 処理利得 | $N_c = R_c / R_b$(数百〜数千) | $M$(周波数スロット数、数百〜数千) |
| 耐狭帯域干渉 | 処理利得で抑圧 | ジャミングされた周波数を回避 |
| 耐広帯域干渉 | 処理利得で抑圧 | 効果限定的 |
| 近遠問題 | 深刻(パワー制御が必要) | 軽微 |
| 同期要求 | チップレベル(厳しい) | ホップレベル(比較的緩い) |
| ホッピング帯域幅 | — | 非常に広くできる(数GHz可能) |
| 多元接続 | CDMA(符号多重) | FH-CDMA(ホッピングパターン多重) |
近遠問題(Near-Far Problem)
DS-SSに特有の深刻な問題が近遠問題です。基地局に近いユーザーの信号は遠いユーザーの信号より圧倒的に強く、逆拡散後もMAIとして残ります。処理利得だけでは抑圧しきれず、近くのユーザーの信号が遠くのユーザーの信号を覆い隠してしまいます。
対策として、基地局が各端末の送信電力を制御するパワーコントロールが不可欠です。W-CDMAでは毎秒1500回ものパワーコントロールコマンドが送られます。
FH-SSでは、ユーザーごとに異なる時刻に異なる周波数を使うため、近遠問題は本質的に緩和されます。
ハイブリッド方式
DS-SSとFH-SSの長所を組み合わせたハイブリッドDS/FH方式も存在します。DS-SS信号を生成した後、さらにその搬送波周波数をホッピングさせます。これにより:
- DS-SSの高い処理利得とFH-SSの広帯域ホッピングを同時に実現
- 近遠問題の緩和とジャミング耐性の両立
- 軍事用通信システム(米軍のJTIDS/Link-16など)で使用
理論的な比較が整理できたところで、Pythonで両方式をシミュレーションし、拡散と逆拡散の動作を視覚的に確認しましょう。
Pythonシミュレーション: DS-SS
まず、DS-SSの拡散・逆拡散プロセスを実装し、処理利得の効果を確認します。
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(42)
# パラメータ
N_bits = 8 # 情報ビット数
N_chips_per_bit = 31 # 1ビットあたりのチップ数(処理利得)
samples_per_chip = 4 # 1チップあたりのサンプル数
# 情報ビット生成(BPSK: +1/-1)
data_bits = 2 * np.random.randint(0, 2, N_bits) - 1
# PN符号生成(M系列の代わりにランダム)
pn_code = 2 * np.random.randint(0, 2, N_chips_per_bit) - 1
# 拡散: 各ビットをPN符号で拡散
spread_signal = np.array([])
for bit in data_bits:
spread_signal = np.append(spread_signal, bit * pn_code)
# サンプリング(波形生成用)
def upsample(signal, factor):
return np.repeat(signal, factor)
data_waveform = upsample(np.repeat(data_bits, N_chips_per_bit), samples_per_chip)
spread_waveform = upsample(spread_signal, samples_per_chip)
pn_waveform = upsample(np.tile(pn_code, N_bits), samples_per_chip)
# 雑音付加(低SNR環境)
snr_db = -15 # 拡散帯域でのSNR
noise_power = np.mean(spread_waveform**2) * 10**(-snr_db/10)
noise = np.sqrt(noise_power) * np.random.randn(len(spread_waveform))
received = spread_waveform + noise
# 逆拡散
despread = received * pn_waveform
# ビット判定(チップ期間で積分)
chip_samples = samples_per_chip
bit_samples = N_chips_per_bit * chip_samples
detected_bits = []
for i in range(N_bits):
start = i * bit_samples
end = (i + 1) * bit_samples
integral = np.sum(despread[start:end])
detected_bits.append(1 if integral > 0 else -1)
detected_bits = np.array(detected_bits)
# 時間軸
t = np.arange(len(data_waveform))
fig, axes = plt.subplots(5, 1, figsize=(14, 12), sharex=True)
axes[0].plot(t, data_waveform, 'b-', linewidth=1.5)
axes[0].set_ylabel('Amplitude')
axes[0].set_title(f'Data bits: {data_bits}')
axes[0].set_ylim([-1.5, 1.5])
axes[0].grid(True, alpha=0.3)
axes[1].plot(t, pn_waveform, 'g-', linewidth=0.5)
axes[1].set_ylabel('Amplitude')
axes[1].set_title(f'PN code ({N_chips_per_bit} chips/bit)')
axes[1].set_ylim([-1.5, 1.5])
axes[1].grid(True, alpha=0.3)
axes[2].plot(t, spread_waveform, 'm-', linewidth=0.5)
axes[2].set_ylabel('Amplitude')
axes[2].set_title('Spread signal (data x PN)')
axes[2].set_ylim([-1.5, 1.5])
axes[2].grid(True, alpha=0.3)
axes[3].plot(t, received, 'r-', linewidth=0.3, alpha=0.7)
axes[3].set_ylabel('Amplitude')
axes[3].set_title(f'Received signal (SNR_spread = {snr_db} dB)')
axes[3].grid(True, alpha=0.3)
axes[4].plot(t, despread, 'c-', linewidth=0.3, alpha=0.7)
# ビット判定結果を重ねて表示
for i in range(N_bits):
start = i * bit_samples
end = (i + 1) * bit_samples
color = 'blue' if detected_bits[i] == data_bits[i] else 'red'
axes[4].axhspan(-0.3, 0.3, xmin=start/len(t), xmax=end/len(t), alpha=0.2, color=color)
axes[4].set_ylabel('Amplitude')
axes[4].set_title(f'Despread signal → Detected: {detected_bits} (BER = {np.mean(detected_bits != data_bits):.2f})')
axes[4].set_xlabel('Sample')
axes[4].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('dsss_demo.png', dpi=150, bbox_inches='tight')
plt.show()
print(f"送信ビット: {data_bits}")
print(f"検出ビット: {detected_bits}")
print(f"処理利得: {10*np.log10(N_chips_per_bit):.1f} dB")
print(f"拡散帯域SNR: {snr_db} dB")
print(f"逆拡散後の推定SNR: {snr_db + 10*np.log10(N_chips_per_bit):.1f} dB")
DS-SSシミュレーションの結果から、スペクトル拡散の威力が明確に確認できます:
- 拡散信号はPN符号によって高速に変化するチップ列になっている — 元のデータビットの構造は目視では判別できず、広帯域雑音のように見えます。これが低被探知性の源です。
- 受信信号はSNR = -15 dBで完全に雑音に埋もれている — 拡散帯域では信号電力が雑音電力の約30分の1しかありません。通常の復調では全く信号を取り出せません。
- 逆拡散後、元のデータビットが復元されている — 処理利得 $G_p = 10\log_{10}(31) \approx 14.9$ dBにより、逆拡散後のSNRは $-15 + 14.9 \approx 0$ dB近くまで改善されます。低いSNRでもビット判定が可能になっています。
次に、スペクトル領域で拡散・逆拡散の効果を確認します。
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(42)
# パラメータ
N_bits = 64
N_chips = 63 # 処理利得
samples_per_chip = 8
fs = 1.0 # 正規化サンプリング周波数
# データ生成と拡散
data = 2 * np.random.randint(0, 2, N_bits) - 1
pn = 2 * np.random.randint(0, 2, N_chips) - 1
spread = np.array([])
for bit in data:
spread = np.append(spread, bit * pn)
# 波形のアップサンプリング
data_wave = np.repeat(np.repeat(data, N_chips), samples_per_chip)
spread_wave = np.repeat(spread, samples_per_chip)
pn_wave = np.repeat(np.tile(pn, N_bits), samples_per_chip)
# 雑音
noise = 3.0 * np.random.randn(len(spread_wave))
received = spread_wave + noise
# 逆拡散
despread = received * pn_wave
# パワースペクトル密度(PSD)
def compute_psd(signal, nfft=4096):
f = np.fft.rfftfreq(nfft)
psd = np.abs(np.fft.rfft(signal, n=nfft))**2 / nfft
# 平滑化
window = 5
psd_smooth = np.convolve(psd, np.ones(window)/window, mode='same')
return f, 10 * np.log10(psd_smooth + 1e-15)
fig, axes = plt.subplots(2, 2, figsize=(14, 8))
f, psd = compute_psd(data_wave)
axes[0, 0].plot(f, psd, 'b-', linewidth=1)
axes[0, 0].set_title('PSD: Original data')
axes[0, 0].set_ylabel('PSD [dB]')
axes[0, 0].set_xlim([0, 0.5])
axes[0, 0].grid(True, alpha=0.3)
f, psd = compute_psd(spread_wave)
axes[0, 1].plot(f, psd, 'g-', linewidth=1)
axes[0, 1].set_title('PSD: Spread signal')
axes[0, 1].set_ylabel('PSD [dB]')
axes[0, 1].set_xlim([0, 0.5])
axes[0, 1].grid(True, alpha=0.3)
f, psd = compute_psd(received)
axes[1, 0].plot(f, psd, 'r-', linewidth=1)
axes[1, 0].set_title('PSD: Received (spread + noise)')
axes[1, 0].set_xlabel('Normalized frequency')
axes[1, 0].set_ylabel('PSD [dB]')
axes[1, 0].set_xlim([0, 0.5])
axes[1, 0].grid(True, alpha=0.3)
f, psd = compute_psd(despread)
axes[1, 1].plot(f, psd, 'c-', linewidth=1)
axes[1, 1].set_title('PSD: After despreading')
axes[1, 1].set_xlabel('Normalized frequency')
axes[1, 1].set_ylabel('PSD [dB]')
axes[1, 1].set_xlim([0, 0.5])
axes[1, 1].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('dsss_spectrum.png', dpi=150, bbox_inches='tight')
plt.show()
PSDの比較から、拡散・逆拡散のスペクトル上の効果が確認できます:
- 拡散前のデータ: 低周波に集中した狭帯域スペクトルです。
- 拡散後: スペクトルが広帯域に「平坦化」されています。元のデータの狭帯域な特徴は消え、白色雑音に近い形状になっています。
- 受信信号(雑音込み): 拡散信号と雑音のスペクトルが混在し、目視では信号の存在が判別できません。
- 逆拡散後: 低周波域にエネルギーが再集中し、元のデータのスペクトル構造が復元されています。雑音成分は広帯域に分散されたままです。
これがスペクトル拡散の本質的な動作です。続いて、FH-SSのシミュレーションに進みましょう。
Pythonシミュレーション: FH-SS
FH-SSでは、搬送波周波数がホッピングパターンに従って切り替わる様子をシミュレーションします。
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(123)
# パラメータ
fs = 50000 # サンプリング周波数 [Hz]
N_symbols = 8 # シンボル数
T_hop = 0.005 # ホッピング周期 [s](= シンボル周期: slow FH)
M = 8 # 周波数スロット数
f_base = 2000 # ベース周波数 [Hz]
f_spacing = 500 # スロット間隔 [Hz]
hop_frequencies = f_base + np.arange(M) * f_spacing # 利用可能周波数
samples_per_hop = int(T_hop * fs)
# データシンボル(BFSK簡易版: +1/-1)
data = 2 * np.random.randint(0, 2, N_symbols) - 1
# ホッピングパターン(PN符号で決定)
hop_pattern = np.random.permutation(M)[:N_symbols] % M
# FH-SS信号生成
fh_signal = np.array([])
time_axis = np.array([])
hop_freq_over_time = np.array([])
for i in range(N_symbols):
t_hop = np.arange(samples_per_hop) / fs
f_hop = hop_frequencies[hop_pattern[i]]
# データで周波数を微小シフト(簡易BFSK)
f_shift = data[i] * 100 # +/-100 Hz
segment = np.cos(2 * np.pi * (f_hop + f_shift) * t_hop)
fh_signal = np.append(fh_signal, segment)
time_axis = np.append(time_axis, t_hop + i * T_hop)
hop_freq_over_time = np.append(hop_freq_over_time, np.full(samples_per_hop, f_hop))
# スペクトログラム計算
fig, axes = plt.subplots(3, 1, figsize=(14, 10))
# ホッピングパターン
for i in range(N_symbols):
f_hop = hop_frequencies[hop_pattern[i]]
t_start = i * T_hop
t_end = (i + 1) * T_hop
axes[0].barh(f_hop/1000, T_hop*1000, left=t_start*1000, height=f_spacing*0.8/1000,
color=plt.cm.Set3(hop_pattern[i]/M), edgecolor='black', linewidth=0.5)
axes[0].text((t_start + T_hop/2)*1000, f_hop/1000,
f'd={data[i]:+d}', ha='center', va='center', fontsize=8)
axes[0].set_ylabel('Frequency [kHz]')
axes[0].set_title('FH-SS Hopping Pattern')
axes[0].set_ylim([f_base/1000 - 0.3, (f_base + M*f_spacing)/1000 + 0.3])
axes[0].grid(True, alpha=0.3)
# 時間波形
axes[1].plot(time_axis * 1000, fh_signal, 'b-', linewidth=0.3)
axes[1].set_ylabel('Amplitude')
axes[1].set_title('FH-SS Time Domain Signal')
axes[1].grid(True, alpha=0.3)
# スペクトログラム
nfft = 256
axes[2].specgram(fh_signal, NFFT=nfft, Fs=fs, noverlap=nfft//2, cmap='inferno')
axes[2].set_ylabel('Frequency [Hz]')
axes[2].set_xlabel('Time [s]')
axes[2].set_title('Spectrogram of FH-SS Signal')
axes[2].set_ylim([f_base - 500, f_base + M * f_spacing + 500])
plt.tight_layout()
plt.savefig('fhss_demo.png', dpi=150, bbox_inches='tight')
plt.show()
print(f"データシンボル: {data}")
print(f"ホッピングパターン: {hop_pattern}")
print(f"使用周波数 [Hz]: {[hop_frequencies[h] for h in hop_pattern]}")
FH-SSシミュレーションの結果から、ホッピングの動作が視覚的に確認できます:
- ホッピングパターン図: 各シンボル期間で異なる周波数スロットが使用されていることがわかります。パターンはPN符号で決まるため、受信側は同じPN符号を持つことでのみ正しい周波数系列を追跡できます。
- 時間波形: 各ホッピング周期の境界で波形の周波数(振動の密度)が不連続に変化しています。これがFH-SSの時間領域での特徴です。
- スペクトログラム: 時間-周波数平面上で信号が「飛び跳ねる」様子が明確に可視化されています。どの瞬間にも1つの狭帯域スロットしか使用していませんが、時間を通じて広い帯域をカバーしています。
最後に、DS-SSとFH-SSの耐干渉性を直接比較するシミュレーションを行いましょう。
DS-SS vs FH-SS: 耐干渉性の比較
狭帯域ジャミング信号に対する両方式の挙動を比較します。
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(42)
# 共通パラメータ
N_bits = 1000
N_chips = 31 # DS-SSの処理利得
# === DS-SS ===
data_dsss = 2 * np.random.randint(0, 2, N_bits) - 1
pn = 2 * np.random.randint(0, 2, N_chips) - 1
# 拡散
spread_chips = np.array([])
for bit in data_dsss:
spread_chips = np.append(spread_chips, bit * pn)
# 狭帯域ジャミング(単一トーン)
jammer_freq = 0.2 # 正規化周波数
t_chips = np.arange(len(spread_chips))
jammer_power_range = np.arange(0, 30, 2) # J/S [dB]
ber_dsss = []
for js_db in jammer_power_range:
js_lin = 10**(js_db/10)
jammer = np.sqrt(2 * js_lin) * np.cos(2 * np.pi * jammer_freq * t_chips)
noise = 0.5 * np.random.randn(len(spread_chips))
received = spread_chips + jammer + noise
# 逆拡散
pn_full = np.tile(pn, N_bits)
despread = received * pn_full
# ビット判定
detected = []
for i in range(N_bits):
start = i * N_chips
end = (i+1) * N_chips
integral = np.sum(despread[start:end])
detected.append(1 if integral > 0 else -1)
ber = np.mean(np.array(detected) != data_dsss)
ber_dsss.append(ber)
# === FH-SS ===
M_slots = 31 # 周波数スロット数(処理利得と揃える)
data_fhss = 2 * np.random.randint(0, 2, N_bits) - 1
hop_pattern_fhss = np.random.randint(0, M_slots, N_bits)
# ジャマーは1つのスロットを妨害
jammer_slot = 5
ber_fhss = []
for js_db in jammer_power_range:
js_lin = 10**(js_db/10)
noise_base = 0.5
detected = []
for i in range(N_bits):
# 受信信号 = data + noise + (ジャミング if same slot)
rx = data_fhss[i] + noise_base * np.random.randn()
if hop_pattern_fhss[i] == jammer_slot:
rx += np.sqrt(2 * js_lin) * (2*np.random.randint(0,2)-1)
detected.append(1 if rx > 0 else -1)
ber = np.mean(np.array(detected) != data_fhss)
ber_fhss.append(ber)
# パーシャルバンドジャミング(複数スロット妨害)
ber_fhss_partial = []
n_jammed_slots = 5 # 5/31スロットを妨害
jammed_slots = set(range(n_jammed_slots))
for js_db in jammer_power_range:
js_per_slot = 10**(js_db/10) / n_jammed_slots # 電力分散
noise_base = 0.5
detected = []
for i in range(N_bits):
rx = data_fhss[i] + noise_base * np.random.randn()
if hop_pattern_fhss[i] in jammed_slots:
rx += np.sqrt(2 * js_per_slot) * (2*np.random.randint(0,2)-1)
detected.append(1 if rx > 0 else -1)
ber = np.mean(np.array(detected) != data_fhss)
ber_fhss_partial.append(ber)
# 結果プロット
fig, ax = plt.subplots(figsize=(10, 6))
ax.semilogy(jammer_power_range, np.array(ber_dsss) + 1e-4, 'bo-', label='DS-SS (narrowband jammer)')
ax.semilogy(jammer_power_range, np.array(ber_fhss) + 1e-4, 'r^-', label='FH-SS (single-slot jammer)')
ax.semilogy(jammer_power_range, np.array(ber_fhss_partial) + 1e-4, 'gs-', label=f'FH-SS (partial-band: {n_jammed_slots}/{M_slots} slots)')
ax.set_xlabel('Jammer-to-Signal Ratio [dB]')
ax.set_ylabel('Bit Error Rate')
ax.set_title('DS-SS vs FH-SS: Anti-jamming Performance')
ax.legend()
ax.grid(True, which='both', alpha=0.3)
ax.set_ylim([1e-4, 1])
plt.tight_layout()
plt.savefig('dsss_vs_fhss_jamming.png', dpi=150, bbox_inches='tight')
plt.show()
耐干渉性比較の結果から、両方式の異なるジャミング耐性特性が明確に見えます:
- DS-SSは狭帯域ジャマーに対して強い — 処理利得 $G_p = 31$(約15 dB)により、ジャマー電力がかなり大きくなるまでBERが低く保たれています。逆拡散がジャミング信号を広帯域に分散させるためです。
- FH-SS(単一スロットジャマー)は影響が限定的 — ジャマーが1スロットだけを妨害する場合、影響を受ける確率は $1/M \approx 3\%$ です。ジャマー電力に関係なくBERが一定レベルに留まる特徴があります。
- FH-SS(部分帯域ジャマー) — ジャマーが電力を分散させて複数スロットを妨害する場合、各スロットのジャミング電力は低下するため、BERの増加は緩やかです。
DS-SSはジャマーを「抑圧する」のに対し、FH-SSはジャマーを「回避する」という、本質的に異なるアプローチであることがシミュレーション結果からも確認できます。
まとめ
本記事では、スペクトル拡散通信の2つの主要方式 — DS-SSとFH-SS — を統一的な視点から比較しました。
- スペクトル拡散 は情報信号を広帯域に拡散させることで、処理利得を得る方式であり、シャノンの定理における帯域幅とSNRのトレードオフを積極的に活用する
- DS-SS はPN符号の直接乗算で帯域を拡散し、逆拡散で信号を復元する。$c^2(t) = 1$ という性質が全ての鍵
- FH-SS は搬送波周波数のランダムな切替えで帯域を拡散し、周波数ダイバーシティ効果を得る
- DS-SSは狭帯域干渉を「処理利得で抑圧」し、FH-SSは干渉を「周波数回避」するという異なる戦略を持つ
- 近遠問題はDS-SS特有の課題であり、パワーコントロールが不可欠
- ハイブリッド方式 により両者の利点を組み合わせることも可能
スペクトル拡散は、CDMA(3G)、GPS、Wi-Fi、Bluetoothなど、現代の無線通信の基盤技術です。
次のステップとして、以下の記事も参考にしてください。