diff --git a/SulTee/SulTee/AnchorInfo.swift b/SulTee/SulTee/AnchorInfo.swift index fef8c36..fa08bcd 100644 --- a/SulTee/SulTee/AnchorInfo.swift +++ b/SulTee/SulTee/AnchorInfo.swift @@ -4,15 +4,22 @@ import Foundation struct AnchorInfo: Identifiable { let id: UInt8 let rangeMetres: Double - let rssiDBm: Double - let ageMs: UInt16 // age reported by tag firmware + let rssiDBm: Double? // nil when not reported (HAL two-anchor format) + let ageMs: UInt16 // age reported by tag firmware; 0 when not reported let receivedAt: Date /// True when the measurement is more than 3 seconds old (local wall-clock). var isStale: Bool { Date().timeIntervalSince(receivedAt) > 3.0 } /// Display string for the anchor identifier. - var label: String { "A\(id)" } + /// IDs 0/1 map to Front/Back (HAL two-anchor format); others show "A". + var label: String { + switch id { + case 0: return "F" + case 1: return "B" + default: return "A\(id)" + } + } /// Formatted range string matching the Flutter app style. var rangeString: String { @@ -21,6 +28,9 @@ struct AnchorInfo: Identifiable { : String(format: "%.1f m", rangeMetres) } - /// Formatted RSSI string. - var rssiString: String { "\(Int(rssiDBm.rounded())) dBm" } + /// Formatted RSSI string, or nil when RSSI was not reported. + var rssiString: String? { + guard let r = rssiDBm else { return nil } + return "\(Int(r.rounded())) dBm" + } } diff --git a/SulTee/SulTee/BLEPackets.swift b/SulTee/SulTee/BLEPackets.swift index f13adeb..e92de49 100644 --- a/SulTee/SulTee/BLEPackets.swift +++ b/SulTee/SulTee/BLEPackets.swift @@ -87,27 +87,46 @@ enum BLEPackets { // MARK: - Ranging notification parser // - // [0] Uint8 anchor count N - // Per anchor (9 bytes, offset = 1 + i×9): - // [+0] Uint8 anchor index - // [+1-4] Int32 LE range mm - // [+5-6] Int16 LE RSSI × 10 (dBm × 10) - // [+7-8] Uint16LE age ms + // HAL firmware format (8 bytes, 2 fixed anchors): + // [0-3] Int32 LE front anchor range mm (id=0) + // [4-7] Int32 LE back anchor range mm (id=1) + // + // Legacy multi-anchor format (future): + // [0] Uint8 anchor count N + // Per anchor (9 bytes): + // [+0] Uint8 anchor index + // [+1-4] Int32 LE range mm + // [+5-6] Int16 LE RSSI × 10 (dBm × 10) + // [+7-8] Uint16LE age ms static func parseRanging(_ data: Data) -> [AnchorInfo] { + let now = Date() + + // HAL two-anchor format: exactly 8 bytes, two Int32 LE range values + if data.count == 8 { + let frontMM = data.readInt32LE(at: 0) + let backMM = data.readInt32LE(at: 4) + return [ + AnchorInfo(id: 0, rangeMetres: Double(frontMM) / 1000.0, + rssiDBm: nil, ageMs: 0, receivedAt: now), + AnchorInfo(id: 1, rangeMetres: Double(backMM) / 1000.0, + rssiDBm: nil, ageMs: 0, receivedAt: now) + ] + } + + // Legacy multi-anchor format: [count][anchorID+rangeMM+RSSI+age] × N guard data.count >= 1 else { return [] } let count = Int(data[0]) - let now = Date() var result: [AnchorInfo] = [] for i in 0..