Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion initEnv.m
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
function addDependencies()

pth = fileparts(mfilename('fullpath'));
addpath(fullfile(pth, 'lib', 'CPP_BIDS'));
addpath(fullfile(pth, 'lib', 'CPP_BIDS', 'src'));
addpath(fullfile(pth, 'lib', 'CPP_PTB'));
addpath(fullfile(pth, 'subfun'));

Expand Down
2 changes: 1 addition & 1 deletion lib/CPP_BIDS
Submodule CPP_BIDS updated 41 files
+32 −0 .github/workflows/moxunit.yml
+14 −3 .gitignore
+7 −5 .travis.yml
+0 −27 checkCppBidsDependencies.m
+0 −0 manualTests/miss_hit.cfg
+0 −0 manualTests/testData/eventsDataDictionary.json
+55 −0 manualTests/test_createDataDictionary.m
+3 −5 manualTests/test_makeRawDataset.m
+0 −0 manualTests/test_userInput.m
+0 −44 printCreditsCppBids.m
+4 −3 src/checkCFG.m
+39 −0 src/checkCppBidsDependencies.m
+3 −3 src/convertSourceToRaw.m
+0 −0 src/createBoldJson.m
+0 −0 src/createDataDictionary.m
+0 −0 src/createDatasetDescription.m
+13 −9 src/createFilename.m
+3 −0 src/miss_hit.cfg
+53 −0 src/printCreditsCppBids.m
+15 −5 src/saveEventsFile.m
+0 −0 src/subfun/createTaskName.m
+0 −0 src/subfun/initializeExtraColumns.m
+1 −1 src/subfun/returnHeaderName.m
+1 −1 src/subfun/returnNamesExtraColumns.m
+0 −0 src/subfun/returnNbColumns.m
+0 −0 src/subfun/setDefaultFields.m
+0 −0 src/subfun/transferInfoToBids.m
+0 −0 src/userInputs.m
+97 −0 tests/README.md
+0 −63 tests/runTests.m
+73 −63 tests/test_checkCFG.m
+18 −8 tests/test_createBoldJson.m
+0 −40 tests/test_createDataDictionary.m
+15 −7 tests/test_createDatasetDescription.m
+44 −31 tests/test_createFilename.m
+28 −4 tests/test_createTaskName.m
+38 −25 tests/test_saveEventsFileInit.m
+37 −27 tests/test_saveEventsFileOpen.m
+19 −13 tests/test_saveEventsFileOpenMultiColumn.m
+32 −23 tests/test_saveEventsFileSave.m
+18 −4 tests/test_setDefaultFields.m
61 changes: 33 additions & 28 deletions setParameters.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,48 +23,54 @@
cfg.eyeTracker.do = false;
cfg.audio.do = false;

cfg = setMonitor(cfg, cfg);
cfg = setMonitor(cfg);

% Keyboards
cfg = setKeyboards(cfg, cfg);
cfg = setKeyboards(cfg);

% MRI settings
cfg = setMRI(cfg, cfg);
cfg = setMRI(cfg);

%% Experiment Design
cfg.names = {'static', 'motion'};
cfg.possibleDirections = [-1 1]; % 1 motion , -1 static
cfg.numBlocks = size(cfg.possibleDirections, 2);
cfg.numRepetitions = 1; % AT THE MOMENT IT IS NOT SET IN THE MAIN SCRIPT
cfg.IBI = 0; % 8;
cfg.IBI = .5; % 8;
% Time between events in secs
cfg.ISI = 0.1;
cfg.ISI = 0.5;
% Number of seconds before the motion stimuli are presented
cfg.onsetDelay = 5;
cfg.onsetDelay = .1;
% Number of seconds after the end all the stimuli before ending the run
cfg.endDelay = 1;
cfg.endDelay = .1;

%% Visual Stimulation

% Number of events per block (should not be changed)
cfg.numEventsPerBlock = 12;
cfg.eventDuration = 1; % second

% speed in visual angles
cfg.dot.speed = 8;
% speed in visual angles / second
cfg.dot.speed = 15;
% Coherence Level (0-1)
cfg.dot.coh = 1;
% Maximum number dots per frame
cfg.dot.maxNbPerFrame = 300;
cfg.dot.coherence = 1;
% nb dots per visual angle square.
cfg.dot.density = .25;

% Dot life time in seconds
cfg.dot.lifeTime = 1;
cfg.dot.lifeTime = 10;

% proportion of dots killed per frame
cfg.dot.proportionKilledPerFrame = .05;

% Dot Size (dot width) in visual angles.
cfg.dot.size = 0.1;
cfg.dot.size = 1;
cfg.dot.color = cfg.color.white;
cfg.dot.dontClear = 0;

% Diameter/length of side of aperture in Visual angles
cfg.diameterAperture = 8;
cfg.aperture.type = 'circle';
cfg.aperture.width = []; % if left empty it will take the screen height
cfg.aperture.xPos = 0;

