GNC/guidance/detour_planner.py — Build-Time Coverage Predictor

Offline oracle: replay an aim-point schedule through the finder's coverage marker and return the area-weighted coverage fraction.

Camera Pose per Schedule Step

At orbit CoM point pc(i)\bm p_c^{(i)} and aim point xaim(i)\bm x_{\text{aim}}^{(i)}:

u^(i)=xaim(i)pc(i)xaim(i)pc(i)\hat{\bm u}^{(i)} = \frac{\bm x_{\text{aim}}^{(i)} - \bm p_c^{(i)}}{\|\bm x_{\text{aim}}^{(i)} - \bm p_c^{(i)}\|}

pe(i)=pc(i)+rcamu^(i)\bm p_e^{(i)} = \bm p_c^{(i)} + r_{\text{cam}} \hat{\bm u}^{(i)}

EE placed on the camera sphere of radius rcamr_{\text{cam}}; look-axis ze=xaim(i)pe(i)\bm z_e = \bm x_{\text{aim}}^{(i)} - \bm p_e^{(i)}.

Camera Orientation

Roll reference fixed to world-xx: ex=[1,0,0]\bm e_x = [1,0,0]^\top.

ze(i)=xaim(i)pe(i)xaim(i)pe(i)\bm z_e^{(i)} = \frac{\bm x_{\text{aim}}^{(i)} - \bm p_e^{(i)}}{\|\bm x_{\text{aim}}^{(i)} - \bm p_e^{(i)}\|}

Re(i)=framexz ⁣(ex,ze(i))R_e^{(i)} = \text{frame}_{xz}\!\left(\bm e_x, \bm z_e^{(i)}\right)

Roll continuity (RprevR_{\text{prev}} chaining) is irrelevant — visibility uses only ze\bm z_e.

Strided Coverage Replay

Strided sweep over NN orbit samples with stride Δs\Delta_s:

S=k=0N/ΔsV ⁣(posekΔs)\mathcal{S} = \bigcup_{k=0}^{\lfloor N/\Delta_s \rfloor} \mathcal V\!\left(\text{pose}_{k\Delta_s}\right)

where V\mathcal V is the visible-triangle set at a given pose. Finder state (seen_triangles, triangle_stats) is deep-snapshotted before the sweep and restored in-place in a finally block — side-effect-free by construction.

Area-Weighted Coverage Fraction

Let S{0,,N1}\mathcal{S} \subseteq \{0,\ldots,N_\triangle-1\} be the seen triangle set.

f^cov=min ⁣(jSAjAtotal,1)\hat f_{\text{cov}} = \min\!\left(\frac{\displaystyle\sum_{j \in \mathcal{S}} A_j}{A_{\text{total}}}, 1\right)

AjA_j = area of triangle jj; Atotal=j=0N1AjA_{\text{total}} = \sum_{j=0}^{N_\triangle - 1} A_j.

This is identical to the sim's inspection.area_coverage_frac by construction.