feat: drop implausible-speed (teleport) locations via heuristic filter#2266
feat: drop implausible-speed (teleport) locations via heuristic filter#2266mvanhorn wants to merge 1 commit into
Conversation
|
This is also a common and annoying issue with the Google Play build of Owntracks. So this pull request would be greatly appreciated! 👍 |
|
@jpmens what's your thoughts here? It's a new preference that allows a user to discard obviously inaccurate points based on a user-defined implausable speed. Coupled with the work I'm doing on passing the locations through a kalman, I think this could help improve location data quality, but interested in your view. |
|
I think this could be quite useful to suppress "teleporting" (but I'm not much of a scifi person so I cannot really judge that :-) ) I think we should take this, as long as you are satisfied with the code, @growse, and I would make the knob configuration-editor - only, i.e. I suggest not adding a UI, not that it would hurt. |
|
Could you rebase? |
|
@jpmens good news - it's already configuration-editor-only. The PR adds no settings UI: maxImplausibleSpeedKmh is a config-import-only integer preference (default 0, disabled), and the only UI-adjacent change is the PreferenceKeyLabels entry so the key renders with a readable label in the config editor. @growse happy to adjust anything else once you've settled on direction; it should compose fine with the Kalman work since this filter only drops the obvious teleports before publishing. |
Add an opt-in implausible-speed filter in LocationProcessor.publishLocationMessage that drops a new fix when it implies travel faster than a configurable maximum ground speed relative to the last published location. Gated by a new config-import-only integer preference maxImplausibleSpeedKmh, defaulting to 0 (disabled) so existing behaviour is unchanged. The filter sits alongside the existing accuracy and network-vs-gps discard heuristics, skips USER and CIRCULAR triggered reports, never blocks the first fix, and guards against zero or negative time deltas. The pure decision lives in an isImplausibleSpeed helper with JVM unit tests. Refs owntracks#2034 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ye471qeNk1R7b7aQsZF5s
97d6ad9 to
9d219d2
Compare
|
Rebased onto master in 9d219d2. The only conflict was additive in DefaultsProvider.kt (the new useGNSSInSignificantMonitoringMode default next to this PR's maxImplausibleSpeedKmh); kept both. GitHub reports the branch mergeable again. |
…-pass fixes New location-quality features on the continuous DEFAULT stream: - Implausible-speed gate: drops fixes whose implied speed from the last published location exceeds maxImplausibleSpeedKmh (default 1000; key matches upstream PR owntracks#2266 for config compatibility). Keeps the last rejected fix so a persistent relocation corroborates itself instead of being locked out by a bad anchor. - Experimental Kalman smoothing (smoothLocationsWithKalmanFilter): single-state filter in the location-kalman module smooths lat/lon only — sensor accuracy survives untouched so the ignoreInaccurateLocations gate, waypoint-transition tolerance, and published acc stay truthful. Process noise adapts to the fix-implied displacement so speedless (network) fixes can't lag the filter. - tst validation: reject non-positive or implausibly-future (>1 day) timestamps. - Driving-boost hardening: 20-min watchdog reverts a boost stuck on when GPS/AR go silent; toggling the feature off mid-drive reverts immediately; the entry dwell re-checks the toggle; forced reverts clear a speed-engaged "automotive" motionactivities so it can't go stale. - MQTT robustness: per-client generation tags on connected/disconnected/publish callbacks no-op superseded clients (state clobber + zombie-client guards); reconnect jobs use KEEP to preserve WorkManager backoff, with expedited REPLACE nudges for queued work and publish timeouts. - ContactsActivity: collect contact events in repeatOnLifecycle(STARTED) with a reconcile on re-entry (stops background reverse-geocoding; upstream candidate). - Experimental preferences screen is now always visible; docs updated. - CI runs location-kalman:test; unit tests for the gate, deserializer, controller, and filter. Full findings log in IMPROVEMENT_PROJECT.md ("Review findings 2026-07-11"). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Summary
Adds an opt-in implausible-speed (teleport) filter to
LocationProcessor.publishLocationMessage. When a new fix would imply travel faster than a configurable maximum ground speed relative to the last published location, the fix is dropped before it is queued for sending or persisted as the current published location.The filter is gated by a new config-import-only integer preference
maxImplausibleSpeedKmh, defaulting to0, which disables the filter and preserves current behaviour. It sits alongside the existing accuracy and network-vs-gps discard heuristics and follows the sameResult.failureplus Timber logging pattern.Why this matters
Users on the OSS (F-Droid/AOSP) flavour report occasional random published locations several miles from the device's true position while the phone is stationary, drawing long straight-line artifacts on the recorder map (#2034, also referenced in #2053). These come from the network/cell-tower provider supplying low-quality fixes that the OSS build forwards without the aggregation Play Services would normally apply.
On 2026-04-30 maintainer @growse scoped the work into two tracked items: (1) Kalman filtering of non-Play-Services locations, and (2) a heuristic to figure out if something looks weirdly wrong and optionally filter it out. This change implements item 2 only. A teleport fix implies an impossible ground speed relative to the previous fix, so dropping fixes above a user-set km/h ceiling removes the artifacts without touching plausible movement.
Behaviour details:
maxImplausibleSpeedKmh = 0(the default) disables the filter.The pure decision lives in an
isImplausibleSpeed(distanceMeters, timeDeltaMillis, maxSpeedKmh)helper so it can be unit tested without Android dependencies.Testing
Added
LocationProcessorTestwith JVM-pure unit tests for the decision helper covering: disabled threshold, a plausible slow move, a teleport (about 15 miles in 5 seconds, implying roughly 17,000 km/h), a zero time delta, and a negative time delta.Gradle was not run locally because this machine has no Java runtime; CI will validate the build and tests.
Refs #2034