compass.py, compassing.py,
ubx_gnss.py, imu_fusion_auto.py, mag_wmm_data.py, task_mag_declination.py,
nav_helpers.py, nav_logger.py, and peer_helpers.py.
GNSS (u-blox)
The receiver is a u-blox module spoken to over the UBX binary protocol.
A representative fix log:
pAcc is the receiver’s own
accuracy estimate used to gate whether a fix is trusted.)
AssistNow (A-GNSS)
To get a fast first fix, the device uses u-blox AssistNow (MGA) assistance data, delivered to the GNSS chip and cached in the filesystem.
Within the GNSS driver, assistance data is only ever loaded from the on-device filesystem
(
Checking VFS for assist now files) and streamed to the receiver in
chunks (Sending AssistNow chunk {}/{} ({} bytes), attempt {}). Delivery of that data over BLE/mesh is
covered by the message-format subsystem, not the GNSS driver.
IMU fusion
imu_fusion_auto.py fuses accelerometer and gyroscope data for orientation and motion.
In v5.0.3 it is the sole fusion module (byte-identical to v5.0.2), imported by
compass, compassing, ble_manager, espnow_conn_v2, nav_logger, and debugger.
v5.0.2 also froze an older imu_fusion.py — an unused Madgwick Fusion/Cal
implementation that nothing imported — which v5.0.3 removes. The fusion engine provides:
- Orientation filter is a Madgwick AHRS with an adaptive beta gain (
madgwick,is_adaptive_beta,_BETA_MIN_SCALE). - Tilt/orientation feeds the 2D↔3D compass mode switch.
- Shake detection (
_SHAKE_ACCEL_DEV,_shake_count,_shake_start_ms) triggers a manual magnetometer bias reset (Manual bias reset triggered via shake pattern,is_shake_reset). - Accel deviation bounds gate magnetometer calibration (
_CAL3D_MAX_ACCEL_DEV).
Magnetic compass & calibration
Heading comes from a magnetometer, corrected for hard/soft-iron errors and for magnetic declination.Calibration
The 2D→3D “bias handoff” upgrades from a flat-plane heading to a full 3D solution once
enough motion is seen:
Bias handoff 2D->3D ({}) | gap_xy={:.1f}uT | 2D age={}s | cals={}.
All of this calibration telemetry lives in imu_fusion_auto; it is unrelated to Vibe Mode
(new_vibe.py, a sound-reactive LED effect — see Power, input & sensors).
The --- Magnetic Calibration Samples --- string cited in earlier revisions existed only in
the unused v5.0.2 imu_fusion.py and is gone in v5.0.3.
Declination (World Magnetic Model)
mag_wmm_data.py embeds the World Magnetic Model — specifically WMM 2025 (valid
2025–2030; WMM_2025) — so the device converts magnetic heading to true heading based on
location, refreshed periodically:
task_mag_declination.py recomputes declination only when the device has moved far
enough or enough time has passed — a power optimization.
The embedded WGS84 axes (6378.137 / 6356.7523142 km) and the 6371.2 km geomagnetic
reference radius — all present as IEEE-754 doubles in the binary — confirm a genuine
spherical-harmonic declination computation rather than a lookup table.
Heading output
The compass surface the UI consumes:Navigation logging
nav_logger.py records navigation events, schedulable remotely:
nav_logger.check_log_storage normally rotates events.bin at 7100 B. In v5.0.3, when
modes.is_nav_log_fast_rotate is set — by the BLE log uploader when no events-*.gz
backlog exists — it rotates at ≥1024 B instead
(Nav log fast rotation at {} B (BLE uploads active, backlog empty)).
Peer staleness & proximity display
How a bonded peer is shown on the ring depends on whether its coordinates are fresh. v5.0.3 changes both the staleness timeout and how stale peers are displayed:Promo activation
compass.activation_check creates a time-limited promo point of interest (log strings
Add Promo Activation: {} | Secs until start: {}, Creating promo activation in: {},
No promo activation, [activation_check] Activation expired,
[activation_check] Activation doesn't start for 12hrs). In v5.0.2 the function existed but
was never called; v5.0.3 launches it from backend_checks.
The promo’s coordinates are embedded in the firmware and are intentionally not reproduced
here.
Receiver lifecycle & auto-reset
The GNSS subsystem manages the receiver end-to-end and can self-heal:
The embedded config frames all begin
B5 62 and their trailing checksum bytes verify, so
the frame contents are confirmed; the CFG-MSGOUT key→message-name mapping is inferred from
the u-blox protocol spec.