Week 2 — 系統架構

資料夾:week02_architecture/

基礎知識導論

Week 2 基礎知識導論 — 系統架構與 CONOPS

Week 1 認識了物件。這一週要學的是:這些物件怎麼被「決定」出來的。
架構不是畫方塊圖,是把一句任務目標翻譯成一組可驗證的工程需求。

1. 這一週在解什麼問題

FIREYE 的任務敘述是「監測火山活動」。這句話沒有任何一個數字。
系統架構的工作,就是把它變成:

對台灣與環太平洋 12 座指定火山,在白天可見光條件下,
以優於 30 m 的地面解析度成像,單一目標重訪時間 < 24 hr,
影像於取得後 12 hr 內下傳至地面站,任務壽命 2 年。

有了這組數字,才有辦法選軌道(Week 4)、算功率(Week 7)、開鏈路(Week 11)。
沒有數字的需求,無法被違反,也就無法被設計。

2. 核心觀念

2.1 需求的三個層級

任務目標 (Mission Objective)     監測火山活動
   ↓ 誰來做、做到什麼程度
任務需求 (Mission Requirements)  30 m GSD / 24 hr 重訪 / 12 hr 延遲
   ↓ 分配到子系統
系統需求 (System Requirements)   軌道高度 500–700 km / 下傳 ≥ 1.5 GB/day / 功率 ≥ 12 W

往下拆叫 flow-down,往上追叫 traceability。每一條系統需求都要能回答
「你是從哪一條任務需求來的」,否則就是工程師自己加的。

2.2 子系統分工(本課 16 週的骨架)

子系統 縮寫 主要職責 對應週次
結構與機構 STR 承受發射載荷、提供介面 Week 5
熱控 TCS 讓每個元件待在溫度範圍內 Week 6
電力 EPS 發電、儲能、配電 Week 7
姿態確定與控制 ADCS 指向與消旋 Week 8
推進 PROP 軌道機動與維持、去軌 Week 10
通訊 TT&C / COMM 遙測、遙控、資料下傳 Week 11
指令與資料處理 C&DH / OBC 排程、儲存、壓縮 Week 12, 13
酬載 P/L 任務本體 Week 12
地面段 GS 地面站、排程、運作 Week 14

耦合才是重點:酬載開機 → 功率上升 → 電池 DOD 上升 → 電池發熱 → 熱控負擔上升 →
可能要換散熱面 → 影響太陽能板面積 → 回頭影響發電。系統工程的價值就在管理這些迴圈。

2.3 CONOPS:把時間軸畫出來

CONOPS 是「衛星一天到晚在做什麼」的敘事。至少要包含:

  1. 模式 (Modes):Safe / Nominal / Imaging / Downlink / Detumble / Charging。
  2. 模式轉換條件:什麼時候切換?誰決定?(地面排程 or 自主?)
  3. 典型一圈的時間軸:日照 → 充電;過火山 → 成像;過地面站 → 下傳;地影 → 低功耗。
  4. 異常處理:失聯多久進 Safe Mode?

CONOPS 決定了功率的 duty cycle,而 duty cycle 直接決定電池大小。
Week 3 的功率預算表如果沒有 CONOPS,就只是在亂填數字。

2.4 架構權衡 (Trade Study) 的正確做法

一個可信的 trade study 需要四件事:

  1. 選項清單 — 至少 3 個真正不同的方案(不是同一方案的三個參數)。
  2. 評估準則 — 事先定義,不能看到結果才決定重視什麼。
  3. 量化模型 — 每個準則要能算出數字(這就是 STK/Python 的用途)。
  4. 敏感度 — 換一組權重,贏家會不會換人?會換就代表結論脆弱。

本課的 trade study 都以 STK 算出的物理量當準則來源,而不是靠直覺打分。

2.5 「Coverage 換 Volume」:本週的核心權衡

同一個成像需求,有兩條完全不同的路:

路線 做法 代價
加覆蓋 更高軌道、更寬視野、更多衛星 解析度變差 / 成本上升(Week 16)
加體積 更大光學、更大儲存、更大天線 質量與功率上升,可能超出 6U(Week 3, 5, 7)

這個權衡沒有唯一解,取決於你把哪一條需求視為不可退讓。
架構的本質就是決定「哪一條需求可以退」。

2.6 介面與 ICD

子系統之間出錯,九成出在介面。介面控制文件 (ICD) 至少要寫:
機械(螺孔、質心、公差)、電氣(電壓、電流、接頭腳位)、資料(協定、速率、封包格式)、
熱(傳導路徑、容許溫度)。

