3

I obtain magnetometer trim register as get NSData() that looks as follows:

<00001a1a 4f56f202 00000000 1dfd421b>

I need to convert it to Int8, UInt8, Int16, UInt16 depending on which byte I access.

Sources from docs:

s8 dig_x1;/**< trim x1 data */
s8 dig_y1;/**< trim y1 data */

s8 dig_x2;/**< trim x2 data */
s8 dig_y2;/**< trim y2 data */

u16 dig_z1;/**< trim z1 data */
s16 dig_z2;/**< trim z2 data */
s16 dig_z3;/**< trim z3 data */
s16 dig_z4;/**< trim z4 data */

u8 dig_xy1;/**< trim xy1 data */
s8 dig_xy2;/**< trim xy2 data */

u16 dig_xyz1;/**< trim xyz1 data *

enter image description here

The main problem is how to access a selected byte in NSData to convert it manually either to Int8 or UIint16 etc?

Generally, how to approach such problem? Should look for a way to manually iterate over NSData and convert each value manualy as well?

theDC
  • 6,364
  • 10
  • 56
  • 98

1 Answers1

4

You can convert data.bytes + offset to a pointer of the appropriate type and then dereference the pointer:

let dig_x1 = UnsafePointer<Int8>(data.bytes).memory
let dig_y1 = UnsafePointer<Int8>(data.bytes + 1).memory
// ...
let dig_z1 = UnsafePointer<UInt16>(data.bytes + 4).memory
let dig_z2 = UnsafePointer<Int16>(data.bytes + 6).memory
// ...

(Note: Here it is assumed that all values in that binary blob are property aligned for their type.)

The data is in little-endian byte order, which is also what all current iOS platforms use. To be on the safe side, convert the data to host byte order explicitly:

let dig_z1 = UInt16(littleEndian: UnsafePointer(data.bytes + 4).memory)
let dig_z2 = Int16(littleEndian: UnsafePointer(data.bytes + 6).memory)
// ...

An alternative is to define a C structure in the bridging header file

struct MagnetometerData {
    int8_t dig_x1;
    int8_t dig_y1;

    int8_t dig_x2;
    int8_t dig_y2;

    uint16_t dig_z1;
    int16_t dig_z2;
    int16_t dig_z3;
    int16_t dig_z4;

    uint8_t dig_xy1;
    int8_t dig_xy2;

    uint16_t dig_xyz1;
} ;

and extract the data in one step:

var mdata = MagnetometerData()
data.getBytes(&mdata, length: sizeofValue(mdata))

This works (if there is no padding between the struct members) because Swift preserves the layout of structures imported from C.


A possible Swift 3 implementation of the first approach is

let dig_x1 = ((data as NSData).bytes).load(as: Int8.self) 
let dig_y1 = ((data as NSData).bytes + 1).load(as: Int8.self)
// ...
let dig_z1 = ((data as NSData).bytes + 4).load(as: UInt16.self) 
let dig_z2 = ((data as NSData).bytes + 6).load(as: Int16.self) 
// ...

Again it is assumed that all values are property aligned for their type.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Thank you very much! One more tiny question: using the method you posted z1 evaluates to 22095, is it a plausible and possible value for z1 in magnetometer trim register? Also, nonetheless it's UInt16 or Int16 the value remains the same, is it correct? – theDC Sep 08 '16 at 12:30
  • It concerns me that regardless whether I set to UInt16 or Int16 the result is the same, is it correct for two's complement? – theDC Sep 08 '16 at 12:37
  • @DCDC: For numbers in the range 0..32767 there is no difference. – Usually the sensor data must be multiplied with some floating point number to get the "real" value, that should be documented for your device, there is no "general formula". – Martin R Sep 08 '16 at 12:39
  • Yes it is documented for sensor data, but I don't have any ranges/methods mentioned to convert magnetometer register trim values, so I took for granted that I don't have to do that. – theDC Sep 08 '16 at 12:41
  • Could provide edit also for Swift 3? I read your post here: http://stackoverflow.com/a/38024025/4286947 But since there are no bytes to skip(I use the first method you privided: data.bytes+1 etc) I'm not sure how to apply it using Swift 3. Thanks in advance! – theDC Sep 20 '16 at 08:31
  • Thanks, maybe it's better idea to start a new question? – theDC Sep 20 '16 at 08:45
  • Lots of appreciation, you're a life saver @martin-r – theDC Sep 20 '16 at 09:10