ターボ符号の理論 — インターリーバと反復復号でシャノン限界に迫る

携帯電話で通話しているとき、電波が弱い場所でも音声が途切れにくいのはなぜでしょうか。深宇宙探査機ボイジャーが何十億キロも離れた場所から、わずか数ワットの電力で地球に鮮明な画像を送れるのはなぜでしょうか。その答えの背景にあるのが誤り訂正符号です。そして1993年、誤り訂正符号の歴史を塗り替える画期的な発明が生まれました — それがターボ符号(Turbo Code)です。

シャノンが1948年に通信路符号化定理を証明してから45年間、研究者たちは「理論限界にどこまで近づけるか」を追い求めてきました。畳み込み符号とビタビ復号は広く実用化されていましたが、シャノン限界からは数dBの隔たりがありました。ターボ符号はその隔たりをわずか0.5 dB以内にまで縮め、通信工学に衝撃を与えました。

ターボ符号を理解すると、以下のような広い分野への見通しが開けます。

  • 移動体通信(3G/4G LTE): 3GPPの3G(WCDMA)および4G LTEのデータチャネルにターボ符号が採用されており、高速移動体通信を支える基盤技術です
  • 深宇宙通信: NASAの火星探査ミッション(MERやMSL)でターボ符号が使われ、限られた送信電力で信頼性の高いデータ伝送を実現しています
  • 反復復号の設計原理: ターボ符号が切り拓いた反復復号の概念は、LDPC符号やポーラー符号のSCL復号にも影響を与え、現代の符号理論全体を貫く設計哲学となっています

本記事の内容

  • ターボ符号の歴史的意義と発明の背景
  • 並列連接構造(2つのRSC符号器+インターリーバ)の直感的理解と数学的定式化
  • MAP復号アルゴリズム(BCJR)の原理:前向き・後向き再帰
  • 反復復号:外部情報の交換による収束メカニズム
  • EXIT chart(外部情報伝達図)による収束解析
  • Pythonでのターボ符号化+反復MAP復号シミュレーションとBERカーブの描画

前提知識

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

ターボ符号の歴史的意義

シャノン限界への挑戦

1948年、クロード・シャノンは通信路符号化定理を証明し、通信路容量 $C$ 以下の伝送レートであれば誤り率を限りなくゼロにできることを示しました。しかし、この定理はランダム符号の平均性能に基づく「存在証明」であり、具体的にどのような符号を使えば限界に到達できるのかは示していませんでした。

その後の数十年間、ハミング符号、BCH符号、リードソロモン符号、畳み込み符号など多くの符号が開発されました。これらはいずれも実用的に優れていましたが、シャノン限界からは数dBの差がありました。特に、AWGN(加法性白色ガウス雑音)通信路で符号化率 $R = 1/2$ の場合、シャノン限界は $E_b/N_0 \approx 0.19$ dBですが、1990年代初頭の最良の符号でも $E_b/N_0 \approx 2$ dB以上を必要としていました。

1993年 — Berrouらの画期的論文

1993年、フランスのClaude Berrou、Alain Glavieux、Punya Thitimajshimaの3名は、ICC(International Conference on Communications)において「Near Shannon Limit Error-Correcting Coding and Decoding: Turbo-Codes」と題した論文を発表しました。この論文は、通信工学の歴史を変える画期的な成果を報告しました。

論文が示した結果は衝撃的でした。符号化率 $R = 1/2$、フレーム長 65,536ビットのターボ符号が、AWGN通信路で $\text{BER} = 10^{-5}$ を $E_b/N_0 \approx 0.7$ dBで達成したのです。シャノン限界 0.19 dBからわずか0.5 dBという驚異的な性能でした。

この結果は当初、多くの研究者に信じがたいものとして受け取られました。しかし、独立した追試により正しいことが確認され、通信工学のコミュニティに「ターボ革命」とも呼ばれる大きなパラダイムシフトを引き起こしました。

ターボ符号の名前の由来

「ターボ」という名前は、自動車のターボチャージャーに由来します。ターボチャージャーでは、排気ガスのエネルギーを使ってタービンを回し、吸気を圧縮してエンジンに送り返します。排気→タービン→吸気→エンジン→排気というフィードバックループによって性能を高める仕組みです。

ターボ符号でも同様に、2つの復号器が互いの出力(外部情報)をフィードバックし合うことで、反復のたびに推定精度を高めていきます。この反復的なフィードバック構造がターボチャージャーと類似しているため、この名前が付けられました。

ターボ符号がなぜこれほどの性能を発揮できるのかを理解するために、まずその符号化構造を見ていきましょう。

並列連接構造の概要

2つの符号器を組み合わせる発想

ターボ符号の符号化器は、なぜ強力なのでしょうか。その直感は意外にもシンプルです。

試験勉強に例えてみましょう。1人の先生が生徒の答案をチェックするよりも、2人の先生がそれぞれ異なる視点で答案をチェックする方が、見落としが少なくなります。しかも、2人の先生に同じ順番で答案を見せるのではなく、問題の順番をシャッフルしてから2人目に渡せば、1人目が見落としやすいパターンの誤りも2人目が発見できる可能性が高まります。

ターボ符号はまさにこの発想です。2つの符号器(先生)が同じ情報ビット列を符号化しますが、2つ目の符号器にはビット列の順番を並べ替えた(インターリーブした)ものを入力します。これにより、1つ目の符号器が弱いパターン(例えば低い重みの符号語を生成してしまう入力パターン)であっても、インターリーバで順番が変わることで、2つ目の符号器では高い重みの符号語が生成される可能性が高くなります。

符号化器のブロック図

ターボ符号の符号化器は以下の3つの要素から構成されます。

  1. RSC符号器1(Recursive Systematic Convolutional Encoder 1): 元の情報ビット列 $\bm{u} = (u_1, u_2, \dots, u_K)$ をそのまま入力し、組織ビット $\bm{x}^s$ とパリティビット $\bm{x}^{p_1}$ を出力する
  2. インターリーバ $\pi$: 情報ビット列の順番を並べ替える置換 $\pi: \{1, 2, \dots, K\} \to \{1, 2, \dots, K\}$
  3. RSC符号器2: インターリーブされた情報ビット列 $\bm{u}’ = (u_{\pi(1)}, u_{\pi(2)}, \dots, u_{\pi(K)})$ を入力し、パリティビット $\bm{x}^{p_2}$ を出力する

送信される符号語は次のように構成されます。

$$ \bm{x} = (\bm{x}^s, \bm{x}^{p_1}, \bm{x}^{p_2}) $$

ここで、$\bm{x}^s = \bm{u}$(組織符号なので情報ビットがそのまま送信される)、$\bm{x}^{p_1}$ はRSC符号器1のパリティ出力、$\bm{x}^{p_2}$ はRSC符号器2のパリティ出力です。

