找到最接近的信标的正确方法 [英] Proper way to find the closest beacon

查看:125
本文介绍了找到最接近的信标的正确方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当调用didRangeBeacons方法时,最接近的信标信标是第一个吗?还是信标列表中具有最低准确性值的信标?都正确吗?谢谢

解决方案

虽然didRangeBeacons:inRegion:回调中的信标列表是按准确性"字段排序的(实际上以米为单位测量距离),但存在许多问题依赖于此:

  • 有时,如果在过去的一秒内没有RSSI(信号强度)样本可用,则信标的精度值为-1.如果中继自动排序,则会给您不正确的结果.

  • 有时,如果在最后一秒未检测到信标,则这些信标会从列表中删除.这是在嘈杂的蓝牙环境中以及不经常发送信标的常见问题.

了解最接近的信标的一种更可靠的方法是自己实施一种算法,该算法将忽略-1的准确度值,并且可以承受信标检测中的临时丢失(例如5秒).

或者,当然,这仅仅是开始.通过添加所有其他各种过滤器以将其调整到您的确切用例,您甚至可以变得更奇特.在不使事情变得过于复杂的情况下,这是我使用的一些Swift代码的基本知识:

let expirationTimeSecs = 5.0
public var closestBeacon: CLBeacon? = nil
var trackedBeacons: Dictionary<String, CLBeacon>
var trackedBeaconTimes: Dictionary<String, NSDate>

override init() {
  trackedBeacons = Dictionary<String, CLBeacon>()
  trackedBeaconTimes = Dictionary<String, NSDate>()
}

public func locationManager(manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], inRegion region: CLBeaconRegion) {
  let now = NSDate()
  for beacon in beacons {
    let key = keyForBeacon(beacon)
    if beacon.accuracy < 0 {
      NSLog("Ignoring beacon with negative distance")
    }
    else {
      trackedBeacons[key] = beacon
      if (trackedBeaconTimes[key] != nil) {
        trackedBeaconTimes[key] = now
      }
      else {
        trackedBeaconTimes[key] = now
      }
    }
  }
  purgeExpiredBeacons()
  calculateClosestBeacon()
}

func calculateClosestBeacon() {
  var changed = false
    // Initialize cloestBeaconCandidate to the latest tracked instance of current closest beacon
    var closestBeaconCandidate: CLBeacon?
    if closestBeacon != nil {
      let closestBeaconKey = keyForBeacon(closestBeacon!)
      for key in trackedBeacons.keys {
        if key == closestBeaconKey {
          closestBeaconCandidate = trackedBeacons[key]
        }
      }
    }

    for key in trackedBeacons.keys {
      var closer = false
      let beacon = trackedBeacons[key]
      if (beacon != closestBeaconCandidate) {
        if beacon!.accuracy > 0 {
          if closestBeaconCandidate == nil {
            closer = true
          }
          else if beacon!.accuracy < closestBeaconCandidate!.accuracy {
            closer = true
          }
        }
        if closer {
          closestBeaconCandidate = beacon
          changed = true
        }
      }
    }
    if (changed) {
      closestBeacon = closestBeaconCandidate
    }
}

func keyForBeacon(beacon: CLBeacon) -> String {
  return "\(beacon.proximityUUID.UUIDString) \(beacon.major) \(beacon.minor)"
}

func purgeExpiredBeacons() {
  let now = NSDate()
  var changed = false
  var newTrackedBeacons = Dictionary<String, CLBeacon>()
  var newTrackedBeaconTimes = Dictionary<String, NSDate>()
  for key in trackedBeacons.keys {
    let beacon = trackedBeacons[key]
    let lastSeenTime = trackedBeaconTimes[key]!
    if now.timeIntervalSinceDate(lastSeenTime) > expirationTimeSecs {
      NSLog("******* Expired seeing beacon: \(key) time interval is \(now.timeIntervalSinceDate(lastSeenTime))")
      changed = true
    }
    else {
      newTrackedBeacons[key] = beacon!
      newTrackedBeaconTimes[key] = lastSeenTime
    }
  }
  if changed {
    trackedBeacons = newTrackedBeacons
    trackedBeaconTimes = newTrackedBeaconTimes
  }
}

