Skip to content

Commit f6f82ab

Browse files
authored
Merge branch 'dev' into dev
2 parents 5df464a + 4f379d8 commit f6f82ab

File tree

9 files changed

+224
-53
lines changed

9 files changed

+224
-53
lines changed

README.md

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,19 @@
2929

3030
# fMRI localizers for visual motion
3131

32-
## Translational Motion
33-
3432
## Requirements
3533

36-
Make sure that the following toolboxes are installed and added to the matlab / octave path.
34+
Make sure that the following toolboxes are installed and added to the matlab / octave path. See the next section on how to install the submodule toolboxes.
3735

3836
For instructions see the following links:
3937

40-
| Requirements | Used version |
41-
| -------------------------------------------------------- | ------------ |
42-
| [CPP_BIDS](https://github.com/cpp-lln-lab/CPP_BIDS) | ? |
43-
| [CPP_PTB](https://github.com/cpp-lln-lab/CPP_PTB) | ? |
44-
| [PsychToolBox](http://psychtoolbox.org/) | >=3.0.14 |
45-
| [Matlab](https://www.mathworks.com/products/matlab.html) | >=2017 |
46-
| or [octave](https://www.gnu.org/software/octave/) | >=4.? |
38+
| Requirements | Used version |
39+
| --------------------------------------------------------------- | ------------ |
40+
| [CPP_BIDS](https://github.com/cpp-lln-lab/CPP_BIDS) (submodule) | 2.1.0 |
41+
| [CPP_PTB](https://github.com/cpp-lln-lab/CPP_PTB) (submodule) | 1.2.0 |
42+
| [PsychToolBox](http://psychtoolbox.org/) | >=3.0.14 |
43+
| [Matlab](https://www.mathworks.com/products/matlab.html) | >=2017 |
44+
| or [octave](https://www.gnu.org/software/octave/) | >=4.? |
4745

4846
## Installation
4947

@@ -56,11 +54,11 @@ git clone --recurse-submodules https://github.com/cpp-lln-lab/localizer_visual_m
5654

5755
## Structure and function details
5856

59-
### visualLocTranslational
57+
### visualMotionLocalizer
6058

61-
Running this script will show blocks of motion dots (soon also moving gratings) and static dots. Motion blocks will show dots(/gratings) moving in one of four directions (up-, down-, left-, and right-ward)
59+
Running this script will show blocks of motion dots and static dots. Motion blocks will show dots moving in one of four directions (up-, down-, left-, and right-ward) (MT+ localizer) or dots moving inward and outward in the peripheral of the screen (MT/MST localizer).
6260

63-
By default it is run in `Debug mode` meaning that it does not run care about subjID, run n., fMRI triggers, Eye Tracker, etc..
61+
Run in `Debug mode` (see `setParameters.m`) it does not care about subjID, run n., Eye Tracker (soon, at the moment it needs to be set off manually), etc..
6462

6563
Any details of the experiment can be changed in `setParameters.m` (e.g., experiment mode, motion stimuli details, exp. design, etc.)
6664

@@ -85,7 +83,7 @@ Any details of the experiment can be changed in `setParameters.m` (e.g., experim
8583
Set `cfg.pacedByTriggers.do` to `true` and you can then set all the details in this `if` block
8684

8785
```matlab
88-
% Time is here in in terms of number repetition time (i.e MRI volumes)
86+
% Time is here in terms of `repetition time (TR)` (i.e. MRI volumes)
8987
if cfg.pacedByTriggers.do
9088
9189
cfg.pacedByTriggers.quietMode = true;
@@ -107,22 +105,27 @@ end
107105

108106
### subfun/doDotMo
109107

108+
Wrapper function that present the dot stimulation (static or motion) per event.
109+
110110
#### Input
111111

112-
- `cfg`: PTB/machine configurations returned by `setParameters` and `initPTB`
113-
- `expParameters`: parameters returned by `setParameters`
114-
- `logFile`: structure that stores the experiment logfile to be saved
112+
- `cfg`: PTB/machine and experiment configurations returned by `setParameters` and `initPTB`
113+
- `thisEvent`: structure that stores information about the event to present regarding the dots (static or motion, direction, etc.)
114+
- `thisFixation`: structure that stores information about the fixation cross task to present
115+
- `dots`: [...]
116+
- `iEvent`: index of the event of the block at the moment of the presentation
115117

116118
#### Output
117119

118120
- Event `onset`
119121
- Event `duration`
122+
- `dots`: [...]
120123

121-
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.
124+
> NB: 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.
122125
123-
### subfun/expDesign
126+
### subfun/expDesign(MtMst)
124127

125-
Creates the sequence of blocks and the events in them. The conditions are consecutive static and motion blocks (Gives better results than randomised). It can be run as a stand alone without inputs to display a visual example of possible design.
128+
These functions, one per MT+ and one per MT/MST localizer, create the sequence of blocks and the events in them. The conditions are consecutive static and motion blocks (Gives better results than randomised). It can be run as a stand alone with input `cfg` from `setParamenters.m` to display a visual example of possible design.
126129

127130
#### EVENTS
128131

setParameters.m

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
% VISUAL LOCALIZER
66

7-
% Initialize the parameters and general configuration variables
7+
% Initialize the general configuration variables structure
88
cfg = struct();
99

1010
% by default the data will be stored in an output folder created where the
@@ -15,7 +15,7 @@
1515

1616
%% Debug mode settings
1717

18-
cfg.debug.do = false; % To test the script out of the scanner, skip PTB sync
18+
cfg.debug.do = true; % To test the script out of the scanner, skip PTB sync
1919
cfg.debug.smallWin = false; % To test on a part of the screen, change to 1
2020
cfg.debug.transpWin = false; % To test with trasparent full size screen
2121

@@ -26,7 +26,7 @@
2626
%% Engine parameters
2727

2828
cfg.testingDevice = 'mri';
29-
cfg.eyeTracker.do = true;
29+
cfg.eyeTracker.do = false;
3030
cfg.audio.do = false;
3131

3232
cfg = setMonitor(cfg);
@@ -36,9 +36,9 @@
3636

3737
% MRI settings
3838
cfg = setMRI(cfg);
39-
cfg.suffix.acquisition = '0p75mmEvTr2p18';
39+
% cfg.suffix.acquisition = '';
4040

41-
cfg.pacedByTriggers.do = false;
41+
cfg.pacedByTriggers.do = true;
4242

4343
%% Experiment Design
4444

@@ -49,14 +49,16 @@
4949
% side of the screen relative to the fixation
5050
% - alternates fixaton left and fixation right
5151
cfg.design.localizer = 'MT';
52-
% cfg.design.localizer = 'MT_MST';
52+
% cfg.design.localizer = 'MT_MST';
5353

5454
cfg.design.motionType = 'translation';
5555
cfg.design.motionDirections = [0 0 180 180];
5656
cfg.design.names = {'static'; 'motion'};
5757

58-
cfg.design.nbRepetitions = 12;
59-
cfg.design.nbEventsPerBlock = 12; % DO NOT CHANGE
58+
% if you have static and motion and `nbRepetions` = 4, this will return 8 blocks (n blocks per
59+
% hemifield in case of MT/MST localizer)
60+
cfg.design.nbRepetitions = 10;
61+
cfg.design.nbEventsPerBlock = 10;
6062

6163
%% Timing
6264

@@ -66,7 +68,7 @@
6668
% IBI
6769
% block length = (cfg.eventDuration + cfg.ISI) * cfg.design.nbEventsPerBlock
6870

69-
cfg.timing.eventDuration = 0.79; % second
71+
cfg.timing.eventDuration = 0.30; % second
7072

7173
% Time between blocs in secs
7274
cfg.timing.IBI = 0;
@@ -81,11 +83,13 @@
8183
if cfg.pacedByTriggers.do
8284

8385
cfg.pacedByTriggers.quietMode = true;
84-
cfg.pacedByTriggers.nbTriggers = 5;
86+
cfg.pacedByTriggers.nbTriggers = 1;
8587

8688
cfg.timing.eventDuration = cfg.mri.repetitionTime / 2 - 0.04; % second
8789

88-
% Time between blocs in secs
90+
% Time between blocs in nb of triggers (remember to consider the nb trigger to wait + 1)
91+
cfg.timing.triggerIBI = 4;
92+
% Time between blocks in secs
8993
cfg.timing.IBI = 0;
9094
% Time between events in secs
9195
cfg.timing.ISI = 0;
@@ -214,12 +218,23 @@
214218
cfg.task.name = 'mt mst localizer';
215219

216220
cfg.design.motionType = 'radial';
217-
cfg.design.motionDirections = [666 666 -666 -666];
218-
cfg.design.names = {'fixation_right'; 'fixation_left'};
221+
cfg.design.motionDirections = [666 -666];
222+
% cfg.design.names = {'motion'};
223+
cfg.design.names = {'static'; 'motion'};
224+
cfg.design.fixationPosition = {'fixation_left'; 'fixation_right'};
225+
% cfg.design.fixationPosition = {'fixation_right'; 'fixation_left'};
219226
cfg.design.xDisplacementFixation = 7;
220227
cfg.design.xDisplacementAperture = 3;
221228

222-
cfg.timing.IBI = 3.6;
229+
% here we double the repetions (2 hemifields)
230+
cfg.design.nbRepetitions = cfg.design.nbRepetitions * length(cfg.design.fixationPosition);
231+
232+
% inward&outward are presented as separated event
233+
cfg.design.nbEventsPerBlock = cfg.design.nbEventsPerBlock * 2;
234+
235+
cfg.timing.IBI = 4;
236+
237+
cfg.timing.changeFixationPosition = 10;
223238

224239
% reexpress those in terms of repetition time
225240
if cfg.pacedByTriggers.do

subfun/assignConditions.m

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@
99
% Get the index of each condition
1010
nameCondition1 = 'static';
1111
nameCondition2 = 'motion';
12+
1213
if isfield(cfg.design, 'localizer') && strcmpi(cfg.design.localizer, 'MT_MST')
13-
nameCondition1 = 'fixation_right';
14-
nameCondition2 = 'fixation_left';
14+
15+
nameCondition1 = 'static';
16+
nameCondition2 = 'motion';
17+
1518
end
1619

1720
CONDITON1_INDEX = find(strcmp(conditionNamesVector, nameCondition1));

subfun/doDotMo.m

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
% (C) Copyright 2018 Mohamed Rezk
22
% (C) Copyright 2020 CPP visual motion localizer developpers
33

4-
function [onset, duration, dots] = doDotMo(cfg, thisEvent, thisFixation, dots)
4+
function [onset, duration, dots] = doDotMo(cfg, thisEvent, thisFixation, dots, iEvent)
55
% Draws the stimulation of static/moving in 4 directions dots or static
66
%
77
% DIRECTIONS
@@ -42,7 +42,13 @@
4242

4343
%% make textures
4444

45-
dotTexture('make', cfg, thisEvent);
45+
if strcmp(cfg.design.localizer, 'MT_MST') && strcmpi(thisEvent.trial_type, 'static') && ~mod(iEvent, 2)
46+
47+
else
48+
49+
dotTexture('make', cfg, thisEvent);
50+
51+
end
4652

4753
apertureTexture('make', cfg, thisEvent);
4854

subfun/expDesignMtMst.m

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
% (C) Copyright 2021 CPP visual motion localizer developpers
2+
3+
function [cfg] = expDesignMtMst(cfg, displayFigs)
4+
5+
%% Check inputs
6+
7+
% Set to 1 for a visualtion of the trials design order
8+
if nargin < 2 || isempty(displayFigs)
9+
displayFigs = 0;
10+
end
11+
12+
% Set variables here for a dummy test of this function
13+
if nargin < 1 || isempty(cfg)
14+
error('give me something to work with');
15+
end
16+
17+
fprintf('\n\nCreating design.\n\n');
18+
19+
[NB_BLOCKS, NB_REPETITIONS, NB_EVENTS_PER_BLOCK, MAX_TARGET_PER_BLOCK] = getDesignInput(cfg);
20+
[~, CONDITON1_INDEX, CONDITON2_INDEX] = assignConditions(cfg);
21+
22+
if mod(NB_REPETITIONS, MAX_TARGET_PER_BLOCK) ~= 0
23+
error('number of repetitions must be a multiple of max number of targets');
24+
end
25+
26+
RANGE_TARGETS = 1:MAX_TARGET_PER_BLOCK;
27+
targetPerCondition = repmat(RANGE_TARGETS, 1, NB_REPETITIONS / MAX_TARGET_PER_BLOCK);
28+
29+
numTargetsForEachBlock = zeros(1, NB_BLOCKS);
30+
31+
if strcmpi(cfg.design.localizer, 'MT_MST') && length(cfg.design.names) == 2
32+
numTargetsForEachBlock(CONDITON1_INDEX) = shuffle(targetPerCondition);
33+
end
34+
35+
numTargetsForEachBlock(CONDITON2_INDEX) = shuffle(targetPerCondition);
36+
37+
%% Give the blocks the names with condition and design the task in each event
38+
while 1
39+
40+
fixationTargets = zeros(NB_BLOCKS, NB_EVENTS_PER_BLOCK);
41+
42+
for iBlock = 1:NB_BLOCKS
43+
44+
% Set target
45+
% - if there are 2 targets per block we make sure that they are at least
46+
% 2 events apart
47+
% - targets cannot be on the first or last event of a block
48+
% - no more than 2 target in the same event order
49+
50+
nbTarget = numTargetsForEachBlock(iBlock);
51+
52+
chosenPosition = setTargetPositionInSequence( ...
53+
NB_EVENTS_PER_BLOCK, ...
54+
nbTarget, ...
55+
[1 NB_EVENTS_PER_BLOCK]);
56+
57+
fixationTargets(iBlock, chosenPosition) = 1;
58+
59+
end
60+
61+
% Check rule 3
62+
if max(sum(fixationTargets)) < NB_REPETITIONS - 1
63+
break
64+
end
65+
66+
end
67+
68+
%% Now we do the easy stuff
69+
cfg.design.blockNames = assignConditions(cfg);
70+
71+
if strcmpi(cfg.design.localizer, 'MT_MST')
72+
73+
if length(cfg.design.names) == 1
74+
75+
nbBlocksPerHemifield = (NB_REPETITIONS / 4) * ...
76+
length(cfg.design.fixationPosition);
77+
78+
else
79+
80+
nbBlocksPerHemifield = (NB_REPETITIONS / 2) * ...
81+
length(cfg.design.fixationPosition);
82+
83+
end
84+
85+
cfg.design.blockFixationPosition = repmat(cfg.design.fixationPosition(1), ...
86+
nbBlocksPerHemifield, ...
87+
1);
88+
89+
if length(cfg.design.fixationPosition) == 2
90+
91+
cfg.design.blockFixationPosition = [cfg.design.blockFixationPosition; ...
92+
repmat(cfg.design.fixationPosition(2), ...
93+
nbBlocksPerHemifield, ...
94+
1)];
95+
96+
end
97+
98+
end
99+
100+
cfg.design.nbBlocks = NB_BLOCKS;
101+
102+
cfg = setDirections(cfg);
103+
104+
speeds = ones(NB_BLOCKS, NB_EVENTS_PER_BLOCK) * cfg.dot.speedPixPerFrame;
105+
cfg.design.speeds = speeds;
106+
107+
cfg.design.fixationTargets = fixationTargets;

subfun/getDirectionBaseVectors.m

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
if isfield(cfg.design, 'localizer') && strcmpi(cfg.design.localizer, 'MT_MST')
1313
CONDITION1_DIRECTIONS = cfg.design.motionDirections;
1414
CONDITION2_DIRECTIONS = cfg.design.motionDirections;
15+
16+
if length(cfg.design.names) == 2
17+
CONDITION2_DIRECTIONS = repmat(-1, size(CONDITION1_DIRECTIONS)); % static
18+
end
19+
1520
end
1621

1722
end

subfun/preTrialSetup.m

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,28 @@
2828

2929
thisEvent.dotCenterXPosPix = 0;
3030

31-
switch thisEvent.trial_type
32-
case 'fixation_right'
33-
cfg.aperture.xPosPix = -abs(cfg.aperture.xPosPix);
31+
if isfield(cfg.design, 'localizer') && strcmpi(cfg.design.localizer, 'MT_MST')
3432

35-
thisEvent.dotCenterXPosPix = cfg.aperture.xPosPix;
33+
thisEvent.fixationPosition = cfg.design.blockFixationPosition{iBlock};
3634

37-
thisFixation.fixation.xDisplacement = cfg.design.xDisplacementFixation;
38-
thisFixation = initFixation(thisFixation);
35+
switch thisEvent.fixationPosition
36+
case 'fixation_right'
37+
cfg.aperture.xPosPix = -abs(cfg.aperture.xPosPix);
3938

40-
case 'fixation_left'
41-
cfg.aperture.xPosPix = +abs(cfg.aperture.xPosPix);
39+
thisEvent.dotCenterXPosPix = cfg.aperture.xPosPix;
4240

43-
thisEvent.dotCenterXPosPix = cfg.aperture.xPosPix;
41+
thisFixation.fixation.xDisplacement = cfg.design.xDisplacementFixation;
42+
thisFixation = initFixation(thisFixation);
4443

45-
thisFixation.fixation.xDisplacement = -cfg.design.xDisplacementFixation;
46-
thisFixation = initFixation(thisFixation);
44+
case 'fixation_left'
45+
cfg.aperture.xPosPix = +abs(cfg.aperture.xPosPix);
46+
47+
thisEvent.dotCenterXPosPix = cfg.aperture.xPosPix;
48+
49+
thisFixation.fixation.xDisplacement = -cfg.design.xDisplacementFixation;
50+
thisFixation = initFixation(thisFixation);
51+
52+
end
4753

4854
end
4955

0 commit comments

Comments
 (0)