情報ビット数 $K$ に対して送信ビット数は $3K$ となるため、基本的な符号化率は $R = K / 3K = 1/3$ です。パリティビットの一部を間引く(パンクチャリング)ことで、$R = 1/2$ など任意の符号化率に調整できます。

なぜ再帰的組織畳み込み(RSC)符号を使うのか

ターボ符号の構成要素として、非再帰的な畳み込み符号ではなく、再帰的組織畳み込み(RSC: Recursive Systematic Convolutional)符号を使うことが本質的に重要です。

非再帰的な畳み込み符号では、重みが2の入力系列(例えば $\bm{u} = (0, \dots, 0, 1, 0, \dots, 0, 1, 0, \dots, 0)$)が低い重みの符号語を生成してしまいます。このような入力パターンは、インターリーバで並べ替えても低い重みのままになりやすいという問題があります。

一方、RSC符号にはフィードバック経路があるため、重みが2の入力に対しても、出力のパリティビット列が長い非ゼロパターン(高い重み)を生成します。RSC符号器がゼロ状態に戻るには、特定のパターンの入力が必要であり、短い非ゼロ区間で済むのは特別な場合に限られます。

具体的に、RSC符号器の伝達関数は次のように表されます。

$$ G(D) = \left[1, \frac{g_1(D)}{g_0(D)}\right] $$

ここで $g_0(D)$ はフィードバック多項式、$g_1(D)$ はフィードフォワード多項式です。$D$ は遅延演算子を表します。「1」は組織ビット(入力がそのまま出力される)を意味し、$g_1(D)/g_0(D)$ はパリティビットの生成を表します。

よく使われるRSC符号のパラメータは拘束長 $K_c = 4$(メモリ数 $m = 3$)で、生成多項式が次の通りです。

$$ g_0(D) = 1 + D^2 + D^3, \quad g_1(D) = 1 + D + D^3 $$

8進表記では $(g_0, g_1) = (15, 13)_8$ と書きます。この符号器は $2^m = 8$ 個の状態を持つトレリスで表現できます。

ここまでで、ターボ符号の符号化構造が明らかになりました。次に、復号の鍵となるインターリーバの役割をもう少し深く掘り下げてから、MAP復号アルゴリズムの解説に進みます。

インターリーバの役割

最小距離の向上

誤り訂正符号の性能を決定する重要な指標の一つが最小ハミング距離 $d_{\min}$ です。$d_{\min}$ が大きいほど、より多くのビット誤りを訂正できます。

単一のRSC符号器だけでは、ある特定の入力パターン(低い入力重みで符号器がすぐにゼロ状態に戻るパターン)が低い重みの符号語を生成します。これが $d_{\min}$ を制限する要因になります。

インターリーバはこの問題を解決します。1つ目の符号器にとって「悪い」入力パターン(低い符号語重みを生成するパターン)は、インターリーバによって並べ替えられた後、2つ目の符号器にとっては「良い」入力パターン(高い符号語重みを生成するパターン)になる可能性が高くなります。

直感的に言えば、2つの符号器が同時に低い重みの符号語を生成してしまう確率は、インターリーバ長 $K$ が大きいほど低くなります。理論的な解析により、ランダムインターリーバを用いた場合のターボ符号の最小距離は、高い確率で $K$ の増加とともに対数的に増大することが知られています。

インターリーバの種類

インターリーバにはいくつかの種類があります。

ランダムインターリーバ: $K$ 個の位置をランダムに並べ替えるものです。理論的な解析が容易で、大きな $K$ に対して良い性能を発揮します。初期の研究で広く使われましたが、構造がないためハードウェア実装やメモリ効率の面で課題があります。

S-ランダムインターリーバ: ランダムインターリーバの改良版で、任意の2つのインデックスが入力と出力の両方で近接しないという条件を満たすように設計されます。具体的には、$|\pi(i) – \pi(j)| > S$ for all $|i – j| \leq S$ という条件を課します。パラメータ $S$ はスプレッドと呼ばれ、$S \approx \sqrt{K/2}$ が経験的に良い選択とされています。

QPP(Quadratic Permutation Polynomial)インターリーバ: 3GPP LTEで採用されたインターリーバで、$\pi(i) = (f_1 \cdot i + f_2 \cdot i^2) \mod K$ という二次多項式で定義されます。代数的構造を持つため並列復号に適しており、また優れた距離特性も備えています。

インターリーバの選択はターボ符号の性能に大きく影響しますが、どのインターリーバを用いるにせよ、復号器側ではインターリーバの構造を既知として利用します。

ここまでで、ターボ符号がなぜ強力なのか — 2つのRSC符号器がインターリーバを介して相互補完的に動作すること — を理解しました。次は、この符号をどのように復号するかという核心的な問題に進みます。ターボ符号の復号は、MAP(最大事後確率)アルゴリズムに基づく2つの要素復号器が外部情報を交換しながら反復的に推定精度を高めていくという、極めて巧妙な仕組みになっています。

MAP復号アルゴリズム(BCJR)

硬判定から軟判定へ

畳み込み符号のビタビ復号は、最も尤度の高い系列全体を見つける最尤系列推定(MLSE)です。これは各ビットの値を0か1に決める「硬判定」を出力します。しかし、ターボ符号の反復復号では、各ビットがどれだけ確からしいかという「確信度」の情報が必要です。

例えば、ある情報ビット $u_k$ について、復号器が「$u_k = 1$ の確率は 0.99」と出力するのと「$u_k = 1$ の確率は 0.51」と出力するのでは、同じ硬判定($u_k = 1$)であっても情報量が全く異なります。前者は非常に確信があるのに対し、後者はほとんど半々です。この確信度の情報を次の復号器に渡すことで、反復復号の性能が大きく向上します。

各ビットの事後確率を計算する復号アルゴリズムがMAP(Maximum A Posteriori)復号であり、その実現アルゴリズムとして1974年にBahl、Cocke、Jelinek、Ravivの4名が提案したBCJRアルゴリズムがあります。

対数尤度比(LLR)

軟判定情報の表現として、対数尤度比(Log-Likelihood Ratio, LLR)を用います。情報ビット $u_k$ に対するLLRは次のように定義されます。

$$ L(u_k) = \ln \frac{P(u_k = 0)}{P(u_k = 1)} $$

ここでは、$u_k = 0$ を $x_k = +1$、$u_k = 1$ を $x_k = -1$ にマッピングするBPSK変調を考えています。LLRの符号が推定ビット値を、絶対値が確信度を表します。

  • $L(u_k) > 0$: $u_k = 0$ と推定(正の値が大きいほど確信が強い)
  • $L(u_k) < 0$: $u_k = 1$ と推定(負の値が大きいほど確信が強い)
  • $L(u_k) \approx 0$: 確信がない(ほぼ半々)

