fix: support v3.4 12-byte ranging packet with best_rssi float

HAL v3.4 extends the range notify from 8→12 bytes:
  [0-3]  int32 LE  front_mm
  [4-7]  int32 LE  back_mm
  [8-11] float32 LE best_rssi dBm  (NEW)

Parser now handles both lengths (8=v3.3, 12=v3.4) by detecting
packet size. RSSI is extracted as Double and assigned to both
Front and Back anchors (shared signal quality); nil for v3.3.
UI already conditionally renders rssiString (guard on Optional).

Added Data.readFloat32LE(at:) helper using IEEE 754 bit-pattern
reinterpretation (little-endian UInt32 → Float(bitPattern:)).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
sl-ios 2026-04-06 16:29:38 -04:00
parent 12338f491e
commit 9197371522

View File

@ -87,10 +87,15 @@ enum BLEPackets {
// MARK: - Ranging notification parser // MARK: - Ranging notification parser
// //
// HAL firmware format (8 bytes, 2 fixed anchors): // HAL firmware format v3.3 (8 bytes, 2 fixed anchors):
// [0-3] Int32 LE front anchor range mm (id=0) // [0-3] Int32 LE front anchor range mm (id=0)
// [4-7] Int32 LE back anchor range mm (id=1) // [4-7] Int32 LE back anchor range mm (id=1)
// //
// HAL firmware format v3.4 (12 bytes, adds signal quality):
// [0-3] Int32 LE front anchor range mm (id=0)
// [4-7] Int32 LE back anchor range mm (id=1)
// [8-11] Float32 LE best_rssi dBm (shared; assigned to both anchors)
//
// Legacy multi-anchor format (future): // Legacy multi-anchor format (future):
// [0] Uint8 anchor count N // [0] Uint8 anchor count N
// Per anchor (9 bytes): // Per anchor (9 bytes):
@ -102,15 +107,18 @@ enum BLEPackets {
static func parseRanging(_ data: Data) -> [AnchorInfo] { static func parseRanging(_ data: Data) -> [AnchorInfo] {
let now = Date() let now = Date()
// HAL two-anchor format: exactly 8 bytes, two Int32 LE range values // HAL two-anchor format: 8 bytes (v3.3) or 12 bytes (v3.4 + RSSI float)
if data.count == 8 { if data.count == 8 || data.count == 12 {
let frontMM = data.readInt32LE(at: 0) let frontMM = data.readInt32LE(at: 0)
let backMM = data.readInt32LE(at: 4) let backMM = data.readInt32LE(at: 4)
let rssi: Double? = data.count == 12
? Double(data.readFloat32LE(at: 8))
: nil
return [ return [
AnchorInfo(id: 0, rangeMetres: Double(frontMM) / 1000.0, AnchorInfo(id: 0, rangeMetres: Double(frontMM) / 1000.0,
rssiDBm: nil, ageMs: 0, receivedAt: now), rssiDBm: rssi, ageMs: 0, receivedAt: now),
AnchorInfo(id: 1, rangeMetres: Double(backMM) / 1000.0, AnchorInfo(id: 1, rangeMetres: Double(backMM) / 1000.0,
rssiDBm: nil, ageMs: 0, receivedAt: now) rssiDBm: rssi, ageMs: 0, receivedAt: now)
] ]
} }
@ -182,5 +190,12 @@ private extension Data {
func readUInt16LE(at offset: Int) -> UInt16 { func readUInt16LE(at offset: Int) -> UInt16 {
UInt16(self[offset]) | (UInt16(self[offset + 1]) << 8) UInt16(self[offset]) | (UInt16(self[offset + 1]) << 8)
} }
func readFloat32LE(at offset: Int) -> Float {
let bits = UInt32(self[offset])
| (UInt32(self[offset + 1]) << 8)
| (UInt32(self[offset + 2]) << 16)
| (UInt32(self[offset + 3]) << 24)
return Float(bitPattern: bits)
}
} }