Week 11 — 通訊系統
week11_comm/基礎知識導論
Week 11 基礎知識導論 — 通訊鏈路預算
鏈路預算是一串乘法(在 dB 域是一串加減)。
任何一個參數靜默沒生效,報告照樣會印出一個看起來很合理的餘裕值 —— 而且是錯的。
這一週是全課「ACK 不等於生效」的示範週。
1. 這一週在解什麼問題
FIREYE 在 550 km、地面站在賓州(AGI HQ)。
UHF 下行、VHF 上行,要求餘裕 >= 3 dB。
問題是:發射功率要多大?天線增益要多少?可以跑多快的位元率?
下雨會不會斷?都卜勒要補多少?
2. 核心觀念
2.1 鏈路方程
Eb/N0 [dB] = EIRP - L_FS - L_atm - L_pol - L_point + G/T - k - 10*log10(Rb)
逐項拆解:
| 項 | 意義 | 典型量級(LEO UHF) |
|---|---|---|
EIRP |
發射功率 + 天線增益 − 饋線損耗 (dBW) | 衛星端 0–5 dBW |
L_FS |
自由空間損耗 | 約 150 dB |
L_atm |
大氣吸收 | UHF < 0.5 dB;Ka 頻段大很多 |
L_pol |
極化失配 | 圓對線性固定 3 dB |
L_point |
指向損耗 | 0.5–3 dB |
G/T |
接收品質因數 (dB/K) | 地面站 −10 至 +10 |
k |
Boltzmann 常數 | −228.6 dBW/(K·Hz) |
Rb |
位元率 | 9.6 kbps 至 数 Mbps |
2.2 自由空間損耗
L_FS = 20*log10( 4*pi*d / λ ) dB λ = c / f
= 92.45 + 20*log10(d_km) + 20*log10(f_GHz) dB
兩個推論:
- 距離加倍,損耗增加 6 dB。 過頂(550 km)與低仰角(約 2000 km 斜距)差約 11 dB ——
所以低仰角時鏈路最緊,鏈路預算要用最壞斜距算,不是用最短距離。 - 頻率加倍,損耗也增加 6 dB。 但高頻可以用更小的天線得到更高增益
(G ∝ (D/λ)²),兩者部分抵消。真正的取捨在天線尺寸、指向精度與大氣衰減。
2.3 天線增益與波束寬
拋物面天線:
G = η_a * ( pi * D / λ )² η_a 約 0.5–0.7
θ_3dB ≈ 70 * λ / D [度]
增益與波束寬是同一件事的兩面:增益高 = 波束窄 = 指向要求嚴。
一個 30 dBi 的天線波束寬只有幾度,若 ADCS 指不準,增益等於白給。
這是通訊與姿態(Week 8)最直接的耦合。
CubeSat 常用低增益的偶極/單極/turnstile 天線,就是為了容忍姿態誤差。
2.4 G/T:接收端的品質因數
G/T = G_rx - 10*log10( T_sys ) [dB/K]
T_sys = T_ant + T_LNA + T_line + ...
T_sys 是系統雜訊溫度。地面站的低雜訊放大器 (LNA) 直接決定它。
G/T 把「增益」與「雜訊」合成一個數 —— 這是比較接收系統的標準指標。
本課實測踩過的坑:STK 的
Model.GOverT欄位吃的是線性比值,不是 dB。
寫Model.GOverT -10想表示 −10 dB,會被拒絕。正確寫法是0.1。
dB 與線性混用是鏈路預算最常見的錯誤來源。
2.5 Eb/N0 門檻與編碼增益
| 調變 / 編碼 | BER 1e-5 所需 Eb/N0 |
|---|---|
| 未編碼 BPSK | 約 9.6 dB |
| 卷積碼 (k=7, r=1/2) | 約 4.5 dB |
| 卷積 + Reed-Solomon 串接 | 約 2.5 dB |
| Turbo / LDPC | 接近 Shannon 極限(約 1 dB) |
編碼增益是最便宜的鏈路餘裕:加 5 dB 編碼增益,等於發射功率變 3 倍,
或天線口徑變 1.8 倍 —— 但編碼只花數位訊號處理的功耗。
代價是頻寬變寬(r=1/2 表示符元率變兩倍)與延遲。
2.6 餘裕:為什麼是 3 dB
Margin = Eb/N0(實際) - Eb/N0(門檻)
3 dB 餘裕代表「功率再掉一半也還能通」。它涵蓋的是:
- 元件老化與溫度漂移
- 指向誤差的變異
- 大氣與電離層的短期擾動
- 模型本身的不確定度
餘裕不是安全係數,是對模型不確定度的定量承認(跟 Week 3 的餘裕哲學一致)。
2.7 都卜勒
LEO 過頂時的徑向速度可達 ±7.5 km/s:
Δf / f = v_r / c → UHF 437 MHz 時約 ±10 kHz
S 頻段 2.2 GHz 時約 ±55 kHz
而且變化率最大的時刻正是過頂前後(仰角最高、訊號最強的時候)。
所以接收機要能追蹤,或地面站要預先補償(用軌道預報算出頻率曲線)。
2.8 頻譜與法規
CubeSat 的 UHF/VHF 常用業餘無線電頻段。這需要:
- IARU 頻率協調(業餘衛星服務)
- 各國主管機關的執照
- ITU 的登記
業餘頻段有嚴格的條件:非商業用途、必須公開遙測格式、發射功率受限。
這是一個法規驅動的設計限制,不是技術限制 —— 跟 Week 1 講的 CDS 一樣。
3. 名詞與符號
| 符號 | 意義 | 單位 | 值 / 典型 |
|---|---|---|---|
EIRP |
等效全向輻射功率 | dBW | — |
L_FS |
自由空間損耗 | dB | 約 150(UHF, LEO) |
G/T |
接收品質因數 | dB/K | STK 欄位吃線性值 |
k |
Boltzmann 常數 | dBW/(K·Hz) | −228.6 |
Eb/N0 |
位元能量對雜訊密度比 | dB | 門檻 9.6(未編碼 BPSK) |
Rb |
位元率 | bit/s | — |
T_sys |
系統雜訊溫度 | K | — |
θ_3dB |
半功率波束寬 | ° | 70λ/D |
| Margin | 餘裕 | dB | 需求 >= 3 |
4. 觀念 → 本週實驗對照
| 觀念 | 出現在本週的哪裡 |
|---|---|
| 雙向鏈路建構 | STKSat_Comm_Connect.txt:UHF 下行 + VHF 上行 |
| Link Budget 報告 | 樣式 "Link Budget - Detailed" |
| 餘裕判定 | week11_verify.py 取最差 Eb/No 減門檻 9.6 dB |
| RF 環境 | 雨衰與大氣模型設定 |
| 回讀驗證 | Transmitter_RM / Receiver_RM ... GetValue |
本週是驗證段的試點週。實測 37 項全 PASS
(最差 Eb/No 47.12 dB,餘裕 37.52 dB)。
導入這一段時抓出三個 0 個 NACK 的靜默失敗:
1.Model.DesignFrequency屬性不存在,要寫Model.AntennaControl.Antenna.DesignFrequency
2. 設定Model.AntennaControl.Antenna Isotropic會沖掉先前寫入的設計頻率,
回到 14.5 GHz 預設值 —— 純粹是指令順序問題
3. STKSat 沿用 Lab1 的掃描起點(250 km / i = 0),鏈路完全沒有 access三個都是 ACK 正常、報告照印、數字全錯。這就是本課要教的東西。
5. 常見誤解
- 「dB 可以相乘。」 — dB 是對數,加減對應線性的乘除。
G/T = -10的坑就是這樣來的。 - 「用最短斜距算鏈路。」 — 要用最壞情況(最低仰角)。差 11 dB 以上。
- 「餘裕 37 dB 太浪費,把功率調小。」 — 先確認那是不是最差時刻的值。
本週的 47.12 dB 已經是最差 Eb/No 了,但要注意分析窗與 access 是否涵蓋低仰角。 - 「增益愈高愈好。」 — 增益高 = 波束窄 = 對 ADCS 的要求上升。整體可能更差。
- 「報告有印出數字就是算對了。」 — 三個靜默失敗都會印出漂亮的數字。
要回讀每個輸入參數。
6. 延伸閱讀
鏈路預算基礎
- Wikipedia: Link budget — 方程與各項定義。
- JPL DESCANSO 系列專書
— 深空通訊的權威系列,免費全文。前幾冊的鏈路分析方法完全適用於 LEO。 - JPL DSN 810-005 模組(深空網路介面規格)
— 真實的鏈路參數與大氣模型數據來源。 - 教科書:Sklar, Digital Communications(Eb/N0 與編碼);
Maral & Bousquet, Satellite Communications Systems。
法規與頻譜
- IARU 業餘衛星頻率協調 — CubeSat 用業餘頻段的第一站。
- ITU-R 太空服務
— 頻率分配與 ITU-R P.618(降雨衰減模型)的來源。 - AMSAT — 業餘衛星社群,有大量實際的 CubeSat 鏈路參數。
地面站與實作
- SatNOGS 開源地面站網路
— 全球分散式地面站,可以看到真實 CubeSat 的訊號與遙測。 - GNU Radio — 軟體無線電,適合做解調與同步的實驗。
- Gpredict — 開源衛星追蹤與都卜勒補償。
- CCSDS 標準 — 太空資料鏈路協定的國際標準。
工具
- Ansys STK 線上說明 — Comm 模組與 RF 環境模型。
本機:C:\Program Files\AGI\STK 12\Help\Content\comm\。
7. 課前自我檢核
- 550 km 過頂與 5° 仰角時的斜距各是多少?自由空間損耗差幾 dB?
G/T要填 −10 dB,在 STK 的欄位裡應該寫什麼數字?為什麼?- 加一組 r=1/2 卷積碼,餘裕增加多少?付出的代價是什麼?
- 天線增益從 3 dBi 換成 20 dBi,對 ADCS 的指向需求有什麼影響?
- 鏈路報告印出了漂亮的餘裕值。列出三種方法證明輸入參數真的生效了。
程式碼
# ============================================================
# STKSat Communications Link - STK Connect Command Script
# Applies the UT<->Satellite comm-scenario conventions from
# the provided markdown reference to SmallSat Part 3
# (Communications Design):
# Downlink 435 MHz UHF (STKSat Tx -> Ground Rx)
# Uplink 145 MHz VHF (Ground Tx -> STKSat Rx)
# Required link margin >= 3 dB
#
# PREREQUISITE - enable Connect server before running:
# Edit > Preferences > Connect -> tick "Allow Connect",
# TCP/IP port 5001, tick Verbose, Apply/OK.
# Batch-load with: # ConFile / "F:\stk\STKSat_Comm_Connect.txt"
#
# Run AFTER OrbitDesignLab_Connect.txt (reuses */Satellite/STKSat),
# or uncomment the STANDALONE block to create the satellite here.
# ============================================================
# ---- STANDALONE (uncomment if STKSat does not yet exist) ----
#New / Scenario STKSat_Comms
#SetAnalysisTimePeriod * "1 Jan 2024 00:00:00.000" "2 Jan 2024 00:00:00.000"
#SetTimePeriod * "1 Jan 2024 00:00:00.000" "2 Jan 2024 00:00:00.000"
#Animate * Reset
#New / */Satellite STKSat
#OrbitWizard */Satellite/STKSat OrbitDesigner SemimajorAxis 6928 Eccentricity 0 Inclination 56 ArgumentOfPerigee 0 RAAN 0 TrueAnomaly 0
# ---- Session distance unit ----
SetUnits / km
# ============================================================
# Ground station: AGI HQ, Exton PA (per SmallSat mission brief)
# ============================================================
# ---- 採用 Week 4 掃描「選定」的任務軌道 ----
# OrbitDesignLab_Connect.txt 把 STKSat 設成 a=6628 km, i=0 —— 那是 Lab1
# (a, i) 掃描的**起點**,不是結論。赤道 250 km 軌道對北緯 40 度的測站
# 完全沒有 access,鏈路預算會印出 "Data Unavailable",而所有指令照樣 ACK。
# 通訊週要用的是掃描選出來的任務軌道:550 km / i=56 度。
SetState */Satellite/STKSat Classical J2Perturbation "UseAnalysisStartTime" "UseAnalysisStopTime" 60 ICRF "UseAnalysisStartTime" 6928.0 0.0 56.0 0.0 0.0 0.0 TrueAnomaly
New / */Facility AGI_HQ
SetPosition */Facility/AGI_HQ Geodetic 40.0416 -75.5983 0.1
SetConstraint */Facility/AGI_HQ ElevationAngle Min 5.0
# ============================================================
# DOWNLINK 435 MHz UHF : STKSat transmitter -> AGI_HQ receiver
# ============================================================
# --- Satellite transmitter (nadir sensor host) ---
New / */Satellite/STKSat/Sensor CommSensor
Point */Satellite/STKSat/Sensor/CommSensor Targeted Tracking Facility/AGI_HQ Hold
New / */Satellite/STKSat/Sensor/CommSensor/Transmitter Sat_UHF_Tx
Transmitter */Satellite/STKSat/Sensor/CommSensor/Transmitter/Sat_UHF_Tx SetValue Model Complex_Transmitter_Model
# Frequency in Hz (435 MHz), power per mission cap (6 W tx budget)
Transmitter */Satellite/STKSat/Sensor/CommSensor/Transmitter/Sat_UHF_Tx SetValue Model.Frequency 435000000.0
Transmitter */Satellite/STKSat/Sensor/CommSensor/Transmitter/Sat_UHF_Tx SetValue Model.Power 2.0
# CubeSat health/imagery downlink data rate (b/sec)
Transmitter */Satellite/STKSat/Sensor/CommSensor/Transmitter/Sat_UHF_Tx SetValue Model.DataRate 9600.0
# Simple dipole-like embedded antenna
Transmitter */Satellite/STKSat/Sensor/CommSensor/Transmitter/Sat_UHF_Tx SetValue Model.AntennaControl.Antenna Isotropic
# 天線型別要先設:改 Antenna 會換掉天線物件,之前寫的 DesignFrequency
# 會被沖回 STK 預設的 14.5 GHz(ACK 照給,但值沒了)
Transmitter */Satellite/STKSat/Sensor/CommSensor/Transmitter/Sat_UHF_Tx SetValue Model.AntennaControl.Antenna.DesignFrequency 435000000.0
Transmitter */Satellite/STKSat/Sensor/CommSensor/Transmitter/Sat_UHF_Tx SetValue Model.Modulator BPSK
Transmitter */Satellite/STKSat/Sensor/CommSensor/Transmitter/Sat_UHF_Tx SetValue Model.Modulator.AutoScaleBandwidth True
# --- Ground receiver at AGI_HQ ---
New / */Facility/AGI_HQ/Sensor GndSensor
Point */Facility/AGI_HQ/Sensor/GndSensor Targeted Tracking Satellite/STKSat Hold
New / */Facility/AGI_HQ/Sensor/GndSensor/Receiver Gnd_UHF_Rx
Receiver */Facility/AGI_HQ/Sensor/GndSensor/Receiver/Gnd_UHF_Rx SetValue Model Simple_Receiver_Model
# G/T in dB/K; auto-track downlink frequency
Receiver */Facility/AGI_HQ/Sensor/GndSensor/Receiver/Gnd_UHF_Rx SetValue Model.GOverT 6
Receiver */Facility/AGI_HQ/Sensor/GndSensor/Receiver/Gnd_UHF_Rx SetValue Model.FrequencyAutoTracking True
# ============================================================
# UPLINK 145 MHz VHF : AGI_HQ transmitter -> STKSat receiver
# ============================================================
New / */Facility/AGI_HQ/Sensor/GndSensor/Transmitter Gnd_VHF_Tx
Transmitter */Facility/AGI_HQ/Sensor/GndSensor/Transmitter/Gnd_VHF_Tx SetValue Model Complex_Transmitter_Model
Transmitter */Facility/AGI_HQ/Sensor/GndSensor/Transmitter/Gnd_VHF_Tx SetValue Model.Frequency 145000000.0
Transmitter */Facility/AGI_HQ/Sensor/GndSensor/Transmitter/Gnd_VHF_Tx SetValue Model.AntennaControl.Antenna.DesignFrequency 145000000.0
# Ground station out-powers the spacecraft by ~an order of magnitude
Transmitter */Facility/AGI_HQ/Sensor/GndSensor/Transmitter/Gnd_VHF_Tx SetValue Model.Power 25.0
Transmitter */Facility/AGI_HQ/Sensor/GndSensor/Transmitter/Gnd_VHF_Tx SetValue Model.DataRate 9600.0
Transmitter */Facility/AGI_HQ/Sensor/GndSensor/Transmitter/Gnd_VHF_Tx SetValue Model.Modulator BPSK
New / */Satellite/STKSat/Sensor/CommSensor/Receiver Sat_VHF_Rx
Receiver */Satellite/STKSat/Sensor/CommSensor/Receiver/Sat_VHF_Rx SetValue Model Simple_Receiver_Model
# Model.GOverT is a LINEAR ratio (units/K), not dB: -10 dB -> 0.1
Receiver */Satellite/STKSat/Sensor/CommSensor/Receiver/Sat_VHF_Rx SetValue Model.GOverT 0.1
Receiver */Satellite/STKSat/Sensor/CommSensor/Receiver/Sat_VHF_Rx SetValue Model.FrequencyAutoTracking True
# ============================================================
# RF Environment: rain + atmospheric absorption (ITU-R)
# ============================================================
RFEnvironment * SetValue PropagationChannel.UseRainModel true
RFEnvironment * SetValue PropagationChannel.RainModel ITU-R_P618-13
RFEnvironment * SetValue PropagationChannel.RainModel.SurfaceTemperature 20.6 degC
RFEnvironment * SetValue PropagationChannel.UseAtmosAbsorptionModel true
RFEnvironment * SetValue PropagationChannel.AtmosAbsorptionModel ITU-R_P676-13
RFEnvironment * SetValue PropagationChannel.AtmosAbsorptionModel.UseApproxMethod false
# ============================================================
# Link budget reports (verify margin >= 3 dB)
# ============================================================
# Downlink (UHF): satellite Tx -> ground Rx
ReportCreate */Facility/AGI_HQ/Sensor/GndSensor/Receiver/Gnd_UHF_Rx Type Display Style "Link Budget - Detailed" AccessObject */Satellite/STKSat/Sensor/CommSensor/Transmitter/Sat_UHF_Tx
# Uplink (VHF): ground Tx -> satellite Rx
ReportCreate */Satellite/STKSat/Sensor/CommSensor/Receiver/Sat_VHF_Rx Type Display Style "Link Budget - Detailed" AccessObject */Facility/AGI_HQ/Sensor/GndSensor/Transmitter/Gnd_VHF_Tx
# Access windows (ground station passes)
Access */Satellite/STKSat */Facility/AGI_HQ TimePeriod UseScenarioInterval
# Dump computed transmitter/receiver parameters
Transmitter_RM */Satellite/STKSat/Sensor/CommSensor/Transmitter/Sat_UHF_Tx GetValue
Receiver_RM */Facility/AGI_HQ/Sensor/GndSensor/Receiver/Gnd_UHF_Rx GetValue
# -*- coding: utf-8 -*-
# ================================================================
# Week 11 驗證段 — 證明鏈路預算的每個參數真的進到 STK 了
#
# 為什麼通訊週特別需要這一段:
# 鏈路預算是一串乘法。任何一個參數靜默沒生效(功率、頻率、天線增益、
# G/T、雜訊溫度),Link Budget 報告照樣會印出一個「看起來很合理」的
# 餘裕值 —— 而且是錯的。ACK 不會告訴你這件事。
#
# 本週腳本原本就有兩個這種錯誤,都是靠回讀才抓到的:
# Model.DesignFrequency -> 這個屬性不存在,要寫到
# Model.AntennaControl.Antenna.DesignFrequency
# Model.GOverT -10 -> G/T 是線性比值不是 dB,-10 會被拒
#
# 執行:
# 1. 開啟 STK 12 桌面版(不能用無介面的 STK Engine)
# 2. python week11_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()))
sys.path.insert(0, os.path.join(HERE, os.pardir, "tools"))
from agi.stk12.stkdesktop import STKDesktop
from stk_verify import Verifier, run_script # noqa: E402
REPO = os.path.abspath(os.path.join(HERE, os.pardir))
PREREQ = os.path.join(REPO, "week04_orbit", "OrbitDesignLab_Connect.txt")
SCRIPT = os.path.join(HERE, "STKSat_Comm_Connect.txt")
SAT = "*/Satellite/STKSat/Sensor/CommSensor"
GND = "*/Facility/AGI_HQ/Sensor/GndSensor"
def main():
app = STKDesktop.AttachToApplication() # 保留 app 參考,否則 COM 介面會失效
root = app.Root
if root.CurrentScenario is not None:
root.CloseScenario()
# STKSat_Comm_Connect.txt 沒有自己的 New / Scenario,要接在 Lab1 的場景上
print("--- 前置 ---")
run_script(root, PREREQ, echo_fail=False)
print("--- 本週腳本 ---")
ack, nack = run_script(root, SCRIPT)
v = Verifier(root)
# (1) 自動:把腳本設過的每一個 SetValue 讀回來比對
v.auto_setvalues(SCRIPT)
# (2) 手動:結構與幾何 —— 這些沒有 SetValue,但錯了鏈路一樣是假的
v.object_exists("*/Facility/AGI_HQ")
v.position("*/Facility/AGI_HQ", 40.0416, -75.5983)
v.elevation_constraint("*/Facility/AGI_HQ", 5.0)
v.object_exists(SAT)
v.object_exists(GND)
v.object_exists(SAT + "/Transmitter/Sat_UHF_Tx")
v.object_exists(SAT + "/Receiver/Sat_VHF_Rx")
v.object_exists(GND + "/Transmitter/Gnd_VHF_Tx")
v.object_exists(GND + "/Receiver/Gnd_UHF_Rx")
# (3) 自訂:鏈路真的算得出餘裕嗎?
# 光看檔案「有沒有產生」不夠 —— 空檔案也會產生。要確認裡面
# 真的有數值列,否則就是另一種靜默失敗。
out = os.path.join(HERE, "week11_linkbudget.txt")
if os.path.exists(out):
os.remove(out)
try:
root.ExecuteCommand(
"ReportCreate " + GND + "/Receiver/Gnd_UHF_Rx Type Save "
'Style "Link Budget - Detailed" '
'File "' + out + '" '
"AccessObject " + SAT + "/Transmitter/Sat_UHF_Tx")
except Exception as e:
v.custom("下行 Link Budget 報告", False, str(e)[:60])
else:
size = os.path.getsize(out) if os.path.exists(out) else 0
ebno = []
if size:
with open(out, encoding="utf-8", errors="ignore") as f:
for line in f:
p = line.split()
# 日期佔 4 個 token,整列共 28 欄;倒數第二欄是 Eb/No (dB)
if len(p) == 28 and p[0][:1].isdigit():
try:
ebno.append(float(p[-2]))
except ValueError:
pass
v.custom("下行 Link Budget 有資料列", bool(ebno), f"{size} bytes,{len(ebno)} 列")
# 手冊要求「餘裕 >= 3 dB」。BPSK 在 BER 1e-5 的門檻 Eb/No 約 9.6 dB,
# 餘裕 = 實際 Eb/No - 門檻。這是本週真正的 pass/fail 判準,
# 而它在原本的腳本裡從來沒有被檢查過(只是把報告丟到畫面上)。
if ebno:
req, need = 9.6, 3.0
worst = min(ebno) - req
v.custom(f"最差鏈路餘裕 >= {need:.0f} dB", worst >= need,
f"最差 Eb/No {min(ebno):.2f} dB - 門檻 {req} dB = 餘裕 {worst:.2f} dB")
ok = v.report("Week 11 驗證段")
print(f"\n對照:腳本 {ack} 條 ACK / {nack} 條 NACK —— "
f"但 ACK 數字本身不能證明任何一個參數生效。")
return 0 if ok else 1
if __name__ == "__main__":
_rc = main()
# 在 notebook 裡不要用 SystemExit 打斷 kernel;在終端機才回傳離開碼
if "ipykernel" not in sys.modules:
raise SystemExit(_rc)