LLR表現は対数領域での演算が可能であり、確率の乗算が加算に変換されるため、数値的に安定で計算効率も良いという利点があります。

BCJRアルゴリズムの定式化

BCJRアルゴリズムは、トレリス上で前向き再帰と後向き再帰を行い、各時刻のビット事後確率を計算します。RSC符号器のトレリスを考え、時刻 $k$ における状態を $s_k \in \{0, 1, \dots, 2^m – 1\}$ とします。

受信系列 $\bm{y} = (y_1, y_2, \dots, y_K)$ が与えられたとき、ビット $u_k$ の事後LLRは次のように計算されます。

$$ L(u_k | \bm{y}) = \ln \frac{\displaystyle\sum_{(s’, s) \in \mathcal{S}_0} \alpha_{k-1}(s’) \cdot \gamma_k(s’, s) \cdot \beta_k(s)}{\displaystyle\sum_{(s’, s) \in \mathcal{S}_1} \alpha_{k-1}(s’) \cdot \gamma_k(s’, s) \cdot \beta_k(s)} $$

ここで、$\mathcal{S}_0$ は $u_k = 0$ に対応する状態遷移 $(s’, s)$ の集合、$\mathcal{S}_1$ は $u_k = 1$ に対応する状態遷移の集合です。各要素の意味は以下の通りです。

前向き確率 $\alpha_k(s)$:

時刻1から $k$ までの受信系列 $y_1, \dots, y_k$ を観測し、時刻 $k$ で状態 $s$ にいる確率です。

$$ \alpha_k(s) = P(s_k = s, y_1, \dots, y_k) $$

前向き再帰により計算します。初期条件は、符号器がゼロ状態($s_0 = 0$)から開始するとして $\alpha_0(0) = 1$、$\alpha_0(s) = 0$ ($s \neq 0$)です。

再帰式は次の通りです。

$$ \alpha_k(s) = \sum_{s’} \alpha_{k-1}(s’) \cdot \gamma_k(s’, s) $$

全ての前の状態 $s’$ について、前の時刻の前向き確率にブランチメトリック $\gamma_k(s’, s)$ を掛けて和をとります。

後向き確率 $\beta_k(s)$:

時刻 $k+1$ から $K$ までの受信系列 $y_{k+1}, \dots, y_K$ を観測する確率を、時刻 $k$ に状態 $s$ にいるという条件のもとで求めたものです。

$$ \beta_k(s) = P(y_{k+1}, \dots, y_K | s_k = s) $$

後向き再帰により計算します。終端条件は、トレリスの最終状態が既知(通常ゼロ状態)であれば $\beta_K(0) = 1$、$\beta_K(s) = 0$ ($s \neq 0$)です。テイル処理を行わない場合は $\beta_K(s) = 1/2^m$ と均一に設定します。

再帰式は次の通りです。

$$ \beta_k(s’) = \sum_{s} \gamma_{k+1}(s’, s) \cdot \beta_{k+1}(s) $$

ブランチメトリック $\gamma_k(s’, s)$:

時刻 $k$ に状態 $s’$ から状態 $s$ に遷移する確率と、その遷移に対応する受信シンボルの尤度を組み合わせたものです。

$$ \gamma_k(s’, s) = P(u_k) \cdot p(y_k | x_k(s’, s)) $$

ここで $P(u_k)$ は入力ビットの事前確率、$p(y_k | x_k(s’, s))$ は遷移 $(s’, s)$ に対応する送信シンボル $x_k$ が送られたときに $y_k$ を受信する確率(チャネル遷移確率)です。

AWGN通信路でBPSK変調($x_k \in \{+1, -1\}$)の場合、チャネル尤度は次のようになります。

$$ p(y_k | x_k) = \frac{1}{\sqrt{2\pi\sigma^2}} \exp\left(-\frac{(y_k – x_k)^2}{2\sigma^2}\right) $$

ここで $\sigma^2 = N_0/2$ は雑音の分散です。

対数領域での計算(Log-MAP)

実際のハードウェア実装やシミュレーションでは、確率値は非常に小さくなるため、全ての計算を対数領域で行うLog-MAPアルゴリズムが使われます。

対数領域での前向き・後向きメトリックを次のように定義します。

$$ \bar{\alpha}_k(s) = \ln \alpha_k(s), \quad \bar{\beta}_k(s) = \ln \beta_k(s), \quad \bar{\gamma}_k(s’, s) = \ln \gamma_k(s’, s) $$

対数領域での再帰式は、max-star演算(Jacobian logarithm)を用いて次のように書けます。

$$ \bar{\alpha}_k(s) = \max^*_{s’} \left[\bar{\alpha}_{k-1}(s’) + \bar{\gamma}_k(s’, s)\right] $$

ここで $\max^*$ 演算は次の関係で定義されます。

$$ \max^*(a, b) = \max(a, b) + \ln(1 + e^{-|a – b|}) $$

$\ln(1 + e^{-|a-b|})$ は補正項で、ルックアップテーブルで実装できます。この補正項を省略すると $\max^*(a, b) \approx \max(a, b)$ となり、Max-Log-MAPアルゴリズムが得られます。Max-Log-MAPは0.1〜0.5 dBほどの性能劣化がありますが、計算が大幅に簡略化されるため、実装では広く使われています。

ここまでで、単一のRSC符号器に対するMAP復号の原理を理解しました。ターボ符号の真の力は、この MAP復号器を2つ用いて外部情報を交換しながら反復する点にあります。次のセクションでは、この反復復号のメカニズムを詳しく見ていきます。

反復復号のメカニズム

外部情報の概念

ターボ符号の反復復号で中核となるのが外部情報(Extrinsic Information)の概念です。これを理解するために、MAP復号器の出力LLRを分解してみましょう。

MAP復号器1が出力する事後LLR $L_1(u_k | \bm{y})$ は、3つの成分に分解できます。

$$ L_1(u_k | \bm{y}) = L_c \cdot y_k^s + L_a(u_k) + L_e^{(1)}(u_k) $$

各項の意味は以下の通りです。

  • チャネル情報 $L_c \cdot y_k^s$: 受信した組織ビット $y_k^s$ から直接得られる情報。チャネル信頼度 $L_c = 4R \cdot E_b / N_0$(BPSK・AWGNの場合は $L_c = 2/\sigma^2$)を乗じたものです
  • 事前情報 $L_a(u_k)$: ビット $u_k$ に関する事前知識。初回の反復では $L_a(u_k) = 0$(等確率の仮定)ですが、2回目以降はもう一方の復号器からの外部情報が使われます
  • 外部情報 $L_e^{(1)}(u_k)$: 復号器1がトレリス構造とパリティビット $\bm{y}^{p_1}$ から独自に推定した、$u_k$ に関する追加情報です

