Skip to content

feat: drop implausible-speed (teleport) locations via heuristic filter#2266

Open
mvanhorn wants to merge 1 commit into
owntracks:masterfrom
mvanhorn:feat/2034-implausible-speed-filter
Open

feat: drop implausible-speed (teleport) locations via heuristic filter#2266
mvanhorn wants to merge 1 commit into
owntracks:masterfrom
mvanhorn:feat/2034-implausible-speed-filter

Conversation

@mvanhorn

Copy link
Copy Markdown

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 to 0, which disables the filter and preserves current behaviour. It sits alongside the existing accuracy and network-vs-gps discard heuristics and follows the same Result.failure plus 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:

  • Strictly opt-in: maxImplausibleSpeedKmh = 0 (the default) disables the filter.
  • Never blocks the first fix (no previous published location to compare against).
  • Skips USER and CIRCULAR triggered reports so on-demand and region reports are never suppressed.
  • Guards against zero and negative time deltas, so there is no divide-by-zero and a non-advancing clock never drops a fix.

The pure decision lives in an isImplausibleSpeed(distanceMeters, timeDeltaMillis, maxSpeedKmh) helper so it can be unit tested without Android dependencies.

Testing

Added LocationProcessorTest with 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

@compuguy

compuguy commented Jul 7, 2026

Copy link
Copy Markdown

This is also a common and annoying issue with the Google Play build of Owntracks. So this pull request would be greatly appreciated! 👍

@growse

growse commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

@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.

@jpmens

jpmens commented Jul 10, 2026

Copy link
Copy Markdown
Member

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.

@growse

growse commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator

Could you rebase?

@mvanhorn

Copy link
Copy Markdown
Author

@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
@mvanhorn mvanhorn force-pushed the feat/2034-implausible-speed-filter branch from 97d6ad9 to 9d219d2 Compare July 11, 2026 18:52
@mvanhorn

Copy link
Copy Markdown
Author

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.

pletch added a commit to pletch/android-nxt that referenced this pull request Jul 11, 2026
…-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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants