The map is lying to you: GPS signal loss in dispatch systems
Here is a scene that plays out in dispatch offices more often than anyone admits. A job comes in. The controller looks at the map, sees a car two streets away, and sends it. Four minutes later the passenger calls to ask where the driver is, and the driver is nine minutes out because the car on the map has been parked in a multi storey since half past two.
Nothing crashed. No error appeared. The map was doing exactly what it was built to do, which was show the last position it received. The problem is that a stale position and a live position look identical on a screen, and the controller had no way to tell them apart.
We hit this properly while building TBMS, the dispatch platform we still maintain for a UK operator. It took us longer than it should have to understand that the fix was not better GPS. It was being honest about uncertainty.
Why the default behaviour is wrong
Almost every tracking system starts the same way. The driver app reads location every few seconds and posts it up. The server stores the newest one. The map draws whatever the server has.
That works until it does not, and when it stops working it does so silently. The app is in a car park with no signal, or the OS has throttled background location to save battery, or the driver force closed the app after their last drop. In all three cases the server keeps holding a position that was true once and is not true now.
A controller looking at that map is being shown a claim with no confidence attached. Two streets away and eleven minutes stale renders exactly the same as two streets away and four seconds fresh.
What we changed
The first change was small and it fixed most of the pain. Every position carries the time it was captured, and the map shows position age wherever a controller can see a vehicle.
Under about thirty seconds, the marker is solid. Past a minute or so it starts to fade. Past five minutes it goes hollow with the age printed next to it. Controllers stopped trusting hollow markers within about a week, which is roughly how long it takes anyone to learn that a particular light means something.
We did not need to explain the colours to anybody. Somebody sent one car to a hollow marker, waited, and told the rest of the office.
Capture time, not arrival time
This one is easy to get wrong and expensive to discover late. If the server timestamps a position when it arrives, then a batch of readings that were queued in a tunnel and uploaded at 15:42 all appear to have happened at 15:42. The driver appears to teleport across three miles in a second, and any speed or route calculation built on top of that is nonsense.
The device knows when it took the reading. That timestamp travels with the reading and the server treats it as authoritative. Arrival time is a separate field and is useful for debugging, nothing else.
Queue instead of drop
Positions taken while offline go into a local queue and upload in order when signal returns. This matters less for the live map, which only cares about the newest reading anyway, and matters enormously for anything historical. Route reconstruction, mileage, time on site, and any dispute about whether a driver actually went where they said all depend on the gaps being filled in rather than lost.
Let the allocation engine see the uncertainty
This was the change that actually moved the numbers. Our allocation logic used to treat every position as equally reliable, which meant a stale car frequently beat a fresh one on distance and won the job. Now position age is an input. A vehicle whose last fix is several minutes old is either weighted down or skipped depending on how contested the job is.
The result is not that allocation became perfect. It became less confidently wrong, which is a different and more useful thing.
The battery problem nobody warns you about
There is a second cause of stale positions that has nothing to do with signal, and it is worse because it looks like signal loss and is not.
Both Android and iOS aggressively restrict what an app can do in the background. Android will doze an app it decides is idle. Manufacturers stack their own battery optimisation on top, and some of them are far more aggressive than stock Android. iOS suspends background execution on its own schedule. The app is not crashed and the phone has full signal, but location updates have quietly stopped arriving.
The honest fix on Android is a foreground service with a persistent notification, which is intrusive and which drivers occasionally complain about. We looked hard for a way around it and did not find one that survives a real shift on a real handset. On some manufacturer builds you also have to walk the driver through disabling battery optimisation during onboarding, because nothing else works.
We spent two weeks convinced we had a network problem. It was a battery optimisation setting on one make of phone that three of the drivers happened to own.
From our own notes, which we kept because it was annoying
How to check whether your system has this problem
You do not need to read any code to find out. Sit with a controller for an hour during a busy period and ask four questions.
- Can you tell from this screen how old any vehicle position is?
- When a driver goes into an underground car park, what does the map do?
- If a driver has no signal for ten minutes, does the route history fill in afterwards or stay blank?
- Does the allocation engine know the difference between a car that reported four seconds ago and one that reported six minutes ago?
If the answers are no, nothing visible, stays blank, and no, then the map in that office is a confident guess and the controllers have probably already learned not to fully trust it. That last part is the real cost. A controller who does not trust the screen starts phoning drivers to confirm, and at that point the dispatch system has become a very expensive way of displaying phone numbers.
What this does not fix
Being straight about the limits. None of this makes GPS accurate indoors, because it is not. Urban canyons still produce readings that put a car through a building. A driver who leaves their phone in the cab and walks off still shows as present.
What changes is that the system stops presenting all of these with the same confidence. It can say I do not know, and a controller who is told I do not know makes a better decision than one who is told something wrong in a convincing tone.
That is most of what we learned. Not a clever algorithm. Just a system that admits the difference between knowing and having known.
Managing Director at DevelopersTech. Our team builds Flutter applications, dispatch systems and guard monitoring platforms that run in daily commercial use.