外部情報 $L_e^{(1)}(u_k)$ は、事後LLRからチャネル情報と事前情報を引くことで得られます。

$$ L_e^{(1)}(u_k) = L_1(u_k | \bm{y}) – L_c \cdot y_k^s – L_a(u_k) $$

外部情報の本質は、「チャネルから直接得た情報と、他の復号器から教えてもらった情報を除いた、この復号器が符号の構造(トレリスとパリティビット)から独自に推定した新しい情報」です。これが重要な理由は、情報の二重カウントを避けるためです。もし事後LLR全体を次の復号器に渡してしまうと、チャネル情報が何度もカウントされ、過度に自信を持った(偏った)推定になってしまいます。

反復復号のアルゴリズム

反復復号の具体的な手順を以下に示します。

初期化: 反復回数 $I_{\max}$ を設定します(通常 $I_{\max} = 6 \sim 12$ 回)。復号器1への事前情報を $L_a^{(1)}(u_k) = 0$(全 $k$)に初期化します。

反復 $i = 1, 2, \dots, I_{\max}$:

ステップ1 — 復号器1の実行:

復号器1は、受信した組織ビット $\bm{y}^s$ とパリティビット $\bm{y}^{p_1}$、および事前情報 $L_a^{(1)}(\bm{u})$ を入力として、BCJRアルゴリズムにより事後LLR $L_1(u_k | \bm{y})$ を計算します。外部情報を抽出します。

$$ L_e^{(1)}(u_k) = L_1(u_k | \bm{y}) – L_c \cdot y_k^s – L_a^{(1)}(u_k) $$

ステップ2 — インターリーブ:

復号器1の外部情報をインターリーブし、復号器2の事前情報とします。

$$ L_a^{(2)}(u_{\pi(k)}) = L_e^{(1)}(u_k) $$

ステップ3 — 復号器2の実行:

復号器2は、インターリーブされた受信組織ビット $\bm{y}’^s$ とパリティビット $\bm{y}^{p_2}$、および事前情報 $L_a^{(2)}(\bm{u}’)$ を入力として、BCJRアルゴリズムにより事後LLR $L_2(u’_k | \bm{y}’)$ を計算します。外部情報を抽出します。

$$ L_e^{(2)}(u’_k) = L_2(u’_k | \bm{y}’) – L_c \cdot y’^s_k – L_a^{(2)}(u’_k) $$

ステップ4 — デインターリーブ:

復号器2の外部情報をデインターリーブし、次の反復で復号器1の事前情報として使います。

$$ L_a^{(1)}(u_k) = L_e^{(2)}(u’_{\pi(k)}) $$

ここで $\pi^{-1}$ はインターリーバの逆置換です。つまり、インターリーバ順で $\pi(k)$ 番目にあった外部情報を、元の順番の $k$ 番目に戻します。

最終判定: 最後の反復が終わった後、復号器2の事後LLR(デインターリーブ済み)に基づいて硬判定を行います。

$$ \hat{u}_k = \begin{cases} 0 & \text{if } L(u_k | \bm{y}) \geq 0 \\ 1 & \text{if } L(u_k | \bm{y}) < 0 \end{cases} $$

反復復号が収束する直感的理由

なぜ、外部情報を交換するだけで性能が向上するのでしょうか。

最初の反復では、両方の復号器はチャネルからの直接的な情報のみに基づいて推定を行います。多くのビットは正しく推定できますが、雑音が大きい一部のビットでは誤りが生じます。

2回目の反復では、復号器1は復号器2からの外部情報を事前情報として受け取ります。復号器2の外部情報には、パリティビット $\bm{y}^{p_2}$ とインターリーブされたトレリス構造から得られた情報が含まれています。これは復号器1がまだ知らない「新しい」情報であり、復号器1の推定精度を向上させます。

反復を重ねるたびに、両方の復号器の確信度が高まり、推定精度が向上していきます。イメージとしては、2人の専門家が互いの意見を聞きながら、各自の分析を修正し、最終的に高い確信を持った結論に収束するプロセスです。

ただし、外部情報に含まれる独立な情報量には限界があるため、反復回数を増やしても無限に性能が向上するわけではありません。通常、6〜10回程度の反復で性能はほぼ収束します。

この収束過程を定量的に解析するためのツールが、次に解説するEXIT chartです。

EXIT chartによる収束解析

EXIT chartとは

EXIT chart(EXtrinsic Information Transfer chart)は、2001年にStephan ten Brinkが提案した、反復復号の収束特性を視覚的に解析するためのツールです。

反復復号のシミュレーションを実際に行うと、反復ごとにBERが改善していく様子が観察できますが、全てのSNRに対してシミュレーションを行うのは計算コストが高くなります。EXIT chartを使えば、個々の復号器の特性を相互情報量で記述し、反復復号の収束・非収束を効率的に予測できます。

相互情報量に基づく特性関数

EXIT chartでは、各復号器の入力と出力を相互情報量で特性化します。

復号器に入力される事前情報のLLR $L_a$ が、真のビット値 $u_k$ との相互情報量 $I_A = I(u_k; L_a)$ を持つとします。$I_A = 0$ は事前情報が全く無い状態(等確率)、$I_A = 1$ は事前情報が完全にビット値を決定している状態です。

復号器が出力する外部情報のLLR $L_e$ と真のビット値 $u_k$ との相互情報量を $I_E = I(u_k; L_e)$ とします。

各復号器は、入力の相互情報量 $I_A$ を出力の相互情報量 $I_E$ に変換する関数(EXIT特性関数)を持ちます。

$$ I_{E,1} = T_1(I_{A,1}, E_b/N_0), \quad I_{E,2} = T_2(I_{A,2}, E_b/N_0) $$

この特性関数は、ガウス分布に従うランダムなLLRを事前情報として与えるモンテカルロシミュレーションにより測定できます。具体的には、$I_A$ に対応する分散 $\sigma_A^2$ を持つガウス分布の事前LLRを生成し、BCJRアルゴリズムを実行して、出力外部情報のLLRから $I_E$ を計算します。

EXIT chartの描画と読み方

EXIT chartは、横軸と縦軸をそれぞれ $I_{A,1}$ と $I_{E,1}$ で描いた2次元平面に、2つの曲線を描画します。

  1. 復号器1のEXIT曲線: 横軸 $I_{A,1}$ に対する $I_{E,1} = T_1(I_{A,1})$ をプロット
  2. 復号器2のEXIT曲線(反転): 復号器2の $I_{A,2} = I_{E,1}$、$I_{E,2} = I_{A,1}$ という対応関係を用いて、$I_{E,2}$ を横軸に、$I_{A,2}$ を縦軸にプロット(つまり復号器2の曲線を軸交換して重ねる)

