feat: drop implausible-speed (teleport) locations via heuristic filter - #2266
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. |
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>
|
/run-tests |
|
Build & Test complete. ✨ |
|
Sorry, one more rebase. There's shuffling in LocationProcessor so just need to fix the merge conflicts. |
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
ec20164 to
1273494
Compare
|
Rebased onto current master and resolved the LocationProcessor conflicts. The branch is now a single commit on top of master. |
|
/run-tests |
|
Build & Test complete. ✨ |
|
Thanks for taking the teleport filter, @growse - implausible-speed points getting dropped should clean up a lot of tracks. |
The implausible-speed filter added in #2266 exempted USER and CIRCULAR triggers but not RESPONSE, so a remote "reportLocation" request could be silently dropped if the reply looked fast relative to the last published fix - unlike every other place in this file that treats RESPONSE as one of the "must always get an answer" triggers (see responseMessageTypes). The filter's exemption now reuses that same set instead of hand-picking two of its three members. Also de-duplicates the speed calculation (previously computed once inside isImplausibleSpeed and again by hand for the log message), and adds a regression test exercising the trigger-exemption behavior at the publishLocationMessage level, which the existing pure-function tests couldn't catch since they don't know about triggers.
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