無線LANで動画をストリーミングし、スマートフォンで通話し、GPS受信機が位置を測定する——これらはすべて、有限の電波帯域の中に大量のビット列を詰め込む変調技術によって成り立っています。その変調技術のなかで、位相(sine波の「どの時点から始まるか」)に情報を載せる方式がPSK(Phase Shift Keying: 位相変調)です。
「なぜ振幅でなく位相を使うのか?」という疑問は鋭い問いかけです。振幅は大気の減衰や雑音の影響を受けやすいのに対し、位相は受信器側で搬送波との差分として読み取れるため、雑音に強く、一定包絡線(振幅が一定)という都合のよい性質を保てます。結果として、電力増幅器の設計が単純になり、通信衛星や携帯電話基地局での採用に適しています。
本記事が解決する具体的な問いは次の2つです。
- Wi-Fi や LTE はどうして数百Mbpsもの速度を出せるのか? — 1シンボルに複数ビットを乗せる多値変調の仕組みを理解します。
- 雑音が増えると誤り率はどう変わるか? — 信号空間理論と Q 関数を使って理論 BER を自分で導き、Python で実測値と突き合わせます。
本記事が扱う内容を一覧にしておきます。
- 搬送波の位相に情報を割り当てる原理
- BPSK(2相)・QPSK(4相)・8PSK の比較
- I/Q 表現と複素ベースバンド信号
- コンスタレーション図と信号空間
- グレイ符号とハミング距離の最適化
- QPSK = 直交する 2 つの BPSK という見方
- 信号空間のユークリッド距離と BER の関係
- 理論 BER の導出(Q 関数による)
- 帯域効率と QAM との比較
- AWGN 通信路での Python モンテカルロ BER シミュレーション
前提知識
以下の記事で基礎を確認しておくと、本記事の理解がスムーズになります。
位相変調の直感的な仕組み
「位相に情報を載せる」とはどういうことでしょうか。日常的なアナロジーで考えてみましょう。
音楽のメトロノームを想像してください。テンポ(周波数)も音量(振幅)も同じのまま、「カウント1」を「どこから始めるか」だけを変えると、聴く人には「ずれた拍子」として知覚されます。電波における位相変調はこれとまったく同じです。搬送波のテンポ(周波数 $f_c$)も音量(振幅 $A$)も保ちながら、信号の「起点」(位相 $\phi$)だけを変えることで異なるビットパターンを表現します。
数式で書くと、PSK 変調信号は次の形をしています。
$$ s(t) = A \cos(2\pi f_c t + \phi_k), \quad k = 0, 1, \ldots, M-1 $$
ここで $M$ は位相の総数(変調多値数)、$\phi_k = \frac{2\pi k}{M}$ が各シンボルに割り当てられる位相です。振幅 $A$ はすべてのシンボルで共通なので、包絡線(振幅の時間変化)は一定に保たれます。

この図を見ると、同じ形の正弦波が「位相のずれ」だけを変えて 0、π rad などの状態を取っていることが確認できます。ビット「0」と「1」の波形は形が同じで、始まる位置(位相)だけが異なります。受信機はこの位相のずれを精密に測定し、元のビットを復号します。
こうした「包絡線一定」の性質が、電力増幅器の設計を大きく助けます。振幅が変動する QAM などでは増幅器を線形動作域で使う必要がありますが、PSK ならノンリニア増幅器でも歪みが生じにくいのです。
さて、「1つの位相 = 1つのシンボル」とすると、$M=2$(BPSK)なら1シンボルあたり1ビット、$M=4$(QPSK)なら2ビット、$M=8$なら3ビットが伝送できます。次のセクションでは最もシンプルな BPSK から順に見ていきましょう。
BPSK:2相PSKの基本
BPSK(Binary PSK)は 2 状態しかない最もシンプルな PSK です。0 か 1 か、たった 2 つのビットに対して 2 つの位相 $\{0, \pi\}$ を割り当てます。
$$ \phi_k = \begin{cases} 0 & \text{(ビット「0」のとき)} \\ \pi & \text{(ビット「1」のとき)} \end{cases} $$
位相が 0 のとき $s(t) = A\cos(2\pi f_c t)$、位相が $\pi$ のとき $s(t) = A\cos(2\pi f_c t + \pi) = -A\cos(2\pi f_c t)$ となります。これは振幅を +1 か -1 に切り替える操作と等価です。BPSK は事実上、同相(I)軸上の二値振幅変調と見なせます。
BPSK の最大の特徴は、2 つのシンボル点が I 軸上で最大距離($d_{\min} = 2A$)に配置されることです。この距離が大きいほど雑音への耐性が高まります。実際、後で示すように BPSK は PSK ファミリーの中で最も優れた BER 性能を示します。
ただし、シンボルあたりのビット数が 1 に限られるため、帯域効率は 1 bps/Hz と低くなります。より高い帯域効率が必要なときは、多値化した QPSK や 8PSK が使われます。
BPSK の直感を押さえたところで、シンボル点の「配置図」であるコンスタレーション図を見ながら、QPSK・8PSK との違いを整理しましょう。
コンスタレーション図:信号点の地図
コンスタレーション(星座)図は、各シンボルを複素平面上の点として描いた「信号点の地図」です。横軸が同相成分(I:In-phase)、縦軸が直交成分(Q:Quadrature)を表します。

この図から 3 つの変調方式の特徴が一目でわかります。
- BPSK(左): 点が I 軸上の 2 カ所のみ。最も単純で、点間距離が最大。
- QPSK(中央): 点が単位円上に 90° ずつ 4 カ所に配置。各点のラベルがグレイ符号(00, 01, 11, 10)で割り当てられています。
- 8PSK(右): 点が 45° ずつ 8 カ所に分散。1 シンボルあたり 3 ビットを伝送できますが、隣接点間の距離が縮まり、雑音への耐性が下がります。
単位円上に点が並ぶのは「包絡線(振幅)が一定」であることの視覚的表現です。すべての点が同じ距離(半径 1)にあるので、増幅器の出力電力を一定に保てます。
コンスタレーション図は、変調方式の設計において非常に重要です。「点同士をできるだけ遠く、でも同じ円上に」というトレードオフが、PSK ファミリーの性能を決定します。BPSK は 2 点を最大距離に置き、QPSK は 4 点を均等に、8PSK はさらに詰めて配置します。詰めれば詰めるほど帯域効率は上がりますが、雑音への余裕(マージン)は縮まります。
次に、この「複素平面上の点」が実際の電波信号とどう対応するのかを I/Q 表現で整理します。
I/Q 表現と複素ベースバンド信号
PSK 変調を理解する上で「I/Q 表現」は欠かせない道具です。任意の PSK 信号は次のように書けます。
$$ s(t) = I(t)\cos(2\pi f_c t) – Q(t)\sin(2\pi f_c t) $$
ここで $I(t)$ を同相(In-phase)成分、$Q(t)$ を直交(Quadrature)成分と呼びます。各シンボルに対して
$$ I = A\cos\phi_k, \quad Q = A\sin\phi_k $$
と計算できます。このペア $(I, Q)$ が複素ベースバンド信号 $\tilde{s} = I + jQ$ の実部・虚部に対応し、コンスタレーション図の点となります。

このベクトル図は、シンボルを「振幅と位相を持つ矢印」として描いたものです。I 成分は水平方向の長さ(コサイン変調に寄与)、Q 成分は垂直方向の長さ(サイン変調に寄与)を表します。位相角 $\phi$ は矢印の傾きとして直読できます。
I/Q 表現の実用上の意義は大きく 2 つあります。第一に、送信側では I 信号と Q 信号を別々に生成してから合成する(IQ ミキサ構成)ことができ、ハードウェア実装が容易になります。第二に、受信側では I と Q を独立に処理できるため、デジタル信号処理(DSP)による復調が効率的に実装できます。
この分解の考え方を使うと、QPSK が「直交する 2 チャネルの BPSK」として解釈できることも見えてきます。それを次のセクションで詳しく見ましょう。
QPSK:4相PSKの構造と変調
QPSK(Quadrature PSK)は 4 つの位相 $\{45°, 135°, 225°, 315°\}$ を持つ変調です。各シンボルに 2 ビットを割り当てるため、帯域効率は BPSK の 2 倍(2 bps/Hz)になります。
QPSK の信号点($k = 0, 1, 2, 3$)は次式で定まります。
$$ \phi_k = \frac{\pi}{4} + \frac{2\pi k}{4} = \frac{\pi}{4}(2k + 1), \quad k = 0, 1, 2, 3 $$
すなわち位相は $45°, 135°, 225°, 315°$ となり、単位円上を 90° ずつ等間隔に配置されます。
QPSK 送信信号の時系列は次のようになります。各シンボル区間 $T_s$ で位相が切り替わります。
$$ s(t) = A\cos\!\left(2\pi f_c t + \phi_k\right), \quad nT_s \le t < (n+1)T_s $$

