Smoothing Out iBeacon Accuracy Data

If you’re using Core Location on iOS to range iBeacons, your app gets a constant stream of data from all iBeacons in signal range of the device. iOS gives you the radio signal strength (RSSI), but it also give you something a little more immediately useful in the form of accuracy. Accuracy takes the RSSI and uses the iBeacon calibration number to come up with a number that represents a distance. So while the accuracy doesn’t tell you how far the beacon is from the device (it might be a lot closer, but with objects in the way), it gives you a better idea than the raw RSSI number.

While the accuracy doesn’t strictly represent a distance, it’s useful in lots of contexts to use it as a proxy for distance in the absence of anything else. The problem is that the number is somewhat unstable. There are a myriad of factors that influence with radio signal strength, so even if your iPhone is stationary, the RSSI and accuracy numbers will fluctuate erratically.

In one of our recent projects, we wanted to show the user a list of all beacons in range, sorted by accuracy. In other words, the closest beacon should be at the top of the list, and the farthest beacon should be at the end of the list. Sorting by the accuracy as provided by Core Location gave us very discontinuous data, and the beacons in the list would jump around, reordering themselves constantly. We addressed this by using a low-pass filter.

The idea is that if you have stream of data, you calculate a weighted average of latest value with the previous filtered value. This final result produces a value with some amount of stability over time.

Give that buildup, the code may be sort of a letdown. Here it is.

    // instance variable _filteredAccuracy keeps the value from the last calculation.
    // filterFactor is a constant between 0 and 1.
    float filterFactor = 0.2;
    _filteredAccuracy = (accuracy * filterFactor) + (_filteredAccuracy * (1.0 - filterFactor));

The filterFactor can be derived in a fancier fashion using a time interval and cutoff frequency. That didn’t make sense for this application; we just experimented with different values until we found one that felt right. You want a value between 0 and 1. You also need to store filtered values until the next delegate call when you get the updated accuracy number.

We’ve tried this in some busy iBeacon areas, and it does a good job calming the movement of beacons in a sorted list. If you’re equidistant from two beacons, you’ll still see those beacons swap position fairly often, but it won’t be quite as frenetic as the raw value.

  • Apple has sample code showing how to use a low-pass filter on accelerometer data.
  • Wikipedia has a very detailed article explaining low pass filters generally.