When the didRangeBeacons method is called, is the closest beacon beacons.first? Or is it the beacon in the list of beacons that has the lowest accuracy value? Are both correct? Thanks

解决方案

While the list of beacons in the didRangeBeacons:inRegion: callback is sorted by the accuracy field (which actually measures distance in meters), there are a number of problems with relying on this:

  • Sometimes beacons have an accuracy value of -1 if no RSSI (signal strength) samples were available in the past second. This gives you incorrect results if you relay on the automatic sorting.

  • Sometimes beacons drop out of the list if they aren't detected in the last second. This is a common problem in noisy bluetooth environments and with infrequently transmitting beacons.

A more robust way to know the closest beacon is to implement an algorithm yourself that ignores accuracy values of -1 and is tolerant of temporary dropouts (say 5 seconds) in beacon detections.

Or course, this is just the beginning. You can get even fancier by adding all kinds of other filters to tune it to your exact use case. Without over complicating things, here's some Swift code I use to the basics:

let expirationTimeSecs = 5.0
public var closestBeacon: CLBeacon? = nil
var trackedBeacons: Dictionary<String, CLBeacon>
var trackedBeaconTimes: Dictionary<String, NSDate>

override init() {
  trackedBeacons = Dictionary<String, CLBeacon>()
  trackedBeaconTimes = Dictionary<String, NSDate>()
}

public func locationManager(manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], inRegion region: CLBeaconRegion) {
  let now = NSDate()
  for beacon in beacons {
    let key = keyForBeacon(beacon)
    if beacon.accuracy < 0 {
      NSLog("Ignoring beacon with negative distance")
    }
    else {
      trackedBeacons[key] = beacon
      if (trackedBeaconTimes[key] != nil) {
        trackedBeaconTimes[key] = now
      }
      else {
        trackedBeaconTimes[key] = now
      }
    }
  }
  purgeExpiredBeacons()
  calculateClosestBeacon()
}

func calculateClosestBeacon() {
  var changed = false
    // Initialize cloestBeaconCandidate to the latest tracked instance of current closest beacon
    var closestBeaconCandidate: CLBeacon?
    if closestBeacon != nil {
      let closestBeaconKey = keyForBeacon(closestBeacon!)
      for key in trackedBeacons.keys {
        if key == closestBeaconKey {
          closestBeaconCandidate = trackedBeacons[key]
        }
      }
    }

    for key in trackedBeacons.keys {
      var closer = false
      let beacon = trackedBeacons[key]
      if (beacon != closestBeaconCandidate) {
        if beacon!.accuracy > 0 {
          if closestBeaconCandidate == nil {
            closer = true
          }
          else if beacon!.accuracy < closestBeaconCandidate!.accuracy {
            closer = true
          }
        }
        if closer {
          closestBeaconCandidate = beacon
          changed = true
        }
      }
    }
    if (changed) {
      closestBeacon = closestBeaconCandidate
    }
}

func keyForBeacon(beacon: CLBeacon) -> String {
  return "\(beacon.proximityUUID.UUIDString) \(beacon.major) \(beacon.minor)"
}

func purgeExpiredBeacons() {
  let now = NSDate()
  var changed = false
  var newTrackedBeacons = Dictionary<String, CLBeacon>()
  var newTrackedBeaconTimes = Dictionary<String, NSDate>()
  for key in trackedBeacons.keys {
    let beacon = trackedBeacons[key]
    let lastSeenTime = trackedBeaconTimes[key]!
    if now.timeIntervalSinceDate(lastSeenTime) > expirationTimeSecs {
      NSLog("******* Expired seeing beacon: \(key) time interval is \(now.timeIntervalSinceDate(lastSeenTime))")
      changed = true
    }
    else {
      newTrackedBeacons[key] = beacon!
      newTrackedBeaconTimes[key] = lastSeenTime
    }
  }
  if changed {
    trackedBeacons = newTrackedBeacons
    trackedBeaconTimes = newTrackedBeaconTimes
  }
}

这篇关于找到最接近的信标的正确方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