反復復号の軌跡は、2つの曲線の間をジグザグに進む「階段状」のパスとして表現されます。

  • 2つの曲線の間にトンネル(開口部)が存在する場合: 階段状パスは $(I_A, I_E) \approx (1, 1)$ に到達でき、復号は収束する(BERが十分小さくなる)
  • 2つの曲線が交差している場合: 階段状パスは交差点で止まり、復号は収束しない

EXIT chartから読み取れる重要な情報は以下の通りです。

  • ターボクリフ(Turbo Cliff): トンネルが開く(2つの曲線が離れる)$E_b/N_0$ がターボ符号のBERカーブの急峻な崖(cliff)に対応します。この値がシャノン限界に近いほど、符号の性能が良いことを意味します
  • トンネルの幅: トンネルが狭いほど、多くの反復回数が必要になります。極限的には、トンネルがちょうど開く $E_b/N_0$ がシャノン限界に対応します
  • 収束点: パスが最終的に到達する $(I_A, I_E)$ の値から、収束後の残存BERを推定できます

EXIT chartは設計ツールとしても有用です。2つの符号器やインターリーバの設計を変更したときの効果を、フルシミュレーションを行わずに予測できます。2つの曲線ができるだけ適合し(面積が最大になり)、かつトンネルが確保されるような設計が理想です。

ここまでで、ターボ符号の理論的な枠組み — 符号化構造、MAP復号、反復復号、そしてEXIT chartによる収束解析 — を一通り理解しました。次のセクションでは、これらの理論をPythonで実装し、実際にBERカーブを描画してターボ符号の性能をシミュレーションで確認します。

ターボ符号のBER理論特性

ウォーターフォールとエラーフロア

ターボ符号のBER(ビット誤り率)対 $E_b/N_0$ カーブには、2つの特徴的な領域があります。

ウォーターフォール(Waterfall)領域: ある $E_b/N_0$ の閾値を超えると、BERが急峻に低下する領域です。これは反復復号が収束し始めるSNR領域に対応します。EXIT chartでトンネルが開くSNRがこの閾値です。ウォーターフォール領域の位置がシャノン限界に近いことが、ターボ符号の最大の特徴です。

エラーフロア(Error Floor)領域: 高SNR領域で、BERの低下が緩やかになる(あるいは一定値に近づく)領域です。これはターボ符号の有限の最小距離 $d_{\min}$ に起因します。$d_{\min}$ が有限であるため、高SNR領域ではBERが $\text{erfc}(\sqrt{d_{\min} \cdot R \cdot E_b/N_0})$ に比例する領域に入ります。

エラーフロアの高さは、ターボ符号の最小距離とその多重度(ある距離を持つ符号語の数)によって決まります。インターリーバ長 $K$ を大きくすると最小距離が増大し、エラーフロアは低くなりますが、完全には消えません。これは、ランダム符号のような指数的に増大する最小距離を持たないためです。

符号化率 $R = 1/3$ と $R = 1/2$ のパンクチャリング

基本的なターボ符号(パンクチャリングなし)の符号化率は $R = 1/3$ です。パンクチャリングにより符号化率を上げることができます。

例えば、$R = 1/2$ を実現するには、2つのパリティビット列 $\bm{x}^{p_1}$ と $\bm{x}^{p_2}$ から交互にビットを間引きます。具体的には、奇数時刻では $x_k^{p_1}$ を送信し $x_k^{p_2}$ を間引き、偶数時刻では $x_k^{p_2}$ を送信し $x_k^{p_1}$ を間引きます。

パンクチャリングは符号化率を上げ帯域効率を向上させますが、冗長性が減るため性能は劣化します。3GPP LTEでは、パンクチャリングパターンを柔軟に設定できるレートマッチング機構が規定されています。

これらの理論的特性がシミュレーションで実際に確認できるかを見てみましょう。

Pythonでの実装

ターボ符号化器の実装

まず、RSC符号器とインターリーバを含むターボ符号化器を実装します。拘束長 $K_c = 4$(メモリ数 $m = 3$)、生成多項式 $(g_0, g_1) = (15, 13)_8$ のRSC符号器を使います。

import numpy as np

def rsc_encode(u, g0_oct=0o15, g1_oct=0o13, m=3):
    """
    再帰的組織畳み込み(RSC)符号器
    u: 情報ビット列 (0/1)
    g0_oct: フィードバック多項式(8進)
    g1_oct: フィードフォワード多項式(8進)
    m: メモリ数
    戻り値: (組織ビット, パリティビット)
    """
    K = len(u)
    # 多項式をビット列に変換
    g0 = [(g0_oct >> i) & 1 for i in range(m + 1)]
    g1 = [(g1_oct >> i) & 1 for i in range(m + 1)]

    # シフトレジスタの状態
    state = np.zeros(m, dtype=int)
    parity = np.zeros(K, dtype=int)

    for k in range(K):
        # フィードバック入力
        feedback = u[k]
        for i in range(m):
            feedback ^= g0[i + 1] * state[i]

        # パリティ出力
        p = feedback * g1[0]
        for i in range(m):
            p ^= g1[i + 1] * state[i]
        parity[k] = p

        # 状態更新(シフトレジスタ)
        state = np.roll(state, 1)
        state[0] = feedback

    return u.copy(), parity


def turbo_encode(u, interleaver):
    """
    ターボ符号化器
    u: 情報ビット列
    interleaver: インターリーバ(置換インデックス配列)
    戻り値: (組織ビット, パリティ1, パリティ2)
    """
    # RSC符号器1
    systematic, parity1 = rsc_encode(u)

    # インターリーブ
    u_interleaved = u[interleaver]

    # RSC符号器2
    _, parity2 = rsc_encode(u_interleaved)

    return systematic, parity1, parity2


def generate_random_interleaver(K):
    """ランダムインターリーバの生成"""
    return np.random.permutation(K)

上のコードでは、RSC符号器をシフトレジスタモデルで実装しています。rsc_encode 関数の中で、フィードバック接続 $g_0$ により入力にシフトレジスタの状態がXOR演算で加えられ、フィードフォワード接続 $g_1$ によりパリティビットが計算されます。turbo_encode 関数は2つのRSC符号器とインターリーバを組み合わせ、組織ビットと2つのパリティビット列を出力します。

BCJRアルゴリズム(Log-MAP)の実装

次に、対数領域でのBCJRアルゴリズムを実装します。数値安定性のため、Log-MAP(max-star演算)を使用します。

