【STK】衛星コンステレーションの構築方法を解説

STK(Systems Tool Kit、旧称AGI STK)は、航空宇宙分野で広く利用されているシミュレーションソフトウェアです。衛星の軌道解析、地上局との通信解析、センサカバレッジの分析など、ミッション設計に必要な機能を備えています。

今回は、STKを使った衛星コンステレーションの構築方法と、Pythonでの代替的なシミュレーション手法について解説します。

本記事の内容

  • STKの概要と主要機能
  • Walkerコンステレーションの設計パラメータ
  • STKでのコンステレーション構築手順
  • Pythonでのコンステレーション解析

前提知識

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

STKの概要

STKは以下の主要機能を持ちます。

  • 軌道伝搬: ケプラー軌道、J2摂動、高精度摂動モデル(HPOP)
  • アクセス解析: 衛星間、衛星-地上局間の可視性計算
  • カバレッジ解析: 地上のグリッドポイントに対するカバー率
  • 通信リンク解析: リンクバジェット計算
  • 3D可視化: 地球と軌道の3Dレンダリング

コンステレーション設計の基本

Walkerデルタパターン

Walker $i:T/P/F$ パターンでコンステレーションを定義します。

$$ \Omega_p = \frac{2\pi p}{P}, \quad M_{p,s} = \frac{2\pi s}{S} + \frac{2\pi F p}{T} $$

ここで $S = T/P$ は1軌道面あたりの衛星数です。

設計変数と目的関数

コンステレーション最適化の典型的な定式化は次の通りです。

$$ \min_{T, P, F, h, i} \quad T \quad (\text{総衛星数の最小化}) $$

$$ \text{subject to:} \quad C(\bm{x}) \geq C_{\text{req}} \quad (\text{被覆率条件}) $$

ここで $C(\bm{x})$ は設計変数 $\bm{x} = (T, P, F, h, i)$ に対する被覆率です。

Pythonでのコンステレーション解析

STKの代替として、Pythonでコンステレーションの被覆率解析を行ってみましょう。

import numpy as np
import matplotlib.pyplot as plt

# 定数
Re = 6371.0  # 地球半径 [km]
mu = 398600.4418  # 重力定数 [km^3/s^2]

def walker_positions(T, P, F, h, inc_deg, t=0):
    """Walkerコンステレーションの全衛星位置を計算

    Returns: (lats, lons) in degrees
    """
    S = T // P
    a = Re + h
    T_orb = 2 * np.pi * np.sqrt(a**3 / mu)
    n = 2 * np.pi / T_orb
    omega_e = 2 * np.pi / 86164.1
    inc = np.radians(inc_deg)

    lats = []
    lons = []

    for p in range(P):
        raan = 2 * np.pi * p / P
        for s in range(S):
            M0 = 2 * np.pi * s / S + 2 * np.pi * F * p / T
            M = M0 + n * t

            lat = np.degrees(np.arcsin(np.sin(inc) * np.sin(M)))
            lon_inertial = np.arctan2(np.sin(M) * np.cos(inc), np.cos(M))
            lon = np.degrees(lon_inertial + raan - omega_e * t) % 360
            if lon > 180:
                lon -= 360

            lats.append(lat)
            lons.append(lon)

    return np.array(lats), np.array(lons)

def coverage_analysis(sat_lats, sat_lons, h, eps_min_deg, grid_res=5):
    """グリッドベースの被覆率解析

    Returns: coverage_fraction, grid_lats, grid_lons, covered_grid
    """
    eps_min = np.radians(eps_min_deg)
    theta_max = np.arccos(Re / (Re + h) * np.cos(eps_min)) - eps_min
    theta_max_deg = np.degrees(theta_max)

    # 地上グリッド
    grid_lats = np.arange(-90, 91, grid_res)
    grid_lons = np.arange(-180, 181, grid_res)
    lat_grid, lon_grid = np.meshgrid(grid_lats, grid_lons)

    covered = np.zeros_like(lat_grid, dtype=bool)

    for slat, slon in zip(sat_lats, sat_lons):
        # 衛星直下点からの角距離
        dlat = np.radians(lat_grid - slat)
        dlon = np.radians(lon_grid - slon)
        a_hav = np.sin(dlat/2)**2 + \
                np.cos(np.radians(slat)) * np.cos(np.radians(lat_grid)) * \
                np.sin(dlon/2)**2
        angular_dist = 2 * np.arcsin(np.sqrt(np.clip(a_hav, 0, 1)))

        covered |= (angular_dist <= theta_max)

    # 面積重み付きカバー率
    weights = np.cos(np.radians(lat_grid))
    coverage = np.sum(covered * weights) / np.sum(weights)

    return coverage, lat_grid, lon_grid, covered

# コンステレーション設計の比較
configs = [
    ("Small (24/3/1)", 24, 3, 1, 1100, 53),
    ("Medium (48/6/1)", 48, 6, 1, 550, 53),
    ("Large (72/6/1)", 72, 6, 1, 550, 53),
]

fig, axes = plt.subplots(1, 3, figsize=(18, 5))

for idx, (name, T, P, F, h, inc) in enumerate(configs):
    lats, lons = walker_positions(T, P, F, h, inc, t=0)
    cov, lat_g, lon_g, covered = coverage_analysis(lats, lons, h, 25)

    axes[idx].pcolormesh(lon_g, lat_g, covered.astype(float),
                         cmap='RdYlGn', alpha=0.5)
    axes[idx].scatter(lons, lats, c='red', s=20, zorder=5)
    axes[idx].set_xlim(-180, 180)
    axes[idx].set_ylim(-90, 90)
    axes[idx].set_xlabel('Longitude [deg]')
    axes[idx].set_ylabel('Latitude [deg]')
    axes[idx].set_title(f'{name}\nCoverage: {cov*100:.1f}%')
    axes[idx].grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

# 衛星数と被覆率の関係
print("=== 衛星数と被覆率の関係 ===")
sat_counts = [12, 24, 36, 48, 60, 72, 96, 120]
coverages = []
for T_sat in sat_counts:
    P_planes = max(3, T_sat // 12)
    lats, lons = walker_positions(T_sat, P_planes, 1, 550, 53, t=0)
    cov, _, _, _ = coverage_analysis(lats, lons, 550, 25, grid_res=5)
    coverages.append(cov)
    print(f"  T={T_sat:3d}, P={P_planes}: Coverage = {cov*100:.1f}%")

fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(sat_counts, [c * 100 for c in coverages], 'bo-', linewidth=2)
ax.set_xlabel('Number of Satellites')
ax.set_ylabel('Coverage [%]')
ax.set_title('Coverage vs Number of Satellites (h=550km, eps_min=25deg)')
ax.grid(True)
ax.set_ylim(0, 105)
plt.tight_layout()
plt.show()

まとめ

本記事では、STKを使った衛星コンステレーションの構築方法について解説しました。

  • STKは航空宇宙分野の標準的なシミュレーションツールである
  • Walkerデルタパターンはコンステレーション設計の基本的な手法で、$T/P/F$ パラメータで定義される
  • コンステレーション最適化は衛星数最小化と被覆率制約の問題として定式化できる
  • Pythonでも簡易的なコンステレーション解析が可能であり、設計の初期検討に活用できる

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