# Common Helper — `ImuPreintegrator`

## Purpose

Shared GTSAM `CombinedImuFactor` preintegration buffer. Both C1 (VIO) and C5 (StateEstimator) consume the same FC IMU window and want the same preintegrated Δ-pose / Δ-velocity / Δ-bias quantities. Centralising the preintegrator avoids two inconsistent integrations of the same IMU stream.

## Used By

- C1 — Visual / Visual-Inertial Odometry.
- C5 — State Estimator.

## Interface (sketch)

```
class ImuPreintegrator:
    def reset_with_bias(bias: ImuBias) -> None
    def integrate_sample(sample: ImuSample) -> None
    def integrate_window(window: ImuWindow) -> None
    def current_preintegration() -> CombinedImuFactor
    def reset_for_new_keyframe() -> CombinedImuFactor   # returns the closed factor; clears state
```

## Implementation Notes

- Wraps GTSAM's `PreintegrationCombinedParams` + `PreintegratedCombinedMeasurements`.
- Holds the gyro/accel noise model from the camera-calibration artifact (which carries IMU-noise covariances per-deployment).
- Single-threaded by design — composition root binds one preintegrator instance to one writer thread.

## Caveats

- Bias drift is the responsibility of the consumers (C1 + C5) who call `reset_with_bias(...)` whenever their estimate of the IMU bias changes.
- The preintegrator does not own a clock — every `integrate_*` call requires a monotonic timestamp on the IMU sample.