def log_sum_exp(a, b):
    """
    max-star演算: ln(exp(a) + exp(b))
    数値安定性のためにmax(a,b) + ln(1 + exp(-|a-b|))を使用
    """
    max_val = np.maximum(a, b)
    return max_val + np.log1p(np.exp(-np.abs(a - b)))


def build_trellis(g0_oct=0o15, g1_oct=0o13, m=3):
    """
    RSC符号器のトレリスを構築
    戻り値: (next_state, output) の辞書
    next_state[state][input_bit] = 次の状態
    output[state][input_bit] = (組織ビット, パリティビット)
    """
    num_states = 2 ** m
    g0 = [(g0_oct >> i) & 1 for i in range(m + 1)]
    g1 = [(g1_oct >> i) & 1 for i in range(m + 1)]

    next_state = np.zeros((num_states, 2), dtype=int)
    output_parity = np.zeros((num_states, 2), dtype=int)

    for s in range(num_states):
        state_bits = [(s >> i) & 1 for i in range(m)]
        for u_bit in range(2):
            # フィードバック
            feedback = u_bit
            for i in range(m):
                feedback ^= g0[i + 1] * state_bits[i]

            # パリティ
            p = feedback * g1[0]
            for i in range(m):
                p ^= g1[i + 1] * state_bits[i]

            # 次の状態
            new_state_bits = [feedback] + state_bits[:-1]
            ns = sum(b << i for i, b in enumerate(new_state_bits))

            next_state[s, u_bit] = ns
            output_parity[s, u_bit] = p

    return next_state, output_parity

上のコードでは、RSC符号器の全状態遷移をテーブルとして事前計算しています。build_trellis 関数は各状態と入力ビットの組み合わせに対して、次状態とパリティ出力を返します。このテーブルをBCJRアルゴリズムの中で参照することで、効率的にブランチメトリックを計算できます。

def bcjr_decode(sys_llr, par_llr, La, Lc, next_state, output_parity, m=3):
    """
    BCJRアルゴリズム(Log-MAP)
    sys_llr: 組織ビットのチャネルLLR (Lc * y_s)
    par_llr: パリティビットのチャネルLLR (Lc * y_p)
    La: 事前情報LLR
    Lc: チャネル信頼度(この関数内では使用しない。sys_llr, par_llrに含まれる)
    next_state: トレリス遷移テーブル
    output_parity: パリティ出力テーブル
    m: メモリ数
    戻り値: (事後LLR, 外部情報LLR)
    """
    K = len(sys_llr)
    num_states = 2 ** m
    NEG_INF = -1e30

    # 前向きメトリック
    alpha = np.full((K + 1, num_states), NEG_INF)
    alpha[0, 0] = 0.0  # 初期状態 = 0

    # 後向きメトリック
    beta = np.full((K + 1, num_states), NEG_INF)
    # 終端処理: ゼロ状態に戻る場合
    beta[K, :] = -np.log(num_states)  # 均一(テイルビットなしの場合)

    # ブランチメトリック計算と前向き再帰
    gamma = np.zeros((K, num_states, 2))  # gamma[k][s][u]

    for k in range(K):
        for s in range(num_states):
            for u_bit in range(2):
                # 組織ビット: u=0 -> x=+1, u=1 -> x=-1
                sys_sign = 1.0 - 2.0 * u_bit
                # パリティビット: p=0 -> x=+1, p=1 -> x=-1
                p_bit = output_parity[s, u_bit]
                par_sign = 1.0 - 2.0 * p_bit

                # ブランチメトリック = 事前情報 + チャネル尤度
                # La[k]の符号: u=0のときLa>0が有利
                g = 0.5 * (1 - 2 * u_bit) * La[k] \
                    + 0.5 * sys_sign * sys_llr[k] \
                    + 0.5 * par_sign * par_llr[k]
                gamma[k, s, u_bit] = g

        # 前向き再帰
        for s in range(num_states):
            for u_bit in range(2):
                ns = next_state[s, u_bit]
                val = alpha[k, s] + gamma[k, s, u_bit]
                alpha[k + 1, ns] = log_sum_exp(alpha[k + 1, ns], val)

        # 正規化(数値安定性)
        max_alpha = np.max(alpha[k + 1, :])
        alpha[k + 1, :] -= max_alpha

    # 後向き再帰
    for k in range(K - 1, -1, -1):
        for s in range(num_states):
            for u_bit in range(2):
                ns = next_state[s, u_bit]
                val = gamma[k, s, u_bit] + beta[k + 1, ns]
                beta[k, s] = log_sum_exp(beta[k, s], val)

        # 正規化
        max_beta = np.max(beta[k, :])
        beta[k, :] -= max_beta

    # 事後LLR計算
    L_post = np.zeros(K)
    for k in range(K):
        num = NEG_INF  # u_k = 0 の対数確率
        den = NEG_INF  # u_k = 1 の対数確率
        for s in range(num_states):
            for u_bit in range(2):
                ns = next_state[s, u_bit]
                metric = alpha[k, s] + gamma[k, s, u_bit] + beta[k + 1, ns]
                if u_bit == 0:
                    num = log_sum_exp(num, metric)
                else:
                    den = log_sum_exp(den, metric)
        L_post[k] = num - den

    # 外部情報 = 事後LLR - チャネル情報 - 事前情報
    Le = L_post - sys_llr - La

    return L_post, Le

このBCJR実装のポイントは3つあります。第一に、全計算を対数領域で行っているため、確率のアンダーフローを防いでいます。第二に、各時刻の前向き・後向きメトリックを正規化することで、数値的な安定性を確保しています。第三に、最終的に外部情報 $L_e = L_{\text{post}} – L_c y^s – L_a$ を計算して返すことで、反復復号での情報交換に直接利用できるようにしています。

ターボ復号器(反復復号)の実装

2つのBCJR復号器を反復的に呼び出すターボ復号器を実装します。

