Skip to content

FEATURE/FIX: GPS fix validity flag in loop() to prevent ghost crossings #37

Description

@FabioRoss

What problem are you trying to solve?

GPS receivers — including the uBlox-based modules recommended in the library's
README — continue emitting NMEA sentences even when they lose satellite fix.
The stale coordinates in those sentences are typically the last known position
(or sometimes (0,0) depending on the receiver's NMEA mode).

If loop() processes those stale coordinates:

  • Ghost crossings: stale lat/lng may be on the wrong side of a sector line,
    triggering a spurious confirmed crossing
  • Odometer spikes: the stale position may be far from the actual last known
    position, causing totalDistanceTraveled to jump by hundreds of meters
  • Delta corruption: a GPS trace point at an incorrect position corrupts the
    reference lap comparison (Issue FEATURE: GPS-based real-time lap delta (gap to reference lap) #29)

The user typically knows whether the fix is valid — their GPS library exposes
a fixQuality or isFixed flag. But there's no way to tell DovesLapTimer
"this fix is bad, skip it" without refactoring the call site to skip loop()
entirely.

Skipping loop() at the call site means the user has to replicate the timing
tick inside their main loop — they may still want millisecondsSinceMidnight
to update, or they may need to call loop() for the state machine to progress
correctly.

Proposed solution

Add an optional bool fixOK = true parameter to loop():

int loop(double currentLat, double currentLng,
         float currentAltitudeMeters,
         float currentSpeedKnots,
         bool fixOK = true);   // backward compatible — default true

When fixOK == false:

  • Skip odometer update (no distanceTraveled += haversine(...))
  • Skip all crossing detection (checkStartFinish, checkSectorLine)
  • Skip delta update (updateDelta)
  • Skip circuit auto-load (if that feature is used — Issue [ON HOLD] FEATURE: GPS proximity circuit auto-detection (instant, no lap required) #35)
  • Still update millisecondsSinceMidnight (time ticks regardless of fix)
  • Still update prevFix* snapshot? Probably NO — a bad fix shouldn't become
    the Catmull-Rom pre-crossing control point

Alternatively: encode "fix invalid" as a distinct loop() return value (e.g. -2),
which some callers may find more natural. Default true parameter is simpler.

EXAMPLE USAGE:

// With TinyGPS++:
bool hasFix = gps.location.isValid() && gps.location.age() < 2000;
lapTimer.loop(
  gps.location.lat(),
  gps.location.lng(),
  gps.altitude.meters(),
  gps.speed.knots(),
  hasFix
);

Alternatives considered

  1. Caller wraps loop() in an if(hasFix) check — simplest workaround.
    Downside: millisecondsSinceMidnight doesn't advance; state machine may
    stall if any internal timing depends on loop() being called regularly.

  2. Separate method updateTimeOnly(unsigned long ms) — allows time to tick
    without running detection. Workable but adds API surface. The fixOK flag
    is more ergonomic: one call site, always.

  3. Library auto-detects bad fix via coordinate sanity check (lat/lng == 0.0) —
    fragile. (0,0) is a valid ocean location; a module in Italy reporting
    (0,0) after fix loss is obvious, but the library can't reliably distinguish
    this from a module correctly reporting (0,0). User code knows best.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions