You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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:
detectClusteredStop rejects the whole cluster if the last point's speed exceeds the threshold (L59-L61) or if any candidate point in the run does (L68).
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:
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.
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.
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).
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.
TripStopHeuristicsServicedecides whether a stop qualifies as aStayby requiring an uninterrupted run of points at or belowstaypointVelocityThreshold, checked against each point'sspeed- 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:detectClusteredStoprejects the whole cluster if the last point's speed exceeds the threshold (L59-L61) or if any candidate point in the run does (L68).detectSustainedSlowStoprequires that all of the last N points satisfyspeed <= stopSpeedThreshold(L119-L120).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:velocityfieldThe device-reported velocity spikes well above
staypointVelocityThresholdat 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
speedfield unconditionally. Options, roughly in order of how surgical they are: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.detectSustainedSlowStop'sallMatchcheck.