這題跟前面幾篇無人機蜂群系列不一樣,是一個純軌道力學計算題,用 nc 連上一個互動式的 shell,題目會即時報數字給你,要在限定次數內把兩個答案送回去。內容本身其實是一道扎實的物理計算題,但真正讓我印象深刻的,是我後來重連一個新的實例時,蠢到把上一輪的答案複製貼上,就一直連續錯了四次

題目在講什麼

An unknown aggressor satellite has been detected in a higher orbit and is threatening critical space infrastructure. Your mission: calculate a Hohmann transfer to intercept it.

翻成白話:偵測到一顆敵方衛星在比我方更高的軌道上,正在威脅重要太空基礎設施。任務是算出一個 Hohmann transfer(霍曼轉移軌道),攔截它。連上題目給的 shell 之後,畫面會告訴你三件事:自己所在圓形軌道的半徑跟速度、目標衛星所在圓形軌道的半徑跟速度、還有你們兩個目前的相位角(phase angle,簡單說就是兩顆衛星目前繞地球的角度差,題目要求要回傳兩個數字:

1
delta_v_burn  wait_time

delta_v_burn 是要花多少速度變化量(Δv,單位 m/s)去點火進入轉移軌道;wait_time 是要先等多少秒才點火,才能讓自己跟目標衛星剛好在同一時間抵達會合點。誤差容許範圍是 Δv ±10 m/s、等待時間 ±60 秒,每條連線只有 5 次提交機會,答錯或連線斷了就要重連拿一組全新的隨機題目

背景知識:什麼是 Hohmann transfer

Hohmann transfer 是航太工程裡最基本、也最省油的軌道轉移方式:如果你在一個低軌道,想換到另一個同心的高軌道,不需要一路加速上去,只要在正確的時間點做兩次點火。

第一次把自己送上一個橢圓形的「轉移軌道」,這個橢圓一端貼著原本的低軌道,另一端剛好貼著目標的高軌道,第二次點火則是在到達高軌道那一端時,把自己「拉直」成新的圓形軌道。

有興趣可以看這個影片:https://youtu.be/KdHI5Fn9Fu8

這題只計分第一次點火(進入轉移軌道那一下),所以問題簡化成:算出第一次點火要花多少 Δv,以及要挑哪個時間點火,才能讓自己跟目標衛星在轉移軌道的另一端準時碰頭。

這其實是個很標準的軌道力學問題,關鍵在兩塊:點火要花多少速度變化量,跟該等多久再點火。

算 Δv:標準 vis-viva 方程式

轉移橢圓的半長軸(transfer ellipse semi-major axis)就是自己軌道半徑跟目標軌道半徑的平均:

1
a_t  = (r1 + r2) / 2

用 vis-viva 方程式(描述橢圓軌道上任一點速度跟半徑關係的公式)算出轉移軌道在起點(也就是自己目前所在半徑 r1)的速度:

1
2
v_p  = sqrt(mu * (2/r1 - 1/a_t))
Δv1 = v_p - v1

這裡有個小訣竅:mu(重力參數,等於地球質量乘上重力常數)不要用課本上的標準地球值 398600.4418 km³/s² 去代,而是直接從題目自己印出來的圓軌道速度反推:

1
mu = v1**2 * r1   # 自洽的重力參數,直接從自己的圓軌道速度反推,不假設任何課本常數

因為在圓形軌道上,重力提供的向心力剛好等於 v² / r = mu / r²,兩邊消一消就是 mu = v² * r。這樣算出來的 Δv,跟伺服器內部算出來的正確答案幾乎完全吻合(誤差在 0.001 m/s 等級),完全不用擔心題目背後用的地球半徑或 GM 常數跟課本定義是否一致。

算等待時間:真正卡住的地方

轉移軌道走完一半橢圓(從起點飛到會合點)需要的時間:

1
t_transfer = π * sqrt(a_t³ / mu)

古典的 Hohmann 交會相位理論是這樣:假設自己在內側、速度較快的軌道(角速度 n1 = v1/r1),目標在外側、速度較慢的軌道(角速度 n2 = v2/r2),要讓兩者剛好在轉移橢圓的另一端(180 度外)相遇,點火那一刻目標必須領先自己一個特定角度:

1
φ_required = π − n2 · t_transfer

(直覺理解:轉移過程中,自己會掃過整整 180 度,而目標只會前進 n2 · t_transfer 這麼多角度,所以目標得先領先這麼多,才能讓兩者同時抵達。)

問題來了:拿到題目印出來的目前相位角 φ0 之後,等待時間該怎麼從 φ0 跟 φ_required 的差推出來?照課本直覺寫的公式:

1
t_wait = [(φ0 − φ_required) mod 2π] / (n1 − n2)

但這個公式是錯的 第一次照這個公式送出 Δv=160.73, wait_time=16997.80,結果 Δv 被接受,但 wait time 被判定錯誤(Wait time error too large

這個部分被拒絕的訊息其實是個很精準的診斷,因為代表轉移軌道本身的物理算式(a_t、v_p、t_transfer)完全沒問題,唯一有問題的是相位角的方向定義

既然只有 wait time 被打回票,問題就一定出在題目給的 phase angle 到底怎麼對應到公式裡的 φ0,而不是轉移軌道力學本身,如果把減法的順序反過來,也就是改成 「φ0 還要往另一個方向繞多少角度才能到達 φ_required」 就得到跟伺服器真正吻合的公式:

1
t_wait = ((phi_required - phase0) % (2*math.pi)) / (n1 - n2)

完整驗證:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import math
r1, r2 = 6938.585e3, 7403.771e3
v1, v2 = 7579.269, 7337.300
phase0 = math.radians(212.256)

n1, n2 = v1/r1, v2/r2
mu = v1**2 * r1
a_t = (r1 + r2) / 2
vp = math.sqrt(mu * (2/r1 - 1/a_t))
dv1 = vp - v1
t_transfer = math.pi * math.sqrt(a_t**3 / mu)
phi_required = math.pi - n2 * t_transfer

t_wait = ((phi_required - phase0) % (2*math.pi)) / (n1 - n2)
print(dv1, t_wait)
# 121.934 26901.33 <- 伺服器自己算出來的答案:121.934 m/s / 26901.426 s

送出 121.935 26900.962:

1
2
3
4
5
Submitted delta-v: 121.935 m/s   Required: 121.934 m/s   Error: 0.001 m/s
Submitted wait time: 26900.962 s Required: 26901.426 s Error: 0.464 s

INTERCEPT SUCCESS!
STARPWN{h0hm4nn_tr4nsf3r_1nt3rc3pt}

以上是最初那版Time to Intercept的完整解法。
後來題目重開了一個 V2 版本,同樣的服務、同樣的評分方式,只是換了個 port,照理說公式直接照搬就能用,實際上也的確如此,但是真正有趣的是接下來發生的事

V2:同一套公式,卻因為手滑連錯四次

V2 的題目描述、格式、誤差範圍完全跟 V1 一樣,唯一差別是題目重新給了一組全新的隨機參數。連上新的實例,畫面印出:

1
2
3
4
5
6
7
8
9
10
YOUR SATELLITE: DEFENDER-1
Orbital Radius: 6900.768 km
Orbital Velocity: 7600.008 m/s

TARGET SATELLITE: AGGRESSOR-X
Orbital Radius: 7167.955 km
Orbital Velocity: 7457.017 m/s

CURRENT GEOMETRY:
Phase Angle: 81.624 degrees

問題是,我在連上這個全新instance之前,才剛結束另一條連線(那條連線的參數是 r1=6850.870 km、r2=7534.987 km、phase=199.425°,算出來的 Δv 是 179.259)連上新的這條之後,前四次提交我只顧著調整 wait time,Δv 卻一直沿用上一條連線算出來的 179.26:

1
2
3
4
第 1 次:179.26  22068.88   -> 兩個都錯
第 2 次:179.26 17492.12 -> 兩個都錯
第 3 次:179.26 20347.45 -> 兩個都錯
第 4 次:179.26 20353.73 -> 兩個都錯

每次回應都是 Delta-v error too large
這其實已經是伺服器在明示:問題出在 Δv 本身要重算,而不是 wait time 猜得不夠準,每條連線的題目參數是固定的(同一條連線的 5 次機會共用同一組 r1/r2/v1/v2/phase),但不同連線之間的參數完全不共用。 把上一條連線算對的 Δv 直接複製貼到新連線,數字看起來很眼熟,卻已經是過期的答案

回頭用新連線印出來的參數重新算一次:

1
2
3
4
5
6
r1, r2 = 6900.768e3, 7167.955e3
v1, v2 = 7600.008, 7457.017
phase0 = math.radians(81.624)
# 套用同一套公式
# dv1 = 71.829
# t_wait = 81080.728

第五次、也是最後一次機會:

1
2
3
> 71.829 81080.728

Here is your flag: STARPWN{dont_print_th3_answer_}

總之這題偷懶沿用上一輪的答案

Flag

1
2
STARPWN{h0hm4nn_tr4nsf3r_1nt3rc3pt}      <- V1
STARPWN{dont_print_th3_answer_} <- V2

心得

  1. 算重力參數不要相信課本常數,直接從題目給的數字反推。 圓形軌道上 mu = v² · r 這個關係式永遠成立,用它反推出來的 mu 自動跟題目內部用的地球模型一致,不用猜測題目作者到底是用哪一版的地球半徑或 GM 常數。
  2. 伺服器的「部分正確」回應是很精準的除錯資訊。 只有 wait time 被判錯、Δv 卻悄悄通過,這代表轉移軌道本身的物理算式(半長軸、vis-viva、轉移時間)已經確認沒問題,問題精確縮小到相位角的方向定義上,不需要重新懷疑整個算法。
  3. 相位角的正負號慣例,是這類交會問題最常見的陷阱。 課本公式假設了某種特定的角度量測方向跟基準,題目給的「裸的」相位角數字如果沒說清楚定義方式,就要有心理準備去試試看方向相反的版本——兩者恰好差了一個會合週期,一個算錯了通常下一步就是換方向試另一個。
  4. 同一題的「V2」不代表數字也一樣。 就算是同一套程式碼、同一種公式,只要是每次連線都重新隨機出題的服務,舊連線算出來的正確答案在新連線裡就是過期資料——而且伺服器通常會老實告訴你「哪一項錯了」,這個訊號比自己盲猜要可靠得多,該做的是回頭用當下畫面印出來的數字整組重算,而不是只調整自己正在盯著看的那個數字。