在 STK 裡的對應物就是物件的父子關係與屬性:感測器掛在衛星上、
天線掛在感測器上、地面站的仰角遮罩約束了鏈路 —— 每一層都是一個介面。

3. 名詞與符號

名詞 意義
CONOPS 運作概念,任務的時間軸敘事
Flow-down 需求由上而下的分解
Traceability 每條需求都追得到來源
Duty cycle 某個負載開機時間佔比
Trade study 有準則、可量化的方案比較
ICD 介面控制文件
Mode 衛星的運作模式
GSD Ground Sample Distance,地面解析度
Revisit time 重訪時間,同一目標兩次觀測的間隔
Margin 餘裕,設計值與需求值的差

4. 觀念 → 本週實驗對照

觀念 出現在本週的哪裡
物件即介面 week02_architecture.txt 建立 Transmitter/Receiver 的掛載路徑
地面站仰角遮罩 腳本裡的 SetConstraint ... ElevationAngle Min 5
Access 當量化準則 用 access 統計比較架構選項
分析窗必須夠長 本週原本 2.5 hr 的窗導致成像 access 為 0,已改為 24 hr
ACK ≠ 生效 week02_verify.py,其中抓出「設定天線型別會沖掉先前寫的設計頻率」

5. 常見誤解

  1. 「架構圖畫完就算架構做完了。」 — 方塊圖不含任何可被違反的內容。
    架構的產出是需求表 + trade study + CONOPS,圖只是索引。
  2. 「先做設計,需求之後補。」 — 補出來的需求一定剛好符合你已經做的設計,
    等於沒有驗證。
  3. 「餘裕留 10% 就好。」 — 早期階段(Phase A/B)業界慣例是質量餘裕 20–30%,
    功率餘裕更高。餘裕不是保守,是對未知的定量承認。
  4. 「access 是 0 表示不可行。」 — 先檢查分析窗長度、仰角約束、感測器錐角有沒有設對。
    本教材親自踩過這個坑。

6. 延伸閱讀

系統工程

任務案例

工具

7. 課前自我檢核

  1. 把「監測火山活動」寫成三條可被違反的量化需求。
  2. 你的 CONOPS 裡,成像模式與下傳模式可以同時發生嗎?這個答案會影響哪一份預算表?
  3. 一份 trade study 的結論在權重微調後就翻盤了,這代表什麼?該怎麼處理?
  4. 為什麼「介面」比「元件」更容易出錯?在 STK 的物件樹裡,介面對應到什麼?
  5. 分析時間窗設 2.5 小時,對一顆 LEO 衛星的成像 access 統計會造成什麼系統性偏差?

程式碼

week02_architecture/week02_architecture.txt
# ============================================================
# Week 2 系統架構 — 通訊、地面站、火點與 access
#
# 本週目標:把 Week 1 的 FIREYE 擴成一個「系統」——加上下行通訊、
# 地面站網路與觀測目標,再用 chain access 把「看得到」量化成
# 「能下傳多少資料」(coverage 換 volume)。
#
# 前置:必須先執行 Week 1 的 week01_eSatellite_scenario.txt,
#       本檔沒有自己的 New / Scenario,單獨執行會整支失敗。
#
# 由原本的 eSatellite_STK_Connect.txt 拆分而來(後半)。
# 驗證段:week02_verify.py
# ============================================================
# ============================================================
# SBand communications: FOV sensor + transmitter
# Tutorial: antenna boresight [0 0 1], FOV 120 deg, DataRate 10 Mbit/s
# (120 deg full cone -> 60 deg SimpleCone half-angle)
# ============================================================
New / */Satellite/FIREYE/Sensor SBandFOV
Define */Satellite/FIREYE/Sensor/SBandFOV SimpleCone 60
Graphics */Satellite/FIREYE/Sensor/SBandFOV SetColor cyan

New / */Satellite/FIREYE/Sensor/SBandFOV/Transmitter SBand
Transmitter */Satellite/FIREYE/Sensor/SBandFOV/Transmitter/SBand SetValue Model Complex_Transmitter_Model
# S-band downlink [ASSUME 2.25 GHz]
Transmitter */Satellite/FIREYE/Sensor/SBandFOV/Transmitter/SBand SetValue Model.Frequency 2250000000.0
# 10 Mbit/s (tutorial)
Transmitter */Satellite/FIREYE/Sensor/SBandFOV/Transmitter/SBand SetValue Model.DataRate 10000000.0
# W [ASSUME]
Transmitter */Satellite/FIREYE/Sensor/SBandFOV/Transmitter/SBand SetValue Model.Power 4.0
Transmitter */Satellite/FIREYE/Sensor/SBandFOV/Transmitter/SBand SetValue Model.AntennaControl.Antenna Isotropic
# 天線型別要先設:改 Antenna 會換掉天線物件,先前寫的 DesignFrequency
# 會被沖回 STK 預設的 14.5 GHz(ACK 照給,值卻沒了)
Transmitter */Satellite/FIREYE/Sensor/SBandFOV/Transmitter/SBand SetValue Model.AntennaControl.Antenna.DesignFrequency 2250000000.0
Transmitter */Satellite/FIREYE/Sensor/SBandFOV/Transmitter/SBand SetValue Model.Modulator BPSK

# ============================================================
# Ground-station network  (Esrange is REAL/named; rest REPRESENTATIVE)
# lat lon alt(km)
# ============================================================
New / */Facility Esrange_Station_KSX
SetPosition */Facility/Esrange_Station_KSX Geodetic 67.8939 21.1061 0.341
SetConstraint */Facility/Esrange_Station_KSX ElevationAngle Min 5.0

New / */Facility Svalbard_SvalSat
SetPosition */Facility/Svalbard_SvalSat Geodetic 78.2290 15.4070 0.450
SetConstraint */Facility/Svalbard_SvalSat ElevationAngle Min 5.0

New / */Facility Inuvik
SetPosition */Facility/Inuvik Geodetic 68.3200 -133.5490 0.100
SetConstraint */Facility/Inuvik ElevationAngle Min 5.0

New / */Facility Fairbanks_ASF
SetPosition */Facility/Fairbanks_ASF Geodetic 64.8600 -147.8500 0.300
SetConstraint */Facility/Fairbanks_ASF ElevationAngle Min 5.0

New / */Facility Troll_Antarctica
SetPosition */Facility/Troll_Antarctica Geodetic -72.0120 2.5350 1.270
SetConstraint */Facility/Troll_Antarctica ElevationAngle Min 5.0

New / */Facility Punta_Arenas
SetPosition */Facility/Punta_Arenas Geodetic -52.9400 -70.8500 0.030
SetConstraint */Facility/Punta_Arenas ElevationAngle Min 5.0

New / */Facility Awarua_NZ
SetPosition */Facility/Awarua_NZ Geodetic -46.5290 168.3800 0.010
SetConstraint */Facility/Awarua_NZ ElevationAngle Min 5.0

# Group stations (the real scenario ships 22 - add the rest here)
New / */Constellation GroundStations
Chains */Constellation/GroundStations Add */Facility/Esrange_Station_KSX
Chains */Constellation/GroundStations Add */Facility/Svalbard_SvalSat
Chains */Constellation/GroundStations Add */Facility/Inuvik
Chains */Constellation/GroundStations Add */Facility/Fairbanks_ASF
Chains */Constellation/GroundStations Add */Facility/Troll_Antarctica
Chains */Constellation/GroundStations Add */Facility/Punta_Arenas
Chains */Constellation/GroundStations Add */Facility/Awarua_NZ

# ============================================================
# Wildfire targets  (REPRESENTATIVE 2020 fire regions)
# ============================================================
New / */Target Wildfire0
SetPosition */Target/Wildfire0 Geodetic 37.05 -119.75 0.5        # California Sierra
New / */Target Wildfire1
SetPosition */Target/Wildfire1 Geodetic 44.30 -121.20 1.0        # Oregon Cascades
New / */Target Wildfire2
SetPosition */Target/Wildfire2 Geodetic 65.50 -150.00 0.2        # Interior Alaska
New / */Target Wildfire3
SetPosition */Target/Wildfire3 Geodetic 62.00 129.00 0.2         # Sakha / Siberia Arctic

New / */Constellation Wildfires
Chains */Constellation/Wildfires Add */Target/Wildfire0
Chains */Constellation/Wildfires Add */Target/Wildfire1
Chains */Constellation/Wildfires Add */Target/Wildfire2
Chains */Constellation/Wildfires Add */Target/Wildfire3

# ============================================================
# Access analysis
# ============================================================
# Imaging: payload sees wildfires
New / */Chain Imaging
Chains */Chain/Imaging Add */Satellite/FIREYE/Sensor/Imager
Chains */Chain/Imaging Add */Constellation/Wildfires
Chains */Chain/Imaging Compute
Report_RM */Chain/Imaging Style "Complete Chain Access"