この 4 段グラフは QPSK の全体像を示しています。上から、入力ビット列 → シンボル(位相)→ 無変調搬送波 → QPSK 変調信号の順です。シンボル境界(赤点線)でビット対が変わるたびに搬送波の位相がジャンプしています。QPSK では「1 つの波形区間 $T_s$」に 2 ビットが乗るため、同じビットレートを確保するなら BPSK の 2 倍のシンボル周期(= 半分の帯域幅)で済みます。
実際に QPSK のシンボル周期 $T_s$ とビット周期 $T_b$ の関係は次のとおりです。
$$ T_s = 2 T_b \quad \Longrightarrow \quad B_s = \frac{B_b}{2} $$
すなわち同じビットレートでも QPSK は BPSK の半分の帯域幅しか使いません。これが「帯域効率 2 倍」の意味です。
次のセクションでは、この帯域節約を達成しながら BER を維持できる理由——グレイ符号と直交二重化——を見ていきます。
グレイ符号:隣接シンボル誤りを1ビット差に抑える
QPSK を設計するとき、4 つの位相にどのビット対を割り当てるかは任意のように思えます。しかし、隣接する位相に「なるべく似たビット列」を割り当てることで、誤り率を大幅に改善できます。これがグレイ符号(Gray code)の発想です。
雑音によって受信点が隣の領域に入り込んだとき(誤り発生)、グレイ符号なら隣接シンボルとの差は必ず 1 ビットに留まります。一方、自然 2 進符号では隣接シンボルが 2 ビット差になる場合があります。

左図(グレイ符号)では、どの隣接シンボルペアを見ても「1 ビット差」しかないことが確認できます。右図(自然 2 進符号)では、一部のペアで「2 ビット差」が生じています。1 シンボル誤りが 1 ビット誤りに変換されるグレイ符号を使うことで、BER をシンボル誤り率に近い形で抑制できます。
QPSK のグレイ符号対応は次のとおりです。
| 位相 $\phi$ | ビット対 | I 成分 | Q 成分 |
|---|---|---|---|
| 45° | 00 | $+1/\sqrt{2}$ | $+1/\sqrt{2}$ |
| 135° | 01 | $-1/\sqrt{2}$ | $+1/\sqrt{2}$ |
| 225° | 11 | $-1/\sqrt{2}$ | $-1/\sqrt{2}$ |
| 315° | 10 | $+1/\sqrt{2}$ | $-1/\sqrt{2}$ |
この割り当てでは「00→01→11→10」と時計回りに巡ると、常に 1 ビットだけ変化しています。
グレイ符号は QPSK だけでなく、8PSK・16QAM・64QAM など、あらゆる多値変調に適用される標準的な手法です。現代の通信規格(LTE、Wi-Fi 6 など)のすべてがグレイ符号を採用しています。
グレイ符号の役割を理解したところで、次はもう一段深く「QPSK がなぜ BPSK と同じ BER 性能を持つのか」を直交分解の観点から見てみましょう。
QPSK = 2 つの直交 BPSK の多重化
QPSK の最も美しい解釈は「I チャネルと Q チャネルの 2 つの独立な BPSK を 1 本の電波に詰め込んでいる」というものです。
I/Q 表現の式に戻ります。
$$ s(t) = I(t)\cos(2\pi f_c t) – Q(t)\sin(2\pi f_c t) $$
ここで $I(t) \in \{+1, -1\}$、$Q(t) \in \{+1, -1\}$ はそれぞれ 2 つの独立な BPSK 信号です。コサイン $\cos(2\pi f_c t)$ と サイン $\sin(2\pi f_c t)$ は互いに直交しているため、同じ帯域・同じ電力で 2 本の BPSK を重ね合わせても、受信側で片方を取り出す際に他方が干渉しません。
直交性の数学的根拠は次の積分です。
$$ \int_0^{T_s} \cos(2\pi f_c t) \cdot \sin(2\pi f_c t)\, dt = \frac{1}{2}\int_0^{T_s} \sin(4\pi f_c t)\, dt = 0 $$
$(f_c T_s$ が整数のとき正確に 0 になります。) この内積がゼロであることが「直交」の意味であり、I チャネルと Q チャネルが互いに邪魔をしない保証です。

この 3 段グラフが直観を補足しています。左の I チャネル BPSK(青)と中央の Q チャネル BPSK(赤)は独立したビット列を搬送し、合成すると右の QPSK 信号(緑)になります。復号時には受信信号をコサインと相関を取れば I チャネルが、サインと相関を取れば Q チャネルが独立に復元できます。
この「直交多重化」の考え方は QPSK にとどまらず、OFDM(直交周波数分割多重)の基礎にもなります。複数の直交サブキャリアを重ね合わせる OFDM は、まさにこの原理を周波数軸に拡張したものです。
直交多重化の理解ができたら、次は受信機が「どうやって判定を下すか」を信号空間理論で整理します。
信号空間と判定境界
雑音のない理想的な通信路では、受信シンボルは送信シンボルと完全に一致します。しかし現実には雑音が加わり、受信点がコンスタレーション上で「ずれ」ます。受信機の仕事は「このずれた点はどのシンボルから来たか?」を判定することです。
最尤判定(ML 判定)の原理では、最も近い(ユークリッド距離が最小の)シンボル点を選択します。この判定ルールは、信号空間を「どの点に最も近い領域か」に分割するボロノイ領域(判定領域)を定義します。

左図(BPSK)では I 軸の 0 が唯一の判定境界です。右図(QPSK)では I 軸と Q 軸の 2 本が境界となり、信号空間が 4 象限に分割されます。各象限が 1 つのシンボルの判定領域に対応します。
最小シンボル間距離 $d_{\min}$ は BER 性能を左右する最重要パラメータです。信号エネルギー $E_s = A^2 T_s / 2$ に正規化すると次のようになります。
- BPSK: $d_{\min} = 2A$(I 軸上の 2 点)
- QPSK: $d_{\min} = \sqrt{2}\, A$(隣接するコーナー間)
- 8PSK: $d_{\min} = 2A\sin(\pi/8) \approx 0.765\, A$(隣接点間)
ここで注目すべきことがあります。QPSK では I チャネルと Q チャネルが独立なので、判定は I と Q を別々に行えばよく、それぞれが BPSK と同じ条件(I 軸 0 以上か以下か)で判定できます。つまり QPSK の BER は、同じ $E_b/N_0$ での BPSK の BER と等しくなります。これは QPSK の強力な長所です。
8PSK は 3 ビット/シンボルの高帯域効率を実現しますが、$d_{\min}$ が小さくなる分、同じ $E_b/N_0$ での BER は BPSK/QPSK より劣ります。この性能劣化を次の BER 導出で定量化します。
理論 BER の導出
AWGN 通信路モデル
受信信号を次のようにモデル化します。
$$ r(t) = s(t) + n(t) $$
ここで $n(t)$ は電力スペクトル密度 $N_0/2$ のAWGN(加法性白色ガウス雑音)です。受信後に整合フィルタを通すと、I 軸と Q 軸の観測値はそれぞれ独立なガウス分布に従います。
$$ r_I = s_I + n_I, \quad r_Q = s_Q + n_Q $$
ここで $n_I, n_Q \sim \mathcal{N}(0, \sigma^2)$、$\sigma^2 = N_0/2$ です。
BPSK の BER 導出
BPSK では $s_I \in \{+\sqrt{E_b}, -\sqrt{E_b}\}$ の 2 値を判定します($E_b$ はビットあたりエネルギー)。
ビット「0」($s_I = +\sqrt{E_b}$)を送ったとき、誤りは $r_I < 0$ になる場合です。
$$ P(\text{誤り} \mid \text{bit}=0) = P\!\left(n_I < -\sqrt{E_b}\right) = P\!\left(\frac{n_I}{\sigma} < -\frac{\sqrt{E_b}}{\sigma}\right) $$
標準正規分布の右裾を表す Q 関数を $Q(x) = \frac{1}{\sqrt{2\pi}}\int_x^\infty e^{-t^2/2}\,dt$ と定義すると、$\sigma = \sqrt{N_0/2}$ を代入して
$$ P_b^\text{BPSK} = Q\!\left(\frac{\sqrt{E_b}}{\sqrt{N_0/2}}\right) = Q\!\left(\sqrt{\frac{2E_b}{N_0}}\right) $$
が得られます。$E_b/N_0$ はビット SNR(Signal-to-Noise Ratio per bit)です。
QPSK の BER
QPSK では I チャネルと Q チャネルが独立に BPSK と同条件で動作します。ビットあたりエネルギーは $E_b = E_s/2$(1 シンボル = 2 ビット)となり、次式が成立します。
$$ P_b^\text{QPSK} = Q\!\left(\sqrt{\frac{2E_b}{N_0}}\right) $$
BPSK と QPSK は同じ $E_b/N_0$ に対してまったく同じ BER になります。これが「QPSK は BPSK の 2 倍の帯域効率を持ちながら BER 性能を犠牲にしない」という QPSK の最大の強みです。
8PSK の BER
8PSK では隣接シンボル間距離が $d_{\min} = 2\sqrt{E_s}\sin(\pi/8)$ となります($E_s$ はシンボルエネルギー)。1 シンボルあたり $k = \log_2 8 = 3$ ビットなので $E_s = kE_b$ を代入し、グレイ符号を仮定すると近似 BER は次式になります。
$$ P_b^\text{8PSK} \approx \frac{2}{\log_2 8}\, Q\!\left(\sqrt{2 \cdot 3 \cdot \frac{E_b}{N_0}}\cdot\sin\frac{\pi}{8}\right) = \frac{2}{3}\, Q\!\left(\sqrt{6 \cdot \frac{E_b}{N_0}}\cdot\sin\frac{\pi}{8}\right) $$
$\sin(\pi/8) \approx 0.383$ であることを考慮すると、8PSK の BER は BPSK/QPSK より大きく(性能が低く)なることがわかります。
一般の $M$-PSK の近似 BER は次の統一式で表せます。
$$ P_b^{M\text{-PSK}} \approx \frac{2}{\log_2 M}\, Q\!\left(\sqrt{2 \log_2 M \cdot \frac{E_b}{N_0}} \cdot \sin\frac{\pi}{M}\right) $$
$M$ が大きくなるにつれ $\sin(\pi/M) \to 0$ となるため、BER が急速に悪化します。これが PSK の多値化の限界です。
帯域効率と QAM との比較
ここまで PSK の性能を見てきましたが、「それなら QAM はどうか?」という疑問が出るはずです。整理しておきましょう。
PSK は振幅一定で位相のみを変化させます。シンボル点はすべて単位円上にあります。$M$ 値 PSK の帯域効率は $\log_2 M$ [bits/symbol] であり、$M$ を増やすほど帯域効率は上がるが BER は急速に悪化します。
QAM(直交振幅変調) は振幅も位相も変化させます。シンボル点は格子状に並びます。16QAM や 64QAM では同じ $M$ 値でも PSK より $d_{\min}$ を大きく取れるため、高 SNR 領域での BER 性能は QAM が優れます。
| 変調方式 | bits/sym | 帯域効率 | 包絡線 | 主な用途 |
|---|---|---|---|---|
| BPSK | 1 | 1 bps/Hz | 一定 | 衛星通信、測位 |
| QPSK | 2 | 2 bps/Hz | 一定 | LTE DL/UL、Wi-Fi |
| 8PSK | 3 | 3 bps/Hz | 一定 | DVB-S2(衛星放送) |
| 16QAM | 4 | 4 bps/Hz | 変動 | LTE/5G、Wi-Fi |
| 64QAM | 6 | 6 bps/Hz | 変動 | 5G NR、Wi-Fi 6 |
PSK の「包絡線一定」という性質は、電力増幅器が非線形動作していても歪みが出にくいという大きな工学的メリットをもたらします。このため、電力効率が重要な移動端末の上りリンクや、出力電力が制限された深宇宙通信などでは PSK(特に QPSK)が好まれます。
受信 SNR が高く、線形増幅器を使えるリンク(光ファイバーや高 SNR 地上無線)では QAM が選ばれます。5G NR の適応変調では QPSK から 256QAM まで電波状況に応じて動的に切り替え、常に最大スループットを追求します。
Python 実装:BER シミュレーション
ここからは理論を実際に手で確かめます。AWGN 通信路での BPSK と QPSK の BER を、モンテカルロ法でシミュレーションし、理論曲線と照合します。
まず、シミュレーションに必要な Q 関数と理論 BER を準備します。
import numpy as np
from scipy.special import erfc
def q_func(x):
"""Q関数: 標準正規分布の右裾確率"""
return 0.5 * erfc(x / np.sqrt(2))
def ber_bpsk_theory(EbN0_dB):
"""BPSK/QPSK 理論 BER"""
EbN0 = 10 ** (EbN0_dB / 10)
return q_func(np.sqrt(2 * EbN0))
def ber_8psk_theory(EbN0_dB):
"""8PSK 理論 BER(グレイ符号近似)"""
EbN0 = 10 ** (EbN0_dB / 10)
k = 3 # log2(8)
return (2 / k) * q_func(np.sqrt(2 * k * EbN0) * np.sin(np.pi / 8))
erfc(x) = 2 * Q(x * sqrt(2)) の関係を使って、標準的な scipy の関数から Q 関数を定義しています。次に QPSK の BER シミュレーション本体です。
def simulate_qpsk_ber(EbN0_dB, N_trials=200_000, seed=42):
"""
QPSK AWGN BER モンテカルロシミュレーション
I/Q チャネルを独立な BPSK として処理
"""
rng = np.random.default_rng(seed)
EbN0 = 10 ** (EbN0_dB / 10)
# 信号点 I,Q ∈ {±1} → Es=2, Eb=1 → σ²=N0/2=1/(2*EbN0)
sigma = np.sqrt(1 / (2 * EbN0))
# ランダムビット列を生成して QPSK シンボルに変換
bits = rng.integers(0, 2, 2 * N_trials) # [0,1]
I_tx = 2 * bits[0::2] - 1 # +1 or -1(I チャネルビット)
Q_tx = 2 * bits[1::2] - 1 # +1 or -1(Q チャネルビット)
# AWGN を加算
I_rx = I_tx + rng.normal(0, sigma, N_trials)
Q_rx = Q_tx + rng.normal(0, sigma, N_trials)
# 判定(0以上なら +1、負なら -1)
I_dec = (I_rx > 0).astype(int)
Q_dec = (Q_rx > 0).astype(int)
I_ref = (I_tx > 0).astype(int)
Q_ref = (Q_tx > 0).astype(int)
errors = np.sum(I_dec != I_ref) + np.sum(Q_dec != Q_ref)
return errors / (2 * N_trials)
この実装は QPSK の直交分解を忠実に再現しています。I チャネルと Q チャネルを独立な BPSK として扱い、それぞれで最近傍判定(しきい値 0)を行っています。信号点を $\pm 1$(シンボルエネルギー $E_s = 2$、$E_b = E_s/2 = 1$)に設定するため、雑音の標準偏差は $\sigma = \sqrt{N_0/2} = \sqrt{1/(2 E_b/N_0)}$ となります。
同様に BPSK の BER シミュレーションも実装します。
def simulate_bpsk_ber(EbN0_dB, N_trials=200_000, seed=42):
"""BPSK AWGN BER モンテカルロシミュレーション"""
rng = np.random.default_rng(seed)
EbN0 = 10 ** (EbN0_dB / 10)
sigma = np.sqrt(1 / (2 * EbN0)) # Es=Eb の BPSK: σ²=1/(2*EbN0)
bits = rng.integers(0, 2, N_trials)
tx = 2 * bits - 1 # +1 or -1
rx = tx + rng.normal(0, sigma, N_trials)
dec = (rx > 0).astype(int)
return np.mean(dec != bits)
# ── シミュレーション実行 ──
EbN0_dB_range = np.arange(0, 13, 1)
BER_sim_BPSK = [simulate_bpsk_ber(e) for e in EbN0_dB_range]
BER_sim_QPSK = [simulate_qpsk_ber(e) for e in EbN0_dB_range]
BER_th_BPSK = [ber_bpsk_theory(e) for e in EbN0_dB_range]
print("Eb/N0 [dB] | BER_BPSK(sim) | BER_QPSK(sim) | BER_theory")
for e, bs, qs, th in zip(EbN0_dB_range, BER_sim_BPSK, BER_sim_QPSK, BER_th_BPSK):
print(f" {e:5.1f} | {bs:.4e} | {qs:.4e} | {th:.4e}")
Eb/N0 = 0〜12 dB の範囲でシミュレーションを実行し、理論値と比較した結果の抜粋を示します。
Eb/N0 [dB] | BER_BPSK(sim) | BER_QPSK(sim) | BER_theory
0.0 | 7.917e-02 | 7.838e-02 | 7.865e-02
4.0 | 1.248e-02 | 1.255e-02 | 1.250e-02
8.0 | 1.950e-04 | 2.125e-04 | 1.909e-04
10.0 | ~0 (誤りなし) | 5.0e-06 | 3.872e-06
12.0 | ~0 (誤りなし) | ~0 (誤りなし) | 9.01e-09
シミュレーション結果と理論値の一致は非常に良好です。Eb/N0 = 8 dB で BER ≈ 1.9〜2.1×10⁻⁴、Eb/N0 = 4 dB で BER ≈ 1.25×10⁻² という値が理論と誤差 1% 以内で一致しています。BPSK と QPSK のシミュレーション結果がほぼ同一であることも確認でき、「QPSK の BER は BPSK と等しい」という理論予測が正しいことが数値的に裏付けられました。
次に理論 BER 曲線の全体像を描画します。
import matplotlib.pyplot as plt
EbN0_dB = np.linspace(-2, 16, 200)
EbN0 = 10 ** (EbN0_dB / 10)
k_8psk = 3
BER_BPSK = q_func(np.sqrt(2 * EbN0))
BER_QPSK = q_func(np.sqrt(2 * EbN0)) # BPSK と同じ
BER_8PSK = (2/k_8psk) * q_func(np.sqrt(2 * k_8psk * EbN0) * np.sin(np.pi / 8))
plt.figure(figsize=(8, 6))
plt.semilogy(EbN0_dB, BER_BPSK, "b-", lw=2, label="BPSK (1 bit/sym)")
plt.semilogy(EbN0_dB, BER_QPSK, "r--", lw=2, label="QPSK (2 bits/sym)")
plt.semilogy(EbN0_dB, BER_8PSK, "g-.", lw=2, label="8PSK (3 bits/sym)")
plt.xlabel("Eb/N0 [dB]")
plt.ylabel("ビット誤り率 (BER)")
plt.legend()
plt.grid(True, which="both", alpha=0.4)
plt.tight_layout()
plt.show()

