近年、低軌道(LEO, Low Earth Orbit)に多数の衛星を打ち上げ、情報通信サービスを提供するLEOコンステレーションが商業化されつつあります。SpaceXのStarlink(スターリンク)が最も有名です。
LEOコンステレーションの設計では、限られた衛星数で地球全体をカバーするための軌道配置の最適化が重要な課題となります。
本記事の内容
- LEOコンステレーションの基本概念
- Walkerデルタパターンの数学的定式化
- 被覆率の計算
- Pythonでのコンステレーションシミュレーション
前提知識
この記事を読む前に、以下の記事を読んでおくと理解が深まります。
LEOコンステレーションの基本
軌道高度と特性
| パラメータ | LEO | MEO | GEO |
|---|---|---|---|
| 高度 [km] | 300-2000 | 2000-35786 | 35786 |
| 軌道周期 [分] | 90-127 | 2-24時間 | 24時間 |
| 遅延 [ms] | 1-7 | 30-120 | ~240 |
| 必要衛星数 | 数百-数千 | 数十 | 3 |
LEOの利点は低遅延ですが、1機の衛星のカバー範囲が狭いため、多数の衛星が必要になります。
Walkerデルタパターン
コンステレーションの標準的な配置方法がWalkerデルタパターンです。$i:T/P/F$ の3つのパラメータで記述されます。
- $i$: 軌道傾斜角 [deg]
- $T$: 総衛星数
- $P$: 軌道面数
- $F$: 位相パラメータ ($0 \leq F < P$)
各軌道面には $S = T/P$ 機の衛星が均等に配置されます。
軌道面の配置
$P$ 個の軌道面の昇交点赤経は均等に分布します。
$$ \Omega_p = \frac{360° \cdot p}{P}, \quad p = 0, 1, \ldots, P-1 $$
衛星の配置
軌道面 $p$ 内の $s$ 番目の衛星の平均近点角は、
$$ M_{p,s} = \frac{360° \cdot s}{S} + \frac{360° \cdot F \cdot p}{T} $$
ここで $s = 0, 1, \ldots, S-1$ です。位相パラメータ $F$ は隣接軌道面間の衛星の位相差を制御します。
被覆率の計算
単一衛星のカバー範囲
高度 $h$ の衛星が最低仰角 $\varepsilon_{\min}$ 以上でカバーできる地表面積は、半頂角 $\theta_{\max}$ の円錐で近似できます。
$$ \theta_{\max} = \arccos\left(\frac{R_E}{R_E + h}\cos\varepsilon_{\min}\right) – \varepsilon_{\min} $$
カバー面積は、
$$ A_{\text{cover}} = 2\pi R_E^2 (1 – \cos\theta_{\max}) $$
全球被覆の必要衛星数
全球(面積 $4\pi R_E^2$)をカバーするために必要な最小衛星数の下限は、
$$ N_{\min} \geq \frac{4\pi R_E^2}{A_{\text{cover}}} = \frac{2}{1 – \cos\theta_{\max}} $$
実際には衛星間のカバー領域の重なりがあるため、この値の1.5から2倍程度の衛星が必要です。
Pythonでのコンステレーションシミュレーション
import numpy as np
import matplotlib.pyplot as plt
# 定数
Re = 6371.0 # 地球半径 [km]
def walker_constellation(T, P, F, h, inc):
"""Walkerデルタパターンのコンステレーションを生成
Parameters
----------
T : int - 総衛星数
P : int - 軌道面数
F : int - 位相パラメータ
h : float - 軌道高度 [km]
inc : float - 軌道傾斜角 [deg]
Returns
-------
sats : list of dict - 各衛星の軌道要素
"""
S = T // P # 1面あたりの衛星数
inc_rad = np.radians(inc)
sats = []
for p in range(P):
raan = 360.0 * p / P # 昇交点赤経 [deg]
for s in range(S):
M = 360.0 * s / S + 360.0 * F * p / T # 平均近点角 [deg]
M = M % 360.0
sats.append({
'plane': p,
'index': s,
'raan': raan,
'M': M,
'inc': inc,
'h': h,
})
return sats
def sat_subpoint(sat, t=0):
"""衛星の直下点(緯度・経度)を計算(簡易モデル)"""
h = sat['h']
a = Re + h
mu = 398600.4418
T_orb = 2 * np.pi * np.sqrt(a**3 / mu)
n = 2 * np.pi / T_orb
inc_rad = np.radians(sat['inc'])
raan_rad = np.radians(sat['raan'])
M_rad = np.radians(sat['M']) + n * t
# 衛星の位置(軌道面内)
lat = np.degrees(np.arcsin(np.sin(inc_rad) * np.sin(M_rad)))
lon_inertial = np.degrees(np.arctan2(
np.sin(M_rad) * np.cos(inc_rad), np.cos(M_rad)))
# 地球自転を考慮
omega_e = 2 * np.pi / 86164.1
lon = (lon_inertial + np.degrees(raan_rad) - np.degrees(omega_e * t)) % 360
if lon > 180:
lon -= 360
return lat, lon
def coverage_angle(h, eps_min_deg):
"""カバー範囲の半頂角 [rad]"""
eps_min = np.radians(eps_min_deg)
theta = np.arccos(Re / (Re + h) * np.cos(eps_min)) - eps_min
return theta
# Starlinkライクなコンステレーション
T = 72 # 総衛星数(簡易版)
P = 6 # 軌道面数
F = 1 # 位相パラメータ
h = 550 # 軌道高度 [km]
inc = 53 # 軌道傾斜角 [deg]
sats = walker_constellation(T, P, F, h, inc)
# 被覆率の計算
eps_min = 25 # 最低仰角 [deg]
theta_cover = coverage_angle(h, eps_min)
A_cover = 2 * np.pi * Re**2 * (1 - np.cos(theta_cover))
A_earth = 4 * np.pi * Re**2
N_min = 2 / (1 - np.cos(theta_cover))
print(f"=== コンステレーション設計 ===")
print(f"Walker {inc}:{T}/{P}/{F}")
print(f"軌道高度: {h} km")
print(f"最低仰角: {eps_min} deg")
print(f"カバー半頂角: {np.degrees(theta_cover):.1f} deg")
print(f"1衛星のカバー面積: {A_cover:.0f} km^2")
print(f"地球全表面積: {A_earth:.0f} km^2")
print(f"必要最小衛星数(下限): {N_min:.0f}")
# 可視化
fig, axes = plt.subplots(1, 2, figsize=(16, 6))
# 衛星の配置(t=0)
lats = []
lons = []
colors = []
for sat in sats:
lat, lon = sat_subpoint(sat, t=0)
lats.append(lat)
lons.append(lon)
colors.append(sat['plane'])
scatter = axes[0].scatter(lons, lats, c=colors, cmap='tab10', s=30, zorder=5)
axes[0].set_xlim(-180, 180)
axes[0].set_ylim(-90, 90)
axes[0].set_xlabel('Longitude [deg]')
axes[0].set_ylabel('Latitude [deg]')
axes[0].set_title(f'Walker {inc}:{T}/{P}/{F} at h={h}km')
axes[0].grid(True, alpha=0.3)
# 各衛星のカバー範囲を円で描画
for lat, lon in zip(lats, lons):
theta_deg = np.degrees(theta_cover)
circle_lats = lat + theta_deg * np.cos(np.linspace(0, 2*np.pi, 50))
circle_lons = lon + theta_deg / np.cos(np.radians(lat)) * np.sin(np.linspace(0, 2*np.pi, 50))
circle_lats = np.clip(circle_lats, -90, 90)
axes[0].plot(circle_lons, circle_lats, 'b-', alpha=0.1, linewidth=0.5)
plt.colorbar(scatter, ax=axes[0], label='Orbital Plane')
# 高度と必要衛星数の関係
altitudes = np.linspace(300, 2000, 100)
for eps in [10, 25, 40]:
N_mins = []
for alt in altitudes:
theta = coverage_angle(alt, eps)
N_min_val = 2 / (1 - np.cos(theta))
N_mins.append(N_min_val)
axes[1].semilogy(altitudes, N_mins, label=f'eps_min = {eps} deg')
axes[1].set_xlabel('Altitude [km]')
axes[1].set_ylabel('Minimum Number of Satellites')
axes[1].set_title('Minimum Satellites for Global Coverage')
axes[1].legend()
axes[1].grid(True)
plt.tight_layout()
plt.show()
まとめ
本記事では、LEOコンステレーションの設計と最適化問題について解説しました。
- LEOコンステレーションは低遅延だが多数の衛星が必要である
- Walkerデルタパターン $i:T/P/F$ はコンステレーション設計の標準的な手法である
- 被覆率は軌道高度と最低仰角から幾何学的に計算できる
- 全球被覆に必要な最小衛星数は $2/(1-\cos\theta_{\max})$ が下限である
- 実用的な設計では衛星間の重複を考慮して1.5-2倍の衛星数が必要となる
次のステップとして、以下の記事も参考にしてください。