# Downlink: SBand FOV sees ground stations
# (data volume estimate = sum(access duration seconds) * 10e6 bits)
New / */Chain Downlink
Chains */Chain/Downlink Add */Satellite/FIREYE/Sensor/SBandFOV
Chains */Chain/Downlink Add */Constellation/GroundStations
Chains */Chain/Downlink Compute
Report_RM */Chain/Downlink Style "Complete Chain Access"

# Link budget (needs a ground receiver; add one per station to use):
# ReportCreate <GndRx> Type Display Style "Link Budget - Detailed" AccessObject */Satellite/FIREYE/Sensor/SBandFOV/Transmitter/SBand
week02_architecture/week02_verify.py
# -*- coding: utf-8 -*-
# ================================================================
# Week 2 驗證段 — 系統架構的物件、鏈路與 access
#
# 本週把單一衛星擴成一個系統。要驗的不只是「物件建出來了」,
# 更重要的是 chain access 真的算得出通聯 —— 沒有 access,coverage 換 volume 就是空談。
#
# 執行:python week02_verify.py
#   離開碼 0 = 全部通過,1 = 有項目對不上
# ================================================================

import os
import sys

# notebook 的 kernel 沒有 __file__。而且啟動 STK Engine 會改變 process 的
# 工作目錄,所以不能事後才靠 os.getcwd() —— notebook 開頭的 bootstrap cell
# 會先把週次目錄釘在 WEEK_DIR。
HERE = (os.path.dirname(os.path.abspath(__file__)) if "__file__" in globals()
        else globals().get("WEEK_DIR") or os.path.abspath(os.getcwd()))
REPO = os.path.abspath(os.path.join(HERE, os.pardir))
sys.path.insert(0, os.path.join(REPO, "tools"))

from stk_verify import Verifier, load_lab
from agi.stk12.stkdesktop import STKDesktop
from stk_verify import run_script      # noqa: E402


def main():
    app = STKDesktop.AttachToApplication()
    root = app.Root
    if root.CurrentScenario is not None:
        root.CloseScenario()
    # 本週沒有自己的 New / Scenario,必須接在 Week 1 的場景上
    run_script(root, os.path.join(REPO, "week01_intro",
                                  "week01_eSatellite_scenario.txt"), echo_fail=False)
    ack, nack = run_script(root, os.path.join(HERE, "week02_architecture.txt"))

    v = Verifier(root)
    v.auto_setvalues(os.path.join(HERE, "week02_architecture.txt"))
    v.object_exists("*/Satellite/FIREYE/Sensor/SBandFOV")
    v.cone_angle("*/Satellite/FIREYE/Sensor/SBandFOV", 60.0)
    v.object_exists("*/Satellite/FIREYE/Sensor/SBandFOV/Transmitter/SBand")

    sc = root.CurrentScenario
    kinds = {}
    for i in range(sc.Children.Count):
        k = sc.Children.Item(i).ClassName
        kinds[k] = kinds.get(k, 0) + 1
    v.expect("地面站數", kinds.get("Facility", 0), 7, tol=0, unit=" 座")
    v.expect("火點目標數", kinds.get("Target", 0), 4, tol=0, unit=" 個")
    v.expect("Constellation 數", kinds.get("Constellation", 0), 2, tol=0, unit=" 個")
    v.expect("Chain 數", kinds.get("Chain", 0), 2, tol=0, unit=" 條")

    # chain access:真的算得出通聯嗎?這才是「coverage 換 volume」的前提
    for chain, label in (("Imaging", "成像"), ("Downlink", "下傳")):
        try:
            ch = root.GetObjectFromPath(f"*/Chain/{chain}")
            dp = ch.DataProviders.GetDataPrvIntervalFromPath("Complete Access")
            res = dp.Exec(sc.StartTime, sc.StopTime)
            durs = res.DataSets.GetDataSetByName("Duration").GetValues()
            v.expect_true(f"{label} chain 有 access", len(durs) > 0,
                          f"{len(durs)} 次,共 {sum(durs)/60:.1f} min")
        except Exception as e:
            v.expect_true(f"{label} chain 有 access", False, str(e)[:60])

    v.expect_true("腳本沒有 NACK", nack == 0, f"{ack} ACK / {nack} NACK")

    ok = v.report("Week 2 驗證段")
    return 0 if ok else 1


if __name__ == "__main__":
    _rc = main()
    if "ipykernel" not in sys.modules:
        raise SystemExit(_rc)