From b1400a2e76ac39324e4a588b651f90f67d67fe7e Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 31 Jul 2026 21:35:32 +0000 Subject: [PATCH] AoA convention: default boresight to the screen-normal (Z_NEG) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Field captures (lens aimed straight at Aether 1) show the Pixel 8 Pro's UWB AoA boresight is the screen-normal, body −Z: aiming −Z at the anchor reads az≈0, el≈0 — the definition of boresight. The engine still defaulted to the historical X_POS assumption, which decodes a boresight target (the very thing the user is aiming at) to body +X — ~90° off — anchoring self-yaw ~110° wrong even though the AoA was perfectly centred. That persistent offset, not drift, is what makes the aimed anchor sit out of frame; the multi-anchor yaw fusion (B+C) faithfully smooths the SAME mis-decoded bearings and so cannot correct it. Set the default to Z_NEG. With the correct boresight the AIMED target decodes to −Z regardless of the azimuth/elevation signs, so "point the lens at X" anchors the heading correctly; the signs + azimuthOffsetDeg only tune the off-boresight anchors and are best dialled in by the on-device auto-calibrator, which is now well-conditioned because the lens aim keeps the cal target at boresight (the old +Y wand pointed it into the noisy ±90° FoV rail). Verified against mofe_aim_localizer. Tests set the convention explicitly, so the default change affects no test. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01WppuiKZt4CuQxX4N7k6SVR --- .../kotlin/com/aether/mofe/engine/AoaCalibration.kt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/common/src/commonMain/kotlin/com/aether/mofe/engine/AoaCalibration.kt b/common/src/commonMain/kotlin/com/aether/mofe/engine/AoaCalibration.kt index c9ac246..da7d64f 100644 --- a/common/src/commonMain/kotlin/com/aether/mofe/engine/AoaCalibration.kt +++ b/common/src/commonMain/kotlin/com/aether/mofe/engine/AoaCalibration.kt @@ -24,9 +24,16 @@ enum class AoaBoresight { X_POS, X_NEG, Y_POS, Y_NEG, Z_POS, Z_NEG } * Fields are @Volatile so the HUD (UI thread) can retune while the engine thread reads. */ object AoaCalibration { - /** Body axis the boresight points along. Default X_POS = the engine's historical - * assumption (az=el=0 ⇒ +X); most phones actually key AoA to the screen-normal. */ - @Volatile var boresight: AoaBoresight = AoaBoresight.X_POS + /** Body axis the boresight (az=el=0) points along. Default Z_NEG = the screen-normal + * out the back of the device — the phone's UWB antenna boresight, and now the aim + * ("lens") axis. FIELD-CONFIRMED on the Pixel 8 Pro: aiming −Z straight at an anchor + * reads az≈0, el≈0 (the definition of boresight). The old X_POS default decoded a + * boresight target to body +X, ~90° off, which anchored yaw ~110° wrong even though the + * AoA was perfectly centred (mofe_aim_localizer). With Z_NEG the AIMED target decodes to + * −Z regardless of the az/el signs, so "point −Z at X" anchors correctly; the signs + + * [azimuthOffsetDeg] only tune the OFF-boresight anchors and are best set by the + * on-device auto-cal (now well-conditioned because the aim keeps the target at boresight). */ + @Volatile var boresight: AoaBoresight = AoaBoresight.Z_NEG /** Added to the measured azimuth (deg) — the fine dial that rotates the anchored * heading in the boresight plane; nudge until headErr → 0. Normalized to (−180,180]. */ @Volatile var azimuthOffsetDeg: Double = 0.0 -- 2.43.0