How to accurately read values of states using BCI2000Remote

Forum for software developers to discuss BCI2000 software development
Locked
rebecacp
Posts: 10
Joined: 14 Jul 2008, 07:05

How to accurately read values of states using BCI2000Remote

Post by rebecacp » 08 Jan 2013, 12:12

Hi,

We are developing an external P300-based BCI application using the BCI2000Remote class. We use a sample frequency of 256Hz and a SampleBlockDuration parameter of 8.
When the last sequence of stimuli presentation starts, we enable a timer that reads every 8 ms the values of StimulusCodeRes and Signal(0,0) and stores them. Our application has 8 different stimuli so there are 8 different values for the StimulusCode state. When the last sequence of stimuli has finished, our application has read from 6 to 8 values for StimulusCodeRes, so it sometimes losses one or two values (it happens approximately 1 or 2 times out of 10). I have checked if it losses a value it is always one of the last four values: for instance, if the last random sequence of stimuli was: 5 3 8 4 7 2 1 6, it would storage: 5 3 8 4 7 1 6 or 5 3 8 4 7 2 6...
I am not sure where the problem is. Are we reading the data properly? What is the best way to read the P300 classification scores using BCI2000Remote?

Many thanks in advance.

Regards,

Rebeca

Code: Select all

/* These variables are declared and initialized previously */
static array<double^>^ ArraySCR = gcnew array<double^>(10);
   // Array which stores the StimulusCodeRes values
static array<double^>^ ArrayCS = gcnew array<double^>(10);
   // Array which stores the scores from the control signal 
static int indexArrays = 0; //index which increases with new values of StimulusCodeRes

private: System::Void timer4_Tick(System::Object^  sender, System::EventArgs^  e)
             {
                 double valueSCR = 0;
                 double valueCS = 0;

                 if(!bci->GetStateVariable("StimulusCodeRes", valueSCR))
                 {
                     MessageBox::Show("Error GetStimulusCodeRes: "+gcnew String( (bci->Result()).c_str() ));
                 }
                 if(!bci->GetControlSignal(1,1, valueCS))
                 {
                     MessageBox::Show("Error GetControlSignal: "+gcnew String( (bci->Result()).c_str() ));
                 }

                 if(valueSCR!=0)
                 {
                     bool control = false;
                     for (int k=0; k<8; k++)
                     {
                         if((double)ArraySCR[k] == valueSCR)
                         {
                             control = true;
                             k = 8;
                         }
                     }
                     if(control==false)
                     {
                         ArraySCR[indexArrays] = valueSCR;
                         ArrayCS[indexArrays] = valueCS;
                         indexArrays++;
                     }
                 }                  
            }

mellinger
Posts: 1163
Joined: 12 Feb 2003, 11:06

Re: How to accurately read values of states using BCI2000Rem

Post by mellinger » 09 Jan 2013, 15:54

Hi,

with a SampleBlockSize of 8, and a sampling rate of 256Hz, each block has a duration of 31.25ms, so in principle polling for a state value every 8ms should be ok in theory.

However, in practice, this kind of setup is not very reliable, for a number of reasons:
* In any operating system, timing accuracy depends on the length of a time slice, and a number of other factors. In Windows, the highest accuracy would be achieved by polling from a separate thread, in a loop that calls Sleep(8) between polls. However, even in that case Windows does may sleep any amount of time between 8 and >20ms, unless you use a special API function that increases timing accuracy, at the cost of increasing scheduling overhead on the entire machine, so Microsoft does not recommend this.
* You say you are using a "Timer" for polling but you do not specify which kind of timer that is. In the case of a Windows API multimedia timer, the above applies. In the case of a timer provided by some intermediate library, things may be much worse. E.g., in the Qt API framework, timer callbacks are called from a thread's event loop, which means that they will not be executed at all until client-defined code returns back to Qt's event loop. From the syntax in your example code, I guess that you are the .net framework in C#. Unfortunately, I'm not familiar with the behavior of that framework, but the absence of locking in your timer callback suggests that it is running in the context of your application's main thread. In that case, it will not reliably run every 8ms, but only if your application is idle, and 8ms have passed since it was run for the last time.
* Your EEG data acquisition device might not emit data packets at regular intervals. For some of the packets, inter-packet duration may be much smaller than 8ms, so even in the case of perfect polling, you would lose some packets. To check, watch the BCI2000 timing window. The smallest value of the indicated actual block duration shows you how accurate polling must be. In any case, you should choose a SampleBlockSize such that timing is as regular as possible for your acquisition device.

Quite generally, data polling is an unreliably way of detecting events, and should only be used as a last resort if there is no event-driven alternative available. For your application, an event-driven alternative would consist in using the AppConnector protocol:
http://www.bci2000.org/wiki/index.php/T ... _Connector
This will send a state values to a UDP port each time BCI2000 finished processing a data block.
In your application, you may then run a thread that waits for data coming in on that port, and which may then set a flag to indicate to your main thread that data is available. Note that this approach requires proper synchronization of that flag (I think C# has some way to declare a variable as being shared amongst multiple threads, that should be used for both the flag, and the Signal(0,0) data written by the thread).

Please let me know if you need further advice (please understand that I cannot tell you how to do certain things in C#/.net, though).

Regards,
Juergen

rebecacp
Posts: 10
Joined: 14 Jul 2008, 07:05

Re: How to accurately read values of states using BCI2000Rem

Post by rebecacp » 22 Jan 2013, 07:54

Hi Juergen,

We finally used the AppConnector protocol. We run a thread that waits for data on the UPD port and indicate us when the data is available. We have made experiments with the application with good results: the maximum responses by row and column fit with the target the user had focused his attention on.

Many thanks for your help!

Regards,

Rebeca

Locked

Who is online

Users browsing this forum: No registered users and 2 guests