# Allan variance, or how long a gyroscope can be trusted > We left five phones logging their gyroscopes overnight and used Allan deviation to work out how many minutes of heading Wheria can borrow between compass corrections. 2026-08-25 · 21 min read · physics, wheria, math · by ntan (ntan) for uranashel Canonical HTML: https://uranashel.com/blog/allan-variance-gyro.html --- Between two trusted compass fixes, Wheria's heading runs on gyroscope alone. Indoors the magnetometer gets vetoed a lot: a steel column 2 m away can rotate apparent north by 40–90°, which is why [the compass post](https://uranashel.com/blog/compass-calibration-story.html) treats that sensor as guilty until proven innocent. While the veto lasts, the filter integrates angular rate and nothing else. So the load-bearing question for the whole heading pipeline is how many seconds that integral stays inside the error the UI can absorb. Inertial navigation people answer that with Allan variance, a tool that needs nothing more exotic than a phone lying still for a long time. In July we gave it 5 phones and a week of nights: stationary gyro logs, 7–9 h each, on a granite tile in the studio. What follows is the bench notes — the three noise types, the computation, the log-log plot, our numbers, and which knobs they pin down in [Wheria](https://uranashel.com/apps/wheria.html)'s filter. ## Three noises live in a resting gyro A phone gyro on a table should read zero. It actually reads Earth's rotation, 15 deg/h, plus everything the silicon adds on top. The best of our five phones has a bias floor of 16 deg/h, so Earth loses by a nose and phone gyrocompassing remains cancelled. The first contribution is white noise on the rate signal: thermal and electronic noise in the MEMS sense loop, uncorrelated from sample to sample. Integrate white rate noise and the angle performs a random walk whose standard deviation grows as `√t`. Datasheets call it angle random walk, N, quoted in deg/√h. It dominates over seconds. The second is uglier. The zero offset itself wanders, slowly, with 1/f flicker character. Calibrate the bias perfectly at 21:00 and by 21:05 it is a slightly different bias. This is bias instability, B, quoted in deg/h, and no amount of averaging removes it, because averaging longer just averages a moving target. It dominates over minutes. A garage walk is minutes. Third, rate random walk: a random walk on the rate itself, K, quoted in deg/h per √h. Under it the angle error grows as `t^1.5`. On consumer parts a clean K is rare, because temperature ramps print the same signature on the curve. ## The Allan deviation recipe The computation dates to 1966 and is pleasantly mechanical. Chop the log into adjacent clusters of duration τ. Average the rate inside each cluster to get `Ω̄_1, Ω̄_2, …` Then measure how much successive averages disagree: `σ²(τ) = ½·⟨(Ω̄_{k+1} − Ω̄_k)²⟩`. Repeat for many values of τ and plot σ(τ) against τ on log-log axes. The factor ½ is what makes the slopes meaningful. For white rate noise with variance σ_w² per sample, a cluster mean over m samples has variance σ_w²/m. Two adjacent means are independent, so their difference carries 2σ_w²/m, and halving recovers σ_w²/m. With m = τ·f_s that gives `σ(τ) = N/√τ`, a straight line of slope −1/2 on the plot. Each noise type plays the same game with a different exponent, which is the entire reason the curve is readable. Cluster times run log-spaced from 2 samples up to about a tenth of the record, 60 points or so. The tenth matters. At τ = 1,000 s an 8 h log holds only 28 non-overlapping clusters, and the relative error of an Allan point is roughly `1/√K` for K cluster pairs, so the right-hand end of every published Allan curve is mush. Overlapping clusters, stride 1 sample instead of stride m, squeeze extra confidence out of the same record for nothing but CPU. This is also why the logs run overnight. A 10 min log supports τ up to about 60 s and says nothing about the floor. Two platform traps. Log the raw stream (on Android the uncalibrated gyro sensor, on iOS the plain gyro data rather than the fused device-motion rate), because runtime bias compensation quietly edits the signal mid-record and stamps steps into the curve. And resample to a uniform grid first; both OSes deliver jittered timestamps, and the fixed 100 Hz resampling stage from [step detection](https://uranashel.com/blog/step-detection-imu.html) serves here unchanged. Charging during a run is banned. A warming battery is a bias ramp. ## The computation in 16 lines With prefix sums every cluster mean costs O(1), and the full curve computes in seconds on a laptop, overlapped version included: ``` // w[0..N-1]: gyro rate samples (deg/s), fs: sample rate (Hz) S[0] = 0 for i in 0..N-1: S[i+1] = S[i] + w[i] // prefix sums for tau in logspace(2/fs, (N/fs)/10, 60): m = round(tau * fs) // samples per cluster if m < 2 or 2*m >= N: continue acc = 0; K = 0 for k in 0 .. N - 2*m: // overlapping clusters a = (S[k+m] - S[k]) / m // mean of cluster at k b = (S[k+2*m] - S[k+m]) / m // mean of the next one acc = acc + (b - a)² K = K + 1 adev[tau] = sqrt(acc / (2·K)) // Allan deviation at tau ``` Feed it deg/s. The point at τ = 1 s, multiplied by 60, is N in deg/√h. The flat minimum, times 3,600 and divided by 0.664, is B in deg/h; the 0.664 is `√(2·ln2/π)` from the flicker-noise derivation, one of those constants you verify once and then trust forever. ## Reading the slopes The finished plot reads by pattern. Left side, slope −1/2: the white-noise region, ARW, with N read directly at τ = 1 s. Middle: the curve flattens where flicker takes over, and the minimum marks bias instability. Right side, slope +1/2: rate random walk, following `σ(τ) = K·√(τ/3)`. A slope of −1 at the very shortest τ is quantization noise from the sensor's output resolution and is harmless. Bumps are the informative part. A periodic disturbance with period T0 prints an oscillation on the curve whose first hump lands near τ ≈ T0/2. On our first night we left the aircon running, and all three phones logged that evening grew a hump at τ ≈ 500 s; the compressor cycle measured about 17 min. The gyros were faithfully reporting the building. Aircon off every night after. ## One week, five phones Bench setup, for the record: 5 phones (two iPhones, one Pixel-class Android, two mid-range Androids, model years 2019–2025), each logged 7–9 h overnight at 100 Hz on a 30 mm granite tile — airplane mode, screen off, unplugged, studio door locked. 8 h at 100 Hz is 2.9 million samples per axis, about 35 MB of float32 per phone. Z-axis numbers, since yaw is what heading uses. ARW ran from 0.15 deg/√h on the newest flagship to 0.9 deg/√h on the 2019 mid-ranger, with the other three between 0.2 and 0.6. Bias instability came out at 16, 19, 24, 31 and 44 deg/h respectively, floors sitting between τ = 80 s and τ = 400 s. Only the two oldest units showed a credible +1/2 tail before the error bars went to mush, and on one of them the tail shrank when we re-ran the log inside a foam box; part of it was thermal, since the studio cools by about 2 °C overnight. Everything lands in the class that MEMS datasheets admit to when read carefully. Now translate to angle. N = 0.3 deg/√h is 0.005 deg/√s; after 60 s of open-loop integration that is 0.04° of heading error, and after a 4 min garage walk, 0.08°. White noise is irrelevant at our timescales. The floor is what bites. B = 20 deg/h is 0.33 deg/min, and it is a floor in the strict sense: the drift that survives any bias calibration, however careful, because flicker is precisely the part left over once averaging stops helping. ## What Wheria does with the numbers The [Kalman post](https://uranashel.com/blog/kalman-filter-parking.html) describes the state vector: position x and y, heading θ, and a slowly drifting gyro bias term. The Allan curve is the datasheet for that fourth state. Its process noise is set from the measured floor and tail, so the bias covariance grows at roughly the instability rate; after about 5 min without a correction the filter has honestly forgotten the old bias, back down to the floor of 0.3–0.7 deg/min depending on the phone. Corrections come from moments when the compass is trusted and from near-still stretches, the same low-turn-rate windows the compass post uses for magnetometer calibration. The curve also sets the gyro-only clock. Stationary, our mid-pack phones drift 0.3–0.7 deg/min once the bias state has converged. Walking is worse, for reasons in the next section, and the 1–3 deg/min quoted in [the compass post](https://uranashel.com/blog/compass-calibration-story.html) is the honest in-pocket figure. Find mode drops from arrow to distance-first guidance once heading standard deviation crosses 25°. At 3 deg/min that limit is 8 min away; the filter flips much earlier, because its heading process noise (about 2° per step under compass trust, nearer 8° in steel-heavy zones) is deliberately fatter than the measured drift. The covariance is an admission of what the filter cannot verify, not a measurement of what the gyro just did. The practical outcome: between magnetometer corrections a mid-2020s phone holds heading to 1–2° across the 30–90 s the filter typically has to bridge in a steel-heavy aisle, and the oldest phone in our test drawer holds 3–5°. Standing 20 m from the car, a 5° heading error displaces the arrow's target about 1.7 m sideways, still narrower than one parking space. That margin is why Wheria survives minutes of vetoed compass without drawing a confident lie. ## Where the stationary curve stops being honest An overnight log on a stone tile is the gyro on its best behavior. A trouser pocket degrades it three ways. Linear-acceleration sensitivity first: consumer gyros register 0.01–0.1 deg/s per g of applied acceleration, and walking applies a ±0.4 g bounce at roughly 2 Hz, part of which rectifies into an apparent bias. Then temperature: leaving 22 °C garage air for a 32 °C pocket drives the bias tempco through a ramp the Allan curve never saw. And the phone rotates relative to the walker, fabric and soft tissue conspiring, motion that must never be booked as heading. The stationary curve is a physical lower bound. Every tuning number in Wheria sits above it on purpose. To run the experiment yourself, the internal Phyzix build we are testing adds an overnight Allan-log mode to [the lab screens](https://uranashel.com/apps/phyzix.html), curve and fitted slopes included; the resampler and the logging plumbing already existed from step counting. Until that ships, the 16 lines above plus one boring night are genuinely all it takes. --- uranashel · [Home](https://uranashel.com/) · [Apps](https://uranashel.com/apps.html) · [Lab](https://uranashel.com/lab.html) · [About](https://uranashel.com/about.html) · [Blog](https://uranashel.com/blog/) · [Developers](https://uranashel.com/developers/) · [API docs](https://uranashel.com/docs/) · [Privacy](https://uranashel.com/privacy.html) Machine-readable: [llms.txt](https://uranashel.com/llms.txt) · [sitemap.xml](https://uranashel.com/sitemap.xml) · [openapi.json](https://uranashel.com/openapi.json) · [API](https://uranashel.com/api/v1/)