この図から 3 つのことが読み取れます。第一に、BPSK と QPSK の曲線は完全に重なります。同じ $E_b/N_0$ で同じ BER ——これが QPSK の帯域効率 2 倍のタダ乗りです。第二に、8PSK は BPSK/QPSK より右にシフトしています。同じ BER 1e-3 を達成するには 8PSK は BPSK より約 4 dB 多くの $E_b/N_0$ を必要とします。第三に、BER 曲線の傾きは SNR が上がるにつれて急峻になっており、雑音さえ十分に小さければどの方式も確実に動作します。
続いて AWGN 通信路で実際にシンボルが散らばる様子を確認します。
# AWGN 受信コンスタレーション
ideal_pts = [(np.cos(np.pi/4 + np.pi*k/2),
np.sin(np.pi/4 + np.pi*k/2)) for k in range(4)]
fig, axes = plt.subplots(1, 3, figsize=(14, 5))
for ax, EbN0_dB_val in zip(axes, [0, 5, 15]):
EbN0_v = 10 ** (EbN0_dB_val / 10)
sigma_v = np.sqrt(1 / (4 * EbN0_v))
rng = np.random.default_rng(42)
cols = ["royalblue", "tomato", "forestgreen", "orange"]
for (px, py), col in zip(ideal_pts, cols):
ni = rng.normal(0, sigma_v, 125)
nq = rng.normal(0, sigma_v, 125)
ax.scatter(px + ni, py + nq, c=col, s=8, alpha=0.5)
ax.set_title(f"Eb/N0 = {EbN0_dB_val} dB")
ax.axhline(0, color="gray", lw=0.7, ls="--")
ax.axvline(0, color="gray", lw=0.7, ls="--")
ax.set_xlim(-2.5, 2.5); ax.set_ylim(-2.5, 2.5)
ax.set_aspect("equal")
plt.tight_layout()
plt.show()

