Regarding Sampling: Increasing Sampling Rate / Having Known Sampling Instants

yttuncelyttuncel Ankara
edited April 2017 in Cyton
Hello all,

I've been discussing with @wjcroft and @pushtheworld for some time now and the focus of our discussion has shifted to hardware related issues, so I decided to reinitialize the discussion under hardware forum as well.

I have 2 questions/aims that I would like to hear about from you:

1) Increasing Sampling Rate. There are lots of topics all around the oBCI forums. Right now there is a bottleneck in the serial communication between the PIC32 and RFduino on the board. Bluetooth 4 seems to support higher throughput, so its not the limiting factor in this case. Since I'm new to the community I do not know much about the previous attempts to overcome this issue. There are alternatives, @Winslow_Strong's usb communication and @yj's SD card only methods eliminate the RFduino and allow for higher sample rates. Is there any other similar work/attempt? What is the current situtation/state of the art with this problem?
I would like to reduce resolution (eliminating noisy bits, 24bits to 16bits if possible) and remove some unused EEG & AUX channels to allow for 500sps. As I'm new, I do not know where exactly the protocol is implemented and which files I need to modify (I do need changes in host and device RFduino firmwares (for protocol changes) and PIC32 firmware (for resolution and protocol changes). I'd be glad if someone showed the files I need to make changes at.