def turbo_decode(sys_r, par1_r, par2_r, interleaver, sigma2,
                 num_iterations=8, m=3):
    """
    ターボ復号器(反復MAP復号)
    sys_r: 受信組織ビット(実数値)
    par1_r: 受信パリティ1(実数値)
    par2_r: 受信パリティ2(実数値)
    interleaver: インターリーバ
    sigma2: 雑音分散 N0/2
    num_iterations: 反復回数
    戻り値: 推定ビット列
    """
    K = len(sys_r)
    next_state, output_parity = build_trellis(m=m)
    deinterleaver = np.argsort(interleaver)

    # チャネル信頼度
    Lc = 2.0 / sigma2

    # チャネルLLR
    sys_llr = Lc * sys_r
    par1_llr = Lc * par1_r
    par2_llr = Lc * par2_r

    # インターリーブされた組織LLR
    sys_llr_int = sys_llr[interleaver]

    # 事前情報の初期化
    La1 = np.zeros(K)

    for iteration in range(num_iterations):
        # 復号器1
        L_post1, Le1 = bcjr_decode(
            sys_llr, par1_llr, La1, Lc,
            next_state, output_parity, m
        )

        # 復号器2の事前情報 = 復号器1の外部情報(インターリーブ)
        La2 = Le1[interleaver]

        # 復号器2
        L_post2, Le2 = bcjr_decode(
            sys_llr_int, par2_llr, La2, Lc,
            next_state, output_parity, m
        )

        # 復号器1の事前情報 = 復号器2の外部情報(デインターリーブ)
        La1 = Le2[deinterleaver]

    # 最終判定(復号器2の出力をデインターリーブ)
    L_final = L_post2[deinterleaver]
    decoded = (L_final < 0).astype(int)

    return decoded

turbo_decode 関数は、復号器1と復号器2を交互に呼び出し、外部情報をインターリーブ/デインターリーブしながら交換するという反復復号の全体構造を実装しています。各反復で、前の反復の外部情報が新たな事前情報として使われることで、推定精度が段階的に向上していきます。

BERシミュレーション

ターボ符号の性能をAWGN通信路上でシミュレーションし、BERカーブを描画します。

import numpy as np
import matplotlib.pyplot as plt

def simulate_turbo_ber(K=1024, EbN0_dB_list=None, num_frames=50,
                       num_iterations=8):
    """
    ターボ符号のBERシミュレーション
    K: 情報ビット長
    EbN0_dB_list: Eb/N0 [dB] のリスト
    num_frames: フレーム数
    num_iterations: 反復復号回数
    """
    if EbN0_dB_list is None:
        EbN0_dB_list = np.arange(-0.5, 3.1, 0.5)

    R = 1.0 / 3.0  # 符号化率
    ber_list = []

    # インターリーバの生成(全フレーム共通)
    interleaver = generate_random_interleaver(K)

    for EbN0_dB in EbN0_dB_list:
        EbN0 = 10 ** (EbN0_dB / 10.0)
        sigma2 = 1.0 / (2.0 * R * EbN0)  # 雑音分散
        sigma = np.sqrt(sigma2)

        total_bits = 0
        total_errors = 0

        for frame in range(num_frames):
            # 情報ビット生成
            u = np.random.randint(0, 2, K)

            # ターボ符号化
            systematic, parity1, parity2 = turbo_encode(u, interleaver)

            # BPSK変調: 0 -> +1, 1 -> -1
            x_s = 1.0 - 2.0 * systematic
            x_p1 = 1.0 - 2.0 * parity1
            x_p2 = 1.0 - 2.0 * parity2

            # AWGN通信路
            noise_s = sigma * np.random.randn(K)
            noise_p1 = sigma * np.random.randn(K)
            noise_p2 = sigma * np.random.randn(K)

            y_s = x_s + noise_s
            y_p1 = x_p1 + noise_p1
            y_p2 = x_p2 + noise_p2

            # ターボ復号
            decoded = turbo_decode(
                y_s, y_p1, y_p2, interleaver, sigma2,
                num_iterations=num_iterations
            )

            # 誤りカウント
            errors = np.sum(u != decoded)
            total_errors += errors
            total_bits += K

        ber = total_errors / total_bits if total_bits > 0 else 0
        ber_list.append(ber)
        print(f"Eb/N0 = {EbN0_dB:.1f} dB: BER = {ber:.2e}")

    return np.array(EbN0_dB_list), np.array(ber_list)

このシミュレーション関数は、各 $E_b/N_0$ の値に対して複数フレームの送受信を行い、平均BERを計算します。符号化率 $R = 1/3$ でのBPSK変調とAWGN通信路を仮定し、雑音分散を $\sigma^2 = 1/(2RE_b/N_0)$ で設定しています。

# シミュレーション実行
np.random.seed(42)
EbN0_dBs = np.arange(-0.5, 3.1, 0.5)
EbN0_dBs_result, ber_result = simulate_turbo_ber(
    K=1024,
    EbN0_dB_list=EbN0_dBs,
    num_frames=30,
    num_iterations=8
)

# 符号化なしBPSKの理論BER(比較用)
from scipy.special import erfc
EbN0_lin = 10 ** (EbN0_dBs / 10.0)
ber_uncoded = 0.5 * erfc(np.sqrt(EbN0_lin))

# BERカーブ描画
plt.figure(figsize=(10, 7))
plt.semilogy(EbN0_dBs_result, ber_result, 'bo-', linewidth=2,
             markersize=8, label='Turbo Code (R=1/3, K=1024, 8 iter)')
plt.semilogy(EbN0_dBs, ber_uncoded, 'r--', linewidth=1.5,
             label='Uncoded BPSK')
plt.axvline(x=0.19, color='green', linestyle=':', linewidth=1.5,
            label='Shannon Limit (R=1/3)')
plt.xlabel('$E_b/N_0$ [dB]')
plt.ylabel('BER')
plt.title('Turbo Code BER Performance (AWGN, BPSK)')
plt.legend(fontsize=11)
plt.grid(True, which='both', alpha=0.3)
plt.xlim([-1, 4])
plt.ylim([1e-6, 1])
plt.tight_layout()
plt.show()

上のグラフからいくつかの重要な特徴が読み取れます。

  1. ウォーターフォール特性: $E_b/N_0 \approx 0.5 \sim 1.5$ dBの領域で、BERが急激に低下しています。これがターボ符号のウォーターフォール領域であり、反復復号が収束し始めるSNR閾値に対応しています。シャノン限界($R = 1/3$ の場合、$E_b/N_0 \approx -0.49$ dB)からの距離は約1〜2 dBであり、フレーム長 $K = 1024$ という比較的短い符号長でも優れた性能を示しています
  2. 符号化利得: 符号化なしのBPSKと比較すると、BER $= 10^{-4}$ のレベルで数dBの符号化利得が得られています。同じBERを達成するために必要な送信電力が大幅に削減されることを意味します
  3. フレーム長 $K$ の効果: $K = 1024$ は実用的なシステムでは中程度の長さです。$K$ を大きくすると(例えば $K = 4096$ や $K = 65536$)、ウォーターフォール領域がさらにシャノン限界に近づき、BERの低下もより急峻になります

反復回数による収束の可視化

反復復号がどのように性能を改善していくかを、反復回数ごとのBERの変化として可視化します。

import numpy as np
import matplotlib.pyplot as plt

