NTNにおけるドップラー事前補償 — 共通補償と参照ポイント方式

LEO衛星から地上の端末(UE)へ5Gの電波を届けようとすると、まず誰もが直面する壁が ドップラーシフト です。高度600 kmを秒速約7.6 kmで飛ぶ衛星と、地上にほぼ静止しているUEの間では、相対速度がリアルタイムで変化し続けます。Sバンド(2 GHz帯)で運用すると、瞬時のキャリア周波数は最大 ±50 kHz 近くずれ、OFDMサブキャリア間隔(SCS)が15 kHzなら数本分のずれに相当します。何もしなければ、復調どころか同期さえ取れません。

しかし衛星通信は止められません。3GPP Rel-17 で標準化されたNR-NTNIoT-NTNは、地上スマートフォンに直接電波を届ける(D2D: Direct-to-Device)アプリケーションや、僻地のIoTセンサとの常時接続を可能にする「空からの基地局」として急速に立ち上がっています。Starlink Direct-to-Cell、AST SpaceMobile、IRIS² といった商用システムは、いずれもこの3GPP NTN仕様の上に成り立っています。

ではどうやって、これほど大きく時間変動するドップラーを扱っているのでしょうか?答えがドップラー事前補償(Doppler pre-compensation) です。本記事では、3GPP NTNが採用する事前補償の理論を「共通項と差分項の分解」という観点から丁寧に解きほぐし、参照ポイント方式の意味、GNSSを使ったUE側補償の役割、そして残差ドップラーがOFDMのサブキャリアスペーシング(SCS)選定にどう跳ね返るのかを、Python実装で可視化しながら学びます。

本記事の内容

  • なぜドップラー事前補償がNTNでは不可欠なのか
  • ドップラーを共通項 $f_{D,c}$ と差分項 $\Delta f_D$ に分解する数学的枠組み
  • 参照ポイント方式(reference-point pre-compensation)の意味と運用
  • GNSSを使ったUE側のドップラー補正と、衛星側DL補償との役割分担
  • OFDMにおけるサブキャリア間干渉(ICI)と残差ドップラーの関係
  • NTNでSCSが15/30/120 kHzから選ばれる理由
  • Pythonによるビーム内残差ドップラー分布のシミュレーション

前提知識

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

なぜドップラー事前補償が必要か

地上の5Gセル基地局と衛星NTNの違いを一つだけ挙げるとすれば、それは「基地局が秒速7.6 kmで動いている」という事実です。地上LTE/NRでもドップラーは存在しますが、移動体UE自身の速度(自動車で30 m/s程度)に由来するため、Sバンドでも数十〜数百 Hz 程度に収まります。これは1サブキャリア間隔(15 kHz)の1 %未満で、受信機側のループ追従で十分対応できます。

一方、NTNでは衛星の軌道速度がドップラーを支配します。Sバンド($f_c = 2$ GHz)でLEO衛星のドップラーは、衛星がUEの真上を通過する瞬間(ゼロドップラー)から、地平線近く(最大ドップラー)まで連続的に大きく変化し、最大 $f_{D,\max} \approx \pm 50$ kHz に達します。Kaバンド(30 GHz)ならば実に $\pm 750$ kHz にもなり、SCS 120 kHzでも6サブキャリア分以上のずれです。受信機が「中心周波数」を探すだけでも一苦労、というレベルの大きさです。

ここで重要なポイントは、衛星と地上の幾何学関係が確定的に予測できることです。衛星の軌道は数百ms先まで非常に正確に予測可能で、UEの位置もGNSSがあれば数mの精度で得られます。「予測できる成分は、受信機が苦労する前に送信側で打ち消してしまえばいい」 — これが事前補償(pre-compensation)のアイデアです。地上携帯網が「予測しないでループで追従する」のに対し、NTNは「予測して打ち消す」というパラダイム転換を行っています。

具体的には、衛星(gNB)側は下りリンク(DL)送信時、各UE方向のドップラーを予測し、その分だけ送信周波数を逆方向にずらして送信します。すると、受信されたDL信号は理想周波数 $f_c$ に戻り、UEは「あたかも地上のセルを受信しているかのように」復調できます。上りリンク(UL)でも同様に、UE側がGNSSの自速度情報からドップラーを推定し、送信周波数を逆方向にずらします。

しかし、ここで一つ重要な疑問が浮かびます — 「衛星のビームに含まれる全UEに対し、それぞれ個別のドップラー補償を送信できるのか?」答えはNoです。物理的に1つのビームが1つの搬送波で送信される以上、全UEで共通の補償しかできません。ここから、ドップラーを共通項と差分項に分けて議論する必要が生まれます。

ドップラーの分解: 共通項と差分項

衛星の位置を $\bm{r}_s(t) \in \mathbb{R}^3$、UE $i$ の位置を $\bm{r}_i$ とします。視線(LOS)方向の相対速度は、衛星速度 $\bm{v}_s = d\bm{r}_s/dt$ をUE方向に射影することで得られます。これを式で書くと:

$$ v_{r,i}(t) = \bm{v}_s(t) \cdot \frac{\bm{r}_i – \bm{r}_s(t)}{\|\bm{r}_i – \bm{r}_s(t)\|} $$

ここで右辺第二項はUEへの単位視線ベクトル $\hat{\bm{u}}_i(t)$ そのものです。ドップラーシフトはこの視線速度に比例し、

$$ f_{D,i}(t) = -\frac{v_{r,i}(t)}{c} f_c $$

となります(衛星が近づくとき $v_{r,i} < 0$ で $f_{D,i} > 0$ となる符号規約)。

ここで、ビームのフットプリント中心(参照ポイント $\bm{r}_0$)におけるドップラーを共通項として取り出します。参照点向きの単位視線ベクトルを $\hat{\bm{u}}_0(t)$ と書くと:

$$ f_{D,c}(t) = -\frac{f_c}{c}\, \bm{v}_s(t) \cdot \hat{\bm{u}}_0(t) $$

各UEのドップラーは、この共通項と差分項(残差) $\Delta f_{D,i}(t)$ の和で表せます:

$$ \begin{equation} f_{D,i}(t) = f_{D,c}(t) + \Delta f_{D,i}(t) \end{equation} $$

両者を引き算すると、差分項を陽に書けます:

$$ \Delta f_{D,i}(t) = -\frac{f_c}{c}\,\bm{v}_s(t) \cdot \left[\hat{\bm{u}}_i(t) – \hat{\bm{u}}_0(t)\right] $$

ここで $\hat{\bm{u}}_i(t) – \hat{\bm{u}}_0(t)$ は「参照点向きとUE向きの単位視線の差」で、UEが参照点に近づくほど小さくなるベクトルです。この事実が次に効いてきます。

この分解の物理的意味は明確です — 衛星が1つの搬送波で送信できる以上、衛星は $f_{D,c}$ しか補償できません。残った $\Delta f_{D,i}$ はUE位置に依存する補償不能成分として残ります。設計上の鍵は、この差分項を「OFDM受信機が許容できるレベル」まで小さく抑えることです。

差分項の大きさを評価するため、参照ポイントからUEまでの地表距離を $d_i = \|\bm{r}_i – \bm{r}_0\|$ として、単位視線ベクトルを参照点まわりにTaylor展開します。衛星と参照点の距離を $\rho_0 = \|\bm{r}_s – \bm{r}_0\|$ とすると、視線ベクトルの一次変分は $|\hat{\bm{u}}_i – \hat{\bm{u}}_0| \approx d_{i,\perp} / \rho_0$ の形になります(ここで $d_{i,\perp}$ は視線に直交する成分)。これを差分項の式に代入すると:

$$ |\Delta f_{D,i}(t)| \approx \frac{f_c\, v_s}{c} \cdot \frac{d_{i,\perp}}{\rho_0} \cdot |\cos\alpha| $$

ここで $\alpha$ は衛星速度ベクトルと変位ベクトルのなす角です。衛星高度を $h$、地表でのビーム半径を $R_b$、仰角を $\theta_{el}$ とすると、ビーム端での最大差分ドップラーは概ね:

$$ \Delta f_{D,\max} \approx \frac{f_c\, v_s}{c} \cdot \frac{R_b}{h+R_e} \cdot \sin(\theta_{el}) $$

