IMU & Inertial Navigation

An IMU measures its own motion and nothing external. From accelerometers and gyroscopes a strapdown algorithm computes position, velocity and attitude by integration — everywhere, underwater, underground, in space, while jammed — at the cost of drift. This page is the long version: what the sensor actually outputs and why it is an integral rather than a rate, the complete error model, the mechanization equations, coning and sculling, the two stages of ground alignment with the filter mathematics written out — state vector, process dynamics and every measurement residual — Schuler oscillation, how the numbers in the filter's covariance come from bench calibration, and a worked navigation-grade example showing why a sensor's error ratios decide which states get estimated.

Symbols used on this page are defined in the Nomenclature → inertial navigation.

Related: Fine alignment and the drift test go deeper on alignment as an estimation problem; IMU acceptance testing is the bench-procedure view; Hardware — IMUs covers parts and grades; Rocket navigation puts it in a flight architecture.

The three things that surprise people. (1) A navigation-grade IMU does not output acceleration and angular rate — it outputs integrated increments, $\Delta\mathbf v$ and $\Delta\boldsymbol\theta$, and that difference is not cosmetic: it is what makes the mechanization vibration-tolerant. (2) Horizontal error does not grow monotonically; it oscillates with an 84.4-minute period set by nothing but $g$ and Earth's radius. (3) The vertical channel is unstable and needs external help on a timescale of minutes, so "an INS drifts slowly" is only true of two of its three axes.

1 · What the sensor actually gives you

Two triads sit in the unit. Accelerometers sense specific force — the non-gravitational part of acceleration — and gyroscopes sense angular rate. But almost no navigation-grade IMU reports those quantities directly.

1.1 ΔV and Δθ, not acceleration and rate

The physical sensing elements are intrinsically integrating. A ring-laser gyro counts interference fringes, and each fringe is a fixed increment of angle, not a rate. A pendulous-integrating-gyro accelerometer, or a force-rebalance accelerometer with a pulse-counting loop, emits pulses each worth a fixed increment of velocity. So the natural output is a count over an interval:

$$ \Delta\boldsymbol\theta_k = \int_{t_{k-1}}^{t_k}\!\boldsymbol\omega_{ib}^b(t)\,\mathrm dt, \qquad \Delta\mathbf v_k = \int_{t_{k-1}}^{t_k}\!\mathbf f^b(t)\,\mathrm dt $$

These are often called delta-angles and delta-velocities. A rate-output IMU (most MEMS parts) samples $\boldsymbol\omega$ and $\mathbf f$ instead, and the consumer has to integrate them. The distinction matters more than it looks:

Increment output (ΔV, Δθ)Rate output (f, ω)
What a sample means The exact integral over the interval — no information lost between samples A point sample of a continuous signal
Under vibration Correct: high-frequency motion inside the interval is still captured in the integral Aliased: motion faster than Nyquist folds down and corrupts the estimate irreversibly
Dropped sample Loses a known, bounded amount of motion Loses an unknown amount — the signal between samples was never measured
Coning/sculling Can be reconstructed from consecutive increments (§3) Needs a high sample rate and is still approximate
Typical grade Navigation, strategic (RLG, HRG, FOG) Consumer, industrial MEMS
Why this is the right interface. A launch vehicle vibrates at hundreds of hertz. If the IMU reports rate at 100 Hz, everything above 50 Hz aliases and you can never recover it. If it reports the integral over each 10 ms window, the vibration is inside the number — the integral of a fast oscillation is small and correct, not aliased. This is why the flight interface for a serious INS is increments, and why a rate-output sensor on a vibrating vehicle needs to run far faster than the navigation loop.

One consequence for the software: increments are interval quantities, so they must be consumed exactly once and their interval must be known. A dropped or duplicated increment message is a direct velocity or attitude error, not a momentary glitch, which is why the timing interface between an IMU and a navigator is such a common source of flight anomalies.

1.2 The complete error model

What you receive is the true increment corrupted by a deterministic and a random stack. Written per triad, for the sensed quantities:

$$ \tilde{\mathbf f}^b = \big(I + S_a + M_a\big)\,\mathbf f^b + \mathbf b_a + \mathbf b_a(t) + \mathbf q_a(\mathbf f) + \boldsymbol\eta_a $$ $$ \tilde{\boldsymbol\omega}^b = \big(I + S_g + M_g\big)\,\boldsymbol\omega^b_{ib} + \mathbf b_g + \mathbf b_g(t) + \Gamma\,\mathbf f^b + \boldsymbol\eta_g $$

with each term meaning something physically distinct:

TermNameStructureObservable when…
$S$Scale factor Diagonal. A gain error, in ppm. Output is proportional to input, so it needs a large input to be visible. A large rate or acceleration is applied — a rate table, or flight
$M$Misalignment / non-orthogonality Off-diagonal. The sensing axes are not exactly the axes you think, so input on one axis leaks into another. Input is applied on one axis while another is observed
$\mathbf b$Turn-on bias Constant within a run, different every power cycle. This is the term alignment estimates. Always present; separable from tilt only by changing geometry
$\mathbf b(t)$In-run bias instability Slowly wandering. Modelled as a first-order Gauss–Markov process, not a constant. Over long runs; sets the Allan-variance floor
$\boldsymbol\eta$White noise Integrates to angle random walk (ARW) and velocity random walk (VRW). Always; averages down as $1/\sqrt T$
$\Gamma\mathbf f$g-sensitivity Gyro bias proportional to acceleration. Matters enormously under sustained thrust. Under high g — a centrifuge, or boost
$\mathbf q_a$Non-linearity / g² Quadratic and vibration-rectification terms. Usually lumped into an error budget rather than estimated. High-g and high-vibration environments

Two observations that drive everything downstream. First, scale factor and misalignment multiply the input, so on a stationary pad — where the only inputs are $1g$ on one axis and $\Omega$ about one axis — they are essentially invisible. That is why they are calibrated on a bench (§9) and not by an alignment filter. Second, bias adds regardless of input, so it is always present and always the dominant concern for a static process. This split is the reason §5 and §6 look the way they do.

The compensation step applies the estimated parameters to the raw increments before the mechanization ever sees them:

$$ \Delta\mathbf v^{\text{comp}} = \big(I + \hat S_a + \hat M_a\big)^{-1} \big(\Delta\tilde{\mathbf v} - \hat{\mathbf b}_a\,\Delta t\big), \qquad \Delta\boldsymbol\theta^{\text{comp}} = \big(I + \hat S_g + \hat M_g\big)^{-1} \big(\Delta\tilde{\boldsymbol\theta} - \hat{\mathbf b}_g\,\Delta t\big) $$

Note $\hat{\mathbf b}\,\Delta t$, not $\hat{\mathbf b}$ — the increment accumulated over $\Delta t$ carries $\Delta t$ worth of bias. Getting this wrong by using the rate-form bias directly is a classic unit bug that produces an error scaling with sample rate.

Flight code usually writes this in a cheaper, per-axis form. If the filter's scale-factor state is defined as a fractional error $s$ on each axis and the bias state $b$ in rate units, the compensation is a division rather than a matrix solve:

$$ \Delta v^{\text{comp}}_i = \Delta\tilde v_i + \frac{b_{a,i}\,\Delta t}{1 - s_{a,i}}, \qquad \Delta\theta^{\text{comp}}_{A,i} = \Delta\tilde\theta_{A,i} + \frac{\tfrac12 b_{g,i}\,\Delta t}{1 - s_{g,i}} $$

Three details in that expression are worth stating explicitly, because each encodes a convention that must match the filter exactly or the correction is applied backwards:

  • The sign is a convention, not a derivation. Here the states are defined so that the correction is added. Elsewhere the same physics is written with a subtraction and the opposite state sign. Either is fine; mixing them doubles the error instead of cancelling it.
  • $1/(1-s)$, not $(1+s)$. These agree to first order, and for a scale factor of a few hundred ppm the difference is negligible — but the reciprocal is the exact inverse of a sensor that reports $(1+s)$ times truth, so it stays correct if the state ever grows.
  • The half-interval increments each get half the bias. $\Delta\theta_A$ and $\Delta\theta_B$ span half the interval apiece, so each carries $\tfrac12 b_g\,\Delta t$. Giving both the full $b_g\Delta t$ is a factor-of-two attitude error that looks exactly like a gyro bias twice the true size.

Cross-axis misalignment ($M$ in the model above) is often not compensated in real time on a well-calibrated navigation-grade unit: the terms are small, they are already trimmed at the factory, and a full matrix inverse per sample costs more than it buys. It remains in the error budget as a consider parameter.

1.3 In practice: two streams, two timestamps, two consumers

§1.1 argued that a navigation-grade IMU reports increments. The complete answer, on real hardware, is that it reports both forms in the same message, and they are not redundant — they are sampled for different consumers and carry different timestamps.

FieldQuantityUnitsConsumed byWhy this form
$\Delta\boldsymbol\theta_A$ Attitude increment, start → mid of intervalrad Navigation
strapdown, alignment, the filter
Exact integrals over the interval. Nothing is lost between samples, and the half-interval split lets the mechanization build the attitude valid at the interval midpoint — see the callout below.
$\Delta\boldsymbol\theta_B$ Attitude increment, mid → end of intervalrad
$\Delta\mathbf v$ Velocity increment over the intervalft/s
$\boldsymbol\omega$ Angular raterad/s Flight control
autopilot, structural filters
Control laws are written in rates and accelerations, and want the instantaneous value at a known instant, not an interval average.
$\mathbf a$ Linear accelerationft/s²
Why $\Delta\theta$ arrives in two halves — and it is not for the coning correction. It is for the velocity rotation. The accelerometers measure $\Delta\mathbf v$ in body axes, but it has to be accumulated in the navigation frame, and the body frame rotates during the interval. Rotating by the attitude at the start of the interval, or at the end, is a first-order choice that leaves an error of order $\Delta\boldsymbol\theta\times\Delta\mathbf v$. Rotating by the attitude at the midpoint is the second-order choice — the midpoint rule — and it captures most of the sculling effect structurally, rather than as an explicit correction term.
$$ \Delta\mathbf v^n = \underbrace{C^n_b\big|_{\text{mid}}\,\Delta\mathbf v^b}_{\text{midpoint rotation}} \;-\;\underbrace{2\,\boldsymbol\omega_{ie}\times\mathbf v^n\,\Delta t}_{\text{Coriolis}} \;+\;\mathbf g^n\,\Delta t $$

The midpoint attitude is obtained by applying only the first half-increment to the running attitude; the second half then carries it to the end of the interval. So the mechanization maintains two attitudes per cycle, with different jobs:

AttitudeBuilt fromUsed for
$C^n_b\big|_{\text{mid}}$ running attitude $\oplus\,\Delta\boldsymbol\theta_A$, plus half the navigation-frame rotation Rotating $\Delta\mathbf v$; the reported angular rate; and the linearisation point for the filter's $F$ matrix (§6.3)
$C^n_b\big|_{\text{end}}$ midpoint attitude $\oplus\,\Delta\boldsymbol\theta_B$, plus the second half The reported attitude, and the starting point for the next interval

The navigation frame is itself rotating, so reaching the midpoint means advancing the body attitude by $\Delta\boldsymbol\theta_A$ and the reference frame by half of its own rotation over the interval. Position is then updated trapezoidally, $\mathbf r \mathrel{+}= \big(\mathbf v + \tfrac12\Delta\mathbf v\big)\Delta t$ — the same second-order idea applied once more. Coning and sculling (§3) remain separate concerns: the halves reduce the velocity-rotation error, not the non-commutativity of the attitude update itself.

Two engineering consequences follow, and both are easy to get wrong:

  • The two streams carry separate times of validity. The increment fields are stamped with the end of the integration interval; the rate/acceleration fields with the instant they were latched. Using one timestamp for both introduces a systematic, sub-sample timing bias — exactly the kind of error that never shows up in a static test and then scales with dynamic pressure.
  • Only the increments may be integrated. Feeding $\boldsymbol\omega$ and $\mathbf a$ into the strapdown as if they were interval averages discards the sensor's own exact integration and reintroduces the rectification errors of §3 at the sample rate.

Message rate matters as well. A navigation-grade ring-laser unit typically publishes at a few hundred hertz — call it $f_{\text{imu}}$ — while the vehicle's control thread runs slower. The navigation manager therefore consumes several IMU messages per control cycle, and every one of them must be integrated; dropping the intermediate samples and using only the most recent one throws away most of the interval and biases the solution. When a message is genuinely missing, the correct behaviour is to reuse the previous sample and flag the gap, not to extrapolate.

A lower-grade MEMS unit in the same architecture may publish at a lower rate and require an anti-aliasing filter on the control-side stream — typically a low-order Butterworth, run at the sensor rate and decimated to the control rate, one filter per channel. The navigation-side increments are not filtered: they are already integrals, and filtering them would destroy the property that makes them useful.

1.4 Where coning and sculling are actually compensated

§3 derives the coning and sculling corrections. A fair question is then: does the navigation software compute them? For a navigation-grade unit the answer is no — and it should not. The corrections are applied inside the IMU, and the increments that cross the interface are already compensated.

EffectCompensated whereWhy there
Coning
attitude non-commutativity
Inside the IMU Needs sub-sample angular history. The sensor has it at its internal rate — typically many times the output rate — and the navigation computer does not.
Sculling
rotation × acceleration rectification
Inside the IMU Same argument: it needs $\boldsymbol\omega$ and $\mathbf a$ correlated within the interval.
Velocity-rotation error
frame rotates during the interval
In the navigation computer Handled structurally by the midpoint attitude of §1.3 — which is precisely why the sensor bothers to split $\Delta\boldsymbol\theta$ in half.
Bias, scale factor In the navigation computer (§1.2) These are filter states; they change as the filter learns them, so they cannot be baked into the sensor.
The division is about who owns the high-rate data. Anything requiring information between output samples must be done by the sensor, because that information never crosses the interface. Anything depending on an estimate that evolves — bias, scale factor, the navigation frame's own rotation — must be done outside, because the sensor does not know it. Coning and sculling fall on the sensor's side of that line; the increments arrive already rectified.

Two practical consequences. First, searching a flight navigation codebase for "coning" can legitimately return nothing — its absence is a statement about the sensor, not a missing feature. Second, the property is lost if the increments are mishandled downstream: averaging several samples together before integrating, or using the rate/acceleration stream as though it were an interval average, reintroduces exactly the rectification errors the sensor spent its internal bandwidth removing.

A lower-grade unit that reports only rate and acceleration shifts the burden the other way: the navigation computer must then form the increments itself and apply the corrections of §3 explicitly, at the highest rate it can sustain. That is a real architectural difference between sensor classes, not a detail — it changes what the software is responsible for being correct about.

1.5 The frames, and the chain between them

Nearly every quantity in inertial navigation is meaningless without saying which frame it is resolved in and relative to what. Six frames appear in a launch-vehicle navigator, and they are not interchangeable:

FrameOriginAxesRotates?Used for
Inertial
ECI
Earth's centreFixed relative to the stars No — this is the one Newton's laws apply in What the gyros and accelerometers actually sense motion with respect to
Earth-fixed
ECEF
Earth's centreRotate with the Earth; one axis through the pole Yes, at $\Omega \approx 15.04^\circ$/h The mechanization and filter frame for a launch vehicle. The pad is surveyed here and GNSS reports here.
Local-level
UEN / NED
The vehicleUp–East–North, or North–East–Down Yes — with the Earth and with vehicle motion Human-readable attitude: roll, pitch, heading. Where tilt and azimuth mean something.
Sensor / IMU
the "centre of navigation"
A point inside the IMU, defined by the supplier The IMU's own axes, also supplier-defined With the vehicle The frame every increment arrives in. The navigation state is propagated for this point.
Vehicle / body A structural reference on the stageVehicle roll/pitch/yaw axes With the vehicle What flight control steers. Related to the sensor frame by a surveyed, fixed rotation.
Antenna The GNSS antenna phase centreUsually aligned to the vehicle frame With the vehicle Where GNSS measurements are actually made — offset from the sensor frame by a lever arm (§7.8).
The sensor frame and the vehicle frame are not the same, and the difference is not small. The IMU is bolted somewhere convenient, not at the vehicle's reference point, and its axes are whatever the supplier chose. The rotation between them is a surveyed installation constant. Get it wrong and every attitude the vehicle reports is wrong by that rotation — a failure that looks like a bias no filter can remove, because it is not a sensor error at all.

The chain, as the software actually composes it:

sensed increments what the filter estimates arrive in the SENSOR frame is the attitude of SENSOR w.r.t. EARTH-FIXED │ │ │ C(vehicle → sensor) │ │ surveyed installation constant, │ │ never estimated │ v v VEHICLE frame ─────────────────────────────► EARTH-FIXED (ECEF) (what flight control steers) (mechanization + filter frame, │ pad survey, GNSS all live here) │ │ │ │ C(ECEF → local-level) │ │ from latitude, longitude │ v │ LOCAL-LEVEL (UEN / NED) │ roll, pitch, HEADING │ — where the azimuth update └── antenna lever arm ──► ANTENNA of §6.5 is formed (GNSS measures here)

Read the notation as $C^{\text{to}}_{\text{from}}$, and note the two distinct jobs. Earth-fixed is where the arithmetic happens: it is non-rotating with respect to the ground, the pad survey is expressed in it, and GNSS needs no conversion. Local-level is where the meaning lives: "tilt" and "azimuth" are statements about the local vertical and local north, so the azimuth pseudo-measurement must be formed there and then mapped back. And inertial is where the sensors are honest — which is why Earth rate and the Coriolis term keep appearing as corrections when integrating in a rotating frame.

A subtlety worth naming: the navigation solution is computed for the sensor point, but almost everything that consumes it cares about a different point — flight control about the vehicle reference, GNSS about the antenna, aerodynamics about the centre of mass. Each requires a lever-arm translation, and under rotation each picks up a $\boldsymbol\omega\times\mathbf l$ velocity term. Frame errors and lever-arm errors are the two most common sources of a solution that is internally consistent and externally wrong.

2 · Strapdown mechanization

Mechanization is the integration of increments into a navigation state. Attitude first, because velocity depends on it.

2.1 Attitude

In continuous form, for the body-to-navigation rotation $C^n_b$:

$$ \dot C^n_b = C^n_b\,[\boldsymbol\omega^b_{ib}\times] - \big[(\boldsymbol\omega^n_{ie} + \boldsymbol\omega^n_{en})\times\big]\,C^n_b $$

The first term is what the gyros sense. The second removes what the gyros sense because the reference frame itself is rotating: $\boldsymbol\omega_{ie}$ is Earth's rotation and $\boldsymbol\omega_{en}$ is the transport rate, the rotation of the local-level frame as the vehicle moves over a curved Earth:

$$ \boldsymbol\omega^n_{en} = \begin{bmatrix} \dfrac{v_E}{R_E + h} \\[6pt] -\dfrac{v_N}{R_N + h} \\[6pt] -\dfrac{v_E\tan L}{R_E + h} \end{bmatrix} $$

That transport term is not a detail — it is the feedback path that creates Schuler oscillation (§6). In discrete form, using the increment directly rather than a sampled rate:

$$ C^n_{b}(t_k) = C^n_b(t_{k-1})\,\exp\!\big([\Delta\boldsymbol\theta^{\text{comp}}_k\times]\big) - \text{(frame-rotation correction)} $$

with the exponential evaluated in closed form by Rodrigues' formula, or equivalently as a quaternion update (see Attitude & rotations). Quaternion renormalisation is applied on a slow schedule — the norm drifts only through round-off, so renormalising every cycle is wasted work, and never renormalising is a slow corruption.

2.2 Velocity and position

$$ \dot{\mathbf v}^n = C^n_b\,\mathbf f^b - \big(2\boldsymbol\omega^n_{ie} + \boldsymbol\omega^n_{en}\big)\times\mathbf v^n + \mathbf g^n(\mathbf r) $$

Three corrections, each easy to get wrong. The $2\boldsymbol\omega_{ie}\times\mathbf v$ term is Coriolis; the $\boldsymbol\omega_{en}\times\mathbf v$ term is the centripetal effect of moving over a curved surface; and $\mathbf g^n(\mathbf r)$ is modelled gravity, including the centrifugal part of local gravity, evaluated at the current estimated position. A gravity-model error is therefore indistinguishable from an accelerometer bias in the vertical channel — which is exactly why it is usually a consider parameter rather than an estimated state.

3 · Coning & sculling — the non-commutativity corrections

Summing gyro increments and calling the result a rotation is wrong whenever the rotation axis moves within the interval, because finite rotations do not commute. Two named errors follow.

3.1 Coning (attitude)

If the angular-rate vector sweeps a cone — two axes oscillating 90° out of phase — the true attitude accumulates a secular drift about the third axis while the naive sum of increments reports zero. The correction, from consecutive sub-interval increments $\boldsymbol\alpha$:

$$ \Delta\boldsymbol\theta = \boldsymbol\alpha_k + \underbrace{\tfrac12\,\boldsymbol\alpha_{k-1}\times\boldsymbol\alpha_k}_{\text{coning}} + \cdots $$

Higher-order algorithms use more sub-samples and more cross terms. The key point: the information needed to reconstruct coning is present in a sequence of increments, which is the deeper reason the ΔV/Δθ interface of §1.1 is preferred.

3.2 Sculling (velocity)

The velocity analogue. When angular rate and specific force oscillate in phase, a rectified velocity error builds up — the sensor is being rotated while it is being accelerated, so a fixed body-frame acceleration paints out a non-zero mean in the navigation frame:

$$ \Delta\mathbf v^n = C^n_b\Big[\Delta\mathbf v_k + \underbrace{\tfrac12\,\boldsymbol\alpha_k\times\Delta\mathbf v_k}_{\text{rotation}} + \underbrace{\tfrac12\big(\boldsymbol\alpha_{k-1}\times\Delta\mathbf v_k + \Delta\mathbf v_{k-1}\times\boldsymbol\alpha_k\big)}_{\text{sculling}}\Big] $$
Why this is not academic. On a launch vehicle, rotorcraft or gun-launched round, uncompensated coning and sculling produce large, silent drift with a perfect sensor. There is no bias to blame and nothing looks wrong in the raw data. High-end systems run the compensation at 1–10 kHz and integrate navigation at a much lower rate — the two-rate structure exists for this reason.

The effect is easiest to believe when you watch it. Two axes oscillate out of phase below; naively summing increments reports zero net rotation about the third axis, yet the body really is rotating:

4 · How each error term grows

A bias does not produce a constant error — it produces a growing one, and the power of $t$ depends on how many integrations sit between the sensor and the quantity you care about.

ErrorSymbolAttitudeVelocityPosition (short term)
Accel bias$b_a$—$b_a t$$\tfrac12 b_a t^2$
Gyro bias$b_g$$b_g t$$\tfrac12 g\,b_g t^2$$\tfrac16 g\,b_g t^3$
Accel white noise (VRW)$\sigma_v$—$\propto\sqrt t$$\propto t^{3/2}$
Gyro white noise (ARW)$\sigma_\theta$$\propto\sqrt t$$\propto t^{3/2}$$\propto t^{5/2}$
Bias instability—random walkdominates long runsdominates long runs
Scale factor$S$$\propto\!\int\!\omega$$\propto\!\int\! f$∝ distance travelled
g-sensitivity$\Gamma$$\propto\!\int\! f$large under boostlarge under boost

These are the short-term (non-rotating-Earth) growth laws. Over times comparable to the Schuler period they are replaced by the bounded oscillatory solutions of §6 — the $t^3$ law for gyro bias is the first term of an expansion of $R\beta(t-\sin\omega_s t/\omega_s)$, valid while $\omega_s t \ll 1$.

Set a bias and watch the estimate peel away from truth, then enable a periodic fix and see it bounded:

5 · Ground alignment — the two stages

Before an INS can navigate it needs an initial state. Position is surveyed and velocity is zero, so the entire problem is attitude — and the only references available to a stationary vehicle are the gravity vector and Earth's rotation vector. This runs in two stages that are not alternatives: fine alignment requires coarse alignment to have run first.

5.1 The block diagram

┌──────────────── GROUND ALIGNMENT (vehicle stationary) ───────────────┐ │ │ ┌──────────┐ ΔΘ, ΔV │ ┌───────────────────┐ ┌──────────────────────┐ │ │ IMU │───────────┼─►│ STAGE 1: COARSE │ attitude │ STAGE 2: FINE │ │ │ 100 Hz+ │ raw │ │ │ C_b^n (coarse) │ │ │ │ ΔΘ / ΔV │ │ │ • average ΔV → g ├─────────────────►│ • full mechanization │ │ └────┬─────┘ │ │ • average ΔΘ → ω │ position │ at high rate │ │ │ │ │ • TRIAD / analytic│ gravity-exposed│ • error-state EKF │ │ │ survey: lat, │ │ coarse attitude │ axis flags │ at low rate │ │ │ lon, alt ──────┼─►│ • levelling only │ │ • ZUPT + fixed-pos │ │ │ │ └───────────────────┘ │ + static azimuth │ │ │ a priori σ │ seconds └──────────┬───────────┘ │ │ from bench ────┼────────────────────────────────────────────────────┤ minutes │ │ calibration │ exit: σ_ψ below req AND flat │ └─────────────────┼────────────────────────────────────────────────────┼────────────────┘ │ │ │ ┌───────────────────────▼──────────────┐ │ │ converged: C_b^n, r, v, b_g, b_a, P │ └────────────────────────────┴───────────────────┬──────────────────┘ │ ┌─────────────────────────────────▼─────────────────┐ │ FLIGHT (or the free-inertial drift check first) │ └───────────────────────────────────────────────────┘