%% Task(s)

Expand All @@ -74,17 +80,16 @@
cfg.task.instruction = '1-Detect the RED fixation cross\n \n\n';

% Fixation cross (in pixels)
% Set the length of the lines of the fixation cross
cfg.fixation.dimensionPix = 10;
% Set the line width for our fixation cross
cfg.fixation.lineWidthPix = 4;
cfg.fixation.xDisplacement = 0; % Manual displacement of the fixation cross
cfg.fixation.yDisplacement = 0; % Manual displacement of the fixation cross
cfg.fixation.color = cfg.color.white;
cfg.fixation.type = 'cross';
cfg.fixation.colorTarget = cfg.color.red;
cfg.fixation.color = cfg.color.white;
cfg.fixation.width = 1;
cfg.fixation.lineWidthPix = 2;
cfg.fixation.xDisplacement = 0;
cfg.fixation.yDisplacement = 0;

cfg.target.maxNbPerBlock = 2;
cfg.target.duration = 0.15; % In secs
cfg.target.duration = 0.05; % In secs

cfg.extraColumns = {'direction', 'speed', 'target', 'event', 'block'};
end
Expand Down Expand Up @@ -124,11 +129,11 @@
cfg.text.color = cfg.color.white;

% Monitor parameters
cfg.screen.monitorWidth = 42; % in cm
cfg.screen.monitorDistance = 134; % distance from the screen in cm
cfg.screen.monitorWidth = 50; % in cm
cfg.screen.monitorDistance = 40; % distance from the screen in cm

if strcmpi(cfg.testingDevice, 'mri')
cfg.screen.monitorWidth = 42;
cfg.screen.monitorDistance = 134;
cfg.screen.monitorWidth = 50;
cfg.screen.monitorDistance = 40;
end
end
191 changes: 54 additions & 137 deletions subfun/doDotMo.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,156 +6,73 @@
%
% Input:
% - cfg: PTB/machine configurations returned by setParameters and initPTB
% - expParameters: parameters returned by setParameters
% - logFile: structure that stores the experiment logfile to be saved
%
% Output:
% -
%
% The dots are drawn on a square that contains the round aperture, then any
% dots outside of the aperture is turned into a NaN so effectively the
% actual number of dots on the screen at any given time is not the one that you input but a
% smaller number (nDots / Area of aperture) on average.

% The dots are drawn on a square with a width equals to the width of the
% screen
% We then draw an aperture on top to hide the certain dots.

%% Get parameters
dontClear = cfg.dot.dontClear;

direction = thisEvent.direction(1);
isTarget = thisEvent.target(1);
speed = thisEvent.speed(1);

coh = cfg.dot.coh;
ndots = cfg.dot.maxNbPerFrame;
dotSizePix = cfg.dot.sizePix;
dotLifeTime = cfg.dot.lifeTime;
dotColor = cfg.dot.color;

targetDuration = cfg.target.duration;

% thisEvent = deg2Pix('speed', thisEvent, cfg);
% dotSpeedPix = logFile.iEventSpeedPix;

diamAperturePix = cfg.diameterAperturePix;
diamAperture = cfg.diameterAperture;

% Check if it is a static or motion block
if direction == -1

% dotSpeedPix = 0;

speed = 0;

dotLifeTime = cfg.eventDuration;
end

%% initialize variables

% Set an array of dot positions [xposition, yposition]
% These can never be bigger than 1 or lower than 0
% [0,0] is the top / left of the square that contains the square aperture
% [1,1] is the bottom / right of the square that contains the square aperture
xy = rand(ndots, 2);

% Set a N x 2 matrix that gives jump size in pixels
% pix/sec * sec/frame = pix / frame
dxdy = repmat( ...
speed * 10 / (diamAperture * 10) * (3 / cfg.screen.monitorRefresh) * ...
[cos(pi * direction / 180.0) -sin(pi * direction / 180.0)], ndots, 1);

% dxdy = repmat(...
% dotSpeedPix / Cfg.ifi ...
% * (cos(pi*direction/180) - sin(pi*direction/180)), ...
% ndots, 1);

% Create a ones vector to update to dotlife time of each dot
dotTime = ones(size(xy, 1), 1);

% Set for how many frames to show the dots
continueShow = floor(cfg.eventDuration / cfg.screen.ifi);

% Covert the dotLifeTime from seconds to frames
dotLifeTime = ceil(dotLifeTime / cfg.screen.ifi);


dots = initializeDots(cfg, thisEvent);

% Set for how many frames this event will last
framesLeft = floor(cfg.eventDuration / cfg.screen.ifi);

%% Start the dots presentation
vbl = Screen('Flip', cfg.screen.win, 0, dontClear);
vbl = Screen('Flip', cfg.screen.win);
onset = vbl;

