Where GPS stops: tracking a workforce indoors at Hajj
GPS gives out at the door, and the busiest doors on earth are at Hajj. How a BLE wristband, a fixed grid of receivers and a CCTV density model kept a workforce findable inside the pilgrim sites — and what indoor positioning honestly costs.
Asghar Ali · Chief Technology Officer, Kakushin.io LTD

Every year, around two million people move across the same few kilometres of ground east of Makkah in the same handful of days. A great deal of that movement is under cover — the tent city at Mina, the multi-level Jamarat bridge, covered walkways and halls — and satellites see none of it. GPS stops at the door, and these are the busiest doors on earth.
The client was NOVSYS, a Saudi company working on crowd operations for Hajj. Their problem was not the pilgrims. It was their own people: a workforce of marshals and staff spread through covered areas, and supervisors who needed to know, at any moment, where each of them was and how dense the crowd around them had become.
Two systems came out of it. An indoor tracking wristband on Bluetooth Low Energy, with a web console and an Android app on top of it, and a separate crowd-density estimator that read the CCTV the sites already had. This is a retrospective on both — what was built, why it was shaped the way it was, and what I would do differently with what exists now.
Why a wristband and not a phone
The obvious answer is an app. Everyone carries a phone; put beacons in the building and let the phone work out where it is. It fails here for reasons that have nothing to do with the radio.
A phone lives in a pocket, is switched off to save battery by the afternoon, needs an app that has to be opened, and belongs to the worker rather than the operation. A band is on the wrist from the start of the shift to the end of it, does exactly one thing, and is issued and collected like a hi-vis vest.
It also inverts who does the work. In the retail model — fixed beacons, phone listening — the phone has to compute its own position and report it, so it needs software, a network connection and a battery budget. Here the band is the beacon. It broadcasts a BLE advertisement carrying its identity and its battery level on a fixed interval, and never listens for anything. That makes it cheap, simple to the point of having no software to update, and long-lived on a small battery.
The advertising interval is the one tuning parameter that matters. A shorter interval means a fresher position and a shorter life. The right value is a conversation with the operations team about how stale a location is allowed to be, not an engineering decision made alone.
Fixed receivers at known points
If the bands only broadcast, something has to listen. Receivers were mounted at surveyed positions — columns, walls, gantries — each one a BLE scanner with a link back to a server. Every receiver reports the same tuple, over and over: which band it heard, how strongly, and when.
That tuple is all the server ever sees. Everything a supervisor looked at — who is in which section, who has not been heard from in ten minutes, whose band is about to die — was derived from a stream of band, receiver, signal strength, timestamp. A Node.js service consumed the stream, kept the latest state per band, and pushed changes out to the console.
Signal strength is not distance
The textbook says a received signal weakens predictably with distance, so three receivers give you a position by trilateration. In a hall full of people, the textbook does not survive contact.
The 2.4 GHz band is absorbed by water, and a human body is mostly water. A band on a wrist held across the chest can lose tens of decibels compared with the same band held out to the side — which, in signal terms, looks like it moved a long way. Steel and concrete reflect the signal, so a receiver hears the direct path and several echoes summed together, and the sum changes as people walk past. In a crowd, the environment itself is moving water.
So the output was never a coordinate. It was a zone with a confidence. The rule was simple: the receiver that hears you strongest, taken over a window rather than a single sample, with hysteresis so that a person standing on a boundary does not flicker between two sections every second. The floor plan was divided into zones sized to the receiver spacing, and each band resolved to one of them.
That sounds like a compromise, and it is — but it was the right one. The questions the supervisors were actually asking were "which section is this marshal in?" and "how many of my people are on the upper level?", not "what are his coordinates to the metre?" Zone-level was what the receiver density could deliver honestly. Answering the real question honestly beats answering a harder question badly.
Two front ends, one truth
The web console was for the control room: a floor plan with one dot per person, per zone, with last-seen time and battery, and search by name or team. Nothing clever, deliberately. The value was that it was live and that it was right.
The Android app, built in PhoneGap so the same code could serve the web, was for people on the floor, and it did something the console did not: indoor navigation. It asked the server where the worker's own band had last been heard, showed them which zone that was, and routed them, zone by zone, to wherever they were needed. The route was a walk over an adjacency graph of zones — not a map with turn-by-turn arrows, because the positioning could not support that precision, and pretending otherwise would have sent people the wrong way.
Connectivity inside those sites is not something to rely on. The app had to tolerate being offline for stretches and catch up when it could, which is a theme of everything I built on PhoneGap in that period.
Density from cameras that were already there
The second system had nothing to do with the bands. The sites had CCTV, and the operation wanted to know how dense the crowd was in each camera's view — continuously, without a person watching every screen.
Counting heads in a Hajj crowd is not feasible with classical image processing. Past a certain density there are no gaps between people and every head occludes the next. But the operational question was, once again, not the hard one. Nobody needed a count. They needed to know whether a section was getting dangerous, and whether it was getting worse.
So the estimator worked on density, not count. With a fixed camera, a background model of the empty scene can be maintained and subtracted from each frame, leaving a foreground mask of where the people are. The fraction of a region covered by foreground, corrected for perspective — a person at the far end of a walkway covers far fewer pixels than one near the lens — gives a density index for that region. Calibrate the index against a handful of manually assessed frames, put thresholds on it, and you have a level per zone over time: low, moderate, high, critical.
The levels fed into the same console the tracking used, so a supervisor saw both where their people were and how the crowd around them was moving.
What I would keep
Defining the output honestly. A zone with a confidence rather than a coordinate; a density level rather than a head count. Both decisions came from asking what the operation would do with the answer, and both held up.
Dumb bands, smart infrastructure. Everything that could go wrong was on the fixed side, where it could be reached, powered and fixed. The thing on a person's wrist had no software to fail.
Reusing what was there. The cameras existed. The columns existed. The cheapest sensor is the one that is already installed.
What I would change now
The radio half has moved on. Bluetooth 5.1 added direction finding, so a receiver with an antenna array can measure the angle a signal arrives from rather than only its strength — and angle survives the body-attenuation problem that strength does not. Ultra-wideband gives genuine sub-metre ranging where the budget stretches to the tags. Either would turn zones into positions.
The vision half has moved further. Crowd counting is now a well-studied problem for convolutional networks trained to produce a density map — a per-pixel estimate of how many people are there, summed to a count — and they handle the occlusion that defeats background subtraction. On current edge hardware that runs beside the camera, which is where it belongs: raw video never has to leave the site, and the control room receives numbers rather than streams.
That last point is the one I have kept thinking about since. Processing at the receiver and at the camera rather than in a distant server is the fog-computing argument, and it is the same argument that decides where inference sits in the systems I am responsible for now. This was the first system where I had a practical reason to care about it, because the distance between the sensor and the server was measured in dropped packets.
Related: the research note on indoor positioning with BLE beacons.