Note the two inputs that are not from the IMU: the surveyed position, which supplies the latitude that gyrocompassing needs and the truth for the position update, and the a priori sensor statistics from bench calibration, which become the filter's initial covariance (§6.5).

§5–§8 are self-contained on purpose. Everything needed to follow alignment end to end is here: the block diagram above, the coarse-stage averaging mathematics (§5.2), the handoff (§5.3), the fine-align filter with its process and measurement models (§6), the two-rate timing that interlocks the mechanization and the filter (§7), and the Schuler error dynamics that govern all of it (§8). You should not need to leave this page. Two companion pages go further in specific directions rather than repeating it: Fine alignment treats the estimator as an observability problem — which states are recoverable while stationary, and why tilt and accelerometer bias are fundamentally inseparable — and the drift test treats the pass/fail decision as a detection problem, deriving a threshold and trading masked faults against false alarms.

5.2 Stage 1 — coarse alignment (levelling)

Process. Accumulate increments over a short window and divide by the elapsed time to recover the mean sensed vectors. Averaging is the entire signal-processing content of this stage:

$$ \bar{\mathbf f}^b = \frac{1}{T}\sum_{k=1}^{N}\Delta\mathbf v_k, \qquad \bar{\boldsymbol\omega}^b = \frac{1}{T}\sum_{k=1}^{N}\Delta\boldsymbol\theta_k, \qquad T = N\,\Delta t $$

Because the vehicle is stationary, the expected values are known: $\bar{\mathbf f}^b$ should be the reaction to gravity and $\bar{\boldsymbol\omega}^b$ should be Earth rate, both expressed in body axes. White noise averages down as $1/\sqrt T$, which is why even a second or two of averaging is enough for the gravity direction — the signal is $1g$ against microgravity-level noise.

Levelling. A single measured vector fixes two of three attitude degrees of freedom. Roll and pitch follow directly:

$$ \phi = \operatorname{atan2}\big(-\bar f_y,\; -\bar f_z\big), \qquad \theta = \operatorname{atan2}\Big(\bar f_x,\; \sqrt{\bar f_y^2 + \bar f_z^2}\Big) $$

Heading. Two options, and a real system usually implements both:

Analytic coarse alignment (TRIAD)

Use both averaged vectors. Gravity and Earth rate are non-parallel, so their cross product completes a frame and the attitude follows in closed form:

$$ \hat{\mathbf u}_1 = \frac{-\bar{\mathbf f}}{|\bar{\mathbf f}|},\quad \hat{\mathbf u}_2 = \frac{\hat{\mathbf u}_1\times\bar{\boldsymbol\omega}} {|\hat{\mathbf u}_1\times\bar{\boldsymbol\omega}|},\quad \hat{\mathbf u}_3 = \hat{\mathbf u}_1\times\hat{\mathbf u}_2 $$

Build the same triad from the known reference vectors and $C^n_b = U_n U_b^{\mathsf T}$. Fast, but the heading is only as good as a few seconds of averaging against a 15°/h signal — i.e. poor. It is an initialisation, not an answer.

This is Wahba's problem with two vectors — see Attitude.

Surveyed / mounting attitude

If the vehicle's orientation on the pad is surveyed, take heading from that directly. Deterministic and instant, and it does not care about gyro quality — but it depends on the survey and on the mechanical interface being what the paperwork says.

Usually the preferred path when available, with the analytic method as the fallback that needs no external information.

A by-product that matters later: gravity-exposed axes. While averaging, check which accelerometer axes actually sense a significant component of gravity:

$$ \text{exposed}_i = \big[\,|\bar f_i| \ge f_{\min}\,\big] $$

Only an exposed axis has any hope of having its bias or scale factor observed, because only there is there an input to observe it against. This boolean triple is handed to the fine-alignment filter as a gate on bias estimation — attempting to estimate a parameter with no excitation does not just fail, it injects process noise that leaks through correlations into the states that are observable.

5.3 What is passed from coarse to fine

PassedFromWhy fine alignment needs it
$C^n_b$ coarse attitudeLevelling + TRIAD or survey The filter is linearised. It needs a starting attitude close enough for small-angle error states to be valid — arcminutes, not degrees.
Position (ECEF / LLA)Survey, converted Truth for the position update; latitude for the Earth-rate reference vector.
Velocity = 0Definition Truth for the zero-velocity update.
Gravity magnitude & modelGravity model at surveyed position Enters the mechanization and the tilt-to-velocity coupling.
Gravity-exposed axis flagsAveraging check above Gates which accelerometer biases the filter is allowed to estimate.
A priori $\hat{\mathbf b}$, $\hat S$, $\hat M$Bench calibration (§9) Initial compensation, and the mean of the initial state.
A priori covariance $P_0$Bench calibration / datasheet (§6.5) Tells the filter how much to trust each initial value — the single most consequential tuning input.
The mechanization and the filter must start from the same state. If the fine-alignment strapdown is seeded from one attitude and the filter's linearisation point is another, the first correction is computed about the wrong operating point and the result is nonsense. This is a real and easily-made integration error.

6 · Stage 2 — fine alignment, as a filter

Heading needs the Earth-rate reference, whose horizontal component is only $\Omega\cos L \approx 10.6^\circ/\mathrm h$ at mid-latitudes. That is comparable to the bias of even a good gyro, so heading and gyro bias must be estimated jointly, over minutes, by a filter. A formula cannot do it.

6.1 Why an error-state formulation

The filter does not estimate the navigation state. It estimates the error in the navigation state while the mechanization integrates the full nonlinear state at high rate:

$$ \mathbf x_{\text{true}} = \hat{\mathbf x} \oplus \delta\mathbf x $$

Three reasons this is the right choice here. The errors genuinely are small, so linearisation is honest rather than a stretch. The error dynamics are slow and smooth even though attitude is being integrated hundreds of times a second, which permits a large rate separation between mechanization and filter. And the nonlinear attitude integration stays in the mechanization where it belongs, rather than inside a linear filter that would have to approximate it.

6.2 The process model

The state — position, velocity, attitude error, and the sensor parameters. A 15-state core is typical for alignment; scale factors are usually carried as consider parameters here and only promoted to estimated states once flight provides the excitation to observe them:

$$ \delta\mathbf x = \begin{bmatrix} \delta\mathbf r & \delta\mathbf v & \delta\boldsymbol\psi & \mathbf b_g & \mathbf b_a \end{bmatrix}^{\mathsf T} \in \mathbb R^{15} $$

Each state earns its place, and — critically — not all of them are observable while the vehicle is stationary:

StateWhy it is estimated hereObservable on a static pad?
$\delta\mathbf r$ (3) Position error. The pad is surveyed, so truth is known. Strong — directly measured
$\delta\mathbf v$ (3) Velocity error. Truth is zero, so whatever the mechanization accumulates is error. This is the channel through which every other state becomes visible. Strong — directly measured
$\delta\psi_N,\delta\psi_E$ (2)
tilt
Mis-resolved gravity drives $\delta\dot{\mathbf v}$ with a gain of $g$, so tilt appears fast as a growing horizontal velocity error. Highest-leverage state for flight. Strong — amplified by $g$
$\delta\psi_D$ (1)
heading
Heading. The whole reason fine alignment exists. Weak — only via Earth rate, needs minutes
$b_{g,N},b_{g,E}$ (2) Horizontal gyro bias integrates into tilt, which gravity then amplifies into velocity error. Good — correlated with tilt
$b_{g,D}$ (1) Vertical gyro bias causes azimuth drift, which leaks Earth rate into a horizontal axis. Weak — aliases with heading
$b_{a,N},b_{a,E}$ (2) Horizontal accelerometer bias. Carried because it is real and matters in flight. Not separable from tilt — see §6.7
$b_{a,D}$ (1) Vertical accelerometer bias. Observable only because that axis sees the full $1g$. Partly — aliases with gravity-model error

Gated by the gravity-exposed flags from §5.2: a bias on an axis with no gravity component has no excitation, and estimating it injects process noise that leaks into the observable states through their correlations. Full observability discussion on Fine alignment §2.1.

The linearised dynamics $\delta\dot{\mathbf x} = F\,\delta\mathbf x + G\mathbf w$, local-level frame:

$$ F = \begin{bmatrix} 0 & I & 0 & 0 & 0 \\ \partial\mathbf g/\partial\mathbf r & -\big[(2\boldsymbol\omega_{ie}+\boldsymbol\omega_{en})\times\big] & -\big[C^n_b\mathbf f^b\times\big] & 0 & C^n_b \\ 0 & F_{\psi v} & -\big[(\boldsymbol\omega_{ie}+\boldsymbol\omega_{en})\times\big] & -C^n_b & 0 \\ 0 & 0 & 0 & -1/\tau_g\,I & 0 \\ 0 & 0 & 0 & 0 & -1/\tau_a\,I \end{bmatrix} $$

The blocks that carry the physics of alignment:

  • $-[C^n_b\mathbf f^b\times]$ — the tilt-to-velocity coupling. On the pad $C^n_b\mathbf f^b \approx [0,0,-g]^{\mathsf T}$, so a tilt of $\delta\psi$ produces a horizontal acceleration error of $g\,\delta\psi$. The factor of $g$ is why tilt is strongly observable: the measurement sees it amplified nearly tenfold per second of integration.
  • $F_{\psi v}$ — the transport-rate feedback, $\partial\boldsymbol\omega_{en}/\partial\mathbf v$, of order $1/R$. Tiny, but non-zero, and together with the row above it closes the Schuler loop (§6.6).
  • $-C^n_b$ in the attitude row — how gyro bias becomes tilt. This is the correlation that makes gyro bias observable at all: bias integrates into tilt, and tilt is visible through gravity.
  • $C^n_b$ in the velocity row — how accelerometer bias becomes velocity error, on the same row and with the same structure as the tilt term. That structural identity is the aliasing problem of §6.6.
  • $-1/\tau\,I$ on the bias rows — the biases are modelled as first-order Gauss–Markov processes, not constants. A pure random walk has unbounded variance, which is physically wrong for a bias that the datasheet bounds; a constant cannot adapt at all. FOGM has a bounded steady state $\sigma^2(1 - e^{-2\Delta t/\tau})$ and a correlation time you can measure.

Discretisation over the filter interval, with $Q$ from the noise coefficients of §9:

$$ \Phi_k \approx I + F\,\Delta t + \tfrac12 F^2\Delta t^2, \qquad Q_k = \int_0^{\Delta t}\!\Phi(\tau)\,G\,Q_c\,G^{\mathsf T}\Phi(\tau)^{\mathsf T}\,\mathrm d\tau $$

In a two-rate implementation the fast side accumulates the coupling terms that $\Phi$ is built from across the whole filter interval at high-rate fidelity, rather than the filter assuming one coarse linearisation over a whole second. Under dynamics that distinction is significant. §7 shows the mechanism — and note it is a running sum on the fast side, with the matrix products deferred to the filter.

6.3 The same filter as flight software writes it

The local-level form above is the clearest way to see the physics, because gravity points down one axis and Earth rate splits into two. Flight software for a launch vehicle usually mechanizes in ECEF instead: the pad is surveyed in ECEF, GNSS reports in ECEF, and the vehicle leaves the region where a local-level frame is even meaningful. The state grows to 18 because scale factor is carried explicitly:

$$ \delta\mathbf x = \begin{bmatrix} \underbrace{\delta\mathbf r}_{3} & \underbrace{\delta\mathbf v}_{3} & \underbrace{\delta\boldsymbol\theta}_{3} & \underbrace{\mathbf b_a}_{3} & \underbrace{\mathbf b_g}_{3} & \underbrace{\mathbf s_a}_{3} \end{bmatrix}^{\mathsf T} \in \mathbb R^{18} $$
IndexStateUnitsMeaning
0–2$\delta\mathbf r$ft Position error of the vehicle reference point w.r.t. ECEF, resolved in ECEF
3–5$\delta\mathbf v$ft/s Velocity error, same frames
6–8$\delta\boldsymbol\theta$rad Attitude error as a rotation vector, from the error quaternion between truth and estimate
9–11$\mathbf b_a$ft/s²Accelerometer bias error, body axes
12–14$\mathbf b_g$rad/sGyro bias error, body axes
15–17$\mathbf s_a$ppmAccelerometer scale-factor error, body axes

