← All articles
Jun 12, 20263 min read

Detecting Driving vs. Walking from GPS Without Lying at Red Lights

  • geolocation
  • GPS
  • React Native
  • algorithms
  • mobile
  • North
Detecting Driving vs. Walking from GPS Without Lying at Red Lights

A small feature in North shows an activity chip next to each family member — walking, driving, still. It looks like an if (speed > threshold) problem. It is not, because the input data is actively hostile.

The enemy: GPS speed is garbage exactly when you need it

The speed field on a location fix comes from the GPS chip's Doppler measurement, and it's good — when you have a real GPS fix. But background location fixes on phones frequently come from cell towers and Wi-Fi positioning instead, and those report speed as -1 or null. The naive classifier reads that as "not moving" and confidently shows "still" for someone doing 90 on the highway, precisely because their phone is in a pocket and the app is backgrounded — the normal state for a passive location app.

So deriveActivity never trusts one signal. It computes speed three ways and takes the best evidence available:

  1. Reported GPS speed — the maximum valid (non-negative) speed across the current batch of fixes. Background delivery hands you fixes in batches, and any single one can be junk; the max-of-valid filter survives that.
  2. Intra-batch displacement — distance between the first and last fix in the batch over the time between them. This needs no speed field at all: two positions and a clock. Cell-tower fixes can't hide motion from arithmetic.
  3. Cross-batch displacement — distance from the previous persisted location, if it's recent enough (under 10 minutes). When the OS delivers single sparse fixes, there's no batch to measure within — but there's history.

Each estimator covers a regime where the others fail: dense fresh fixes (1), batched degraded fixes (2), sparse fixes (3). The classifier takes the strongest defensible signal rather than averaging in the garbage.

Hysteresis: a red light is not a parking spot

The second failure mode is flapping. Drive through city traffic with a threshold-only classifier and you become a pedestrian at every red light, a driver at every green — the chip strobes and recipients learn to ignore it.

The fix is the same one thermostats have used forever: asymmetric state transitions. Entering "driving" requires sustained speed above the threshold. Leaving it requires speed below the threshold for three continuous minutes (DRIVE_HOLD_MS). Red lights last seconds to a couple of minutes; actually arriving somewhere keeps you slow for longer. The hold window cleanly separates the two without any extra sensors.

This encodes an asymmetry in the meaning of the states, not just the mechanics: "driving" is sticky because journeys are continuous — brief slowness during one is almost never the end of it. The classifier's job isn't to report instantaneous velocity; it's to report which story the person is in.

Expiry: old activity is not activity

Last rule, easy to forget: classification results age out. The UI only renders walking/driving chips while the underlying fix is less than 10 minutes old; anything older falls back to neutral "still." A "driving" badge computed forty minutes ago describes a journey that may have ended — showing it is a small lie of the same species as "Sharing live" on stale data, and it gets the same treatment: evidence or silence.

The shape of the solution

Strip away the domain and the pattern is reusable for any classifier fed by flaky real-world sensors:

  • Multiple estimators, regime-aware — every input fails somewhere; know where, and have the next estimator ready.
  • Validity-filter before aggregating-1 is not a speed; never let sentinel values into the math.
  • Hysteresis on state exits — match the hold time to the real-world duration of false exits (red lights ≈ minutes → hold = 3 min).
  • TTL on outputs — a classification is a perishable good; stamp it and stop serving it past its date.

About fifty lines of logic — and the difference between an activity chip people trust and one they screenshot when it calls grandma's parked car a hike.

WRITTEN BY

Shahzaib Muhammad Akram

Senior Frontend EngineerCyberjaya, Malaysia