Packing and Unpacking Data in PSLab Android App

In PSLab we communicate with PSLab Hardware device, to exchange data, i.e we give a command or instruction and it responses accordingly. So this giving and receiving is in terms of packed byte string. Thus, we need some solid knowledge to pack and unpack data. In python communication library, there is struct module available. In JAVA we can use NIO’s ByteBuffer or implement our own functions. In this blog post I discuss both methods.  

In Python we have struct module for packing data in byte strings. As different languages interpret data types differently like Java takes 4 bytes for int and C++ takes 2 bytes for int. To send and receive data properly, we pack data in a byte string and unpack on other side with it’s data type properties. In PSLab, we have to communicate with device for various applications like getting calibration data during power up time as raw data doesn’t make much sense until calibration is applied on it.

You also need to take care of order of sequence of bytes like there are generally two types of order in which a sequence of bytes are stored in memory location:

  • Big – Endian: In which MSB is stored first.

    Source: Wikipedia
  • Little – Endian: In which LSB is stored first.

    Source: Wikipedia

In Python

The standard sizes and format characters of particular data type can be seen in the image below.

Format C Type Python Type Standard
x Pad byte No value
c char string of length 1 1
b signed char integer 1
B unsigned char integer 1
? _Bool bool 1
h short integer 2
H unsigned short integer 2
i int integer 4
I unsigned int integer 4
l long integer 4
L unsigned long integer 4
q long long integer 8
Q unsigned long long integer 8
f float float 4
d double float 8
s char[] string
p char[] string
P void* integer

Source: Python Docs

For Packing data

import struct
struct.Struct(“B”).pack(254)   # Output ->  b’\xfe’
a = struct.Struct(“I”).pack(2544)   # Output -> b’\xf0\t\x00\x00′

Now a is the byte string that has packed value as 2544, this can be send to some device byte by byte and reconstructed on receiving side by knowing how many bytes does the data type received contains.

For Unpacking data

import struct
struct.unpack(“I”,a)  # Output -> (2544,)

In JAVA

For Packing data

Suppose you have to pack an integer, in java int takes 32 bits (4 bytes)

Using JAVA’s NIO’s ByteBuffer

byte[] bytes = ByteBuffer.allocate(4).putInt(2544).array();

If you want hardcore method to see what exactly is happening, use

byte[] intToByteArray(int value){
 return new byte[]{
     (byte)value >>> 24,
     (byte)value >>> 16,
     (byte)value >>> 8,
     (byte)value
  };
}

“>>>” is used for unsigned shifting, you can use according to your requirements.

After you have your byte array, you can easily create a string out of it and transmit.

For Unpacking data

Using JAVA’s NIO’s ByteBuffer

int fromByteArray(byte[] bytes){
int a = ByteBuffer.wrap(bytes).getInt();
return a;
}

It assumes that byte array is stored as Big Endian, if bytes in byte array is stored as Little Endian, add order() after wrap()

int fromByteArray(byte[] bytes){
int a = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt();
return a;
}

Note: Make sure the bytes array that you provide has same number of bytes as that of the data type that you are trying to unpack. For example: if you want int, bytes array should have 4 bytes as int type in JAVA has 4 bytes. If you want short, bytes array should have 2 bytes as short type in JAVA has 2 bytes.

To visualise underlying implementation, see

int from byteArray(byte[] bytes){
return bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3];
}

In all above implementation big-endian order was assumed, you can modify function if you are using little-endian or some other sequence.

References