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.
- Little – Endian: In which LSB is stored first.
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.
You must be logged in to post a comment.