feat(nav): course-over-ground ring in UITask (compass without magnetometer)

Phase 2. A 5-fix GPS ring sampled every ~1 s in UITask::loop, filled
whenever there's a valid fix — independent of trail recording, so the
heading is available to navigation at all times, not only while logging
a trail.

- pushCogFix() rejects gross outliers (a fix implying > 50 m/s since the
  previous one) so a single bad GPS reading can't swing the heading.
- currentCourse(deg) returns the bearing across the window (oldest→newest)
  once the window spans ≥ 6 m of real movement; below that it holds the
  last good heading, and returns false only until the first valid heading
  exists. This is the "two absolute bearings (To / Hdg)" heading source
  for the upcoming nav view.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-06-03 08:20:59 +02:00
co-authored by Claude Opus 4.8
parent acec8e94dd
commit 11c0b7c4a5
2 changed files with 65 additions and 0 deletions
+15
View File
@@ -80,6 +80,18 @@ class UITask : public AbstractUITask {
TrailStore _trail;
uint32_t _next_trail_sample_ms = 0;
// Course-over-ground ring — a heading source independent of trail recording.
// Filled from the same periodic GPS poll regardless of _trail.isActive().
// Heading = bearing across the window (oldest→newest) once the cumulative
// movement clears COG_MIN_MOVE_M; gross GPS jumps are rejected on insert.
static const int COG_RING = 5;
struct CogFix { int32_t lat, lon; uint32_t ms; };
CogFix _cog[COG_RING];
uint8_t _cog_head = 0, _cog_count = 0;
int _cog_deg = -1; // last good heading, -1 = none yet
uint32_t _next_cog_sample_ms = 0;
void pushCogFix(int32_t lat, int32_t lon);
// Ping state
bool _ping_active = false;
uint32_t _ping_tag = 0;
@@ -131,6 +143,9 @@ public:
void gotoAutoAdvertScreen();
void gotoTrailScreen();
TrailStore& trail() { return _trail; }
// Current course over ground in degrees (0..359), or false if not enough
// recent movement to derive a stable heading. Independent of trail logging.
bool currentCourse(int& deg_out) const;
void playMelody(const char* melody);
void stopMelody();
bool isMelodyPlaying();