Transitioned the autodev state to phase 21, reflecting the completion of Step 5 and the drafting of Step 6 epics. Revised the architecture documentation to clarify the roles of the Tile Manager and its components, ensuring accurate representation of the system's operational flow. Updated glossary entries for Flight State and Operator to incorporate recent changes and enhance clarity on component interactions and responsibilities.
7.4 KiB
C5 — State Estimator
1. High-Level Overview
Purpose: own the GTSAM iSAM2 + IncrementalFixedLagSmoother (K=10–20 keyframes per D-C5-3) state. Fuse VioOutput (C1), PoseEstimate (C4), and FC IMU/attitude windows (C8 inbound) into the posterior pose with native 6×6 covariance via Marginals (D-C5-5 = (c)). Emit the smoothed corrected current frame to C8 for FC delivery; emit smoothed past-keyframes to C13 (FDR only — AC-4.5 internal smoothing, NOT FC retroactive correction).
Architectural Pattern: Strategy with two concrete implementations: GtsamIsam2StateEstimator (production-default) and EskfStateEstimator (mandatory simple-baseline). Selection at startup (ADR-001), BUILD_* gating (ADR-002), composition-root wired (ADR-009).
Upstream dependencies:
- C1 →
VioOutput(relative pose + IMU bias). - C4 →
PoseEstimate(absolute satellite-anchored pose); C4 adds factors directly to C5's iSAM2 graph (shared substrate). - C8 inbound side → FC
ImuWindow+AttitudeWindow+GpsHealth(for warm-start AC-5.1, blackout AC-NEW-8, spoofing-promotion AC-NEW-2 / F7).
Downstream consumers:
- C8 outbound side (per-FC encoder) →
EmittedExternalPosition(5 Hz periodic to FC). - C6 (mid-flight tile gen via orthorectifier; C5 supplies the
PoseEstimate+ quality_metadata for tile emission). - C13 FDR (smoothed past-keyframe estimates, source-set switch events, spoofing-rejection events).
2. Internal Interfaces
Interface: StateEstimator
| Method | Input | Output | Async | Error Types |
|---|---|---|---|---|
add_vio |
VioOutput |
None |
No | EstimatorDegradedError, EstimatorFatalError |
add_pose_anchor |
PoseEstimate |
None |
No | EstimatorDegradedError, EstimatorFatalError |
add_fc_imu |
ImuWindow |
None |
No | EstimatorDegradedError |
current_estimate |
() |
EstimatorOutput (smoothed current keyframe) |
No | — |
smoothed_history |
n_keyframes: int |
list[EstimatorOutput] |
No | — |
health_snapshot |
() |
EstimatorHealth |
No | — |
Input DTOs: see C1, C4, C8.
Output DTOs:
EstimatorOutput:
frame_id: uuid
position_wgs84: LatLonAlt
orientation_world_T_body: Quat (w, x, y, z)
velocity_world: Vector3 (m/s)
covariance_6x6: Matrix6
source_label: enum {satellite_anchored, visual_propagated, dead_reckoned}
last_satellite_anchor_age_ms: int
smoothed: bool — true for entries from `smoothed_history`
emitted_at: monotonic_ns
EstimatorHealth:
isam2_state: enum {INIT, TRACKING, DEGRADED, LOST}
keyframe_count: int
cov_norm_growing_for_s: float — AC-NEW-8 monotonicity check
spoof_promotion_blocked: bool — AC-NEW-2 / AC-NEW-8 gate state
3. External API Specification
Not applicable.
4. Data Access Patterns
C5 holds the GTSAM iSAM2 state in memory; persistent storage is only via FDR writes (C13 owns the file). No DB queries.
Storage Estimates
| Table/Collection | Est. Row Count (1yr) | Row Size | Total Size | Growth Rate |
|---|---|---|---|---|
| In-memory keyframe window | up to 20 keyframes resident | ~2 KB / keyframe (factors + values) | ~40 KB | bounded by IncrementalFixedLagSmoother K=10–20 |
C5 is bounded by design — no unbounded growth.
5. Implementation Details
Algorithmic Complexity:
- iSAM2 update on factor add: amortised
O(K)in keyframe count for the typical case;O(K^2)worst-case on relinearisation. Marginals.marginalCovariance(pose_key):O(K^3)in keyframe-window size; the dominant per-frame cost (~30–90 ms steady-state).IncrementalFixedLagSmootherkeeps the active window bounded — older keyframes are marginalised out.
State Management:
- iSAM2 graph + Values + Marginals lifecycle for the flight.
- Source-label state machine: tracks the AC-NEW-2 / AC-NEW-8 spoofing-promotion gate (≥10 s + visual consistency check before re-promoting a previously-spoofed FC GPS source).
- Last-anchor-age timer for AC-1.3 binning.
Key Dependencies:
| Library | Version | Purpose |
|---|---|---|
| GTSAM (Python + C++) | per Plan-phase pin | iSAM2 + CombinedImuFactor + BetweenFactorPose3 + GenericProjectionFactorCal3DS2 + Marginals |
gtsam_unstable.IncrementalFixedLagSmoother |
per Plan-phase pin | Bounded keyframe window (D-C5-3 K=10–20) |
| Eigen | matches GTSAM | Lie-algebra math |
Error Handling Strategy:
EstimatorDegradedError: factor add yielded poor convergence; covariance inflated; emitEstimatorOutputwith degraded label.EstimatorFatalError: iSAM2 numerical failure, KEYFRAME_LIMIT exceeded, etc.; emit noEstimatorOutputfor this tick. AC-5.2 fallback (3 s no estimate → FC IMU-only) applies.- Spoof-promotion gate: never re-introduce a previously-spoofed FC GPS source until BOTH (i) FC
gps_health == STABLE_NON_SPOOFEDfor ≥ 10 s AND (ii) the next satellite-anchored frame agrees with the FC GPS within a configurable tolerance (AC-NEW-8). Document every reject in FDR + GCS STATUSTEXT.
6. Extensions and Helpers
| Helper | Purpose | Used By |
|---|---|---|
ImuPreintegrator |
shared with C1 | C1, C5 |
SE3Utils |
shared with C1, C4 | C1, C4, C5 |
WgsConverter |
shared with C4, C8 | C4, C5, C8 |
SourceLabelStateMachine |
spoofing-promotion gate logic | C5 only — keep inside the component |
7. Caveats & Edge Cases
Known limitations:
- AC-4.5 internal smoothing is onboard only; the FC log is forward-time only. The smoothed past-keyframe estimates go to FDR, not back to the FC.
- iSAM2 +
IncrementalFixedLagSmootherrequires careful key management; missing keys cause silent factor-add failures — the implementation MUST log everyadd_*call's success/failure status.
Potential race conditions:
- Single writer thread for the iSAM2 graph by design. C1 + C4 + C8-inbound deliver to a timestamp-ordered merge queue ahead of C5's writer thread.
Performance bottlenecks:
Marginals.marginalCovariance(pose_key)is the per-frame hot spot. D-CROSS-LATENCY-1 hybrid degrades C4's covariance recovery (not C5's) under thermal throttle.
8. Dependency Graph
Must be implemented after: C1 (input), C4 (input + shared graph), C8 inbound (FC IMU prior).
Can be implemented in parallel with: C6, C13 — independent paths.
Blocks: C8 outbound (no per-frame estimate), F3 / F4 / F5 / F7 / F9 / F10.
9. Logging Strategy
| Log Level | When | Example |
|---|---|---|
| ERROR | EstimatorFatalError; iSAM2 numerical failure; AC-5.2 path imminent |
C5 fatal iSAM2 failure; frame=12345; AC-5.2 fallback |
| WARN | EstimatorDegradedError; spoofing-promotion blocked; cov norm growing >2× steady |
C5 degraded: cov_inflation=3.1, spoof_block=true |
| INFO | Strategy ready; warm-start applied; spoof-promotion gate state changes | C5 ready: estimator=gtsam_isam2, K=15 |
| DEBUG | per-frame factor adds + smoothed history depth | C5 frame=12345 vio_added=true pose_added=true imu_added=true smoothed_n=15 |
Log format: structured JSON. Log storage: stdout / journald / FDR via C13 (ERROR + WARN always; smoothed past-keyframe entries always go to FDR per AC-4.5; spoofing-promotion-block events go to FDR + GCS STATUSTEXT).