klabhub / neurostim

Design and run visual neuroscience experiments using Matlab and the Psychophysics Toolbox.
MIT License
5 stars 4 forks source link

bug-fix: missing support for adaptive in rsvp #223

Closed adammorrissirrommada closed 2 months ago

adammorrissirrommada commented 2 months ago

RSVP is implemented as "mini-trials" using a design object. But, the logic for updating params from the design spec left out the logic to support adaptive parameters like jitter.

Here's a minimal example that previously did not call the updateOrientation() with each rsvp frame but does in this PR.

function sog
%% 
% Stream of Gratings Example
% 
% Shows how a single stimulus ( a grating in this case) can be shown as a 
% rapid visual stream (RSVP) with one or more of its properties changing within a
% trial. 
%
% The experiment starts with a red dot, click with a mouse to fixate, then
% the stream of gratings will show.
% 
% BK - Feb 2016

%% Prerequisites. 
import neurostim.*

%% Setup CIC and the stimuli.
c = myRig;         % Create Command and Intelligence Center...
c.trialDuration = 5000;
c.screen.color.background = [0.5 0.5 0.5]; 

% Create a Gabor stimulus to adadot. 
g=stimuli.gabor(c,'grating');           
g.color             = [0.5 0.5 0.5];
g.contrast          = 0.25;
g.Y                 = 0; 
g.X                 = 0;
g.sigma             = 3;                       
g.phaseSpeed        = 0;
g.orientation       = 15;
g.mask              ='CIRCLE';
g.frequency         = 3;
g.on                =  1000; % Start showing 250 ms after the subject starts fixating (See 'fixation' object below).

% We want to show a rapid stream of gratings. Use the factorial class to
% define these "conditions" in the stream.
stream =design('ori');           % Define a factorial with one factor
%stream.fac1.grating.orientation = 0:30:359; % Assign orientations
stream.fac1.grating.Y = 0;
stream.randomization = 'RANDOMWITHOUTREPLACEMENT'; % Randomize
stream.conditions(:).grating.orientation = plugins.jitter(c,{g},'distribution',@updateOrientation);
g.addRSVP(stream,'duration',6*1000/c.screen.frameRate,'isi',3*1000/c.screen.frameRate); % Tell the stimulus that it should run this stream (in every trial). 5 frames on 2 frames off.

%% Define conditions and blocks
% We will show the stream of gratings with different contrasts in each
% trial.
% d=design('contrast');           % Define a factorial with one factor
% d.fac1.grating.contrast = 0.1:0.2:1; % From 10 to 100% contrast
% d.randomization = 'RANDOMWITHOUTREPLACEMENT';

% Or (if the stream already varies contrast and orientation, we vary
% nothing across trials).
d=design('dummy');           % Define a factorial with one factor
% Nothing to vary here but we need at least one condition
d.conditions(1).grating.X = 0; % Dummy
blck=block('block',d);                  % Define a block based on this factorial
blck.nrRepeats  =10;                        % Each condition is repeated this many times 

%% Run the experiment   
c.cursor = 'arrow';
% Now tell CIC how we want to run these blocks 
c.run(blck);

function val = updateOrientation(o)
val = o.orientation+5;
adammorrissirrommada commented 2 months ago

This is partly a symptom of the fact that the logic to update parameters is not actually packed into the design class, but rather happens in block.m. That logic was partially duplicated into the rsvp code when it was first created. A better solution would be to consolidate into something encapsulated in design (or block, or at least somewhere), but that's too much to think about right now!

adammorrissirrommada commented 2 months ago

I also think it would be nice if addRSVP() could optionally accept a function handle as the first input rather than a design object, and in that case, simply call the function with every update (similar to how the noise stimuli are implemented) rather than cycle through a design object "trial" list. Any thoughts on the merit of this?

adammorrissirrommada commented 2 months ago

@bartkrekelberg and @cnuahs , sorry to bombard you with PRs. @dshimaoka is trying to run an experiment and ran into a bunch of problems that are fixed across these new PRs. Can you perhaps let us know if you'll have time to review any time soon? If not, I suggest that @dshimaoka creates a branch of his own and merge all three of my bug fixes into that one for use in the meantime. You can also review the code yourself and test it in your own rigs and for your purpose.