オーダー評価としては、$\Delta f_{D,\max}$ はビーム半径 $R_b$ に比例し、ビームを小さくすれば差分項も小さくなります。逆に大型ビーム(数百 km 直径)では差分ドップラーが無視できないサイズに成長します。具体的な数値を入れてみると、Sバンド・LEO・$R_b = 50$ km・仰角45°で $\Delta f_{D,\max} \approx 280$ Hz、$R_b = 500$ km では約2.8 kHz になります。ビーム径とドップラーが線形にスケールする様子が読み取れます。

ここまでで「衛星側で打ち消せる共通項」と「打ち消せない差分項」の構造が見えました。次に、この共通項を「どの点を基準に決めるか」という運用上の選択 — 参照ポイント方式 — を見ていきます。

参照ポイント方式とは

3GPP NTN(TR 38.821, TS 38.300)では、衛星側DL事前補償の基準点をビーム参照ポイント(beam reference point) と呼びます。典型的にはビームのフットプリント中心、より厳密にはビームのゲインパターン最大点が選ばれます。

参照ポイント方式のワークフローは以下の通りです:

  1. 衛星側 — 自身の軌道暦から、現時刻 $t$ におけるビーム参照ポイント $\bm{r}_0(t)$ への視線ドップラー $f_{D,c}(t)$ を計算する
  2. 衛星側 — DL送信周波数を $f_c – f_{D,c}(t)$ にプリシフトして送信
  3. UE側受信 — UE位置でのドップラーは $f_{D,c}(t) + \Delta f_{D,i}(t)$ なので、受信周波数は $f_c + \Delta f_{D,i}(t)$ となり、共通項は打ち消される
  4. UE側補正 — 残った差分項 $\Delta f_{D,i}(t)$ をUE自身のGNSS情報で更に補償する

この方式の利点は、衛星側補償ロジックがシンプル(1ビームあたり1つのシフト量)で、ビーム内で共通したパイロット信号が使え、初期セルサーチ時にUEが「ある程度近い周波数」を探索すれば同期できる点にあります。3GPPのN1初期同期手順は、まさにこの「共通項補償後の周波数の探索」を前提に設計されています。

参照点の選び方には複数の流派があります。「地心点(衛星から地球中心方向)」を選ぶ実装はシンプルでビーム制御と独立に決まりますが、ビーム中心とずれる場合にビーム内残差が大きくなります。「ビームフットプリント中心」はビーム内残差を最小化しますが、衛星のオンボード演算が増えます。「サブ衛星点」はビームがオフナディアの場合に大きな共通ドップラーが残るので、低仰角運用では不利です。実際のRel-17 NTN実装ではビームフットプリント中心が主流です。

一方、欠点もあります。参照ポイントから離れたビーム端のUEは大きな残差ドップラーを抱え、UE側補償(次節)に強く依存することになります。また、ビーム参照ポイントの位置はSIB(System Information Block)として下りリンクで配信されるため、UEはこの位置情報を取得しなければGNSSベースの自己補償ができません。3GPP TS 38.331 では新規IE(情報要素)として NTN-Config が定義され、衛星エフェメリス、ビーム参照点、共通TA(Timing Advance)が含まれます。

ここで自然な疑問が生まれます — 「UEはGNSSがある前提なのか?」答えはRel-17 NR-NTN/IoT-NTNではYesです。UE側がGNSSで自位置と衛星位置を知ることが、ドップラー補償・タイミングアドバンス計算の前提条件として規定されています。次節でこのUE側補償の役割を詳しく見ていきます。

UE側補償(GNSS利用)

UE側の補償は、衛星側の参照ポイント補償で残った差分項 $\Delta f_{D,i}(t)$ を、UE自身が計算して打ち消す処理です。具体的な手順は次のようになります。

UE $i$ は、まずブロードキャストされたSIBから衛星エフェメリス $\bm{r}_s(t), \bm{v}_s(t)$ と、ビーム参照ポイント $\bm{r}_0$ を取得します。次に自身のGNSS受信機から自位置 $\bm{r}_i$ を得ます。これらを使い、自身が知覚するドップラー $f_{D,i}(t)$ と参照点ドップラー $f_{D,c}(t)$ をそれぞれ計算し、その差を求めます:

$$ \widehat{\Delta f_{D,i}}(t) = -\frac{f_c}{c}\bm{v}_s(t) \cdot \left[\frac{\bm{r}_i – \bm{r}_s(t)}{\|\bm{r}_i – \bm{r}_s(t)\|} – \frac{\bm{r}_0 – \bm{r}_s(t)}{\|\bm{r}_0 – \bm{r}_s(t)\|}\right] $$

この $\widehat{\Delta f_{D,i}}(t)$ がUE側で予測される差分ドップラーで、UEは自身の局所発振器(LO)を逆方向にシフトすることで、DL受信時にもUL送信時にもこの差分を打ち消します。

UL(上り)補償の場合、UEは送信周波数を $f_c – \widehat{\Delta f_{D,i}}(t) – f_{D,c}^{UL}(t)$ に設定する必要があります。すなわちUE側は共通項と差分項の両方をUL補償することが基本です(衛星が運用する方式によっては共通項のみ衛星でUL補償する変種もあります)。これは「DL補償は衛星、UL補償はUE」という非対称な役割分担を意味しており、衛星-地上の物理層プロトコルが地上NRと厳密に等価ではない理由のひとつです。

ここで実際の運用上の課題が浮かびます。GNSSによる位置誤差($\sim 3$ m)、衛星エフェメリスの誤差($\sim 50$ m)、UEの局所発振器のドリフト($\sim$ ppmレベル)が、いずれも残差ドップラーに寄与します。たとえば1 ppmのLO誤差は2 GHzキャリアで2 kHzに相当し、これだけで15 kHz SCSの13 % を占めます。GNSSと衛星エフェメリスの誤差からくるドップラー予測誤差は通常数Hz〜数十Hzオーダーで、LO誤差より小さいことが多いです。これらを足し合わせた実効残差ドップラー $\delta f$ は、典型的なLEO・Sバンドで $\sim 100$ Hz オーダーに抑えられます。これは1サブキャリア15 kHzの1 %未満で、OFDMの正常な復調を保てる範囲です。

なお、UEがGNSSロックを失った場合のフォールバックも標準で議論されています。一定時間ロスト時には「直近で観測したGNSS位置」と「衛星エフェメリスからの外挿」で補償を継続し、それでも続く場合はセル再選択に移ります。GNSS非対応UE(一部のIoT機器)は、原則NTNを使えません。

UE側補償の役割をまとめると次のようになります。衛星側DL補償は「ビーム全体に共通したオフセット」を打ち消す粗い補償であり、UE側補償は「UE位置に依存する細かい残差」を打ち消す精密な補償です。両者の協調により、ようやくNTNはOFDMの厳密な周波数要件を満たします。

では実際に、参照ポイント補償後にどの程度の残差ドップラーが残るのでしょうか?それがOFDMにどう影響するかを次に見ていきます。

補償残差のOFDMサブキャリアへの影響(ICI)

OFDMは隣接サブキャリア間が直交するように設計されており、これが成立するのは送受信間の周波数オフセット $\delta f$ がサブキャリア間隔 $\Delta f_{sc}$ に対し十分小さい場合だけです。残差ドップラーは、この直交性を壊しサブキャリア間干渉(Inter-Carrier Interference, ICI) を引き起こします。直感的には、本来一直線に並んでいた直交基底(複素正弦波)が、ドップラーによってわずかに角度がずれ、隣のサブキャリア成分と「内積が0でなくなる」状態です。

OFDMシンボル長 $T_s = 1/\Delta f_{sc}$ にわたって、サブキャリア $k$ で送信された複素シンボル $X_k$ は、復調側で次のように観測されます。残差ドップラー $\delta f$ がある場合、受信側でサブキャリア $m$ に対するDFT後の信号は:

$$ Y_m = X_m \cdot S(0) + \sum_{k \neq m} X_k \cdot S(m – k) + N_m $$

右辺第一項が希望信号、第二項がICI、第三項が雑音です。ここで干渉係数 $S(l)$ は:

