Hi Josh,
I am trying to Sync the rotary encoder data with my fiber photometry recordings. But I haven't figured it out. Could you give me some suggestions? Currently, I only have a Bpod State Machine r2, the rotary encoder module, and a rotary encoder. There is no trial structure during my recordings and the Fiber photometry system could send or record a TTL signal.
Thanks for your help.
Best,
Ke
Hello Ke,
Here's my suggested approach:
-Capture rotary encoder data via USB instead of the microSD card. Relevant commands are:
- startUSBStream()
- data = readUSBStream()
- stopUSBStream()
as documented here.
This will allow you to use the '#' command passed from the state machine, to add an event timestamp to the rotary encoder data.
-Run a single trial on the state machine, for the duration of your experiment. Make sure the trial ends when your experiment ends, so your trial data will be saved.
-In the 'Output Actions' section of the first state, trigger two looping global timers. The first timer is attached to BNC1 --> Photometry System. The second timer (in sync with the first) sends the '#' command to the rotary encoder. Each timer loop is also timestamped in your Bpod behavior data, so this repeating event will be timestamped on all 3 systems. Both timers loop in sync at 10Hz, more than sufficient to account for any clock drift.
Code: LoadSerialMessages('RotaryEncoder1', {['#' 1]}); % Set Message 1 as the REM command to sync
sma = NewStateMachine;
sma = SetGlobalTimer(sma, 'TimerID', 1, 'Duration', 0.05, 'OnsetDelay', 0,...
'Channel', 'BNC1', 'OnLevel', 1, 'OffLevel', 0,...
'Loop', 1, 'SendGlobalTimerEvents', 1, 'LoopInterval', 0.05);
sma = SetGlobalTimer(sma, 'TimerID', 2, 'Duration', 0.05, 'OnsetDelay', 0,...
'Channel', 'RotaryEncoder1', 'OnMessage', 1, 'OffMessage', 0,...
'Loop', 1, 'SendGlobalTimerEvents', 1, 'LoopInterval', 0.05);
sma = AddState(sma, 'Name', 'TimerTrig', ...
'Timer', 0,...
'StateChangeConditions', {'Tup', 'MyNextState'},...
'OutputActions', {'GlobalTimerTrig', '11'}); % Binary strings can trigger multiple timers
Please let me know if this works for you!
-Josh
10-05-2021, 10:29 PM
(This post was last modified: 10-05-2021, 10:41 PM by ke_chen.)
Hi Josh,
Thanks so much for your suggestion. The Sync works.
But I have a problem to record enough new positions with readUSBStream. In many scenarios, I could only record the first ~1650 new positions, and all the events and new positions after that were missing. Maybe I didn't use it correctly. I attached the script I use to test the recording for 60s.
Thanks for your help.
Best,
Ke
Code: global BpodSystem
%% Resolve Rotary Module USB port
if (isfield(BpodSystem.ModuleUSB, 'RotaryEncoder1'))
%% Create an instance of the Rotary module
R = RotaryEncoderModule(BpodSystem.ModuleUSB.RotaryEncoder1);
else
error('Error: To run this protocol, you must first pair the Rotary module with its USB port. Click the USB config button on the Bpod console.')
end
R.startUSBStream()
LoadSerialMessages('RotaryEncoder1', {['#' 1]}); % Set Message 1 as the REM command to sync
sma = NewStateMachine;
sma = SetGlobalTimer(sma, 'TimerID', 1, 'Duration', 0.05, 'OnsetDelay', 0,...
'Channel', 'BNC1', 'OnLevel', 1, 'OffLevel', 0,...
'Loop', 1, 'SendGlobalTimerEvents', 1, 'LoopInterval', 0.05);
sma = SetGlobalTimer(sma, 'TimerID', 2, 'Duration', 0.05, 'OnsetDelay', 0,...
'Channel', 'RotaryEncoder1', 'OnMessage', 1, 'OffMessage', 0,...
'Loop', 1, 'SendGlobalTimerEvents', 1, 'LoopInterval', 0.05);
sma = AddState(sma, 'Name', 'TimerTrig', ...
'Timer', 0,...
'StateChangeConditions', {'Tup', 'Recording'},...
'OutputActions', {'GlobalTimerTrig', '11'}); % Binary strings can trigger multiple timers
sma = AddState(sma, 'Name', 'Recording', ...
'Timer', 60,...
'StateChangeConditions', {'Tup', '>exit'},...
'OutputActions', {}); % Binary strings can trigger multiple timers
SendStateMatrix(sma);
RawEvents = RunStateMatrix;
if ~isempty(fieldnames(RawEvents)) % If trial data was returned
BpodSystem.Data = AddTrialEvents(BpodSystem.Data,RawEvents); % Computes trial events from raw data
BpodSystem.Data.Rotary = R.readUSBStream();
R.stopUSBStream()
SaveBpodSessionData; % Saves the field BpodSystem.Data to the current data file
end
Hello Ke,
If your code doesn't call R.readUSBStream() often enough to avoid buffer overruns, you'll need to initialize the USB stream with:
Code: R.startUSBStream('UseTimer');
This will start a MATLAB timer process in the background to read incoming data from the buffer to a private field of the R object every 100ms.
You can retrieve the data as you are already doing, with:
Code: Data = R.readUSBStream();
-Josh
(10-06-2021, 03:49 PM)Josh Wrote: Hello Ke,
If your code doesn't call R.readUSBStream() often enough to avoid buffer overruns, you'll need to initialize the USB stream with:
Code: R.startUSBStream('UseTimer');
This will start a MATLAB timer process in the background to read incoming data from the buffer to a private field of the R object every 100ms.
You can retrieve the data as you are already doing, with:
Code: Data = R.readUSBStream();
-Josh Thanks, Josh. It is working now.
|