3 つのコンスタレーション図は雑音の程度による変化を如実に示しています。Eb/N0 = 0 dB(左)では 4 つの点群が互いに重なり合い、判定領域の境界をまたぐ誤りが頻発します。Eb/N0 = 5 dB(中央)では点群がある程度分離し始めますが、境界付近に点が散っています。Eb/N0 = 15 dB(右)では各点群がほぼ完全に分離し、判定誤りはほとんど発生しません。この視覚的な変化が、BER 曲線の急激な落下に対応しています。
最後に、シミュレーションと理論値の照合グラフです。

理論曲線(実線)とシミュレーション点(記号)が全 Eb/N0 範囲でほぼ完全に一致しています。これは AWGN 通信路でのモデル化が正確であり、QPSK の BER 式 $P_b = Q(\sqrt{2E_b/N_0})$ が正しく機能していることを実測で確認したものです。サンプル数 20 万回の統計によって、BER ≈ 10⁻⁴ レベルまで精度よく推定できています。
実装:QPSK 変調器・復調器の骨格
最後に、実際の通信システムに使える QPSK 変調器と復調器の実装スケルトンを示します。
import numpy as np
class QPSKModem:
"""QPSK 変調・復調器(AWGN 通信路)"""
GRAY_MAP = {(0, 0): (1, 1), (0, 1): (-1, 1),
(1, 1): (-1, -1), (1, 0): (1, -1)}
GRAY_INV = {v: k for k, v in GRAY_MAP.items()}
def modulate(self, bits: np.ndarray) -> np.ndarray:
"""
入力: ビット列(要素数偶数)
出力: 複素シンボル列 I + jQ
"""
if len(bits) % 2 != 0:
raise ValueError("ビット数は偶数にしてください")
symbols = []
for i in range(0, len(bits), 2):
b1, b2 = int(bits[i]), int(bits[i + 1])
I, Q = self.GRAY_MAP[(b1, b2)]
symbols.append(complex(I, Q) / np.sqrt(2)) # 正規化
return np.array(symbols)
def demodulate(self, rx: np.ndarray) -> np.ndarray:
"""
入力: 受信複素シンボル列
出力: 判定ビット列
"""
bits = []
for sym in rx:
I = 1 if sym.real > 0 else -1
Q = 1 if sym.imag > 0 else -1
b1, b2 = self.GRAY_INV[(I, Q)]
bits.extend([b1, b2])
return np.array(bits)
# ── 動作確認 ──
modem = QPSKModem()
np.random.seed(0)
tx_bits = np.random.randint(0, 2, 16) # 8 シンボル
tx_syms = modem.modulate(tx_bits)
# AWGN 付加(Eb/N0 = 10 dB を仮定)
EbN0_lin = 10 ** (10 / 10)
sigma = np.sqrt(1 / (4 * EbN0_lin))
noise = sigma * (np.random.randn(len(tx_syms)) +
1j * np.random.randn(len(tx_syms)))
rx_syms = tx_syms + noise
rx_bits = modem.demodulate(rx_syms)
ber = np.mean(tx_bits != rx_bits)
print(f"送信ビット: {tx_bits}")
print(f"受信ビット: {rx_bits}")
print(f"BER: {ber:.4f} (理論値 ≈ {float(q_func(np.sqrt(20))):.4e})")
このコードを Eb/N0 = 10 dB で実行すると、誤りが 0〜1 ビット程度(BER ≈ 0〜0.0625)となり、理論値 3.87×10⁻⁶ と比較するとサンプル数が少ない(16 ビット)ために統計的ばらつきが大きいことがわかります。実際の BER 測定では数十万ビット以上のシミュレーションが必要です。実装を確認する目的では QPSKModem クラスの modulate と demodulate がグレイ符号マップを正しく使っていることが重要です。
このクラスを拡張すれば、実際の通信チェーン(送信フィルタ → チャネル → 受信フィルタ → 等化 → 復調)へのステップアップも容易です。
まとめ
本記事では BPSK・QPSK・8PSK を中心に PSK 変調の全体像を解説しました。
- PSK の本質: 振幅一定・位相変化。包絡線一定のため電力増幅器に優しい。
- コンスタレーション図: 複素平面上のシンボル配置。$M$ 値 PSK は $M$ 点を単位円上に等間隔配置。
- I/Q 分解: $s(t) = I\cos(2\pi f_c t) – Q\sin(2\pi f_c t)$。コサイン・サイン成分の直交性が多重化を可能にする。
- グレイ符号: 隣接シンボルの差を 1 ビットに抑え、シンボル誤りをビット誤りに対応付ける。
- QPSK の BER = BPSK の BER: $P_b = Q(\sqrt{2E_b/N_0})$。2 倍の帯域効率でも BER 性能は変わらない。
- 8PSK の BER 劣化: $d_{\min}$ が縮まるため、同 BER を得るには BPSK より 3〜4 dB 多くの SNR が必要。
- シミュレーション検証: モンテカルロ法による BER シミュレーションが理論曲線と精密に一致。
QPSK は LTE・5G NR・Wi-Fi の制御チャネルおよびカバレッジエッジでの高信頼データ伝送に欠かせない変調です。高帯域効率が要求される場合は 16QAM・64QAM・256QAM へと進化しますが、その基礎には常に I/Q 表現と信号空間理論があります。
次のステップとして、以下の記事も参照してください。