The attitude error being a rotation vector rather than three Euler angles matters: it has no singularity, it composes correctly with the quaternion the mechanization actually propagates, and the covariance block is directly interpretable as an uncertainty ellipsoid about the estimated axes.

With the state in that order, $F$ is:

$$ F = \begin{bmatrix} 0 & I_3 & 0 & 0 & 0 & 0 \\ \partial\dot{\mathbf v}/\partial\mathbf r & -2\big[\boldsymbol\Omega_{ie}\times\big] & -C^e_b\big[\mathbf a_{\text{sensed}}\times\big] & C^e_b & 0 & C^e_b \\ 0 & 0 & -\big[\boldsymbol\Omega_{ib}\times\big] & 0 & I_3 & 0 \\ 0 & 0 & 0 & -\tfrac{1}{\tau_a}I_3 & 0 & 0 \\ 0 & 0 & 0 & 0 & -\tfrac{1}{\tau_g}I_3 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 \end{bmatrix} $$

where the gravity-gradient block, for a spherical primary plus the centrifugal term, is

$$ \frac{\partial\dot{\mathbf v}}{\partial\mathbf r} = \frac{\mu}{\|\mathbf r\|^3}\left[\frac{3\,\mathbf r\mathbf r^{\mathsf T}}{\|\mathbf r\|^2} - I_3\right] + \|\boldsymbol\Omega_{ie}\|^2 I_3 $$

Reading the rows against the local-level version of §6.2:

  • Only $-2[\boldsymbol\Omega_{ie}\times]$ survives in the velocity row, not the transport-rate term. There is no transport rate in ECEF — the frame does not rotate with the vehicle's motion over the surface — so the Coriolis term is the whole story and the $1/R$ feedback of the local-level form is gone. The Schuler loop has not disappeared; it is now implicit in the gravity-gradient block.
  • $C^e_b$ appears twice in the velocity row, once for bias and once for scale factor. Same structure, same row: this is the §6.7 aliasing again, now with a third participant. Bias, scale factor and tilt all push on velocity through the same channel, and on a static pad the measurement cannot tell them apart without help.
  • $I_3$ couples gyro bias into the attitude row. Sign and frame are the whole content of this block — it is what makes gyro bias observable at all, via tilt, via gravity.
  • The scale-factor row is exactly zero. Scale factor is modelled as a constant, not a Gauss–Markov process: it is a manufacturing property that does not wander within a flight. Bias does wander, which is why those rows carry $-1/\tau$.

The process noise is diagonal by block and simply proportional to the propagation interval — velocity random walk on the velocity block, angle random walk on attitude, and a bias-drift term on each bias block:

$$ Q = \operatorname{diag}\big(0,\; Q_{v},\; Q_{\theta},\; Q_{b_a},\; Q_{b_g},\; Q_{s_a}\big)\,\Delta t_{\text{prop}} $$

Two implementation details that are easy to underestimate. A small floor is added to every diagonal of $Q$: an exactly-zero diagonal makes the factorised update singular the moment a state becomes perfectly known. And the propagation interval is measured from the actual timestamps of consecutive filter calls, not assumed to be the nominal period — the scheduler is not guaranteed to be periodic, and assuming it is silently mis-scales $Q$ on every late cycle.

Which IMU states are estimated is a property of the IMU, not of the filter. The 18 states exist in the structure regardless; which ones are estimated versus held as consider parameters is chosen per sensor, from what that sensor's errors actually do to the mission.
IMU classAccel biasGyro biasAccel scale factorReasoning
Navigation-grade RLG
e.g. HG9900 class
considerconsiderestimated Scale-factor error dominates free-inertial position error for this grade, and the axis nearest local vertical is observable under $1g$. The gyro biases are small enough to ignore over the flight and barely observable without attitude aiding. Horizontal accel bias is indistinguishable from tilt (§6.7) and the vertical one is not needed to close the budget.
Tactical / MEMS
lower grade, larger errors
estimated
gravity-exposed axes only
estimatedconsider Here the biases are large enough that leaving them unestimated will not close the requirement. Estimating gyro bias also substantially slows tilt-error growth. Accel bias is enabled only on axes carrying enough projected gravity to be observable.
Intermediate
with azimuth aiding enabled
considerestimatedestimated With an azimuth pseudo-measurement available, all three gyro axes become somewhat observable while stationary, so gyro bias is worth estimating alongside scale factor.

The pattern is consistent: estimate the error that dominates your mission, and only if the pad geometry makes it observable. Estimating an unobservable state does not merely fail to help — it injects process noise that leaks into the states you can observe, through exactly the correlations that made you hope it was observable.

A practical corollary: the gravity-exposure gate from §5.2 is evaluated at the end of coarse alignment, when the axis-to-gravity geometry is first known, and it can override the per-sensor default. A filter reset re-applies the gate rather than restoring the defaults blindly.

6.4 The measurement model

Nothing external is available. Every measurement is a statement about what truth must be because the vehicle is bolted down — which is what converts an unobservable attitude problem into an observable one.

UpdateMeasurement $\mathbf z$$H$$R$ reflects
Zero velocity (ZUPT) $\hat{\mathbf v} - \mathbf 0 = \delta\mathbf v$ $\begin{bmatrix}0 & I & 0 & 0 & 0\end{bmatrix}$ Mechanization noise and any real pad motion — sway, wind, crew activity. Truth itself has no uncertainty.
Fixed position $\hat{\mathbf r} - \mathbf r_{\text{survey}}$ $\begin{bmatrix}I & 0 & 0 & 0 & 0\end{bmatrix}$ Survey error, and the IMU-to-survey-point lever arm.
Static azimuth $\hat\psi - \psi_{\text{ref}}$ from accumulated Earth-rate observation $\begin{bmatrix}0 & 0 & \mathbf e_D^{\mathsf T} & 0 & 0\end{bmatrix}$ A pseudo-measurement — correlated with data the filter already used, so $R$ must be inflated and the update throttled to a long period.

The update itself is standard, but the numerical form is not optional at these condition numbers:

$$ K = P^-H^{\mathsf T}\big(HP^-H^{\mathsf T}+R\big)^{-1}, \qquad P^+ = (I-KH)P^-(I-KH)^{\mathsf T} + KRK^{\mathsf T} $$

The Joseph form shown is symmetric by construction; the short form $P^+=(I-KH)P^-$ loses symmetry and can go indefinite over a long run with a very accurate measurement. Production systems go further and factorise, updating $U$ and $D$ in $P = UDU^{\mathsf T}$, which doubles effective precision and cannot produce a negative variance. See NASA nav-filter best practices.

Why a pseudo-measurement must be throttled. The static-azimuth update is derived from the same gyro data the filter has already processed. Applying it every cycle double-counts that information: the covariance shrinks faster than the real information justifies, the filter becomes over-confident, and it stops listening to the measurements that do carry new information. The result is a filter that reports a small $\sigma_\psi$ and is wrong — the single most dangerous failure mode in alignment, because everything downstream trusts that number.

Written out concretely, in the ECEF formulation of §6.3, the three residuals are almost disappointingly simple — which is the point. Each is a difference against a quantity that is known because the vehicle is stationary:

$$ \mathbf z_{\text{pos}} = \hat{\mathbf r}_{\text{ECEF}} - \mathbf r_{0,\text{ECEF}}, \qquad \mathbf z_{\text{vel}} = \hat{\mathbf v}_{\text{ECEF}} - \mathbf 0 = \hat{\mathbf v}_{\text{ECEF}} $$

with $\mathbf r_{0}$ the position handed over by coarse alignment (§5.3). Both have $H = \begin{bmatrix} I_3 & 0 & \cdots \end{bmatrix}$ in the appropriate columns — a plain identity block picking off the three error states being measured, and nothing else.

The azimuth update is the only one with real structure. It compares the attitude the filter currently believes against the attitude implied by the initial alignment, and keeps only the component about the local vertical:

$$ \delta\mathbf q = \big(\mathbf q^{\text{est}}\big)^{-1} \otimes \mathbf q^{\text{obs}}, \qquad \mathbf p = \begin{bmatrix} 2\,\delta q_x & 0 & 0\end{bmatrix}^{\mathsf T}, \qquad \mathbf z_{\text{az}} = C^{\text{body}}_{\text{ref}}\;\mathbf p $$

The factor of two and the single retained component are both the small-angle approximation: for a small rotation, the vector part of the error quaternion is half the rotation vector, and zeroing the other two components is what makes this an azimuth-only update rather than a full attitude reset. That approximation is only valid because coarse alignment already removed the large errors — which is exactly why the update must also be throttled to a period short enough that azimuth cannot drift out of the small-angle regime between applications.

Updates are applied one scalar at a time. Rather than inverting a $3\times3$ innovation covariance, each of the three components is processed as an independent scalar update in sequence. For diagonal $R$ this is algebraically equivalent, avoids a matrix inverse entirely, and is what makes the factorised $UDU^{\mathsf T}$ form practical. It also means a per-component $\chi^2$ gate can reject one axis without discarding the other two.

One counter-intuitive safeguard: after an azimuth update, the azimuth variance is deliberately inflated back up if it has fallen below a threshold. Driving $\sigma_\psi$ toward zero before launch would be actively harmful — it tells the filter to stop believing any future evidence, and the true heading is still subject to wind loading and structural flex that the model does not capture. Tilt, by contrast, is left alone: it is genuinely well determined by gravity, and forcing uncertainty back into it would inject error into a good estimate.

Note also what is not in the list: there is no attitude measurement, no external heading reference, no magnetometer. Everything above is derived from the vehicle being bolted to a surveyed pad on a rotating Earth. That is the entire information budget available to ground alignment, and it is why heading takes minutes while tilt takes seconds.

6.5 Where the initial covariance comes from

$P_0$ is not a tuning knob picked to make the plots look nice. Each diagonal block is a statement about a physical quantity, with a defensible source:

BlockInitial 1σ fromReasoning
$\delta\mathbf r$Survey accuracy Centimetres to decimetres. Tight, because the pad really is surveyed.
$\delta\mathbf v$Expected pad motion Not zero. The vehicle sways; setting this to zero tells the filter the ZUPT is perfect and invites it to fit sway as sensor error.
$\delta\psi$ tiltCoarse-alignment accuracy $\approx b_a/g$ Directly from the accelerometer bias repeatability, because that is what limits levelling. A 50 µg turn-on repeatability gives ~10 arcsec.
$\delta\psi$ headingCoarse heading quality Degrees if from a short analytic alignment; much tighter if from survey. Must be generous enough to contain the truth or the filter will reject the very measurements that would fix it.
$\mathbf b_g$Turn-on repeatability, from bench data Note: repeatability, not in-run stability. The filter is estimating what the bias is this power cycle, and run-to-run scatter is exactly that uncertainty.
$\mathbf b_a$Turn-on repeatability Same argument. Gated by the gravity-exposed flags from §5.2.
Repeatability, not stability, sets $P_0$. This is the distinction that catches people. A datasheet's headline "bias stability" is usually in-run wander — how much the bias moves during a run, which belongs in $Q$ via the FOGM model. What belongs in $P_0$ is turn-on-to-turn-on repeatability: how different the bias is this power cycle from the calibrated mean. Repeatability is typically much larger, and using the stability number instead produces an over-confident filter that will not correct a real bias because it does not believe one can exist.

Worked through with round numbers for a notional navigation-grade unit at $L=45^\circ$ — illustrative values, not anyone's specification, but the reasoning is what transfers:

BlockBench number→ $P_0$ 1σArithmetic
$\delta\mathbf r$ horiz.Survey to 0.05 m0.05 mTaken directly
$\delta\mathbf v$ horiz.Pad sway ~0.1 m/s RMS0.1 m/s Not zero — the ZUPT is not perfect, and saying it is invites the filter to fit sway as bias
$\delta\psi$ tiltAccel repeatability 50 µg≈ 10 arcsec $b_a/g = 50\times10^{-6}$ rad $= 5\times10^{-5}\times206265$
$\delta\psi$ headingCoarse analytic align≈ 0.5–2° A few seconds of averaging against $\Omega\cos L$ — deliberately generous so the truth is contained
$\mathbf b_g$Turn-on repeatability 0.01 °/h0.01 °/h $= 4.85\times10^{-8}$ rad/s. Sets the heading floor at $b_g/(\Omega\cos L)\approx 200$ arcsec
$\mathbf b_a$Turn-on repeatability 50 µg50 µg Gated to gravity-exposed axes only (§5.2)
The two ways to get $P_0$ wrong, and they fail differently. Too small and the filter rejects the measurements that would have corrected a real error — the innovations look like outliers against an over-tight covariance, so they get edited out and the filter confidently converges to the wrong answer. Too large and it converges slowly and is unnecessarily sensitive to measurement noise early on, but it will still get there. If you must be wrong, be wrong on the large side; and check the normalised innovations either way (FDI decision functions).