while continueShow

% L are the dots that will be moved
L = rand(ndots, 1) < coh;

% Move the selected dots
xy(L, :) = xy(L, :) + dxdy(L, :);

% If not 100% coherence, we get new random locations for the other dots
if sum(~L) > 0
xy(~L, :) = rand(sum(~L), 2);
end

% Create a logical vector to detect any dot that has:
% - an xy position inferior to 0
% - an xy position superior to 1
% - has exceeded its liftime
N = any([xy > 1, xy < 0, dotTime > dotLifeTime], 2) ;

% If there is any such dot we relocate it to a new random position
% and change its lifetime to 1
if any(N)
xy(N, :) = rand(sum(N), 2);
dotTime(N, 1) = 1;
end

% Convert the dot position to pixels
xy_pix = floor(xy * diamAperturePix);

% This assumes that zero is at the top left, but we want it to be

while framesLeft

[dots] = updateDots(dots, cfg);

%% Center the dots
% We assumed that zero is at the top left, but we want it to be
% in the center, so shift the dots up and left, which just means
% adding half of the aperture size to both the x and y direction.
xy_pix = (xy_pix - diamAperturePix / 2)';

% NaN out-of-circle dots
% We use Pythagore's theorem to figure out which dots are out of the
% circle
outCircle = sqrt(xy_pix(1, :).^2 + xy_pix(2, :).^2) + ...
dotSizePix / 2 > (diamAperturePix / 2);
xy_pix(:, outCircle) = NaN;

%% PTB draws the dots stimulation

% Draw the fixation cross
color = cfg.fixation.color;
% adding half of the screen width in pixel to both the x and y direction.
thisEvent.dot.positions = (dots.positions - cfg.screen.winWidth / 2)';
%% make textures
dotTexture('make', cfg, thisEvent);

apertureTexture('make', cfg, thisEvent);

%% draw evetything and flip screen
dotTexture('draw', cfg, thisEvent);
apertureTexture('draw', cfg, thisEvent);

% If this frame shows a target we change the color
if GetSecs < (onset + targetDuration) && isTarget == 1
color = cfg.fixation.colorTarget;
thisFixation.fixation = cfg.fixation;
thisFixation.screen = cfg.screen;
if thisEvent.target(1) && GetSecs < (onset + cfg.target.duration)
thisFixation.fixation.color = cfg.fixation.colorTarget;
end
drawFixationCross(cfg, color);

% Draw the dots
Screen('DrawDots', cfg.screen.win, xy_pix, dotSizePix, dotColor, cfg.screen.center, 2);

Screen('DrawingFinished', cfg.screen.win, dontClear);

vbl = Screen('Flip', cfg.screen.win, vbl + cfg.screen.ifi, dontClear);

drawFixation(thisFixation);

Screen('DrawingFinished', cfg.screen.win);

vbl = Screen('Flip', cfg.screen.win, vbl + cfg.screen.ifi);

%% Update counters

% Check for end of loop
continueShow = continueShow - 1;

% Add one frame to the dot lifetime to each dot
dotTime = dotTime + 1;

framesLeft = framesLeft - 1;

end

%% Erase last dots

drawFixationCross(cfg, cfg.fixation.color);

Screen('DrawingFinished', cfg.screen.win, dontClear);

vbl = Screen('Flip', cfg.screen.win, vbl + cfg.screen.ifi, dontClear);

drawFixation(cfg);
Screen('DrawingFinished', cfg.screen.win);
vbl = Screen('Flip', cfg.screen.win, vbl + cfg.screen.ifi);
duration = vbl - onset;

end
7 changes: 4 additions & 3 deletions subfun/expDesign.m
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@

% Set directions for static and motion condition
motionDirections = [0 90 180 270];
staticDirections = [-1 -1 -1 -1];
staticDirections = [0 90 180 270];
% staticDirections = [-1 -1 -1 -1];

%% Check inputs

Expand All @@ -59,7 +60,7 @@
% Get the parameters
names = cfg.names;
numRepetitions = cfg.numRepetitions;
speedEvent = cfg.dot.speed;
dotsSpeed = cfg.dot.speedPixPerFrame;
numEventsPerBlock = cfg.numEventsPerBlock;
maxNumFixTargPerBlock = cfg.target.maxNbPerBlock;

Expand Down Expand Up @@ -93,7 +94,7 @@

cfg.design.blockNames = cell(nrBlocks, 1);
cfg.design.directions = zeros(nrBlocks, numEventsPerBlock);
cfg.design.speeds = ones(nrBlocks, numEventsPerBlock) * speedEvent;
cfg.design.speeds = ones(nrBlocks, numEventsPerBlock) * dotsSpeed;
cfg.design.fixationTargets = zeros(nrBlocks, numEventsPerBlock);

for iMotionBlock = 1:numRepetitions
Expand Down
Loading