E2E テレコマ動作確認システム

実機フライトソフト / SILS の振る舞いを、テレコマンドバス (MQTT) 経由で外部から E2E 検証する仕組みです。 「RESET を送ると本当にソフトウェアリセットがかかるか」「CONTROL,OFF で制御が止まるか」「CAPTURE で画像が降りてくるか」といったシナリオを、コマンド送信と受信テレメトリの観測だけで自動判定します。

実機と SILS は同じ MQTT トピック (sat/command / sat/telemetry / sat/image) と共通テレメトリスキーマ (shared/shared/telemetry_schema.json) を喋るため、同一のシナリオコードを --target=sils|hardware で両対象に当てられます

構成

flowchart LR
    subgraph Test[pytest E2E ハーネス ground/e2e/]
        TC[TelecommandClient<br/>MQTT ラッパ]
        SC[シナリオテスト群]
        SC --> TC
    end
    TC -- publish sat/command --> B[(MQTT ブローカー<br/>mosquitto)]
    B -- sat/telemetry / sat/image --> TC

    B <-- TCP80/81 --> BR[bridge.py]
    BR <-- WiFi --> HW[実機 Pico W]
    B <-- 直結 --> SILS[SILS]

    style HW fill:#fdd
    style SILS fill:#ddf

テストは MQTT ブローカーにだけ依存します。--target=hardware では別途 bridge.py + 実機が、--target=sils では SILS が、同じブローカーにぶら下がっている前提です。 実機 / SILS の差 (リセット時の挙動・所要時間・期待 reset_reason) は e2e/targets.pyTargetProfile に閉じ込め、シナリオ本体は共通化しています。

ファイル構成

ファイル

役割

e2e/telecommand.py

TelecommandClient — コマンド送信、テレメトリ・画像の subscribe と待機ヘルパ

e2e/targets.py

TargetProfile — sils / hardware ごとのタイムアウト・期待値

conftest.py

--target / --broker-host オプション、fixtures、マーカー skip

tests/test_reset.py

ソフトウェアリセット試験 (フラッグシップ)

tests/test_control.py

コマンド → テレメトリ反映 (parametrize でケース追加可)

tests/test_target_capture.py

撮影・画像ダウンリンク

tests/test_liveness.py

テレメトリ疎通・レート・コマンドカウンタ

RESET 試験の判定基準

テレメトリの uptime / command_count / reset_reason の変化から、再起動の成立を判定します。

指標

RESET 前

RESET 後

意味

uptime [ms]

大きい値

大幅に低下

起動からの経過時間がリセット = 再起動した

command_count

> 0

ほぼ 0

コマンドカウンタがリセット

reset_reason

直前要因

SOFTWARE/WATCHDOG

リセット要因 (補助判定)

  • 主基準は複合 (uptime 低下 + command_count リセット)。SILS は接続維持のまま値が即時リセットされ、実機は瞬断後に bridge.py が再接続して低 uptime で復帰します。どちらも同じ条件式で検出できます。

  • reset_reason は補助判定です。実機の値は vreg_and_chip_reset_hw->chip_reset (main.cpp) の生値で、ブリングアップ時に実測して targets.pyHARDWARE.expected_reset_reason を校正します。未校正でも再起動自体の検証は独立した別テストで担保されます。

実行方法

SILS (CI 相当・ローカル)

uv sync --all-packages --dev                   # 一度だけ全パッケージ同期
cd ground && docker compose up -d mosquitto    # MQTT ブローカー
uv run --no-sync python -m sils                 # 別シェルで SILS 起動
uv run --no-sync pytest ground/e2e/tests --target=sils -v

--no-syncuv run の自動同期がワークスペースの editable (sils / simulator) を prune して SILS が起動できなくなるのを防ぐため (uv sync --all-packages --dev 実施済みが前提)。

実機 (HILS)

# mosquitto + 実機 Pico W 稼働 + bridge.py 起動 の状態で
uv run pytest ground/e2e/tests --target=hardware -v

テストケースの追加と pytest 引数

素の pytest に乗っているため、標準の引数・機能がそのまま使えます。

  • 単純なケース (コマンド → テレメトリ反映) は tests/test_control.pyCASES 表に 1 行追加するだけで増やせます。

  • 複雑なシナリオは専用 test_*.py に「1 関数 = 1 シナリオ」で追加します。

  • マーカーで対象を絞れます: hardware (実機限定 / sils では自動 skip)、slow (RESET 等)、image (撮影系)。

  • 主な引数: -k <式> (名前選択)、-m <マーカー> (種別選択)、-x / --maxfail (失敗で停止)、-v / -s (出力)、--target / --broker-host (独自)。

# 例: 制御系のみ、低速試験を除外して実行
uv run pytest ground/e2e/tests -m "not slow" -k control --target=sils -v

既知の課題 / TODO

姿勢制御が目標近傍で整定せず自動撮影が発火しない

test_target_triggers_auto_capture (TARGET → ミッション成功時の自動撮影) は現在 xfail です。

  • 症状: 目標角に対し太陽方向は許容誤差 (±10°) 内まで寄るものの、制御がバンバン制御 (固定量 pulse_delta_us のキック) のため整定せず、角速度が ±18°/s 規模で振動する限界サイクルに入る。STABLE 状態かつ |error| < 許容 かつ 角速度 < 0.5°/s が同時に成立して is_target_in_angle が立つ瞬間が、1Hz のテレメトリ/撮影判定境界と安定して一致しない。結果、is_control_finished() を契機とする自動撮影が発火しない。

  • 重要: これは SILS で再現する不具合であり、同一の制御ロジックを使う実機でも発火しない。したがって hardware 限定にして隠すのではなく、xfail として可視化する。

  • 影響箇所 (移植関係・同期修正が必要):

    • satelite/firmware/control.cppSTABLE 状態 (実機 FSW)

    • satelite/sils/sils/controller.pySTABLE 状態 (SILS 移植)

  • 修正方針 (いずれか / 組み合わせ):

    1. error に比例してパルス変化量を縮小する比例制御化 (目標近傍でキックを弱める)

    2. デッドバンド導入 — 小誤差では LOTATE に遷移させない

    3. 整定保持 — |error| < 許容 を一定時間継続したら is_target_in_angle を確定する

  • 完了条件: 上記修正後、is_control_finished() が安定して立つことを確認し、test_target_triggers_auto_capture@pytest.mark.xfail を外す。