Skip to content

Accept fixed-ambiguity GNSS heading and align the mount to boat-forward - #72

Merged
georgesleen merged 2 commits into
mainfrom
gnss-heading-alignment
Aug 16, 2026
Merged

Accept fixed-ambiguity GNSS heading and align the mount to boat-forward#72
georgesleen merged 2 commits into
mainfrom
gnss-heading-alignment

Conversation

@georgesleen

Copy link
Copy Markdown
Collaborator

Dual-antenna GNSS heading works again on the team's mosaic-go-H, so the firmware can stop running mag-only. Three things stood in the way.

The firmware discarded every GNSS heading

bring_up asked the receiver for AttitudeResolution::Float, a workaround from when the aux antenna looked dead. Under Float the receiver reports AttEuler mode 1, and gnss_bridge accepts only the fixed-ambiguity modes 2 and 4 by design, so every sample arrived valid=false. Measured on hardware: 596 of 596 samples rejected while the receiver had a healthy solution (AuxAntPositions error=0, 11 to 12 satellites). Switched to Fixed.

The mount was never calibrated

Both offsets are now measured on the boat, level and mounted:

  • imu_mount: the BNO085's -X axis points at the bow, hence the 180 yaw. Roll and pitch are the board's residual tilt in its bracket, solved so the filter reports 0.000 / 0.000 in this attitude.
  • gnss.baseline_offset_deg stays 0: the antennas sit on the centerline with the aux one forward, so the main to aux baseline already points at the bow. Now documented as verified rather than left at a default.
  • mti_yaw.offset_seed_deg: -131.7 to -64.7, read off the filter's own learned offset once it converged against fixed-ambiguity headings, which beats a hand compass as a reference.

p0_offset_deg2 stays at 4.0. Loosening it was considered, since GNSS now owns absolute heading, but that would give up the boot anchor and the outage hold for no gain: the seed is a real measurement again, not a provisional one.

A stale persisted offset shadowed the seed

The flash blob held -101.401 and re-latched every boot, so no tuning.toml change could take effect. offset_store::VERSION goes 1 to 2, which rejects it on load. A persisted offset is only valid for the frame it was learned in, so a mount or baseline change has to invalidate it; the comment now says so.

Verified on hardware

Pico 2, stationary, before and after:

before after
GNSS samples accepted 0 / 596 (mode=1) 449 / 449 (mode=2)
fused vs GNSS heading 135 deg apart agree to 0.002 deg
heading sigma 2.51 deg 0.355 deg
roll / pitch, boat level -1.18 / -5.62 -0.086 / +0.217
mag offset frozen at -101.401 -66.0, tracking GNSS
mag accuracy 2 3

Gate rejects 0, |quat| 1.00000, gyro biases under 0.005 deg/s throughout.

Test changes

Recalibrating broke three tests, which is itself the finding: they snapshotted physical measurements.

  • test_default_tuning_enables_mti_yaw asserted the exact MtiYawConfig, and test_load_mount_reads_gnss_section asserted baseline_offset_deg == 0.0. Both now assert the property that matters (the mag measurement is on, seeded and pinned; the values are in range) and leave exact parsing to the fixture-based loader tests next to them.
  • test_default_tuning_has_zero_imu_mount is deleted. It only restated a measurement, and test_load_tuning_reads_imu_mount_section already covers the loader.
  • test_boot_anchor built its SimulatedSource with an identity imu_mount while handing the filter the shipped tuning, so the filter de-rotated a rotation the synthetic IMU never had. It now derives imu_mount from the same config it runs, as __main__ does.

Note that five other sim test files have the same latent coupling (load_tuning plus an identity-mount SimulatedSource). They pass only because GNSS is present to pull heading back, so the mag offset absorbs the error. Left alone here.

217 native tests and 211 sim tests pass; pio run -e pico2 builds.

Not fixed here: about 6% of telemetry lines are still corrupted by the unmutexed sink. Separate PR.

@georgesleen
georgesleen merged commit ec35984 into main Aug 16, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant