At UpDownLeftRight I’ve been putting together a demo that speaks with our hardware prototype. As crazy as it sounds I’ve been using JavaScript to do this.
When creating a demo speed of creating is the most important factor. We needed it to work on an iPad and already had some of the JavaScript code written, so choosing PhoneGap was a no brainer.
I’ve used it in the past and although I wouldn’t recommend it for most applications, it certainly is great for creating prototypes and demos.
Reading byte streams
Our hardware has a lot of information about movement and gestures that it needs to send over the wire. To do this efficiently it sends a stream of 8 bit integers that can be transformed into the original dataset.
Doing these transformations was my first practical project working with binary, bits and bytes, so it’s been fun wrapping my head around it.
JavaScript doesn’t really handle the stream too well:
console.log(steam)
// ""
In order to read it you have to convert it in to an array of bytes:
var bytes = new Uint8Array(stream);
// [255, 0, 64, 47, 180 .. ]
You can then convert your bytes in to binary, bits, hexadecimal, decimal or floats. All of the representations have their uses and how to convert from one to the other is not exactly obvious.
To understand how the conversions work you can use these online tools.
I’ve also created a JavaScript plugin to help you.
Upper and Lower nibbles
Sometimes the original data is longer than 8 bits. To encode this data it is split into multiple parts — nibbles. 8 bits can store integers 0 to 255. So lets say we wanna encode 500 low level this is what we do.
500 in binary is 0000000111110100 read form the right, that is 16 bits. So we would split it in two nibbles 00000001 (upper) and 11110100 (lower).
In our Uint8Array these would show up as their byte values [1, 244]
. In order to read this as the full 16 bit we have two options:
- Convert to hexadecimal, concatenate and convert to decimal.
- Convert to binary, concatenate and convert to decimal.
1. Convert to hexadecimal
var upper = ConvertBase.byte2hex(1); // 1
var lower = ConvertBase.byte2hex(244); // F4
var hex = upper + lower // 1FA
var dec = ConvertBase.hex2dec(hex); // 500
2. Convert to binary
var upper = ConvertBase.byte2bin(1); // 00000001
var lower = ConvertBase.byte2bin(244); // 11110100
var binary = upper + lower // 0000000111110100
var dec = ConvertBase.bin2dec(binary); // 500
Converting from ints to floats
What happens if the source data is a float? There are a couple of ways to store floats as ints. On our particular hardware a float is converted to an int by multiplying with a sufficiently large number and rounding the result.
The number that is used to multiply stays constant and to convert the resulting ints underlaying float value we need to know that constant. To get the float value we divide the int with the constant, which is why we call it the fraction. Our fractions follow a pattern and can be referred to as fraction 1, 2, 3, etc. Fractions are simply 1 in binary right shifted by the fraction number.
Fraction | Binary | Decimal |
---|---|---|
1 | 00000010 | 2 |
2 | 00000100 | 4 |
3 | 00001000 | 8 |
4 | 00010000 | 16 |
5 | 00100000 | 32 |
... | ... | ... |
Let’s say a float is encoded with fraction 14 into a 16 bit signed integer. To decode it we would right shift 1 by 14 and divide our int with the result:
var integer = 26624;
var fraction = 1 << 14; // 16384
var float = integer / fraction; // 1.625
A signed 16 bit int can only store -32767 to 32767. Given that we’re using fraction 16384 we can easily calculate the bounds of the floats we can store.
var floatBound = 32767 / fraction; // 1.999938965
Meaning we can store floats from -1.99 to 1.99.
In my gist you will find signedIntToFloat(num, fraction)
and unsignedIntToFloat(num, fraction)
to help you with these conversions.
Converting from unsigned to signed
Most of our output is unsigned ints, but in some cases we need to convert from unsigned to signed. Here’s the solution from my gist.
unsignedToSignedInt: function(number) {
// if the number is desired as a float from an int with a binary point
// convert the unsigned int to a float with the binary point at the {fraction} bit
// convert from unsigned int to signed
var b = new ArrayBuffer(2);
var u = new Uint16Array(b);
var i = new Int16Array(b);
u[0] = number;
var signedNumber = i[0];
return signedNumber;
}
Bitwise operations
I’m far from confident working with on a bit level, so for me I much prefer to convert to integers and floats as soon as possible. But if you’re so inclined JavaScript actually gives you the option to work at a bit level using bitwise operators:
Operator | Usage | Description |
---|---|---|
Bitwise AND | a & b |
Returns a one in each bit position for which the corresponding bits of both operands are ones. |
Bitwise OR | a | b |
Returns a one in each bit position for which the corresponding bits of either or both operands are ones. |
Bitwise XOR | a ^ b |
Returns a one in each bit position for which the corresponding bits of either but not both operands are ones. |
Bitwise NOT | ~ a |
Inverts the bits of its operand. |
Left shift | a << b |
Shifts a in binary representation b (< 32) bits to the left, shifting in zeroes from the right. |
Sign-propagating right shift | a >> b |
Shifts a in binary representation b (< 32) bits to the right, discarding bits shifted off. |
Zero-fill right shift | a >>> b |
Shifts a in binary representation b (< 32) bits to the right, discarding bits shifted off, and shifting in zeroes from the left. |
Sources
- https://bocoup.com/weblog/getting-bitwise-with-javascript/
- http://stackoverflow.com/questions/3756880/best-way-to-get-two-nibbles-out-of-a-byte-in-javascript
- http://stackoverflow.com/questions/9354860/how-to-get-the-value-of-a-bit-at-a-certain-position-from-a-byte
- http://michalbe.blogspot.co.uk/2013/03/javascript-less-known-parts-bitwise.html
- http://ss64.com/convert.html