Watch the consequence: the filter drives uncertainty down as $1/\sqrt T$ until it reaches a floor set by bias, and no additional dwell helps. Latitude drives the heading floor as $1/\cos L$:

6.6 Consider states — bias is not the only parameter

Some error sources must be accounted for but should not be estimated: gravity-model error, the IMU-to-reference-point lever arm, survey error, and any sensor parameter with no excitation in this phase (scale factors, on a static pad). Estimating them lets the filter fit noise; ignoring them yields an optimistic covariance. The consider filter resolves this by partitioning:

$$ \begin{bmatrix}\delta\dot{\mathbf x}\\ \dot{\mathbf c}\end{bmatrix} = \begin{bmatrix}F & F_c\\ 0 & 0\end{bmatrix} \begin{bmatrix}\delta\mathbf x\\ \mathbf c\end{bmatrix} + \begin{bmatrix}\mathbf w\\ \mathbf 0\end{bmatrix}, \qquad \mathbf z = H\,\delta\mathbf x + H_c\,\mathbf c + \boldsymbol\nu $$

The gain is computed as if $\mathbf c$ did not exist, so no estimate of $\mathbf c$ is ever formed and the filter cannot chase it. But the covariance carries its contribution, with the sensitivity $S = \partial\delta\hat{\mathbf x}/\partial\mathbf c$ propagated alongside $P$:

$$ P^{\text{true}} = P^{\text{filter}} + S\,P_{cc}\,S^{\mathsf T} $$

So the reported uncertainty answers the question that actually matters operationally: how wrong could I be, including the things I chose not to estimate?

Estimate / consider / ignore is a decision per error source, and "ignore" needs a number justifying it. Full treatment on Fine alignment and Linear covariance analysis.

6.7 The one degeneracy you cannot filter away

A horizontal accelerometer bias and a tilt error enter the horizontal specific force identically:

$$ f_{\text{horiz}} = \underbrace{g\,\delta\theta}_{\text{tilt}} + \underbrace{b_a}_{\text{bias}} + \text{noise} $$

One measurement, two unknowns, the same structure up to the constant $g$. The observable quantity is the combination $g\,\delta\theta + b_a$; the direction $(\delta\theta, b_a)\propto(1,-g)$ is in the null space and no dwell time recovers it. The filter converges on whatever mixture makes the static solution correct — which is the right answer to the question the measurement asks, and means that on any static test the deterministic accelerometer terms are cancelled by construction. Separation requires changing the geometry: rotating the unit (§9.2) or flying.

7 · How the two rates actually interlock

Everything above describes what the filter computes. This section is about when — and it is where most of the real engineering lives, because the mechanization runs at sensor rate while the filter runs perhaps a hundred times slower. Two questions have non-obvious answers: what does the filter do about all the motion it never saw, and what happens to a correction that is already stale when it arrives.

The division of labour. The fast loop owns the state; the slow loop owns the uncertainty. The mechanization integrates every increment and never waits for the filter. The filter wakes up occasionally, decides what the accumulated error must have been, and hands back a correction. They communicate through exactly two objects: a buffer of accumulated dynamics going up, and a correction vector coming down.
fast loop — every IMU sample (hundreds of Hz) ┌──────────────────────────────────────────────────────────────────────┐ │ compensate increments (§1.2) │ │ │ │ │ ├──> mechanization: attitude(mid, end), velocity, position │ │ │ └─> published to flight control every cycle │ │ │ │ │ └──> accumulate F-couplings into the current "bucket" │ │ (a running sum, not a matrix product) │ └───────────────────────────────┬──────────────────────────────────────┘ │ bucket buffer (circular, N slots) v slow loop — once per filter cycle (~1 Hz) ┌──────────────────────────────────────────────────────────────────────┐ │ 1. build Φ across [t_prev , t_now] from the buckets │ │ 2. ONE time update: P ← Φ P Φᵀ + Q │ │ 3. measurement updates, in order: position → velocity → azimuth │ │ each applied as independent scalar updates │ │ 4. form correction = −x̂, stamp it with t_now, toggle a flag │ └───────────────────────────────┬──────────────────────────────────────┘ │ correction vector + timestamp v back in the fast loop, on the next sample after the flag flips ┌──────────────────────────────────────────────────────────────────────┐ │ 5. propagate the correction forward: t_now → current time │ │ 6. apply it to the mechanization state; add IMU errors to the │ │ compensation parameters used in step 1 │ └──────────────────────────────────────────────────────────────────────┘

7.1 The bucket buffer — why not one Φ per sample

The filter needs $\Phi$ over the whole interval since its last run. The naive options are both bad: form a full $\Phi$ every IMU sample and multiply them (expensive, and most of the work is thrown away), or linearise once over the entire second (cheap, and wrong under any dynamics, because $F$ depends on the current attitude and specific force which have both changed).

The resolution is to accumulate at sensor rate but only as sums. Each bucket covers a fixed short span and holds the running sum of the coupling terms over the samples that fell in it, together with its own start and end timestamps. No matrix exponential, no matrix product — just addition, at sensor rate. When the bucket fills, the writer advances to the next slot in a circular buffer.

At filter time the sums are normalised by the frame count to recover a mean $F$ per bucket, and $\Phi$ is assembled as a chained product over the buckets the interval spans:

$$ \Phi(t_{\text{prev}}\!\to t_{\text{now}}) \;=\; \Phi_{\text{end}}^{\text{partial}} \cdot \Big(\textstyle\prod_{k}\Phi_k^{\text{full}}\Big) \cdot \Phi_{\text{start}}^{\text{partial}} $$

The two end factors are the important detail. The filter's interval boundaries do not land on bucket boundaries, so the first and last buckets are used with a shortened $\Delta t$ — the sliver from $t_{\text{prev}}$ to the end of its bucket, and from the start of the last bucket to $t_{\text{now}}$. The buffer must therefore be long enough to straddle both propagation times; sizing it is (buckets − 1) × bucket span > longest propagation interval + scheduling latency.

This is the direct answer to "does it propagate start→mid, then mid→end?" No. The half-increments are a mechanization device (§1.3) and the filter never sees them separately — the bucket accumulates their sum. There is exactly one time update per filter cycle, spanning the whole interval since the last one, and all the measurement updates for that cycle happen after it at a single epoch. The sub-interval structure lives entirely in how faithfully $\Phi$ was built, not in extra update steps.

7.2 The correction is stale on arrival — so propagate it

The filter's correction is computed from a state valid at $t_{\text{now}}$, but by the time it reaches the mechanization, more samples have been integrated. Applying it directly would correct the present using a statement about the past. So the same $\Phi$ machinery is reused in the other direction:

$$ \delta\mathbf x_{\text{apply}} = \Phi\big(t_{\text{now}} \to t_{\text{current}}\big)\; \delta\mathbf x_{\text{filter}} $$

then the navigation state is corrected and the sensor-error states are accumulated into the running compensation parameters of §1.2 — accumulated, not replaced, because each filter cycle estimates the remaining error after the previous correction was already being applied.

Three failure modes this structure is designed against. (1) Applying a correction twice — the handoff is guarded by a toggle that the fast loop compares against its own copy, so a correction is consumed exactly once no matter how the two loops interleave. (2) Letting a bad number into the integral — the correction is checked for finiteness before use, because a single NaN reaching the mechanization is unrecoverable. (3) Assuming the slow loop is periodic — the propagation interval is computed from the actual timestamps, so a late cycle scales $\Phi$ and $Q$ correctly instead of silently mis-scaling both.

There is also a deliberate asymmetry worth noticing: the filter is feed-forward only. It never writes directly into the mechanization's integration loop; it publishes a correction that the fast loop chooses to consume at a well-defined point. That is what allows the navigation solution to keep meeting a hard real-time deadline while the filter takes as long as it needs, and what allows the filter to be reset or even to fail without stopping the vehicle from navigating.

The same pattern generalises beyond alignment. In flight the slow loop fuses GNSS instead of pad pseudo-measurements, and measurements arrive with real latency — so the buffered-history approach becomes load-bearing rather than merely efficient: a measurement valid 200 ms ago is applied against the $\Phi$ that spans from that epoch, not against the current state. See Rocket navigation for the flight-mode architecture.

7.3 The complete cycle, written out

The sections above described the architecture in words. What follows is the arithmetic: every index, every symbol, and a worked cycle for both the pad case (pseudo-measurements) and the flight case (GNSS). Read the notation table first — the rest is unambiguous once the indices are pinned down.

7.4 Notation and timebase

There are two clocks and it is essential to keep them apart. The fast index $k$ counts IMU samples; the slow index $m$ counts filter cycles. Between two filter cycles there are $N$ IMU samples:

$$ N \;=\; \frac{T_{\text{sfm}}}{\Delta t} \;=\; f_{\text{imu}}\,T_{\text{sfm}}, \qquad\text{e.g. } f_{\text{imu}} = 300\ \text{Hz},\; T_{\text{sfm}} = 1\ \text{s} \;\Rightarrow\; N = 300 $$
fast loop, index k ... k-1 k k+1 ... k+N-1 k+N ... ────┼───────┼───────┼─────────┼───────┼────► │ │ │ each k spans one interval: t_k ──► t_k + Δt = t_{k+1} with an interior midpoint: t_k ──► t_{k,mid} ──► t_{k+1} │ │ slow loop, index m ────┴─ m ──────────────────────────── ┴─ m+1 ──► t_m t_{m+1} └────────── T_sfm = N·Δt ─────────┘
SymbolMeaningLives inUnits
Indices and time
$k$IMU sample index (fast loop)NM—
$m$Filter cycle index (slow loop)SFM—
$N$IMU samples per filter cycleboth—
$\Delta t$IMU sample interval, $t_{k+1}-t_k$NMs
$T_{\text{sfm}}$Nominal filter period; not assumed — see $\Delta t_m$SFMs
$\Delta t_m$Actual propagation span, $t_m - t_{m-1}$, from timestampsSFMs
$t_{k,\text{mid}}$Midpoint of interval $k$, $t_k + \Delta t/2$NMs
Sensor data at sample $k$
$\Delta\mathbf v_k$Velocity increment, body axes, over interval $k$NMft/s
$\Delta\boldsymbol\theta_{A,k}$Attitude increment, start → midpointNMrad
$\Delta\boldsymbol\theta_{B,k}$Attitude increment, midpoint → endNMrad
Navigation state (the NM's property)
$\mathbf r_k,\;\mathbf v_k$Position, velocity in the reference frameNMft, ft/s
$\mathbf q_k$Attitude quaternion, body → reference, valid at $t_k$NM—
$C_k,\;C_{k,\text{mid}}$Rotation matrices at end and midpoint of interval $k$NM—
Uncertainty (the SFM's property)
$\delta\mathbf x_m$Error-state estimate, 18×1 (§6.3)SFMmixed
$P_m$Error-state covariance, 18×18, held as $U D U^{\mathsf T}$SFMmixed
$\Phi_{m}$Discrete transition over $[t_{m-1},t_m]$, 18×18SFM—
$Q_m$Process noise over the same spanSFMmixed
$H,\;R,\;K$Measurement Jacobian, noise variance, gainSFMmixed
The two shared objects
$\mathcal B$Bucket buffer: accumulated $F$-couplings, NM → SFMsharedmixed
$\mathbf c_m$Control vector: $-\delta\hat{\mathbf x}_m$ plus its timestamp, SFM → NMsharedmixed
$\tau_m$Toggle: flips when $\mathbf c_m$ is freshshared—

Superscript convention: $(-)$ is before a measurement update (a priori), $(+)$ is after (a posteriori). A hat denotes an estimate. Note $\Delta t_m$ rather than $T_{\text{sfm}}$ in every equation below — the slow loop is not guaranteed periodic, and using the nominal period would mis-scale both $\Phi$ and $Q$ on any late cycle.

7.5 The fast loop: state, at every $k$

Step 1 — compensate, using the sensor-error parameters currently latched (these change only when a new correction arrives, §7.7):

$$ \Delta\mathbf v_k^{c} = \Delta\tilde{\mathbf v}_k + \frac{\hat{\mathbf b}_a\,\Delta t}{1-\hat{\mathbf s}_a}, \qquad \Delta\boldsymbol\theta_{A,k}^{c} = \Delta\tilde{\boldsymbol\theta}_{A,k} + \frac{\tfrac12\hat{\mathbf b}_g\,\Delta t}{1-\hat{\mathbf s}_g} \quad\text{(and likewise for }B) $$

Step 2 — attitude to the midpoint, then to the end. $\otimes$ is quaternion product; $\boldsymbol\omega_{\text{ref}}$ is the reference frame's own rotation rate:

$$ \mathbf q_{k,\text{mid}} = \mathbf q\big(\!-\!\Delta\boldsymbol\theta_{A,k}^{c}\big)\otimes\mathbf q_k \otimes\mathbf q\!\left(\tfrac12\boldsymbol\omega_{\text{ref}}\Delta t\right), \qquad \mathbf q_{k+1} = \mathbf q\big(\!-\!\Delta\boldsymbol\theta_{B,k}^{c}\big)\otimes\mathbf q_{k,\text{mid}} \otimes\mathbf q\!\left(\tfrac12\boldsymbol\omega_{\text{ref}}\Delta t\right) $$

Step 3 — velocity and position, rotating by the midpoint attitude (§1.3):

$$ \Delta\mathbf v_k^{\text{ref}} = C_{k,\text{mid}}\,\Delta\mathbf v_k^{c} - 2\,\boldsymbol\omega_{ie}\times\mathbf v_k\,\Delta t + \mathbf g(\mathbf r_k)\,\Delta t $$ $$ \mathbf r_{k+1} = \mathbf r_k + \Big(\mathbf v_k + \tfrac12\Delta\mathbf v_k^{\text{ref}}\Big)\Delta t, \qquad \mathbf v_{k+1} = \mathbf v_k + \Delta\mathbf v_k^{\text{ref}} $$

Step 4 — accumulate into the current bucket. This is the only thing the fast loop does for the filter, and it is addition only. For a bucket $j$ spanning some set of samples $\mathcal K_j$:

$$ \mathcal B_j \mathrel{+}= F\big(\mathbf r_k,\;C_{k,\text{mid}},\;\mathbf a_k\big), \qquad n_j \mathrel{+}= 1, \qquad t^{\text{end}}_j \leftarrow t_{k+1} $$

where $F(\cdot)$ means the handful of 3×3 coupling blocks of §6.3 — gravity gradient, the tilt-to-velocity term, the bias and scale-factor columns — each summed independently. No matrix exponential and no matrix product occurs at rate $f_{\text{imu}}$.

Step 5 — publish. The state goes to flight control every cycle, and a copy $(\mathbf r,\mathbf v,\mathbf q)$ with its timestamp is written into a ring buffer for later interpolation (§7.8).

7.6 The slow loop: uncertainty, once per $m$

Step 1 — build $\Phi_m$ from the buckets. Normalise each bucket's sum by its frame count to recover a mean $F$, form a per-bucket transition, and chain them:

$$ \bar F_j = \frac{1}{n_j}\mathcal B_j, \qquad \Phi_j = I + \bar F_j\,\Delta t_j + \tfrac12\big(\bar F_j\Delta t_j\big)^2 + \dots $$ $$ \Phi_m = \Phi^{\text{part}}_{j_{\text{end}}}\!\left(\delta t_{\text{end}}\right) \cdot \!\!\prod_{j=j_{\text{start}}+1}^{\,j_{\text{end}}-1}\!\! \Phi_j \cdot \Phi^{\text{part}}_{j_{\text{start}}}\!\left(\delta t_{\text{start}}\right) $$

with the two partial spans being exactly the slivers left over because the filter's boundaries do not land on bucket boundaries:

$$ \delta t_{\text{start}} = t^{\text{end}}_{j_{\text{start}}} - t_{m-1}, \qquad \delta t_{\text{end}} = t_m - t^{\text{start}}_{j_{\text{end}}} $$

Step 2 — one time update for the whole span:

$$ \delta\mathbf x^-_m = \Phi_m\,\delta\mathbf x^+_{m-1} \;(=\mathbf 0), \qquad P^-_m = \Phi_m\,P^+_{m-1}\,\Phi_m^{\mathsf T} + Q_m, \qquad Q_m = \operatorname{diag}(\dots)\,\Delta t_m $$

$\delta\mathbf x^+_{m-1}=\mathbf 0$ because the correction was handed to the NM and the error state was reset — the filter's job each cycle is to estimate the error remaining. Only the covariance carries memory across cycles. This is what makes it a feedback (reset) formulation rather than a feed-forward one.

Step 3 — measurement updates, in a fixed order, each component as an independent scalar update:

$$ \text{for each scalar } i:\quad K_i = \frac{P\,\mathbf h_i^{\mathsf T}}{\mathbf h_i P\,\mathbf h_i^{\mathsf T} + R_i}, \qquad \delta\mathbf x \mathrel{+}= K_i\big(z_i - \mathbf h_i\,\delta\mathbf x\big), \qquad P \leftarrow \big(I - K_i\mathbf h_i\big)P $$

On the pad the three updates of §6.4 run in the order position → velocity → azimuth. In flight they are replaced by GNSS (§7.8). In practice $P$ is never formed during the update at all: $U$ and $D$ are updated directly and $P = UDU^{\mathsf T}$ is reconstituted only when needed for output.

Step 4 — emit the control vector. By convention it is the negative of the estimated error, stamped with the epoch it is valid at, and the toggle is flipped:

$$ \mathbf c_m = -\,\delta\hat{\mathbf x}^+_m, \qquad t(\mathbf c_m) = t_m, \qquad \tau_m = \lnot\,\tau_{m-1} $$

7.7 The hand-back: what crosses the boundary

This is the part that is easy to get wrong, because the correction is already stale when it arrives. Suppose the filter finished at $t_m$ but the fast loop is now at sample $k^\star$, with $t_{k^\star} > t_m$. The NM notices $\tau$ changed, and does three things:

(a) Propagate the correction forward — reusing the same buckets, but only over the 9 PVA states, because the sensor-parameter rows of $F$ are zero or nearly so:

$$ \delta\mathbf x^{\text{PVA}}_{\text{apply}} = \Phi^{\text{PVA}}\big(t_m \to t_{k^\star}\big)\;\mathbf c_m^{\text{PVA}}, \qquad \delta\mathbf x^{\text{sensor}}_{\text{apply}} = \mathbf c_m^{\text{sensor}} \;\;\text{(copied, not propagated)} $$

(b) Apply to the navigation state — additive on position and velocity, multiplicative on attitude:

$$ \mathbf r_{k^\star} \mathrel{+}= \delta\mathbf r, \qquad \mathbf v_{k^\star} \mathrel{+}= \delta\mathbf v, \qquad \mathbf q_{k^\star} \leftarrow \mathbf q\big(\!-\!\delta\boldsymbol\theta\big)\otimes\mathbf q_{k^\star} $$

(c) Accumulate the sensor errors into the parameters used by step 1 of the fast loop — accumulate, not overwrite:

$$ \hat{\mathbf b}_a \mathrel{+}= \delta\mathbf b_a,\qquad \hat{\mathbf b}_g \mathrel{+}= \delta\mathbf b_g,\qquad \hat{\mathbf s}_a \mathrel{+}= \delta\mathbf s_a $$
DirectionContentsWhenNotably absent
NM → SFM Current $\mathbf r,\mathbf v,\mathbf q$ and their timestamp; the initial $\mathbf r_0,\mathbf q_0$ from coarse alignment; latitude/longitude; the bucket buffer; the gravity-exposed axis flags Every filter cycle $m$ Nothing about uncertainty — the NM has no $P$
SFM → NM The control vector (18 corrections + epoch + toggle); a few scalar health $\sigma$s for telemetry; a sequence number When the filter completes Not $P$, not $\Phi$, not the gain. The covariance never leaves the SFM
The asymmetry is the design. The NM holds the state and no covariance; the SFM holds the covariance and only a borrowed copy of the state. Neither can corrupt the other's core data structure, and the 18×18 $P$ — the expensive, numerically delicate object — crosses no interface. What crosses is an 18-vector and a timestamp.

7.8 A full cycle, both cases

Case A — on the pad. No external sensor. Take $N=300$, and suppose the filter ran at $t_m$ and the fast loop is at $k^\star$ when the result lands:

k = 0 .. 299 NM: compensate, mechanize, accumulate bucket, publish state (attitude at mid AND end each sample; ΔV rotated by mid) │ │ at t_m: NM hands over { r, v, q, t_m, buckets, r_0, q_0, lat, lon } v m SFM: Φ_m ← chain over buckets spanning [t_{m-1}, t_m] P⁻ = Φ_m P⁺ Φ_mᵀ + Q_m ← ONE time update z_pos = r̂ − r_0 → 3 scalar updates z_vel = v̂ − 0 → 3 scalar updates z_az from δq, vertical component only → 1 update (throttled; azimuth variance re-inflated after) c_m = −δx̂⁺ , stamp t_m , flip toggle │ │ toggle seen at sample k* v k* NM: δx ← Φ^PVA(t_m → t_{k*}) c_m^PVA (sensor part copied) r += δr ; v += δv ; q ← δq ⊗ q b̂_a += δb_a ; b̂_g += δb_g ; ŝ_a += δs_a k*+1 ... fast loop continues with the corrected state and the updated compensation parameters

Case B — in flight, with GNSS. Two things change, and both are about time. The measurement is not valid at $t_m$ — it is valid at the receiver's own epoch $t_{\text{pps}}$, typically some hundreds of milliseconds earlier. And the measurement is of the antenna, not the navigation reference point.

NM (every k) ... writes (r_k, v_k, q_k, t_k) into a PVA ring buffer ... ────────┬───────────┬───────────┬────────► t_{k-2} t_{k-1} t_k ▲ │ t_pps falls BETWEEN samples GNSS receiver pseudoranges + delta-ranges valid at t_pps, arriving late │ v GNSS processing 1. interpolate the ring buffer to t_pps → r(t_pps), v(t_pps), q(t_pps) ← latency handled HERE 2. add the antenna lever arm: r_ant = r(t_pps) + C(t_pps) · l_ant 3. predicted range to each satellite s: ρ̂_s = ‖r_s − r_ant‖ + c·δb 4. residual z_s = ρ_s^meas − ρ̂_s │ v SFM (cycle m) Φ_m , ONE time update as before χ² gate on every residual → reject outliers PR updates (scalar, per satellite) DR updates (scalar, per satellite) c_m = −δx̂⁺ ... identical hand-back to Case A

The pseudorange row of $H$ is the line-of-sight unit vector, which is what makes GNSS observe position directly and attitude not at all:

$$ \mathbf h_s = \begin{bmatrix} -\hat{\mathbf u}_s^{\mathsf T} & \mathbf 0 & -\hat{\mathbf u}_s^{\mathsf T}\big[C\,\mathbf l_{\text{ant}}\times\big] & \cdots & 1 & \cdots \end{bmatrix}, \qquad \hat{\mathbf u}_s = \frac{\mathbf r_s - \mathbf r_{\text{ant}}}{\|\mathbf r_s - \mathbf r_{\text{ant}}\|} $$

The middle block is the only reason a position measurement says anything about attitude at all: rotating the vehicle moves the antenna on its lever arm. With a short lever arm that term is small, which is why GNSS alone aligns attitude slowly and why the manoeuvres matter more than the fixes.

Two different latency problems, two different fixes — do not conflate them. The measurement is old, and is handled by interpolating the state history back to $t_{\text{pps}}$ before forming the residual. The correction is old, and is handled by propagating it forward to $t_{k^\star}$ after the filter runs. One looks backwards through a state buffer; the other looks forwards through $\Phi$. Getting either wrong produces a filter that is stable, confident, and biased.

Counting the epochs in Case B: the measurement is valid at $t_{\text{pps}}$, the filter's covariance is valid at $t_m$, and the correction is applied at $t_{k^\star}$ — three distinct times in one cycle, with $t_{\text{pps}} < t_m < t_{k^\star}$. Every one of them has to be carried explicitly. This is the single biggest source of subtle error in a two-rate navigator, and the reason every structure above carries a timestamp rather than an assumed period.

8 · Schuler oscillation in a strapdown INS

Here is the result that makes inertial error behaviour counter-intuitive: horizontal error does not grow without bound — it oscillates, with a period of 84.4 minutes, and that period is the same for every stationary IMU on Earth regardless of quality.

Why this matters beyond theory. A launch vehicle is designed to fly free inertial — on the IMU alone, with no GNSS — because GNSS is the one sensor an adversary can remove. Jamming is cheap; spoofing is worse, because a spoofed solution looks valid. So the error dynamics below are not an academic curiosity: they are the vehicle's navigation performance in the case it was designed for. Which is why it also matters enormously that the two sources of error behave differently — a tilt error is bounded, while a gyro bias ramps (§8.3), and it is the ramp that a pre-flight drift check exists to catch.

8.1 The causal chain, step by step

Before any algebra, the mechanism in words, because the loop is easy to state and easy to get backwards. Put an IMU on a level surface and give its estimate of "down" a small error $\delta\theta$ — the hardware is perfect, only the estimated orientation is wrong:

① The accelerometers correctly sense the reaction to gravity, magnitude g, pointing "up" in the true vertical. ② The navigator resolves that vector using its OWN estimate of attitude, which is tilted by δθ. It therefore subtracts gravity in slightly the wrong direction. ③ The leftover is a FAKE horizontal acceleration. Nothing is accelerating, but the navigator computes: a_horizontal = g · δθ ④ It integrates that into a FAKE velocity: δv = ∫ g·δθ dt and again into a FAKE position: δp = ∫ δv dt ⑤ Now the subtle part. A strapdown navigator must continuously re-point its local-level frame to keep "down" aimed at the Earth's centre as the vehicle moves over the curve of the planet. The rate at which it must rotate is v / R. Believing it is moving at δv, it rotates its own frame at δv / R … ⑥ … which CHANGES THE TILT. The estimated vertical has been rotated because of an error that the tilt itself created: δθ̇ = − δv / R ⑥ feeds back into ②. The loop is closed, and the sign is negative.
Why negative feedback gives oscillation rather than decay. The feedback is negative but it acts through two integrations — tilt to velocity, then velocity back to tilt. A negative loop with two integrators and no dissipation is precisely a harmonic oscillator, like a mass on a spring. It does not decay because nothing in the loop removes energy: there is no damping term, only the two integrators and the sign flip. That is why the error rings forever at one frequency instead of either growing without bound or settling out.

So the headline result is a good one: a pure tilt error does not run away. It converts back and forth between tilt and velocity error indefinitely, and both stay bounded. That is the Schuler mechanism working in your favour — and it is also why a bias, which drives the loop from outside rather than merely setting its initial condition, behaves completely differently (§8.3).

8.2 The same thing as equations

Take the two coupled error equations from the $F$ matrix of §6.2, keeping only the horizontal channel and one axis for clarity. Step ③ is the first equation; step ⑥ is the second:

$$ \delta\dot v = g\,\delta\theta + \alpha + \nu(t) \qquad\text{(tilt mis-resolves gravity; }\alpha\text{ = accel bias)} $$ $$ \delta\dot\theta = -\frac{\delta v}{R} + \beta + n(t) \qquad\text{(transport rate feeds velocity error back into tilt; }\beta\text{ = gyro bias)} $$

The second equation is the crucial one and it is easy to miss why it exists. The mechanization continuously rotates its idea of "down" to follow the curved Earth as it believes it is moving. If it thinks it is moving at $\delta v$ when it is not, it rotates its local-level frame at $\delta v/R$ — creating a tilt error out of a velocity error. That is a genuine feedback path, and it is negative.

┌──────────────────────── the Schuler loop ─────────────────────────┐ │ │ │ δθ (tilt error) ──── × g ────► δv̇ ──── ∫ ────► δv │ │ ▲ mis-resolved │ │ │ │ gravity │ │ │ └──────── ∫ ◄──── × (−1/R) ◄──────────────────────┘ │ │ δθ̇ transport rate │ │ │ │ gain around the loop = −g/R ⇒ ω_s = √(g/R), T_s = 84.4 min │ └───────────────────────────────────────────────────────────────────┘

Eliminating $\delta v$ with the sensor terms zero gives a simple harmonic oscillator:

$$ \delta\ddot\theta + \frac{g}{R}\,\delta\theta = 0, \qquad \omega_s = \sqrt{\frac{g}{R}} = 1.24\times10^{-3}\ \mathrm{rad/s}, \qquad T_s = \frac{2\pi}{\omega_s} \approx 84.4\ \mathrm{min} $$

With initial conditions $\delta\theta_0$ and $\delta v_0$ the solution is a constant-amplitude oscillation — nothing external drives it, and nothing damps it:

$$ \delta\theta(t) = A\cos(\omega_s t + \varphi), \qquad A = \sqrt{\delta\theta_0^2 + \Big(\frac{\delta v_0}{R\omega_s}\Big)^2}, \qquad \varphi = \operatorname{atan2}\!\Big(\frac{\delta v_0}{R\omega_s},\,\delta\theta_0\Big) $$ $$ \delta v(t) = R\omega_s A\,\sin(\omega_s t + \varphi) $$
The exchange rate. $R\omega_s = \sqrt{gR} \approx 7906\ \mathrm{m/s}$ per radian of tilt, or about 0.038 m/s per arcsecond. This one constant converts an alignment specification directly into a velocity-error budget, and it is fixed by the planet — not by your sensor. An arcsecond of residual tilt is worth 3.8 cm/s of velocity error, always.

8.3 Two different mechanisms: initial condition vs forcing

This is the heart of it, and it is the distinction that makes a drift test interpretable. A tilt error and a sensor bias both produce velocity and position error, but they enter the loop in fundamentally different ways:

A tilt error sets the initial condition

$\delta\theta_0$ is where the oscillator starts. Nothing continues to drive it. The loop converts that initial energy back and forth between tilt and velocity forever, at constant amplitude.

Bounded. The mean over a full cycle is zero. Averaged over many cycles it contributes nothing to a position rate.

A gyro bias forces it continuously

$\beta$ is a persistent input. Every second, the gyro reports rotation that is not happening, so tilt is being manufactured continuously — the loop never gets to give the error back.

Unbounded in position. The oscillation acquires a non-zero mean, and integrating a non-zero mean is a ramp.

The mathematics makes this exact. Differentiate the velocity equation and substitute the tilt equation:

$$ \delta\ddot v + \omega_s^2\,\delta v = g\beta + \dot\alpha $$

Look at what happened to the two biases. The accelerometer bias $\alpha$ appears only as its derivative — and a constant bias has zero derivative, so it vanishes from the forcing entirely. It survives only through the initial condition it imposes, $\delta\dot v|_0 = \alpha$: a kick at $t=0$ and then nothing. The gyro bias $\beta$ survives undifferentiated, as a constant forcing term on the right-hand side. A constant force on a harmonic oscillator does not change its frequency; it shifts its equilibrium. The oscillation now rings about $R\beta$ instead of about zero:

Gyro bias β — offset, and a ramp

$$ \delta v(t) = R\beta\,(1-\cos\omega_s t) $$ $$ \delta p(t) = R\beta\!\left(t - \frac{\sin\omega_s t}{\omega_s}\right) $$

Velocity oscillates about a mean of $R\beta$ — never negative, always biased one way. Position has a genuine ramp of slope $R\beta$. This is the only term that produces a position drift rate.

Accel bias α — bounded

$$ \delta v(t) = \frac{\alpha}{\omega_s}\sin\omega_s t $$ $$ \delta p(t) = \frac{\alpha}{\omega_s^2}(1-\cos\omega_s t) $$

Both oscillate about zero and stay bounded forever. No ramp. A 100 µg bias gives a bounded position error of about 640 m — large, but it does not accumulate.

8.4 Why gyro bias dominates the position-error rate

Now the consequence that matters for a health check. Two metrics are normally computed from a free-inertial run: an RMS velocity error and a radial position error rate — the least-squares slope of radial position error against time. They are sensitive to different things, and the difference follows directly from §8.3:

$$ \text{RPE rate} \;=\; \frac{\displaystyle\int_0^T t\,\delta p(t)\,\mathrm dt}{\displaystyle\int_0^T t^2\,\mathrm dt} $$

A least-squares slope through the origin is a weighted mean, with weight $t$. Feed it a zero-mean oscillation and the positive and negative half-cycles cancel — the answer tends to zero as the window covers more cycles. Feed it a ramp and you recover the ramp slope exactly. So:

Error sourceEnters as$\delta p(t)$ shape RMS velocityRPE rate
Gyro bias βForcing termRamp $R\beta t$ plus oscillation ContributesDominates — the only ramp
Initial tilt $\delta\theta_0$Initial conditionBounded oscillation about 0 Contributes≈ 0 over many cycles
Accel bias αInitial condition onlyBounded oscillation about 0 Contributes≈ 0 — no ramp
Gyro ARWStochastic forcingRandom walk, grows as $\sqrt t$ Contributes stronglyAdds scatter, zero mean
Accel VRWStochastic forcingRandom walk, small NegligibleNegligible
The RPE rate is essentially a gyro-bias meter. Of everything in the error budget, only gyro bias produces a sustained position ramp — so a radial-error-rate metric is close to a direct measurement of gyro bias, with everything else contributing scatter rather than slope. That is exactly what makes it a good health check: gyro bias is also the error that hurts most in free-inertial flight, because it is the one whose position error grows as $t^3$ rather than $t^2$. The metric and the threat are well matched.

For the gyro-bias-only case both metrics are closed-form, and their ratio is a pure number with no sensor parameters in it at all — a useful consistency check on a pair of thresholds:

$$ \mathrm{RMS}[\delta v] = \sqrt{\tfrac32}\,R\beta, \qquad \text{RPE rate} \approx R\beta \qquad\Longrightarrow\qquad \frac{\mathrm{RMS}[\delta v]}{\text{RPE rate}} = \sqrt{\tfrac32} \approx 1.225 $$

Which is why the drift-test page treats two thresholds sitting at exactly this ratio as evidence that one was derived from the other rather than independently.

8.5 The random walks

These need variance propagation, because they have no deterministic waveform:

$$ \sigma_{\delta v}^{\text{ARW}} = R\omega_s\sqrt{Q_n\!\left(\frac{t}{2}-\frac{\sin 2\omega_s t}{4\omega_s}\right)}, \qquad \sigma_{\delta v}^{\text{VRW}} = \sqrt{Q_\nu\!\left(\frac{t}{2}+\frac{\sin 2\omega_s t}{4\omega_s}\right)} $$

Both grow as $\sqrt t$ and the loop restrains both identically — only the sign of the $\sin$ term differs. In practice ARW dominates by orders of magnitude, because it enters multiplied by $R\omega_s \approx 7906$ while VRW enters with unit gain.

Run the loop below. Start with a tilt error and no bias to see the pure undamped oscillation; add an accelerometer bias and watch the oscillation shift onto a new centre; add gyro bias to see the ramp appear. Then try each damping scheme:

8.6 Three natural frequencies, and one of them diverges

ModePeriod / time constantCauseStable?
Schuler84.4 min Tilt ↔ velocity through gravity and transport rate Yes — oscillatory, bounded
Foucault24 h / sin L Earth rotation modulating the Schuler oscillation — it appears as a slow envelope on the 84-minute ringing Yes — a modulation
Verticalτ ≈ 570 s Altitude error → weaker modelled gravity → apparent upward acceleration → larger altitude error No — exponential divergence

The vertical channel deserves its own equation because its sign is the opposite of the horizontal case:

$$ \delta\ddot h = \frac{2g}{R}\,\delta h \quad\Longrightarrow\quad \delta h(t)\sim e^{t/\tau},\qquad \tau=\sqrt{\frac{R}{2g}}\approx570\ \mathrm s $$

Positive feedback, so errors double every few minutes. Every terrestrial INS therefore constrains height externally — a baro-altimeter in flight, the surveyed height during alignment, GNSS when available. "An INS drifts slowly" describes the horizontal channels only.

8.7 Damping it, and why internal damping is a trade

The oscillation is undamped because the loop has no dissipation. Two ways to add some, and they are not equivalent:

Internal damping — trades, does not fix

Feed the system's own velocity error back into the loop. The oscillation decays, but the system now settles onto a steady-state offset instead of oscillating about the right answer. You have exchanged a bounded oscillation for a bounded bias — sometimes desirable (a ship's INS wants a quiet output), but no information was added.

External velocity aiding — the only real fix

An independent velocity reference — GNSS, Doppler, odometry, or a zero-velocity constraint while stationary — supplies new information, which both damps the loop and removes the error. This is why the ZUPT of §6.4 works so well and why GNSS-INS is the workhorse in flight.

A fuller treatment, including the full menu of suppression methods and the Foucault modulation, is on Schuler oscillation in strapdown INS.

9 · Calibration — where the numbers come from

Every coefficient in §1.2 and every entry in $P_0$ and $Q$ traces to a measurement made on a bench. This is the part of inertial navigation that is least glamorous and most decisive.

9.1 Allan variance — separating the noise processes

Take a long static log, split it into bins of length $\tau$, and compute the variance of the differences of adjacent bin averages:

$$ \sigma_A^2(\tau) = \frac{1}{2(N-1)}\sum_{k=1}^{N-1}\big(\bar y_{k+1}-\bar y_k\big)^2, \qquad \bar y_k = \frac{1}{\tau}\int_{t_k}^{t_k+\tau}\! y(t)\,\mathrm dt $$

The differencing is what makes this useful: it is insensitive to a constant offset, so what remains is purely the noise structure. Each physical process contributes a known power of $\tau$, so plotting $\sigma_A(\tau)$ on log-log axes turns identification into reading slopes:

SlopeProcessRead asGoes into
$-1$Quantization noise$\sqrt3\,Q/\tau$Usually negligible in the filter
$-\tfrac12$White noise — ARW (gyro), VRW (accel) $N/\sqrt\tau$; read $N$ at $\tau=1$ s$Q$ — the dominant process-noise term
$0$Bias instability (flicker floor) The minimum of the curve, $\times\sqrt{2\ln2/\pi}$ FOGM steady-state σ, and the best achievable averaging
$+\tfrac12$Rate random walk$K\sqrt{\tau/3}$$Q$ for the bias state
$+1$Rate ramp$R\,\tau/\sqrt2$Usually a thermal trend to be removed

The total is the root-sum-square of the terms, which gives the curve its characteristic bathtub shape. The minimum is the single most quoted number — it is the bias instability, and the $\tau$ at which it occurs is the longest averaging time that still helps. Averaging beyond it makes things worse, because bias wander has started to dominate the white noise you were trying to average down.

What the Allan plot does and does not give you. It characterises noise, and it is the right tool for $Q$. It says nothing about turn-on repeatability, because a single continuous log never power cycles — and repeatability is what sets $P_0$ (§6.5). Those need a different test entirely: many power cycles, each with a fresh bias estimate, and the scatter of those is the number you want.

9.2 Rate-table testing — scale factor and misalignment

A precision turntable applies a known angular rate, which is the only way to observe a parameter that multiplies the input. Mount the unit, command a rate, and regress output against input:

$$ \tilde\omega_i = (1+S_i)\,\omega^{\text{cmd}}_i + b_i + \sum_{j\ne i} M_{ij}\,\omega^{\text{cmd}}_j + \eta $$

The procedure, and the reason for each step:

  1. Multiple rate magnitudes. Scale factor is the slope and bias the intercept of output-vs-input. A single rate gives one point, and infinitely many (slope, intercept) pairs pass through one point — the two are unidentifiable. This is the single most important thing about the test.
  2. Both directions. Reversing the rate flips the sign of the scale-factor contribution while the bias stays put, which separates them robustly and also exposes scale-factor asymmetry — a different gain for positive and negative rates, common in some technologies.
  3. Rates spanning the operational range. Scale factor is not perfectly linear; testing only at low rate tells you nothing about behaviour at the rates the vehicle will actually see.
  4. Each axis on the table axis in turn. Commanding rate about one axis while observing the others gives the off-diagonal misalignment terms $M_{ij}$ directly.
  5. Least squares over all points. With $n$ rates in 2 directions you have $2n$ equations for a handful of parameters — solve in the least-squares sense, and the residual is your confidence in the result.

Drag "rates used" down to 1 in the sim below and watch the estimate become meaningless — that is the degeneracy, and it is why an acceptance procedure specifies a rate schedule:

9.3 Multi-position (tumble) testing — accelerometer bias and scale

Gravity is a free, extremely stable, precisely known 1 g input. Rotating the unit through known orientations projects it onto the sensing axes in known ways:

$$ \tilde{\mathbf f}^b_{(m)} = (I+S_a+M_a)\,C^b_{n,(m)}\,\mathbf g^n + \mathbf b_a + \boldsymbol\eta $$

Stack the positions and solve. The classic six-position test puts each axis alternately up and down, which is elegant because the sum and difference of an up/down pair separate the two parameters immediately:

$$ b_a = \frac{\tilde f_{\text{up}} + \tilde f_{\text{down}}}{2}, \qquad 1 + S_a = \frac{\tilde f_{\text{up}} - \tilde f_{\text{down}}}{2g} $$

The bias is the average of the two readings and the scale factor comes from their difference. More positions (12, 24) add redundancy and expose misalignment and non-linearity. Note that this works for exactly the reason a static alignment cannot separate bias from tilt (§6.7): rotating the sensor modulates a sensor-fixed error while the reference stays put. Geometry is what breaks the degeneracy.

9.4 Repeatability, and thermal

Two more tests that do not fit the above pattern but matter more than either:

  • Turn-on repeatability. Power cycle many times, re-estimating the bias each time. The scatter of those estimates is what $P_0$ must contain (§6.5). A unit can have superb in-run stability and poor repeatability, and for a system that aligns fresh before every mission, repeatability is what you actually care about.
  • Thermal calibration. Map bias and scale factor against temperature, then compensate in real time from the unit's own temperature sensors. Biases are strongly temperature-dependent, and a thermal gradient across the unit is a different and harder problem than a uniform temperature — which is why warm-up transients get their own specification.

9.5 From test data to filter parameters

Filter quantityComes fromTest
$Q$ white-noise termsARW, VRW coefficientsAllan variance, slope −½ at τ=1 s
$Q$ bias-state termsFOGM σ and τAllan variance minimum and its location
$P_0$ bias blocksTurn-on repeatabilityMany-power-cycle test
$P_0$ tilt block$b_a/g$ from accel repeatabilityTumble + repeatability
Compensation $\hat S$, $\hat M$Scale factor, misalignmentRate table, tumble
Consider-parameter $P_{cc}$Un-estimated residualsWhatever the above leaves over

The bench-procedure view of all of this, with the test-design reasoning, is on IMU acceptance testing.

10 · Grades of IMU

Representative error levels by grade — order-of-magnitude, vendor-typical:

GradeGyro biasGyro ARWAccel biasUnaided driftSelf-align?Typical technology
Consumer MEMS10–100 °/h0.5–2 °/√h1–50 mg km in seconds–minutesNoVibratory MEMS
Industrial MEMS1–10 °/h0.1–0.3 °/√h0.5–3 mg tens–hundreds m/minNoHigh-grade MEMS
Tactical0.1–1 °/h0.02–0.1 °/√h0.1–1 mg ~1 nmi/h aidedMarginalFOG, high-end MEMS
Navigation0.001–0.01 °/h0.002–0.02 °/√h10–50 µg ~1 nmi/h unaidedYes, minutesRLG, FOG, HRG
Strategic< 0.001 °/h< 0.001 °/√h< 1–10 µg metres/hYes, preciseRLG, ESG, HRG

Conversions: 1 °/h = 4.848×10⁻⁶ rad/s; 1 mg = 9.807×10⁻³ m/s²; 1 nmi = 1852 m. Earth rate is 15.041 °/h — a gyro must resolve well below that to gyrocompass at all, which is the practical definition of "navigation grade." Part-level detail is on Hardware — IMUs.

The gyrocompassing limit is worth seeing directly. Push latitude toward the pole and watch achievable heading accuracy collapse no matter how long you average, because the north-pointing signal is only $\Omega\cos L$:

10.1 A worked example: the HG9900 class

The Honeywell HG9900 is a useful concrete anchor for the "navigation grade" row above, because it is the unit actually flown on launch vehicles and its broad characteristics are published. It pairs ring-laser gyros with precision quartz resonant accelerometers in a single sealed enclosure with its own processor, and it is the sensor the whole of §5–§8 is implicitly written about.

PropertyRepresentative figureConsequence for the software above
Gyro technologyRing-laser (RLG) No moving parts, no g-sensitivity worth modelling, and output is naturally an angle increment — the interferometer counts fringes, which is $\Delta\theta$ (§1.1).
Accelerometer technologyQuartz resonant / vibrating beam Frequency output integrated to a velocity increment. Excellent scale-factor stability, which is why scale factor is worth carrying as an estimated state (§6.3).
Gyro bias stability≈ 0.003 °/h Five thousand times smaller than Earth rate, so gyrocompassing closes comfortably — this is what makes self-alignment in minutes possible at all.
Angle random walk≈ 0.002 °/√h Sets the noise floor that fine alignment asymptotes to (§6.5), and the $Q_\theta$ block.
Accel bias stability≈ 25 µg Small enough that leaving accel bias as a consider state still closes the flyout budget.
Accel scale factortens of ppm The dominant free-inertial position error for this grade — hence estimated, not considered.
Output ratea few hundred Hz Several IMU messages per control cycle, all of which must be integrated (§1.3).
InterfaceSerial / bus, with built-in test Supplies the BIT word that drives the health logic below.

Figures are order-of-magnitude, from published product literature; exact numbers are configuration- and variant-dependent and belong in the vendor's specification, not a blog. What matters here is the ratio between them, because that is what selects the estimated states.

This is why the HG9900 row in §6.3 looks the way it does. Compare its two dominant terms. A 25 µg accel bias contributes a horizontal acceleration error of $2.5\times10^{-4}\,\mathrm{m/s^2}$ — but on a static pad it is indistinguishable from tilt (§6.7), so estimating it buys nothing. A few tens of ppm of scale-factor error, by contrast, acts on the full thrust acceleration during ascent — tens of m/s², not $g$ — so it produces an error that grows with the integral of a much larger signal, and the vertical axis is observable on the pad under $1g$. The sensor's error ratios, not filter aesthetics, decide that scale factor is estimated and bias is not.

10.1.1 Health monitoring, and why it belongs in the sensor path

A navigation-grade unit reports its own health, and the software layer that consumes increments is also the natural place to police it. Four independent checks, each with a distinct failure signature:

CheckDetectsResponse
Built-in test word Internal fault the sensor itself has diagnosed — laser dropout, temperature excursion, power rail Latch invalid after $N$ consecutive failures; a single transient does not condemn the unit
Sequence number gap Dropped message on the bus Reuse the previous sample, count the gap, latch stale past a threshold
Non-finite data NaN or Inf anywhere in the message Reject the sample outright — a single NaN integrated once poisons the state permanently
Monotonic timestamp Clock rollback, replayed or reordered packet Reject; a negative $\Delta t$ integrates the solution backwards
Detect and report, but decide elsewhere. The sensor layer's job is to say what it observed, not to take vehicle-level action. Whether an unhealthy IMU means switch strings, coast on the last good state, or abort is a decision that needs context this layer does not have. Conflating the two is how a recoverable single-message dropout turns into a mode transition.

Note the asymmetry between the checks: the count-based ones tolerate transients because bus glitches are expected and harmless if brief, while the non-finite and timestamp checks reject immediately. The difference is recoverability. A missed message costs you one interval of information. A NaN integrated into the attitude quaternion, or a negative $\Delta t$, corrupts the state in a way no amount of subsequent good data will repair — the strapdown has no mechanism to un-integrate.

11 · Bounding the drift

An IMU is a relative sensor: excellent over seconds, unbounded over hours. It is therefore always fused with an absolute, lower-rate reference through an estimator — the IMU propagates the state at high rate, and GNSS, a star tracker, a zero-velocity constraint or terrain matching bounds the error. In GPS-denied conditions this is the backbone of the alt-PNT cascade; on spacecraft the gyro plus star-tracker pair is the MEKF; on the pad it is the zero-velocity update of §6.3.

12 · Further reading

  • P. G. Savage, Strapdown Analytics, 2nd ed., Strapdown Associates, 2007 — the definitive reference for mechanization, coning/sculling algorithms and error propagation.
  • P. D. Groves, Principles of GNSS, Inertial, and Multisensor Integrated Navigation Systems, 2nd ed., Artech House, 2013 — Ch. 4–5 (mechanization, error dynamics), Ch. 15 (alignment).
  • D. H. Titterton and J. L. Weston, Strapdown Inertial Navigation Technology, 2nd ed., IET, 2004 — §10 alignment, §12 testing and calibration.
  • J. A. Farrell, Aided Navigation: GPS with High Rate Sensors, McGraw-Hill, 2008 — error-state filter design and observability.
  • IEEE Std 952-2020 — inertial sensor error model terminology and the Allan-variance form used here. IEEE Std 647-2006 (gyro test procedures), IEEE Std 1293-2018 (accelerometer test procedures).
  • NASA/TP-2018-219822, Navigation Filter Best Practices — Joseph form, FOGM bias models, consider filters, underweighting. Summarised on this site.
  • M. S. Grewal, L. R. Weill, A. P. Andrews, Global Positioning Systems, Inertial Navigation and Integration, 3rd ed., Wiley, 2013 — worked alignment and calibration examples.

Related pages: Attitude & rotations · Fine alignment · Ground-align drift test · Schuler oscillation · IMU acceptance testing · Filtering · EKF · GNSS · Hardware — IMUs.