$$ S(l) = \frac{\sin\left(\pi(\epsilon – l)\right)}{N \sin\left(\frac{\pi(\epsilon – l)}{N}\right)} \cdot e^{j\pi(\epsilon – l)\left(1 – \frac{1}{N}\right)} $$

で表され、$\epsilon = \delta f / \Delta f_{sc}$ は正規化周波数オフセット、$N$ はDFTサイズです。$\epsilon = 0$ なら $S(0) = 1, S(l \neq 0) = 0$ で完全直交、$\epsilon \neq 0$ では $S(0) < 1$ となり、かつ $S(l \neq 0) \neq 0$ で他サブキャリアからの漏れ込みが発生します。

ここから2つの劣化が起きます。第一に、希望サブキャリアの振幅が $|S(0)| < 1$ に減衰します。これを定量化すると、$\epsilon$ が小さい範囲では:

$$ |S(0)|^2 \approx \mathrm{sinc}^2(\epsilon) \approx 1 – \frac{(\pi\epsilon)^2}{3} $$

第二に、他サブキャリアからの漏れ込みが信号成分に重なり、ICI雑音となります。$N$ が十分大きい場合の総ICI電力を全サブキャリア $k \neq m$ について和を取って近似すると:

$$ P_{ICI} \approx \frac{(\pi\epsilon)^2}{3} \cdot P_s $$

つまり、信号対干渉比(SIR)は $\epsilon$ の二乗で悪化します。1 %のオフセット($\epsilon = 0.01$)でSIRはおよそ 40 dB、$\epsilon = 0.1$(10 %)では 20 dBまで劣化します。実用上のリンクバジェットを考えると、$\epsilon < 0.05$(5 %)程度が許容範囲とされます。これより大きいとQPSK・16QAM等の高次変調でビット誤り率(BER)が急増し、誤り訂正でカバーしきれなくなります。

この基準を具体的なNTN設計に当てはめてみましょう。Sバンド・LEOで参照ポイント補償後の残差ドップラーが最大 $\Delta f_{D,\max} = 5$ kHz(直径100 km ビーム想定)の場合:

SCS $\epsilon_{\max}$ 判定
15 kHz 0.33 NG(ICI過大)
30 kHz 0.17 微妙(GNSSなしでは厳しい)
120 kHz 0.04 OK

つまり、GNSS補償なしでビーム端まで広くカバーしたい場合、より広いSCSが必要になります。逆にGNSS補償ありで残差が数百Hzまで下げられるなら、15 kHz SCSでも十分です。次節では、この設計トレードオフを定量的に整理します。

サブキャリアスペーシング選定の指針

NR標準では、SCSは 15, 30, 60, 120, 240 kHz から選択できます。地上NRの典型はFR1で30 kHz、FR2で120 kHzです。NTNでは何を選ぶべきでしょうか?

3GPP TS 38.211 / TR 38.821 の議論を整理すると、NTN用SCS選定の指針は次の3つの要素から決まります。

  1. 残差ドップラー — 参照ポイント補償後・UE側GNSS補償ありで、$\epsilon = \delta f / \Delta f_{sc} < 0.05$ を満たすこと
  2. シンボル長と遅延広がり — 衛星-地上往復伝搬遅延が長く(LEO 600 km ですら片道2 ms)、サイクリックプレフィックス(CP)長が遅延広がりに対し十分か
  3. 位相雑音 — Kaバンド以上では衛星側LO位相雑音が大きく、SCSが狭いと位相雑音による劣化が無視できない

これら3要素は互いに引き合うトレードオフ関係にあります。SCSを広くすればドップラー耐性と位相雑音耐性が上がる一方、シンボル長 $T_s = 1/\Delta f_{sc}$ が短くなりCP長も比例して短縮されるため、遅延広がりに弱くなります。NTNでは複数衛星・地上反射・電離層遅延などが遅延広がりに寄与するので、SCSをただ広げればよいわけではありません。

これらを踏まえ、3GPP Rel-17 NTNでは下記が一般的に採用されます:

  • Sバンド NR-NTN: SCS = 15 kHz または 30 kHz。UE側GNSS補償ありを前提とし、残差を $\epsilon < 0.05$ に抑える
  • Kaバンド NR-NTN: SCS = 60 kHz / 120 kHz。位相雑音とドップラー両面から広いSCSが必要
  • IoT-NTN(NB-IoT): SCS = 3.75 kHz / 15 kHz。シンボル長を伸ばしてカバレッジ最大化を狙う反面、ドップラー要件が厳しく GNSS補償が必須

これを「ビーム径とSCS」の観点で見ると、興味深いトレードオフが浮かびます。ビーム径が大きいほど差分ドップラーが大きく広いSCSが必要、ただしSCSが広いほどCP長が短くなり長い伝搬遅延広がりに弱くなります。LEO直径100 km級のビームでSバンドなら、15-30 kHz SCSとGNSS補償の組み合わせがバランスポイントになります。逆に、商用LEOコンステレーション(Starlink等)のように直径数百km級のビームを使う場合は、120 kHz SCSへの移行が現実的です。

このように、ドップラー事前補償の設計は単に「周波数を打ち消す」だけでなく、OFDMの基本パラメータ選定にまで波及します。実感を持って理解するために、次節でPythonによるシミュレーションを行いましょう。

Pythonでビーム内残差ドップラー分布シミュレーション

LEO衛星が地上を通過する場面を想定し、ビーム内に散在するN個のUEがそれぞれ感じるドップラーを計算します。そして「参照ポイント補償後」の残差ドップラーを可視化し、ビーム径とSCSの関係を直感的に掴みましょう。

シミュレーションのセットアップは以下のとおりです:

  • LEO衛星高度: 600 km、軌道速度: 7.56 km/s
  • キャリア周波数: 2 GHz(Sバンド)
  • ビーム参照ポイント: UEから真上のサブ衛星点付近
  • UE: ビーム中心から半径 $R_b$ 以内にランダム配置
  • 衛星はUEの真上付近を北方向に飛行
import numpy as np
import matplotlib.pyplot as plt

# 定数
c = 299_792_458.0          # 光速 [m/s]
R_e = 6378.137e3           # 地球半径 [m]
fc = 2.0e9                 # キャリア周波数 [Hz] (Sバンド)
h_sat = 600e3              # 衛星高度 [m]
mu = 3.986e14              # 地球重力定数 [m^3/s^2]

# 衛星軌道速度(円軌道近似)
v_sat = np.sqrt(mu / (R_e + h_sat))
print(f"衛星軌道速度: {v_sat:.1f} m/s")

# サブ衛星点を原点とする局所東-北-天頂(ENU)座標で扱う
def sat_position(t, h=h_sat, v=v_sat):
    """衛星位置(ENU): 時刻0でサブ衛星点直上、北方向に飛行"""
    return np.array([0.0, v * t, h])

def sat_velocity(t, v=v_sat):
    """衛星速度ベクトル(ENU)"""
    return np.array([0.0, v, 0.0])

def doppler_shift(r_ue, t):
    """UE位置 r_ue におけるドップラーシフト [Hz]"""
    r_s = sat_position(t)
    v_s = sat_velocity(t)
    los = r_ue - r_s
    los_hat = los / np.linalg.norm(los)
    v_radial = np.dot(v_s, los_hat)   # 視線方向速度 [m/s]
    return -v_radial / c * fc          # ドップラー [Hz]

ここでまずシミュレーション基盤を定義しました。衛星はサブ衛星点を起点に北方向(ENUのy軸)に飛行し、UE座標は地表(z=0)平面に配置します。doppler_shift 関数はUE位置と時刻からドップラーを返します。LEO円軌道速度は約7.56 km/s と出力され、教科書値(7.5〜7.7 km/s)と整合しています。

次にビーム内にN個のUEをランダム配置し、各UEのドップラーを計算します。

# ビーム半径とUE配置
R_beam = 50e3              # ビーム半径 50 km (直径100 km)
N_ue = 500                 # UE数

np.random.seed(42)
# 円盤内にランダム配置(極座標で一様化)
rho = R_beam * np.sqrt(np.random.uniform(0, 1, N_ue))
phi = np.random.uniform(0, 2*np.pi, N_ue)
ue_x = rho * np.cos(phi)
ue_y = rho * np.sin(phi)
ue_z = np.zeros(N_ue)

