首页 > 解决方案 > 如何使用 Swift 将一对字节转换为浮点数

问题描述

我正在使用本文通过 BLE 与 IoT 传感器进行通信。在文章中,提到了这句话:

前两个字节似乎不属于数据(可能是表示它是数据包的前缀),但其余的更有趣。对于加速度计,我们得到三个有符号的 16 位整数(小端序),可以简单地缩放到我们设置的范围以获得我们的设置序列。因此,带符号的 16 位整数的 +/-2^15 范围对应于 +/-16g,从而得到因子 1/2048。为了获得以 m/s² 为单位的加速度,我们应用了 9.81/2048 的系数。因此,相应的蓝牙部分显示为:

<output char="326a9006-85cb-9195-d9dd-464cfbbae75a" conversion="int16LittleEndian" offset="2" length="2">accXRaw</output>
<output char="326a9006-85cb-9195-d9dd-464cfbbae75a" conversion="int16LittleEndian" offset="4" length="2">accYRaw</output>
<output char="326a9006-85cb-9195-d9dd-464cfbbae75a" conversion="int16LittleEndian" offset="6" length="2">accZRaw</output>

要阅读这段代码,我正在运行这段 Swift 代码:

private func sensor(from characteristic: CBCharacteristic) {
    guard let characteristicData = characteristic.value,
    let _ = characteristicData.first else { return }
    let data = characteristic.value!
    var values = [UInt8](repeating: 0, count: data.count)
    data.copyBytes(to: &values, count: data.count)
    print("values = \(values)")
}

打印后的结果是:

values = [3, 4, 250, 255, 199, 249, 91, 191]

就像文章提到的那样,我可以确认前两个字节不属于任何数据,并且一直在重复。字节值 [2-7] 不断变化,这让我更加确信这些对代表 accXRaw、accYRaw 和 accZRaw。我现在要做的是将对转换为双打。

例如:

values[2], values[3] = [250 255] (accXRaw)

values[4], values[5] = [199 249] (accYRaw)

values[6], values[7] = [91 191]  (accZRaw)

在文章中,作者通过一个 int16 little endian 来做到这一点。我想对 swift 5 做同样的事情,但不确定我是否做得正确。这是我的代码:

let xAxis = Float(bitPattern: UInt32(littleEndian: [values[2], values[3], 0x00, 0x00].withUnsafeBytes { $0.load(as: UInt32.self) }))
let yAxis = Float(bitPattern: UInt32(littleEndian: [values[4], values[5], 0x00, 0x00].withUnsafeBytes { $0.load(as: UInt32.self) }))
let zAxis = Float(bitPattern: UInt32(littleEndian: [values[6], values[7], 0x00, 0x00].withUnsafeBytes { $0.load(as: UInt32.self) }))
    
print("x=\(xAxis), y=\(yAxis), z=\(zAxis)");

结果打印输出为:

values = [3, 4, 250, 255, 199, 249, 91, 191]

x=9.1827e-41, y=8.9603e-41, z=6.8645e-41

这些数字看起来很奇怪,我怀疑我做错了什么。我是否正确阅读了字节对(至少与文章一致)?如果不是,我犯了什么错误?

标签: swiftbluetooth-lowenergy

解决方案


您的问题是您不应该使用 bitPattern 初始化程序和/或使用 UInt32(littleEndian:) 初始化程序来初始化 Float。您需要将这 2 个字节转换为 Int16,将其强制转换为 Float,然后乘以 9.81/2048 的因子以获得其加速度。

对此进行扩展,您可以创建一个数值初始化程序,该初始化程序采用符合 DataProtocol(数据或字节 [UInt8])的对象:

extension Numeric {
    init<D: DataProtocol>(_ data: D) {
        var value: Self = .zero
        let size = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: $0)} )
        assert(size == MemoryLayout.size(ofValue: value))
        self = value
    }
}

然后您可以使用子数据(两个字节)初始化您的 Int16 对象。

let bytes: [UInt8] = [3, 4, 250, 255, 199, 249, 91, 191]
let xData = bytes[2..<4]
let yData = bytes[4..<6]
let zData = bytes[6..<8]

let factor: Float = 9.81/2048

let xAxis = Float(Int16(xData)) * factor
let yAxis = Float(Int16(yData)) * factor
let zAxis = Float(Int16(zData)) * factor

print("x:", xAxis, "y:", yAxis, "z:", zAxis)  // x: -0.028740235 y: -7.6305327 z: -79.27036

推荐阅读