Search code examples
iosswiftgpsbluetooth-lowenergycbperipheral

Using Swift's Location BLE GPS To Decode location_and_speed


My application currently works as I am able to retrieve the values from the decoding location_and_speed characteristic. However, I am having difficulties reading the documentation from Bluetooth to decode/extract the values from the device.

Below is my code:

 func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor 

    characteristic: CBCharacteristic,  error: Error?) {           
            print(#function)
            print(characteristic.value?.count ?? 0)
            guard let data = characteristic.value else {return}

            let byteArray = [UInt8](data)

            // TODO: decode the value to available gps data???
    }  

Any help would be appreciated, Thank You!


Solution

  • Digging around and with thanks to xdappcactory.com I found the solution. this is the struct that I came up with, only decoding speed and location, but still usable

    // MARK: - decode helpers
    struct LocationAndSpeed {
        enum PositionStatus : Int {
            case none
            case ok
            case estimated
            case lastKnown
        }
    
        enum SpeedAndDistanceFormat : Int {
            case _2D
            case _3D
        }
    
        enum ElevationSource : Int  {
            case position
            case barometricAirPressure
            case databaseService
            case other
        }
    
        enum HeadingSource : Int {
            case movementBased
            case magneticCompass
        }
    
        let instantaneousSpeedPresent: Bool
        let totalDistancePresent : Bool
        let locationPressent : Bool
        let elevationPressent : Bool
        let headingPressent : Bool
        let rollingTimePressent : Bool
        let utcTimePressent : Bool
        let positionStatus : PositionStatus
        let speedAndDistanceFormat : SpeedAndDistanceFormat
        let elevationSource : ElevationSource
        let headingSource : HeadingSource
    
        let speed : CLLocationSpeed?
        let distance : CLLocationDistance?
        let latitude : CLLocationDegrees
        let longitude : CLLocationDegrees
        let elevation : CLLocationDistance?
        let heading : CLLocationDirection?
        let rollingTime : TimeInterval?
        let utcTime : Date?
    
        enum LocationAndSpeedError : Error {
            case invalidSize
        }
    
        init(data: Data) throws {
            guard data.count >= 10 else {throw LocationAndSpeedError.invalidSize}
          //  let byteArray = [UInt8](data)
    
            // handle the flags
            let flagsData = data.subdata(in: 0..<2)
            let flags = flagsData.withUnsafeBytes { (pointer: UnsafePointer<UInt16>) -> UInt16 in return pointer.pointee }
            instantaneousSpeedPresent = (flags & .speed) != 0
            totalDistancePresent = (flags & .distance) != 0
            locationPressent = (flags & .location) != 0
            elevationPressent = (flags & .elevation) != 0
            headingPressent = (flags & .heading) != 0
            rollingTimePressent = (flags & .rollingTime) != 0
            utcTimePressent = (flags & .utcTime) != 0
    
            positionStatus = PositionStatus(rawValue: Int((flags & .position) >> 7)) ?? .none
            speedAndDistanceFormat = SpeedAndDistanceFormat(rawValue: Int((flags & .format) >> 9)) ?? ._2D
            elevationSource = ElevationSource(rawValue: Int((flags & .elevationSource) >> 10)) ?? .other
            headingSource = HeadingSource(rawValue: Int((flags & .headinSource) >> 12)) ?? .movementBased
    
            var index = 2
            if (instantaneousSpeedPresent){
                let speedData = data.subdata(in: index ..< index + 2)
                speed = Double(speedData.withUnsafeBytes { (pointer: UnsafePointer<UInt16>) -> UInt16 in return pointer.pointee }) / 100
                index += 2
            } else {
                speed = -1
            }
    
           if (totalDistancePresent) {
                distance = 0//Double((UInt32(byteArray[index]) << 16) + (UInt32(byteArray[index + 1]) << 8) + UInt32(byteArray[index + 2])) / 10
                index += 3
            } else {
                distance = -1
            }
    
            // I don't know if this is the right data....
    
    
    
            let latData = data.subdata(in: index..<index+4)
            let lat = latData.withUnsafeBytes { (pointer: UnsafePointer<Int32>) -> Int32 in return pointer.pointee }
            latitude = Double(lat) / 10000000.0
    
            index += 4
    
            let lonData = data.subdata(in: index..<index+4)
            let lon = lonData.withUnsafeBytes { (pointer: UnsafePointer<Int32>) -> Int32 in return pointer.pointee }
    
    
            longitude = Double(lon) / 10000000.0
    
            index += 4
    
            elevation = nil
            heading = nil
            rollingTime = nil
            utcTime = nil
        }
    
        var coordinate: CLLocationCoordinate2D {
            return CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
        }
    }