# 観測時刻(衛星がサブ衛星点を通過してから少しずれた時点)
t_obs = 60.0   # [s] サブ衛星点通過から60秒後

# 各UEのドップラー
f_d = np.array([doppler_shift(np.array([ue_x[i], ue_y[i], ue_z[i]]), t_obs)
                for i in range(N_ue)])

# 参照ポイント(ビーム中心 = 原点)のドップラー
f_dc = doppler_shift(np.array([0.0, 0.0, 0.0]), t_obs)

# 差分(残差)ドップラー
delta_f_d = f_d - f_dc

print(f"参照点ドップラー f_Dc = {f_dc/1e3:.2f} kHz")
print(f"残差ドップラー範囲: {delta_f_d.min():.1f} 〜 {delta_f_d.max():.1f} Hz")
print(f"残差ドップラー標準偏差: {delta_f_d.std():.1f} Hz")

このコードは、ビーム半径50 km(直径100 km)の円盤内に500個のUEを配置し、衛星がサブ衛星点を通過してから60秒後(衛星が約450 km北に進んで斜めから見下ろす状況)のドップラーを各UEで計算します。f_dc は中心UE(参照点)のドップラーで、これを衛星側プリ補償で打ち消します。残った delta_f_d がUE側補償が打ち消すべき差分項です。極座標で rhosqrt(uniform) にしているのは、円盤内に一様分布させるための定石です。

実行すると、参照点ドップラーは数十 kHz オーダー(衛星が斜めから見える時間帯)、残差ドップラーは数百 Hz オーダーになることが確認できます。ビーム端(半径50 km)でも残差は数百 Hz 程度に抑えられており、Sバンド・SCS 15 kHzでも実用域にあります。

続いて、可視化してビーム内分布を見てみましょう。

fig, axes = plt.subplots(1, 2, figsize=(13, 5))

# 左: ビーム内の残差ドップラー分布(地表マップ)
ax = axes[0]
sc = ax.scatter(ue_x/1e3, ue_y/1e3, c=delta_f_d, cmap='RdBu_r',
                s=18, vmin=-delta_f_d.std()*3, vmax=delta_f_d.std()*3)
circle = plt.Circle((0, 0), R_beam/1e3, fill=False,
                     edgecolor='k', linestyle='--')
ax.add_artist(circle)
ax.set_xlabel('East [km]')
ax.set_ylabel('North [km]')
ax.set_title(f'Residual Doppler in Beam (t={t_obs}s, beam r=50km)')
ax.set_aspect('equal')
plt.colorbar(sc, ax=ax, label='Residual Doppler [Hz]')

# 右: 半径方向の残差ドップラー
ax = axes[1]
r_ue = np.sqrt(ue_x**2 + ue_y**2) / 1e3   # [km]
ax.scatter(r_ue, delta_f_d, s=15, alpha=0.6)
ax.axhline(0, color='k', linewidth=0.5)
ax.set_xlabel('Distance from beam center [km]')
ax.set_ylabel('Residual Doppler [Hz]')
ax.set_title('Residual Doppler vs. Distance from Reference Point')
ax.grid(alpha=0.3)

plt.tight_layout()
plt.show()

左図はビームを上から見たマップで、UE位置に応じた残差ドップラーをカラーで示します。衛星が北方向に飛行しているため、衛星進行方向(北側)に位置するUEはより小さなドップラー、後方(南側)UEはより大きなドップラーを感じることが視覚的に分かります。これは視線方向と衛星速度ベクトルの角度の違いに起因しています。カラーパターンが「南北方向の勾配」になっているのは、まさに $\bm{v}_s \cdot (\hat{\bm{u}}_i – \hat{\bm{u}}_0)$ の方向依存性を反映しています。

右図は参照点からの距離 $r$ に対する残差ドップラーの散布図で、距離に比例して残差が広がっていく様子が見えます。同じ距離でも方位角によって正負が分かれており、これは差分ドップラーが「ベクトル的な現象」であることを反映しています。ビーム端(50 km)での残差は概ね $\pm$ 数百 Hz に収まっており、これは15 kHz SCS の $\epsilon \approx 0.03$(3 %)相当で、ICI劣化は約 0.4 dB と十分に許容範囲です。

最後に、ビーム径を変えて残差ドップラーがどう変化するかを確認します。

beam_radii = np.array([10, 25, 50, 100, 200, 400]) * 1e3  # [m]
N_per_beam = 300
results = []

for R_b in beam_radii:
    rho = R_b * np.sqrt(np.random.uniform(0, 1, N_per_beam))
    phi = np.random.uniform(0, 2*np.pi, N_per_beam)
    xs, ys = rho * np.cos(phi), rho * np.sin(phi)
    f_d_local = np.array([doppler_shift(np.array([xs[i], ys[i], 0.0]), t_obs)
                          for i in range(N_per_beam)])
    delta = f_d_local - f_dc
    results.append((R_b, delta.std(), np.abs(delta).max()))

results = np.array(results)
plt.figure(figsize=(8, 5))
plt.plot(results[:,0]/1e3, results[:,1], 'o-', label='Std (1 sigma)')
plt.plot(results[:,0]/1e3, results[:,2], 's--', label='Max |Delta f|')

# SCS の許容線 (epsilon=0.05 基準)
for scs in [15e3, 30e3, 120e3]:
    plt.axhline(0.05*scs, linestyle=':', alpha=0.6,
                label=f'5% of {int(scs/1e3)} kHz SCS')

plt.xlabel('Beam radius [km]')
plt.ylabel('Residual Doppler [Hz]')
plt.title('Residual Doppler vs Beam Size (S-band, h=600km)')
plt.xscale('log')
plt.yscale('log')
plt.legend(fontsize=9)
plt.grid(alpha=0.3, which='both')
plt.tight_layout()
plt.show()

このグラフから、設計上の重要な示唆が読み取れます。第一に、残差ドップラーはビーム半径にほぼ比例して大きくなります(両対数軸で傾き1の直線)。これは前節の理論予測 $\Delta f_{D,\max} \propto R_b$ と一致しています。第二に、SCSごとの $\epsilon=0.05$ 許容線との交点が、そのSCSで運用可能なビーム最大半径を示しています。15 kHz SCSなら最大半径100 km程度、30 kHzなら200 km程度、120 kHzなら400 km以上まで余裕があるという指針が得られます。

実際のNTN運用では、GNSSによるUE側補償が入るため許容ビーム径はさらに大きくできますが、「GNSSが使えないフォールバック時の最低限」として上記の解析は重要な設計入力になります。商用設計では、このシミュレーションを各仰角・各衛星パス位相について実行し、最悪条件でも $\epsilon < 0.05$ を満たすSCSを選定する流れになります。

まとめ

本記事では、3GPP NTNにおけるドップラー事前補償の理論と実装を解説しました。

  • NTNではLEO衛星の高速移動により、Sバンドで $\pm 50$ kHz、Kaバンドで $\pm 750$ kHz もの大きなドップラーが生じる
  • 衛星と地上の幾何学は予測可能なので、送信側でドップラーを逆方向にプリシフトする事前補償が有効
  • 1つのビームでは個別補償ができないため、ドップラーを共通項 $f_{D,c}$(参照点)差分項 $\Delta f_{D,i}$(UE位置依存) に分解する
  • 衛星側DLは参照点ドップラーをプリ補償、UE側はGNSSで差分項を補償する役割分担
  • 残差ドップラーは正規化オフセット $\epsilon = \delta f / \Delta f_{sc}$ を通じてOFDMのICIを引き起こし、$\epsilon < 0.05$ が実用上の目安
  • SCS選定はビーム径・残差ドップラー・遅延広がり・位相雑音のトレードオフで決まる
  • Pythonシミュレーションにより、残差ドップラーはビーム半径に概ね比例することが確認できた

ドップラー事前補償は、NTNが地上5Gと「外見上同じプロトコル」で動作するための核心技術です。送信側が苦労することで、UE側の受信機が地上モードとほぼ同じ複雑度で動くのが、3GPP NTN設計思想の真髄です。

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