Skip to content

Stop detection trusts raw device velocity even when it contradicts actual GPS movement #592

Description

@ardenn

Note

I investigated this with Claude's help while reviewing my own GeoPulse data (timeline preferences, trip/stay tables, and GPSLogger) to fine-tune my timeline classification settings. This specific issue came out of that investigation, not from reading the code cold, so please let me know if anything below needs more detail to reproduce.

TripStopHeuristicsService decides whether a stop qualifies as a Stay by requiring an uninterrupted run of points at or below staypointVelocityThreshold, checked against each point's speed - the raw, device-reported velocity field ("in meters per second", populated directly from the ingested point, never recalculated). Both detection paths gate on it directly:

Neither path cross-checks against position movement between consecutive points.

Concrete example

A ~5.5-minute genuine stop (2026-08-21, ~09:24:46–09:33:16 UTC) never registered as a Stay, so a single trip with a 6-minute break stayed merged as one continuous 41-minute journey instead of splitting into two trips. Looking at the raw points:

From → To Time gap Actual distance moved Implied speed Reported velocity field
09:30:15 → 09:31:50 95s 73.1m 0.77 m/s (2.8 km/h) 12.42 m/s (44.7 km/h)
09:31:50 → 09:32:23 33s 56.4m 1.71 m/s 3.35 m/s
09:32:23 → 09:32:58 35s 39.8m 1.14 m/s 7.42 m/s

The device-reported velocity spikes well above staypointVelocityThreshold at points where actual position barely moved (walking pace at most). Each spike resets the "all points below threshold" check, so the required duration never accumulates, even though the person was standing still the whole time by every position-based measure.

This looks like a device GPS recorder/GPSLogger issue (noisy instantaneous velocity from a poor fix), not real movement - and it seems like a common enough class of noise that relying solely on the raw field will misfire for other users with similar hardware/apps too.

Proposed fix

When deciding stop-detection continuity, don't trust the raw speed field unconditionally. Options, roughly in order of how surgical they are:

  1. Cross-check getSpeed() against the position-derived speed (distanceTo() between consecutive points ÷ time gap) for that same interval, and use the lower/more conservative of the two, or discard a reading that's wildly inconsistent with implied displacement.
  2. Add a "confidence" gate - a speed spike lasting only one sample, surrounded by low-speed samples, could be treated as a single-point outlier rather than breaking the continuity run in detectSustainedSlowStop's allMatch check.
  3. Simplest: an option to use position-derived speed instead of the raw field for stop-detection, for users whose GPS source produces unreliable velocity (this seems common with apps like GPSLogger on Android).

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions