4

I am interested in a new IoT project called OpenBCI, which is basically an open source EEG for reading and processing brain waves and other biodata. In their docs they claim that the data transmitted over-the-air (via RFDuino) sends 24-bit data. To convert the 24-bit values into 32-bit signed integers, they suggest the following Java-friendly Processing code:

int interpret24bitAsInt32(byte[] byteArray) {
    int newInt = (
        ((0xFF & byteArray[0]) << 16) |
        ((0xFF & byteArray[1]) << 8) |
        (0xFF & byteArray[2])
    );

    if ((newInt & 0x00800000) > 0) {
        newInt |= 0xFF000000;
    } else {
        newInt &= 0x00FFFFFF;
    }

    return newInt;
}

I guess I'm trying to understand exactly what is going on here. Let's take the first blurb of code:

int newInt = (
    ((0xFF & byteArray[0]) << 16) |
    ((0xFF & byteArray[1]) << 8) |
    (0xFF & byteArray[2])
);
  • Why is it safe to assume there are 3 bytes in the input?
  • What is the significance of the 0xFF value?
  • What is the purpose of the left-bitshifting (<<)?

The second segment is also a bit of a mystery:

if ((newInt & 0x00800000) > 0) {
    newInt |= 0xFF000000;
} else {
    newInt &= 0x00FFFFFF;
}
  • Why newInt & 0x00800000? What's the significance of 0x00800000?
  • Why the if-else based on postive vs. non-negative result of the above operation?
  • What is the significance of 0xFF000000 and 0x00FFFFFF?

I guess there's just a lot of handwaving and magic going on with this function that I'd like to understand better!

smeeb
  • 27,777
  • 57
  • 250
  • 447
  • That last part is an obscure way to sign-extend, the else branch is a no-op. More obvious would be `newInt = (newInt << 8) >> 8` – harold Apr 14 '16 at 20:03

1 Answers1

9

Why is it safe to assume there are 3 bytes in the input?

24 bits / 8 bits = 3 bytes

What is the significance of the 0xFF value?

It's a bit mask. 0b11111111 in binary. In this situation it's used as a quick way to convert the byte value into an int.

What is the purpose of the left-bitshifting (<<)?

Once the int value has been retrieved in the previous instruction it's shifted 16 bits to the left to take the position of the third byte of the int (counting from the right). This is due to the byte array storing the bytes in big-endian format, where the MSB comes first. The next byte is only shifted 8 bits to take up the position of the second byte, and the last one does not need to be shifted at all since it's already in the the first byte spot.

Why newInt & 0x00800000? What's the significance of 0x00800000?

Well 0x80 is another bit mask to get the MSB of a certain byte. 0x00800000 corresponds to the MSB of the third byte (i.e. the byte in byteArray[0] that was shifted 16 bits left in the previous process). This also corresponds to the MSB of the whole 24-bit value.

Why the if-else based on postive vs. non-negative result of the above operation?

Determining whether the MSB of the 24-bit value is 1 or 0 will tell us if it's negative or positive number, respectively, in two's complement notation.

What is the significance of 0xFF000000 and 0x00FFFFFF?

If the number is negative in it's 24 bit representation, we also want it to be negative as a 32-bit value in two's complement, that's why we have to fill up the 4th and final byte with 0b11111111 (0xFF) using the OR operator.
If it was already positive then the fourth byte can be 0x00, with the following 3 bytes being the same as the original 24 bit value - accomplished by masking with 0x00FFFFFF.

James Buck
  • 1,640
  • 9
  • 13
  • Awesome, awesome answer @James Buck (+1) - thank you! Just curious, if I was implementing this in Java, and needed a unit test to confirm whether this works or not, where might I find 24-bit signed integers and their corresponding 32-bit signed values, to verify the test results with? :-) Thanks again! – smeeb Apr 15 '16 at 00:27
  • @smeeb Well you could pick any random 24 bit number, put them in a two's complement binary to decimal converter, such as [this](http://www.exploringbinary.com/twos-complement-converter/). Then you can put that same binary number in a 3 byte array and the above code should convert it to a 32 bit integer with the exact same value as the calculator. This code will ensure any 24-bit value has it's value preserved when converted to a 32-bit format. – James Buck Apr 15 '16 at 17:12
  • Hi, @JamesBuck could you please help me by showing a sample input and output for the given function (interpret24bitAsInt32(byte[] byteArray))? I'm really confused with this function's input & output. The OpenBCI doc said they have sent the three bytes into this function at once. Sample three bytes I have captured from the device are - 01 42 BB (values are 24-bit signed, MSB first). For these three values, how should I pass the parameters into the function and what should be the output of these three values? – Samitha Wijesekara Feb 12 '23 at 19:08