def simulate_convergence(K=1024, EbN0_dB=1.0, num_frames=50,
                         max_iterations=12):
    """
    反復回数ごとのBER変化をシミュレーション
    """
    R = 1.0 / 3.0
    EbN0 = 10 ** (EbN0_dB / 10.0)
    sigma2 = 1.0 / (2.0 * R * EbN0)
    sigma = np.sqrt(sigma2)

    interleaver = generate_random_interleaver(K)
    deinterleaver = np.argsort(interleaver)
    next_state, output_parity = build_trellis()
    Lc = 2.0 / sigma2

    ber_per_iter = np.zeros(max_iterations)

    for frame in range(num_frames):
        u = np.random.randint(0, 2, K)
        systematic, parity1, parity2 = turbo_encode(u, interleaver)

        x_s = 1.0 - 2.0 * systematic
        x_p1 = 1.0 - 2.0 * parity1
        x_p2 = 1.0 - 2.0 * parity2

        y_s = x_s + sigma * np.random.randn(K)
        y_p1 = x_p1 + sigma * np.random.randn(K)
        y_p2 = x_p2 + sigma * np.random.randn(K)

        sys_llr = Lc * y_s
        par1_llr = Lc * y_p1
        par2_llr = Lc * y_p2
        sys_llr_int = sys_llr[interleaver]

        La1 = np.zeros(K)

        for it in range(max_iterations):
            L_post1, Le1 = bcjr_decode(
                sys_llr, par1_llr, La1, Lc,
                next_state, output_parity
            )
            La2 = Le1[interleaver]
            L_post2, Le2 = bcjr_decode(
                sys_llr_int, par2_llr, La2, Lc,
                next_state, output_parity
            )
            La1 = Le2[deinterleaver]

            # 各反復後のBER
            L_final = L_post2[deinterleaver]
            decoded = (L_final < 0).astype(int)
            ber_per_iter[it] += np.sum(u != decoded)

    ber_per_iter /= (num_frames * K)
    return ber_per_iter


# シミュレーション実行
np.random.seed(42)
ber_convergence = simulate_convergence(
    K=1024, EbN0_dB=1.0, num_frames=30, max_iterations=12
)

# 収束グラフ描画
plt.figure(figsize=(9, 6))
iterations = np.arange(1, len(ber_convergence) + 1)
plt.semilogy(iterations, ber_convergence, 'bo-', linewidth=2, markersize=8)
plt.xlabel('Number of Iterations')
plt.ylabel('BER')
plt.title('Turbo Decoding Convergence ($E_b/N_0$ = 1.0 dB, K = 1024)')
plt.grid(True, which='both', alpha=0.3)
plt.xticks(iterations)
plt.tight_layout()
plt.show()

このグラフから、反復復号の収束特性が明確に観察できます。

  1. 初回反復: 反復なし($i = 1$)の段階ではBERは比較的高く、単一のMAP復号器と同程度の性能です。事前情報がゼロなので、チャネル情報とトレリス構造のみに基づいた推定になっています
  2. 急速な改善: 2〜4回目の反復で最も大きな性能改善が見られます。これは、外部情報の交換により両方の復号器が互いの知見を効果的に活用し始める段階です
  3. 収束: 6〜8回目以降、BERの改善はほぼ平坦になります。外部情報に含まれる新しい情報量が飽和し、追加の反復による利得がなくなっていることを示しています。実用的なシステムでは、この収束速度を考慮して反復回数を設定します

ターボ符号の実用と発展

標準化と採用

ターボ符号は、発表後すぐに通信規格に採用されました。

3G WCDMA(UMTS): 3GPPのRelease 99(1999年)で、データチャネルの誤り訂正符号としてターボ符号が採用されました。拘束長 $K_c = 4$、生成多項式 $(15, 13)_8$ の8状態RSC符号器を2つ使用し、内部インターリーバのサイズは最大5,114ビットです。

4G LTE: 3GPPのLTE(Long Term Evolution)でもターボ符号が継続して採用されました。QPPインターリーバが導入され、最大6,144ビットのフレーム長をサポートしています。QPPインターリーバは並列復号を可能にし、高スループットの復号器実装を実現しました。

深宇宙通信(CCSDS): NASAのCCSDS(Consultative Committee for Space Data Systems)規格にもターボ符号が含まれています。火星探査ローバー「スピリット」「オポチュニティ」、火星科学実験室「キュリオシティ」などのミッションでターボ符号が使用されました。

5G NR: 5G(NR: New Radio)では、データチャネル(PDSCH/PUSCH)にはLDPC符号が、制御チャネル(PDCCH/PUCCH)にはポーラー符号が採用され、ターボ符号は使われていません。これは、高スループット・低レイテンシ要件に対してLDPC符号の方が復号器の並列化に適しているためです。

ターボ原理の広がり

ターボ符号が切り拓いた「反復復号」の概念は、符号理論にとどまらず、通信システム全体に広がりました。

ターボ等化: 通信路の等化器と誤り訂正復号器の間で軟情報を反復交換するターボ等化は、符号間干渉(ISI)のある通信路で大きな性能向上を実現します。

ターボMIMO検出: MIMO(Multiple-Input Multiple-Output)システムにおいて、MIMO検出器と誤り訂正復号器の間で反復処理を行うことで、空間多重の干渉除去性能が向上します。

シリアル連接ターボ符号(SCCC): 並列連接(PCCC)に対して、2つの符号器を直列に連接した構造です。エラーフロアがPCCCよりも低いという特徴があり、深宇宙通信などの超低BER要件のアプリケーションに適しています。

このように、ターボ符号の核心にある「軟情報の反復交換」というアイデアは、現代の通信システム設計における基本原理の一つとなっています。

まとめ

本記事では、ターボ符号の理論について解説しました。

  • 並列連接構造: 2つのRSC符号器がインターリーバを介して同じ情報ビット列を異なる視点から符号化することで、単一の符号器では達成できない高い最小距離を実現する
  • MAP復号(BCJRアルゴリズム): 前向き・後向き再帰により各ビットの事後確率を計算し、硬判定ではなく軟判定(LLR)を出力する。これが反復復号の基盤となる
  • 反復復号: 2つのMAP復号器が外部情報を交換しながら反復することで、各反復で推定精度が向上し、シャノン限界に迫る性能を達成する
  • EXIT chart: 相互情報量を用いて反復復号の収束特性を視覚的に解析でき、ターボ符号の設計を効率化するツールである
  • ウォーターフォールとエラーフロア: ターボ符号のBERカーブは、急激にBERが低下するウォーターフォール領域と、有限の最小距離に起因するエラーフロア領域の2つの特徴を持つ

ターボ符号は、3G/4G移動体通信や深宇宙通信で広く実用化され、シャノン限界に迫る実用的な誤り訂正を実現しました。また、反復復号という設計原理は、ターボ等化やターボMIMO検出など通信システム全体に波及しています。

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