Reconstruct uV from ADS1299 24-bit value broken into 3 bytes in matlab

edited December 2016 in Other Platforms
Hi there,

Firstly, apologies if I'm breaking the forum rules by asking a question about a non openBCI product. I am using an openBCI board in the project I'm working on, but am currently focusing on passive/active sensor designs.
I have bought an EEG acquisition system which uses active sensors. The software which comes with the device is limited and not of much benefit to me.
For the moment I'm just trying to get the native system working before trying the active sensors with the openBCI board.

The device uses the ADS1299 as well, with a microcontroller (unknown brand/model), and transmits the data over bluetooth to a USB dongle at 3,000,000 baud.
The packet is 21 bytes long. The only bytes of interest to me are:

 -  Byte 1 = Start byte = 255 (unambiguous value, i.e. can't appear as any other data byte)
 -  Byte 2 = packet counter
 -  Byte 3 = ADC Channel 1 - MSB
 -  Byte 4 = ADC Channel 1 - LSB2
 -  Byte 5 = ADC Channel 1 - LSB1

In other words, it breaks the 24-bit ADC value into 3 bytes and transmits them in the order MSB, LSB2, LSB1.
I'm writing a Matlab script to parse and interpret the data. Parsing the data is fine:

  starts = find(rawData == 255);
  ADC_Bytes = rawData([starts+2 starts+3 starts+4]);

My issue arises now when trying to reconstruct the ADC value. The support documentation only gives the following information to obtain the ADC value.

 "The formula for reconstructing to a 2's complement, signed int32 is:
     data = (MSB << 24 + LSB2 << 17 + LSB1 << 10)
  The conversion factor for EEG channel to volts is:
     EEG = data*(5/3)*(1/2^32) "      % I'd imagine this corresponds to (ADC_Value*Vref)/2^32 but there's a programmable gain of 3 setand Vref is 5, so hence the 5/3.

Using this method in Matlab with the following lines:

   ADC_Value = (bytes(:,1)*(2^24)) + (bytes(:,2)*(2^17)) + (bytes(:,3)*(2^10));
   ADC_Value_Volts = (ADC_Value*(5/3))*(1/(2^32));

The output of ADC_Value is in the range 416712704 to 420345856.
The output of ADC_Value_Volts is in the range 0.1617 to 0.1631 volts. This should be in the microvolts range instead of this which is is 160 millivolts roughly.

Using the given software, I can record and export an EDF file of the data. Importing the EDF file into Matlab, the range of values are bounded by -16384 and 16384. But the actual EEG data range varies around -100 to 500 (microvolts) depending on the REF and GND contact quality. The picture below shows about 2 mins of EEG recording, starting/finishing with me taking on/off the sensors, hence the bouncing between rails of -16384 to 16384. 

image

Can someone please shed any light on the conversion method?
I don't understand the method of left shifting the bytes 24, 17 and 10 respectively?
I have tried left shift of 16, 8, 0 (which to me makes more sense) and scaling by EEG*(5/3)*(1/2^24) instead. The output values are again in the range of 0.1590 to 0.1597.

Or does any one have any matlab code/ideas for reconstructing a 24-bit ADC value as a precision number?
The only information I have been able to obtain from the provider on this is the 2 lines of code shown above.

Again, apologies if this post disregards any rules. I will be able to compile a comparison of various passive/active sensors used with the openBCI once I am finished though which is of benefit to the openBCI community.

Thanks,

Mark

Comments

  • wjcroftwjcroft Mount Shasta, CA
    Mark, hi.

    A couple comments.

    You show the packet layout and say the start byte is "(unambiguous value, i.e. can't appear as any other data byte)". This is not true, at least on OpenBCI. The ADC data values especially the low order bytes, can take on any values. So the start byte is helpful, but not a guarantee you are synced.

    re: sample Matlab code, another approach see here,

    http://openbci.com/forum/index.php?p=/discussion/359/direct-matlab-serial-interface-vs-lsl

    re: decoding bytes and microvolts, see last section of,

    http://docs.openbci.com/software/02-OpenBCI_Streaming_Data_Format

    The shift values you show (24 etc.) don't look right.

    Regards,

    William

  • Hi William,

    Thanks for your time.
    The support documentation explicitly states that "by design, no other byte in the data stream can have the value 0xFF making it an unambiguous start code".

    I tried using atom2626's code for parsing and interpreting as int32.
    I read 11000 samples from the serial port (at 250 Hz sample rate) and did the following:

        starts = find(rawData == 255);
        bytes = rawData([starts+2 starts+3 starts+4]);
        byte_a = uint8(0);
        byte_b = uint8(bytes(:,1));
        byte_c = uint8(bytes(:,2));
        byte_d = uint8(bytes(:,3));
        int32_data = zeros(length(byte_b),1);
        
        for i = 1:size(byte_b)
            % check if it is a negative number
            if bitand(uint8(byte_b),uint8(128),'uint8')
                % extend the sign bit, we are dealing with two's complement
                byte_a = bitor(byte_a,uint8(255),'uint8');
            end
            % Typecast 4 bytes to 32bit signed int
            % swpabytes from Big Endian to Little Endian
            int32_data(i,1) = swapbytes(typecast([byte_a byte_b(i) byte_c(i) byte_d(i)],'int32'));
        end

    This results in values in the range 1229320 to 1722514.
    When I use the scaling formula from openBCI's documentation: (4.5/3)*(1/(2^23-1)
    I get values in the range of 0.2 Volts again.
    When I use the scaling formula from the supplier's documentation: (4.5/3)*(1/(2^32)
    I get values in the range of 500 microVolts.
    Which is in the right range.
    However, it is mostly a 65 Hz wave (I'm in Ireland where mains is 50 Hz, so I don't think its related to mains).
    When I filter out the 65 Hz, I'm left with barely nothing.
    So this method doesn't seem to work either.

    When I use the software which comes with the device, the EEG signal is definitely correct, so it's not a hardware issue.

    Interestingly, LSL does have an App for the device (it's a Cognionics device).
    And the method used for reconstructing the EEG data uses the same MSB<<24, LSB2<<17,LSB1<<10 method as shown in the documentation. Then uses a scaling factor of (1/2^32).

    I can't use LSL for this project however as I need to directly compare multiple devices simultaneously and to do so properly, I want use the same real-time signal processing methods from my own Matlab script. And from reading through the LSL apps for the various devices, there are quite a few discrepancies with parsing/scaling used in comparison to the suppliers documentation.

    So, in other words, until I can mange to correctly reconstruct the EEG data in Matlab, I'm at a stand-still unfortunately.
    The shifting values of 24, 17 and 10 has me completely stumped, and the fact that the LSL code uses it too ensures that it's not an error.

    Thanks for your time and help.

    Mark

  • wjcroftwjcroft Mount Shasta, CA
    Suggest emailing Cognionics directly, as this is their device. They are clearly not sending the raw ADS1299 values, but their derivation.

    The whole idea of using LSL is to interface with multiple devices simultaneously. And it comes with great Matlab support,



  • wjcroftwjcroft Mount Shasta, CA
    "When I filter out the 65 Hz, I'm left with barely nothing."

    Have you tried a simple experiment of seeing the FFT alpha peak at O2 or Pz with your eyes closed? Both with the Cognionics software and with your Matlab routine, based on Cognionics instructions?

    You may be parsing the Cognionics stream correctly, but have not sent it the proper setup instructions to get it started. If the LSL driver works, study it to see exactly what they do with the serial port stream.

Sign In or Register to comment.