I have a couple of questions about timing.
My understanding of the BCI2000 system is that it takes in one block of data at a time from the DAQ system and then processes this block. This would lead to a delay between when one single sample is taken by the data acquisition system and when it is processed by BCI2000. Thus, there is a latency between the time stamp of the data being processed and the time stamp of the recorded data. What I would like to do is correct for that latency.
I am currently recording the time using GetBCItime_ms() in two different places. The first is in Application\TTask.cpp UpdateDisplay(), which is where I process the BCI data and decide what to display to the screen. The other place where a time stamp is being recorded is in EEGsource\BufferRead\BufferReadADC.cpp, which is right after one of the blocks is written. What I was expecting to see when looking at the times I had saved was that the BufferReadADC timer would have give the same time value for several cycles as it records a single block and then jumps after it gets updated with a new block. This is exactly what I saw. For the TTask timer, I was expecting to see new timer values each cycle, but instead it looks like the BufferReadADC timer, only delayed. At first I thought this might be due to timer precision, but it looks like GetBCItime_ms should have pretty high resolution because it uses a good timer. Why do GetBCItime_ms calls in TTask return values that are step-like?
In a related note, how are state variables saved? Are they aligned with the acquired data or are they simply written whenever they are updated?
What I would like to know, in short, is how to go about properly fixing latencies between acquisition and processing. Is it possible to get the aquire time of data with accuracy greater than that of one acquisition block? Or is the best I can do to find latencies simply to subtract the BufferReadADC time stamp from the TTask time stamp.
Thanks,
Paul Hammon
UCSD
questions about timing
-
gschalk
- Posts: 615
- Joined: 28 Jan 2003, 12:37
Re: Timing questions ...
Paul,
These are excellent questions. There are multiple facets to this issue.
First, under Windows it is impossible to align events to a data stream at the time resolution of a reasonable sampling rate. Thus, BCI2000 processes blocks of signals rather than individual samples. In consequence, the time resolution for system operations, INCLUDING update of state values, corresponds to SampleBlockSize/SamplingRate*1000 ms.
Second, there is a certain sequence of events that should be considered in your situation:
Source acquires a block of data. Immediately after it acquired it, the block is sent on to SignalProcessing. As soon as this is done, the processed control signals are sent to the User Application where they lead to some stimulus update. In a typical situation, the latency between the end of data acquisition and the end of stimulus update are only very few ms. Also, you do not have to time stamp these times yourself. They are recorded by the SourceTime and StimulusTime states. Once the stimulus is updated, the resulting state variables are passed back to the Source module (where they typically arrive before the next block of data is ready). As soon as the next block of data is ready, it is stored to disc along with the state variables that resulted from the PREVIOUS data block. Then, the same cycle begins again. If you are really critical about timing, there is another fine point: Let's assume that the latency from the end of data collection to the time of stimulus update is near zero (which, at least for typical situations, is correct), then the stimulus is updated at the end of the acquisition of one block of data (and thus, the beginning of acquisition of the NEXT block of data). Thus, if you turn on a stimulus, set a state variable to reflect that change, then it is correct to associate this state variable with the NEXT block of data, rather than the current block of data. In contrast, you might, for example, use a state variable to mark artifacts in the EEG. In this case, SignalProcessing would determine whether or not there is an artifact in the CURRENT block of data. Consequently, a corresponding state value should be associated with the CURRENT, rather than the next, block of data. Because the system does not know which states it should associate with the current or the next block of data, you have to take this into account yourself.
In consequence, you only get latency estimates for every sample block, not every sample. Thus, to subtract the source time from the stimulus time is probably the best you can do.
It is worth pointing out that, when it comes down to it, making timing requirements very stringent requires a completely different system design and implementation, most likely on a real-time platform. Because this step has tremendous practical disadvantages, and because timing latencies on the order of few ms are not of an issue for the largest fraction of BCI experiments, I chose to use the present approach over the real-time system approach.
I hope this helps,
Gerv
These are excellent questions. There are multiple facets to this issue.
First, under Windows it is impossible to align events to a data stream at the time resolution of a reasonable sampling rate. Thus, BCI2000 processes blocks of signals rather than individual samples. In consequence, the time resolution for system operations, INCLUDING update of state values, corresponds to SampleBlockSize/SamplingRate*1000 ms.
Second, there is a certain sequence of events that should be considered in your situation:
Source acquires a block of data. Immediately after it acquired it, the block is sent on to SignalProcessing. As soon as this is done, the processed control signals are sent to the User Application where they lead to some stimulus update. In a typical situation, the latency between the end of data acquisition and the end of stimulus update are only very few ms. Also, you do not have to time stamp these times yourself. They are recorded by the SourceTime and StimulusTime states. Once the stimulus is updated, the resulting state variables are passed back to the Source module (where they typically arrive before the next block of data is ready). As soon as the next block of data is ready, it is stored to disc along with the state variables that resulted from the PREVIOUS data block. Then, the same cycle begins again. If you are really critical about timing, there is another fine point: Let's assume that the latency from the end of data collection to the time of stimulus update is near zero (which, at least for typical situations, is correct), then the stimulus is updated at the end of the acquisition of one block of data (and thus, the beginning of acquisition of the NEXT block of data). Thus, if you turn on a stimulus, set a state variable to reflect that change, then it is correct to associate this state variable with the NEXT block of data, rather than the current block of data. In contrast, you might, for example, use a state variable to mark artifacts in the EEG. In this case, SignalProcessing would determine whether or not there is an artifact in the CURRENT block of data. Consequently, a corresponding state value should be associated with the CURRENT, rather than the next, block of data. Because the system does not know which states it should associate with the current or the next block of data, you have to take this into account yourself.
In consequence, you only get latency estimates for every sample block, not every sample. Thus, to subtract the source time from the stimulus time is probably the best you can do.
It is worth pointing out that, when it comes down to it, making timing requirements very stringent requires a completely different system design and implementation, most likely on a real-time platform. Because this step has tremendous practical disadvantages, and because timing latencies on the order of few ms are not of an issue for the largest fraction of BCI experiments, I chose to use the present approach over the real-time system approach.
I hope this helps,
Gerv
-
phammon
- Posts: 6
- Joined: 01 Feb 2005, 20:06
Thank you for the informative answer to my question. It has cleared up much of my confusion about timing details.
I do, however, have one further question related to timing. While running our stimulus module within BCI2000 we have noticed a couple of times when the program appears to hang. Looking at the time stamps in StimulusTime, I noticed that when the program appears to hang or pause there can be large jumps in the time recorded in StimulusTime between blocks. These time differences can be on the order of 1 second. We guess that this is due to other system processes taking control of the CPU. How does BCI2000 behave in such a case? Does it skip samples or does it simply wait until it regains control of the CPU and then try to catch up on later blocks? Is there some way to tell when the program has overrun its internal buffers and starts skipping samples?
Thanks,
Paul Hammon
UCSD
I do, however, have one further question related to timing. While running our stimulus module within BCI2000 we have noticed a couple of times when the program appears to hang. Looking at the time stamps in StimulusTime, I noticed that when the program appears to hang or pause there can be large jumps in the time recorded in StimulusTime between blocks. These time differences can be on the order of 1 second. We guess that this is due to other system processes taking control of the CPU. How does BCI2000 behave in such a case? Does it skip samples or does it simply wait until it regains control of the CPU and then try to catch up on later blocks? Is there some way to tell when the program has overrun its internal buffers and starts skipping samples?
Thanks,
Paul Hammon
UCSD
-
gschalk
- Posts: 615
- Joined: 28 Jan 2003, 12:37
More timing questions ...
Paul,
The behavior of BCI2000 is quite simple. In order to ensure deterministic and correct system behavior, the source module does not send out a new block of signals until it has received the state vector back from the Application. In such a scenario, it depends on the implementation of the particular Source module whether or not you will lose data and whether or not this will be reported to the user. For example, the DT2000 source module uses ring buffers supported by the Data Translation driver. Thus, you essentially never lose data.
To a certain extent, one does not want any such behavior in a system that supposedly reacts to brain signals in real time. The system should simply never fall behind and feedback should be updated very quickly after data are collected. What you describe seems to suggest one of two possibilities:
1) The system is very close to processing capacity and thus sometimes, signal processing and feedback update takes longer than one SampleBlock. It would be interesting to increase the SampleBlockSize (to decrease the frequency of stimulus updates and invokation of signal processing) and to see whether the problem persists. If it goes away, you might need a faster machine.
2) The implementation of the Source module depends strongly on Windows. For this reason, we have avoided using the Windows messaging system. With this system, it is possible that messages arrive very late or are even lost. If timing of data acquisition depends on Windows messages or other processes that are similarly unreliable, then this could also account for what you describe.
I hope this helps,
The Gerv
The behavior of BCI2000 is quite simple. In order to ensure deterministic and correct system behavior, the source module does not send out a new block of signals until it has received the state vector back from the Application. In such a scenario, it depends on the implementation of the particular Source module whether or not you will lose data and whether or not this will be reported to the user. For example, the DT2000 source module uses ring buffers supported by the Data Translation driver. Thus, you essentially never lose data.
To a certain extent, one does not want any such behavior in a system that supposedly reacts to brain signals in real time. The system should simply never fall behind and feedback should be updated very quickly after data are collected. What you describe seems to suggest one of two possibilities:
1) The system is very close to processing capacity and thus sometimes, signal processing and feedback update takes longer than one SampleBlock. It would be interesting to increase the SampleBlockSize (to decrease the frequency of stimulus updates and invokation of signal processing) and to see whether the problem persists. If it goes away, you might need a faster machine.
2) The implementation of the Source module depends strongly on Windows. For this reason, we have avoided using the Windows messaging system. With this system, it is possible that messages arrive very late or are even lost. If timing of data acquisition depends on Windows messages or other processes that are similarly unreliable, then this could also account for what you describe.
I hope this helps,
Who is online
Users browsing this forum: No registered users and 0 guests
