RSSI gives you a coarse distance, but in a real room multipath and the human body push the error to ±3-5 m. If a BLE Tag needs sub-meter accuracy for find-my-item or zone entry, you need ranging. This article covers Two-Way Ranging (TWR), why clock drift dominates, and how to fuse it with RSSI.

The ranging idea

Instead of inferring distance from received power, measure the flight time of a radio packet. For a round trip:

d = c * (T_round – T_reply) / 2

where T_round is measured at the tag (send to receive-echo) and T_reply is the anchor’s known processing delay. Light travels 0.3 m/ns, so a 1 ns timing error is 0.15 m of distance error.

Single-Sided TWR (SS-TWR)

The tag sends at t0, the anchor replies at t1 after a fixed delay Δ, and the tag receives at t2.

T_round = t2 – t0

T_reply = t1 – (t0 + Δ) -> known

d = c * (T_round – T_reply) / 2

If the two clocks are not perfectly synchronized, a frequency offset f_off corrupts the result:

error = c * f_off * T_round / 2

With 20 ppm crystals and T_round = 10 µs, the clock error is 20e-6 * 10e-6 = 2e-10 s → 6 cm. Acceptable, but at 100 µs round trip it grows to 60 cm. That is why you must minimize round-trip time or cancel the offset.

Symmetric Double-Sided TWR (SDS-TWR)

Add a second exchange so the tag’s and anchor’s clock offsets cancel:

d = c/4 * ( (t4-t1) – (t3-t2) + (t6-t3) – (t5-t4) ) …

In practice the formula reduces to a symmetric pair of round trips whose offset terms subtract out, leaving only asymmetry in processing delays (typically <1 ns). SDS-TWR is the standard for BLE/UWB ranging because it removes the crystal-accuracy requirement.

BLE implementation reality

Bluetooth LE has no native RTT primitive like UWB’s fine-time Ranging. You implement it with:

  • Connection event timing: measure the interval between a master command and slave response at the Link Layer.
  • CTE / AoA frames: use the Constant Tone Extension for phase-based timing.
  • GATT request/response: simplest, but the host stack latency (1-10 ms) dominates, giving coarse resolution.

A 1 µs timer gives 150 m of ambiguity, so you need timestamping at the PHY/Radio layer, not the application layer.

RSSI fusion

TWR is accurate but power-hungry (several ms of active TX/RX at a few mA per measurement). RSSI is passive and cheap but biased. Fuse them in a Kalman filter:

x_k = A x_{k-1} + w (motion model)

z_RSSI = d_RSSI + v1 (noisy, biased)

z_TWR = d_TWR + v2 (sparse, accurate)

The filter trusts TWR when available and falls back to RSSI between measurements, smoothing the track and cutting the power budget by ranging only every Nth slot.

Power cost

Method Active time Current Accuracy
RSSI passive 0.5 ms listen 5-8 mA ±3-5 m
SS-TWR ~4 ms 5-8 mA ±0.5 m
SDS-TWR ~8 ms 5-8 mA ±0.1-0.3 m

Ranging every 1 s roughly triples the average current versus RSSI-only, so budget the interval against battery life.

Practical checklist

  • Use SDS-TWR to cancel crystal offset.
  • Calibrate antenna + processing delay once at the factory.
  • Timestamp at the radio, not the app layer.
  • Fuse with RSSI in a Kalman filter to save power.
  • Keep round-trip time short to limit drift error.

A well-designed BLE Tag achieves 0.1-0.3 m with SDS-TWR and keeps battery life intact by ranging sparingly and leaning on RSSI between fixes.