2) Having known sampling instants (with reference to start of stimulus). I want to have my first sample at a known instant for better averaging. Initially I thought interrupting the PIC32 with trigger input to command ADS to start sampling would be a good idea but @wjcroft corrected me by saying that ADS has to run/sample continuously. @pushtheworld gave me a great advice, to start the stimulus at the first sampling instant after "start stimulus" button is pressed on my system (I'm using an FPGA for stimulus generation). I would love to hear others opinions on the matter as well before implementing the mentioned idea.

Thank you for the great product and community!
Yigit

Comments

  • So your going to want to do all of this is the Pic32 code and not touch the rfduino code. I'd say if you got rid of all the aux bytes you only have 31 bytes per message at a max rate of 250 packets per second. So in theory you could do 500 SPS at 16bits for 7 channels for a total of 28 bytes. 8 would not work because 2*16 equals 32... but yea I see NO reason why we could not, once made, add this to the examples section and just use a next free stop byte for it.
  • yjyj France , Bordeaux
    @yttuncel

    Hello yttuncel,

    If you are ready to modify the pic32 code and the pc code (device and host) then you could compress the 16bit data by a factor of 4, using ADPCM : see    "Adaptive Differential Pulse Code Modulation Using PIC® Microcontrollers".
    Look for "Microchip application note AN643" on google...

    The quality is very good, I verified it under matlab...
    I found only one probleme : If some data were lost then the resulting shift (or step) is kept, the error is kept... With adpcm we need an "error free" communication.

    This way you will keep some free room (31-2x16/4) bytes , enough to tag and much more,  the record with your FPGA-trigger.

    y.j.


  • @pushtheworld
    Is this doable without touching the RFduino codes? I assumed not, but if they check the start and stop bytes and not the length of the packet, then it should be done without modifying them. So in PIC32 code I will both decrease sample length (3bytes to 2bytes) and number of transmitted channels, right? 

    I will take your advice into consideration, that's interesting. I did not understand why the kept error is a problem though. Isn't it the case with current connection as well? Is there a method to account for lost data (some kind of interpolation) with current approach?
  • yjyj France , Bordeaux
    Isn't it the case with current connection as well?

      Yes , "every thing is relative"

    If we lost raw datas, the values of the new incoming datas are true.

    After ADCPM "uncompression" the new uncompressed values are no longer reliable. One no longer know where  we stand.
    This makes me think that we could time to time , send some  raw data from one chanel... in order to reset the algo combined with "time stamping" the data to know if some miss.

    I am not an expert in this area and I do not know if there is a method to account for ADPCM lost data .

    If you dont need 8 channels and 24bits/sample , then yes, what you plan to do is the more simple (and the more simple is the best...)

      y.j.

  • You don't need to change any of the RFDuino code as long as you always send 33 bytes per packet to the rfduino from the pic, where the first byte is 0x41 then 31 bytes of data, then 0xCX, where X is 0-F. This is written about much better in the learning section on the website, there is an example or two as well.
  • @yj
    I see now. I will first look into the algorithm, discuss with my colleagues and share with you if I have any ideas. Thank you.

    But if I remove unused channels from the packet: 4 channels and 1 aux channel (2 bytes per each channel) we have 10 bytes and start and stop bytes for a total of 12 bytes. What I'm saying is if rfduino firmware is looking at the length of a packet I need to change it. If it is only checking start and stop bytes then I do not need to change it. But from what you are saying, if I reduce the number of bytes per packet I need to change rfduino firmware as well. I will look into it.
  • 1) how many channels do you want?
    2) YES! That's exactly correct, you would need to change the rfduino code to send less bytes per packet. Also remember you could send 3 samples per packet if you only have 10 bytes of data.
  • wjcroftwjcroft Mount Shasta, CA
    @yttuncel, as AJ said, it's better to put multiple samples per 31 byte packet. The radio packet rate will remain at 250 hz. It's likely not possible to have a radio packet rate of higher than that.

  • @pushtheworld

    1) 4 will suffice with VEP studies. Higher sampling isn't that necessary for ERP studies anyway, so one can do ERP recording with more channels at native (250hz) sampling rate.
    2) Sampling at 750hz and sending at 250hz seems feasible to me as well. This way I do not need to think about radio packet rate as @wjcroft mentioned.

    Alright I will try implementing, I'll keep asking if I get stuck.

    Thank you!
  • yttuncelyttuncel Ankara
    edited December 2016
    Another question. What is the reason behind the PIC -> RFduino bottleneck? I know rfduino works at higher baud rate. Is this caused by PIC (not likely the case in my opinion).

    addendum: I want to start with a more basic approach. I will sample at 2khz and store data in SD. I will send every 8th sample to PC. Isn't it very easy to do with a slight modification to: https://github.com/OpenBCI/OpenBCI_32bit_Library/blob/master/examples/BoardWithSD/BoardWithSD.ino ? Just increment a counter at every avaliable channel data and call send command when the counter reaches 8?
  • wjcroftwjcroft Mount Shasta, CA
    The PIC to RFduino serial port bottleneck is because running the RFduino receive serial port faster than 115200 baud, results in loss of data because of interrupt service routines from the radio link, lock out the non-interrupt based serial receive loop. The RFduino serial UART hardware has some internal buffering capability, but not enough to go faster than 115200 without data loss.

    Note that even if this bottleneck were solved, the radio link itself limits the max throughput available; both in terms of raw bit rate and number of packets per second. Unclear what that limit is exactly, but it is surely not 'double' of the 250 hz default rate currently used.

    One possible solution for the PIC -> RFduino serial port limitation (that I've mentioned before), is to code a very short high level interrupt routine (in assembler, not C) on the RFduino side. This interrupt level would be higher than the radio interrupt level. And the interrupt routine would only be a few instructions in length. Basically just storing the incoming byte into a buffer and exiting the interrupt immediately. This seems like the radio and serial port would not have the interference issue with that solution. But... there is still the next level bottleneck of the radio link throughput. So this doesnt buy that much.

    The real solution for faster speed has been discussed on other threads: you can choose one of these approaches: wired optoisolated USB; faster Bluetooth 3.x vs the limitations of Bluetooth 4.x (BLE); or wifi. AJ has a wifi prototype working with his next gen firmware.

  • @wjcroft
    But don't we know the exact same chip works at 1M baud at host side? I mean the same interrupt hierarchy should follow on the PC side, only difference being that there is a serial (usb) transmit loop being interrupted by the radio link instead of the receive loop in obci side. Is usb protocol what makes the difference between the two sides?

    Bluetooth throughput is in the order of Mbits/s, isnt it? Why are you saying "there is still the next level bottleneck of the radio link throughput"?

    There shouldn't be a problem with your suggestion iff this short receive interrupt is not causing any issues with the ongoing transmission (I doubt that uC on the RFduino can run a few instructions (I do not know the exact clock speed) without affecting the transmission, assuming that the transmission speed is very fast. Wouldn't the interrupt cause dropped bits/corrupt packets if this is the case?)

    Do you have any comments about my addendum in the above post?
  • wjcroftwjcroft Mount Shasta, CA
    edited December 2016
    Yigit, hi.

    re: interrupts, no, as I mentioned the RFduino radio interrupts are what are causing the data lossage. The receive side loop on the 'device' (mainboard) RFduino is much more sensitive. This is because the UART hardware receive buffer is only so big. If more data arrives than it holds, the data is lost. This happens during radio interrupts when the main loop (non interrupt level) is locked out for extended periods. It is true that AJ added the ability to change the host side baud rate, but this does not address the bottleneck on the device RFduino. I'm unclear how the UART is arranged on the host side, but it does not have the same lockout issue. 

    re: Bluetooth data rates. No, look at the specs for BT 3 vs BT 4. BT 4 (BLE) is "low energy" and much lower throughput than BT 3. It's intended for "always on" devices such as chest straps, physio monitoriing, etc. Powered by small batteries. BT 3 is used for example in high quality audio applications, where a lot of data is being moved. The RFduino is a BT 4 device which happens to have a special mode called GZLL, which pushes more data than the standard BT 4 protocols. But it still cannot reach BT 3 rates. Not by a long shot.

  • Hello again,

    I'm trying to increase the sampling rate, store all the samples in the SD card and send every 8th sample to PC. For this I have done the following:

    1- Increased the sampling rate by writing "10110011" instead of "10110110" to ADS's register (2khz instead of 250hz) in "OpenBCI_32bit_Library.cpp". Last 3 bits determine the sampling rate. 

    WREG(CONFIG1,0xB3,BOARD_ADS);

    2- Changed the firmware on the board as follows (I've highlighted the parts which are relevant):

    #include <DSPI.h>
    #include <OBCI32_SD.h>
    #include <EEPROM.h>
    #include <OpenBCI_32bit_Library.h>
    #include <OpenBCI_32bit_Library_Definitions.h>

    // Booleans Required for SD_Card_Stuff.ino
    boolean addAccelToSD = false; // On writeDataToSDcard() call adds Accel data to SD card write
    boolean addAuxToSD = true; // On writeDataToSDCard() call adds Aux data to SD card write
    boolean SDfileOpen = false; // Set true by SD_Card_Stuff.ino on successful file open

    int triggerPin = 18;        // the CNY17 Collector is on pin 18
    int LED = 11;              // the LED
    int triggerValue;        // used to hold the latest trigger reading
    int analogValue;        // used to hold the latest sensor reading
    int lastTriggerValue;    // used to remember the latest trigger state
    boolean state = HIGH;        // used to toggle the on-board LED
    int cnt = 0;
    char sdChar = 'A'; // 5min SD record

    void setup() {
      pinMode(triggerPin, INPUT);        // set the button pin direction
      triggerValue = lastTriggerValue = digitalRead(triggerPin); 
      
      // Bring up the OpenBCI Board
      board.begin();
      // Init SD Card
      sdProcessChar(sdChar);
      // Notify the board we want to use accel data
      board.useAccel = false;
      board.useAux = true;
      
    }

    void loop() {
      if (board.streaming) {
        if (board.channelDataAvailable) {
          //increment a counter
          cnt=cnt+1;
          // Read from the ADS(s), store data, set channelDataAvailable flag to false
          board.updateChannelData();

          //Digital Trigger Read
          triggerValue = digitalRead(triggerPin);    // feel the trigger pin
          if (triggerValue != lastTriggerValue){  // if it's changed,
            if (triggerValue == LOW){    // if it's gone from HIGH to LOW
                   // 0x6220 converts to PI in GUI
                   board.auxData[0] = 0x6220; 
                   state = !state;        // toggle the state variable
                   digitalWrite(LED,state);    // toggle the LED for user useability
            }
            lastTriggerValue = triggerValue; // keep track of the changes
          }

          //Analog Trigger Read
          analogValue = analogRead(A7);
          board.auxData[1] = analogValue;

          
          // Verify the SD file is open
          if(SDfileOpen) {
            // Write to the SD card, writes aux data
            writeDataToSDcard(board.sampleCounter);
          }
          if(cnt==8){
            cnt=0;
            if (board.timeSynced) {
              // Send time synced packet with channel data, current board time, and an accel reading
              //  X axis is sent on sampleCounter % 10 == 7
              //  Y axis is sent on sampleCounter % 10 == 8
              //  Z axis is sent on sampleCounter % 10 == 9
              board.sendChannelDataWithTimeAndRawAux();
            } else {
              // Send standard packet with channel data
              board.sendChannelDataWithRawAux();
            }
          } 
        }
      }
      // Check serial 0 for new data
      if (board.hasDataSerial0()) {
        // Read one char from the serial 0 port
        char newChar = board.getCharSerial0();

        // Send to the sd library for processing
        sdProcessChar(newChar);

        // Send to the board library
        board.processChar(newChar);
      }
    }

    Instead of waiting 'A' from python, variable sdChar tells it to create a 5min long file on the SD Card.

    Results:

    1- It creates 3 .txt files on the SDcard, 2 of which are empty. Last one is not empty, but I cannot get MATLAB to read it with csvread or EEGLAB. I cannot observe the characters inside the file, it is similar to:

    image
    but instead of "2016..." there are lots of "Xp"s all around between the "NUL"s and other nonascii characters. Is this expected, if so, how do I read this file? I assume those are hex values, but how do I convert them?

    How can I verify the samples sent to the computer are in fact every 8th sample? I tried to use the GUI, as it stores the data with sample counter in the first column, but it is not working.

    65, 732.42, 732.42, 732.40, -732.42, 732.42, 732.42, 732.42, 732.42, 0.00, 0.00, 3.42
    0, -91913.21, -187500.02, -187500.02, 187500.00, -187500.02, -187500.02, -187500.02, -187500.02, -4.10, 0.00, 0.00
    65, 3662.11, 732.42, 732.40, -732.42, 732.42, 732.42, 732.42, 732.42, 0.00, 0.00, 3.94
    65, 5126.95, 732.42, 732.40, -732.42, 732.42, 732.42, 732.42, 732.42, 0.00, 0.00, 1.50
    65, 6591.80, 732.42, 732.40, -732.42, 732.42, 732.42, 732.42, 732.42, 0.00, 0.00, -0.51
    65, 8056.64, 732.42, 732.40, -732.42, 732.42, 732.42, 732.42, 732.42, 0.00, 0.00, -1.89
    65, 9521.49, 732.42, 732.40, -732.42, 732.42, 732.42, 732.42, 732.42, 0.00, 0.00, -3.07
    65, 10986.33, 732.42, 732.40, -732.42, 732.42, 732.42, 732.42, 732.42, 0.00, 0.00, -4.00
    65, 12451.17, 732.42, 732.40, -732.42, 732.42, 732.42, 732.42, 732.42, 0.00, 0.00, 3.58
    65, 13916.02, 732.42, 732.40, -732.42, 732.42, 732.42, 732.42, 732.42, 0.00, 0.00, 3.07
    65, 15380.86, 732.42, 732.40, -732.42, 732.42, 732.42, 732.42, 732.42, 0.00, 0.00, 2.62
    65, 16845.71, 732.42, 732.40, -732.42, 732.42, 732.42, 732.42, 732.42, 0.00, 0.00, 2.27
    65, 18310.55, 732.42, 732.40, -732.42, 732.42, 732.42, 732.42, 732.42, 0.00, 0.00, 1.98
    65, 19775.39, 732.42, 732.40, -732.42, 732.42, 732.42, 732.42, 732.42, 0.00, 0.00, 1.73
    65, 21240.24, 732.42, 732.40, -732.42, 732.42, 732.42, 732.42, 732.42, 0.00, 0.00, 1.50
    65, 22705.08, 732.42, 732.40, -732.42, 732.42, 732.42, 732.42, 732.42, 0.00, 0.00, 1.31
    65, 24169.92, 732.42, 732.40, -732.42, 732.42, 732.42, 732.42, 732.42, 0.00, 0.00, 1.18
    65, 25634.77, 732.42, 732.40, -732.42, 732.42, 732.42, 732.42, 732.42, 0.00, 0.00, 1.09
    65, 27099.61, 732.42, 732.40, -732.42, 732.42, 732.42, 732.42, 732.42, 0.00, 0.00, 1.02
    65, 28564.46, 732.42, 732.40, -732.42, 732.42, 732.42, 732.42, 732.42, 0.00, 0.00, 0.99
    65, 30029.30, 732.42, 732.40, -732.42, 732.42, 732.42, 732.42, 732.42, 0.00, 0.00, 0.93
    65, 31494.14, 732.42, 732.40, -732.42, 732.42, 732.42, 732.42, 732.42, 0.00, 0.00, 0.93

    sample counter is almost always 65. Do you have any comments?
  • yjyj France , Bordeaux
    edited December 2016
    @yttuncell ,

    - Do you have aproximately 5 x 60 x 250 lines in the  last "not empty"  5' recorded txt file?

    - What do you get in the *.txt files from the un-modified code?
    (Some SD cards that perfectly work on your P.C., will not be usable on the OBCI Card)

    - On which code modification does the result become wrong?

    I suppose that you already tried these points...
    Have you made any progress, gained any further indices?

    Your approach is interesting,
    keep in mind that in general, but mostly at 2kHz, data will be lost during wait busy time before block writing on the SD...
    http://openbci.com/forum/index.php?p=/discussion/546/solution-for-session-freezing-overruns-2khz-sample-rate-with-sd-only/p1

       Sincere encouragements,
        y.j.


  • yttuncelyttuncel Ankara
    edited December 2016
    @yj

    Hello,

    I've uploaded the default firmware to check for errors. So with the unmodified code, from python interface:

    - When I generate a file with command 'a', the corresponding file has 16384 rows and 16 columns. Address counter goes to 262.128.
    - I've installed a hex viewer/editor. What do I need to see in the .txt file? Does it store the samples as described in here? Are all 33 bytes stored in the txt file? How can I modify what is stored in the card, from SD_Card_Stuff.ino? How do I visualize the stored data in the SD Card?
    - Once I understand what is stored in the SD card in the default firmware, I will continue on with my modifications.

    Example Images:
  • yjyj France , Bordeaux
    @yttuncel,

    The command 'a' generates à 512 blocks file, a block 512 bytes ;   512*512 = 262.144 = 262.128 (last value of your counter) +16 bytes (content of the last line)    as you write. Thus it seems correct.

    You should see at the end of this file , an ascii written footer ; 512 bytes , unused filled with zeros.

    Do you use the v2 from pushtheworld?

    Any way the best way to understand is to look at the code.

    In the v1 the function that writes on the SD is  :
     void writeDataToSDcard(byte sampleNumber)
     
    Strange : in the ascii window of your dump file NGGOG I do not see the commas.
    In the v1 txt files were binary hexconverted and comma limited, starting by the sample number, ending, if required, with 6 bytes of auxiliary datas...
    In your dump I see mostly binary data...
    May be things have been changed in the V2... even if "txt" file extension has be kept.

     Sincerely,
      y.j.
  • @yj

    I understand what you mean. In @biomurph 's github, I found a HEX converter (https://github.com/biomurph/OBCI_HEX_SD_File_Converter) to convert the .TXT files to CSV with decimal data. In that repo there were example .TXT files which suit your explanations. It really is meaningful: (a part near the end of the file) 
    image

    Here I can see the data as well as the footer you mentioned. The converter I found perfectly converts these into CSV (with decimal values).

    Unfortunately, our SD card recordings are nothing similar to this. They look like this: (the start of the file)

    image

    Here there is no data at all. The converter also gives errors.

    Yes, I am using v2 from @pushtheworld. Could the writeDataToSDcard function in v2 be bugging out? I will have to compare the two functions it seems. Where can I find writeDataToSDcard of v1?

    Maybe I can make v1's function compatible to v2.

    Thank you,
    Yiğit
  • edited December 2016
    What type of SD card are you using? Is the SD the right format? Are you able to stream to the GUI?

    The SD code barely changed from V1 to V2.
  • yjyj France , Bordeaux
    edited December 2016
    @yttuncel,

    Pushtheworld is right,   first of all,   ensure yourself that your SD card  works.
    These SD are rather ticklish, I got incredible problems with 16gb using the very same ref, one works the other dont...
    Have you red  :   http://docs.openbci.com/Tutorials/09-SD_Card
    Currently we use an 8gb SanDisk Ultra.

    A bug in the code is unlikely (and would be known) but if you want have a look at the V1 it is in
     http://docs.openbci.com/Tutorials/02-Upload_Code_to_OpenBCI_Board copy pasted :

    You can find the previous OpenBCI firmware and libraries on our github repository.

    https://github.com/OpenBCI/OpenBCI_32bit

    https://github.com/OpenBCI/OpenBCI_32bit_Library/tree/maint/1.0.0

    • OpenBCI_32bit
      • This is the firmware that runs on the OpenBCI 32bit Board
    • OBCI_SD
      • Supports writing raw data to on-board SD card
    • OpenBCI_32_Daisy
      • The OpenBCI 32bit Library
    y.j.

  • @pushtheworld

    SD Card I'm using should be this
     image

    I have no idea if this should work or not. I haven't read the SD Card tutorial, and now that I do, I will try suggestions mentioned there. I formatted the card with windows' own utility, not with a 3rd party software. I'll try downloading the software and format the SD once again with recommended settings. If not I'll try getting a sandisk one.

    Yes I am able to stream to the GUI or to python/lsl. I am on default firmware right now. 

    I'll let you guys know if anything new comes up.  
    As always, thank you for your time!


  • @yj

    I've tried overwriting the SD card with SD Formatter software, but nothing has changed. The resulting files do not make any sense. The sizes of the generated files hold. a -> 256kB A -> 5.5MB etc., but the inside is a mess.

    Do you think that getting a sandisk SD Card would make any difference? Is that really the source of the issue? One other thing: From GUI (v1), when I select save to sd card option, no file is created on the card. Could these be related?
  • yjyj France , Bordeaux
    @yttuncel,

    I do not have the V2, but someone from the forum or from OBCI project,  should be able to quickly tell you if SD writing is ok or not...

    Can you read the footer at the end ?  It should be ascii.

    y.j.
  • @yj

    Maybe I can try working on v1. Do I need to change the firmware on the dongle or the python script on PC side to work with v1?

    There isn't any footer at the end. Would you like me to send you the file via email, so you can take a look yourself?
  • yjyj France , Bordeaux
    @yttuncel,

    The last 512 bytes block begins by a short ascii message and  is filled with zeros.
    If you do not have zeros (at least 256)  at the end , it might be a SD card problem.

    Try any 8gb SD card ... Some good quality and  very fast 16gb cards were not working when I was trying to find a reliable one... But some low price 8gb gave me good results...

    And wait for confirmation from other V2 users...

        y.j.
  • @yj

    Ok, I'll try getting another SD Card. Do you remember which cards worked other than the two on the tutorial page? This is the txt file generated http://www.filedropper.com/showdownload.php/obci19 if you want to take a look by the way.


    I'm facing another problem. Channel 1 always produces a triangular wave in my openBCI. Nothing I did changed this. I defaulted the channel settings, tried changing the channel 1 properties, even turned down channel 1, but it is always the same signal:
    image
    Why would this happen? 
  • @wjcroft

    I'm thinking that this could be the sample counter, because its period is 255 samples and is a sawtooth function. Why would I observe sample numbers in the 1st channel? I'm using default firmware, default library, everything set to default to be brief.
    (I don't think that a broken ADS would result in such a signal. What do you say?)

    Could this be the cause of not successfully writing to SD card?
  • yttuncelyttuncel Ankara
    edited December 2016
    UPDATE:

    I bought a SanDisk card thats on the Tutorial Page and its working now. Very interesting. Using the other card totally wrecks the recorded data and saved data format. I have no idea why.

    On a side note, I have solved the other problem as well. I did not update my rfduino firmwares on dongle and board while going from v1 to v2. I reverted back to v1 and it seems ok now. The protocol messed up most probably somewhere in between.

    I still want to use firmware v2 though, where can I find the corresponding rfduino drivers? @pushtheworld @wjcroft

    ADD: OK, nevermind, found the documentation about dongle update.

    Thanks for the help @yj.
  • edited April 2017
    Hi all, the wifi shield is just about done and entering beta! Signup here for one of 10 free beta tester boards! https://docs.google.com/forms/d/e/1FAIpQLSfjMzITl3Z_bMxCZlXaUVHxj0vVZ6oXyk3G05epOMYWBOiAFA/viewform






Sign In or Register to comment.