Skip to content

Commit 7abbfe6

Browse files
authored
Merge pull request #38 from Remi-Gau/remi-fix_randomize
fix randomize
2 parents ede2bb5 + 11b3ae5 commit 7abbfe6

File tree

11 files changed

+194
-125
lines changed

11 files changed

+194
-125
lines changed

.github/workflows/moxunit.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
uses: joergbrech/[email protected]
2020
with:
2121
tests: tests
22-
src: src
22+
src: subfun
2323
with_coverage: true
2424
cover_xml_file: coverage.xml
2525
- name: Code coverage

README.md

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,34 @@
1+
[![](https://img.shields.io/badge/Octave-CI-blue?logo=Octave&logoColor=white)](https://github.com/cpp-lln-lab/localizer_visual_motion/actions)
2+
![](https://github.com/cpp-lln-lab/localizer_visual_motion/workflows/CI/badge.svg)
3+
4+
[![Build Status](https://travis-ci.com/cpp-lln-lab/localizer_visual_motion.svg?branch=master)](https://travis-ci.com/cpp-lln-lab/localizer_visual_motion)
5+
6+
<!-- vscode-markdown-toc -->
7+
* 1. [Requirements](#Requirements)
8+
* 2. [Installation](#Installation)
9+
* 3. [Structure and function details](#Structureandfunctiondetails)
10+
* 3.1. [visualLocTranslational](#visualLocTranslational)
11+
* 3.2. [setParameters](#setParameters)
12+
* 3.3. [subfun/doDotMo](#subfundoDotMo)
13+
* 3.3.1. [Input:](#Input:)
14+
* 3.3.2. [Output:](#Output:)
15+
* 3.4. [subfun/expDesign](#subfunexpDesign)
16+
* 3.4.1. [EVENTS](#EVENTS)
17+
* 3.4.2. [TARGETS:](#TARGETS:)
18+
* 3.4.3. [Input:](#Input:-1)
19+
* 3.4.4. [Output:](#Output:-1)
20+
21+
<!-- vscode-markdown-toc-config
22+
numbering=true
23+
autoSave=true
24+
/vscode-markdown-toc-config -->
25+
<!-- /vscode-markdown-toc -->
26+
127
# fMRI localizers for visual motion
228

329
# Translational Motion
430

5-
## Requirements
31+
## 1. <a name='Requirements'></a>Requirements
632

733
Make sure that the following toolboxes are installed and added to the matlab / octave path.
834

@@ -16,7 +42,7 @@ For instructions see the following links:
1642
| [Matlab](https://www.mathworks.com/products/matlab.html) | >=2017 |
1743
| or [octave](https://www.gnu.org/software/octave/) | >=4.? |
1844

19-
## Installation
45+
## 2. <a name='Installation'></a>Installation
2046

2147
The CPP_BIDS and CPP_PTB dependencies are already set up as submodule to this repository.
2248
You can install it all with git by doing.
@@ -25,17 +51,17 @@ You can install it all with git by doing.
2551
git clone --recurse-submodules https://github.com/cpp-lln-lab/localizer_visual_motion.git
2652
```
2753

28-
## Structure and function details
54+
## 3. <a name='Structureandfunctiondetails'></a>Structure and function details
2955

30-
### visualLocTranslational
56+
### 3.1. <a name='visualLocTranslational'></a>visualLocTranslational
3157

3258
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)
3359

3460
By default it is run in `Debug mode` meaning that it does not run care about subjID, run n., fMRI triggers, Eye Tracker, etc..
3561

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

38-
### setParameters
64+
### 3.2. <a name='setParameters'></a>setParameters
3965

4066
`setParameters.m` is the core engine of the experiment. It contains the following tweakable sections:
4167

@@ -51,34 +77,34 @@ Any details of the experiment can be changed in `setParameters.m` (e.g., experim
5177
- Instructions
5278
- Task #1 parameters
5379

54-
### subfun/doDotMo
80+
### 3.3. <a name='subfundoDotMo'></a>subfun/doDotMo
5581

56-
#### Input:
82+
#### 3.3.1. <a name='Input:'></a>Input:
5783
- `cfg`: PTB/machine configurations returned by `setParameters` and `initPTB`
5884
- `expParameters`: parameters returned by `setParameters`
5985
- `logFile`: structure that stores the experiment logfile to be saved
6086

61-
#### Output:
87+
#### 3.3.2. <a name='Output:'></a>Output:
6288
- Event `onset`
6389
- Event `duration`
6490

6591
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.
6692

67-
### subfun/expDesign
93+
### 3.4. <a name='subfunexpDesign'></a>subfun/expDesign
6894
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.
6995

70-
#### EVENTS
96+
#### 3.4.1. <a name='EVENTS'></a>EVENTS
7197
The `numEventsPerBlock` should be a multiple of the number of "base" listed in the `motionDirections` and `staticDirections` (4 at the moment).
7298

73-
#### TARGETS:
99+
#### 3.4.2. <a name='TARGETS:'></a>TARGETS:
74100
- If there are 2 targets per block we make sure that they are at least 2 events apart.
75101
- Targets cannot be on the first or last event of a block
76102

77-
#### Input:
103+
#### 3.4.3. <a name='Input:-1'></a>Input:
78104
- `expParameters`: parameters returned by `setParameters`
79105
- `displayFigs`: a boolean to decide whether to show the basic design matrix of the design
80106

81-
#### Output:
107+
#### 3.4.4. <a name='Output:-1'></a>Output:
82108
- `expParameters.designBlockNames` is a cell array `(nr_blocks, 1)` with the name for each block
83109
- `expParameters.designDirections` is an array `(nr_blocks, numEventsPerBlock)` with the direction to present in a given block
84110
- `0 90 180 270` indicate the angle

initEnv.m

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,34 +17,28 @@
1717
octaveVersion = '4.0.3';
1818
matlabVersion = '8.6.0';
1919

20+
installlist = {'io', 'statistics', 'image'};
21+
2022
if isOctave
2123

2224
% Exit if min version is not satisfied
2325
if ~compare_versions(OCTAVE_VERSION, octaveVersion, '>=')
2426
error('Minimum required Octave version: %s', octaveVersion);
2527
end
2628

27-
installlist = {'statistics', 'image'};
2829
for ii = 1:length(installlist)
30+
31+
packageName = installlist{ii};
32+
2933
try
3034
% Try loading Octave packages
31-
disp(['loading ' installlist{ii}]);
32-
pkg('load', installlist{ii});
35+
disp(['loading ' packageName]);
36+
pkg('load', packageName);
3337

3438
catch
35-
errorcount = 1;
36-
while errorcount % Attempt twice in case installation fails
37-
try
38-
pkg('install', '-forge', installlist{ii});
39-
pkg('load', installlist{ii});
40-
errorcount = 0;
41-
catch err
42-
errorcount = errorcount + 1;
43-
if errorcount > 2
44-
error(err.message);
45-
end
46-
end
47-
end
39+
40+
tryInstallFromForge(packageName);
41+
4842
end
4943
end
5044

@@ -72,17 +66,34 @@
7266

7367
end
7468

75-
%%
76-
%% Return: true if the environment is Octave.
77-
%%
7869
function retval = isOctave
70+
% Return: true if the environment is Octave.
7971
persistent cacheval % speeds up repeated calls
8072

8173
if isempty (cacheval)
8274
cacheval = (exist ('OCTAVE_VERSION', 'builtin') > 0);
8375
end
8476

8577
retval = cacheval;
78+
79+
end
80+
81+
function tryInstallFromForge(packageName)
82+
83+
errorcount = 1;
84+
while errorcount % Attempt twice in case installation fails
85+
try
86+
pkg('install', '-forge', packageName);
87+
pkg('load', packageName);
88+
errorcount = 0;
89+
catch err
90+
errorcount = errorcount + 1;
91+
if errorcount > 2
92+
error(err.message);
93+
end
94+
end
95+
end
96+
8697
end
8798

8899
function addDependencies()

miss_hit.cfg

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
# style guide (https://florianschanda.github.io/miss_hit/style_checker.html)
12
line_length: 100
23
regex_function_name: "[a-z]+(([A-Z]){1}[A-Za-z]+)*"
34
suppress_rule: "copyright_notice"
45
exclude_dir: "lib"
5-
exclude_dir: "Visual-loc_radial"
6+
7+
# metrics limit for the code quality (https://florianschanda.github.io/miss_hit/metrics.html)
8+
metric "cnest": limit 4
9+
metric "file_length": limit 500
10+
metric "cyc": limit 15
11+
metric "parameters": limit 5

setParameters.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
% cfg.design.motionType = 'translation';
4040
% cfg.design.motionType = 'radial';
4141
cfg.design.motionType = 'translation';
42+
cfg.design.motionDirections = [0 0 180 180];
4243
cfg.design.names = {'static'; 'motion'};
4344
cfg.design.nbRepetitions = 10;
4445
cfg.design.nbEventsPerBlock = 12; % DO NOT CHANGE

subfun/expDesign.m

Lines changed: 29 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@
2222
% TARGETS
2323
%
2424
% Pseudorandomization rules:
25-
% (1) If there are 2 targets per block we make sure that they are at least 2
25+
% (1) If there are more than 1 target per block we make sure that they are at least 2
2626
% events apart.
2727
% (2) Targets cannot be on the first or last event of a block.
28-
% (3) Targets can not be present more than 2 times in the same event
28+
% (3) Targets can not be present more than NB_REPETITIONS - 1 times in the same event
2929
% position across blocks.
3030
%
3131
% Input:
@@ -57,16 +57,20 @@
5757

5858
% Set variables here for a dummy test of this function
5959
if nargin < 1 || isempty(cfg)
60-
error('give me something to work with')
60+
error('give me something to work with');
6161
end
62-
63-
fprintf('\n\nCreating design.\n\n')
62+
63+
fprintf('\n\nCreating design.\n\n');
6464

6565
[NB_BLOCKS, NB_REPETITIONS, NB_EVENTS_PER_BLOCK, MAX_TARGET_PER_BLOCK] = getInput(cfg);
6666
[~, STATIC_INDEX, MOTION_INDEX] = assignConditions(cfg);
6767

68-
RANGE_TARGETS = [1 MAX_TARGET_PER_BLOCK];
69-
targetPerCondition = repmat(RANGE_TARGETS, 1, NB_REPETITIONS / 2);
68+
if mod(NB_REPETITIONS, MAX_TARGET_PER_BLOCK) ~= 0
69+
error('number of repetitions must be a multiple of max number of targets');
70+
end
71+
72+
RANGE_TARGETS = 1:MAX_TARGET_PER_BLOCK;
73+
targetPerCondition = repmat(RANGE_TARGETS, 1, NB_REPETITIONS / MAX_TARGET_PER_BLOCK);
7074

7175
numTargetsForEachBlock = zeros(1, NB_BLOCKS);
7276
numTargetsForEachBlock(STATIC_INDEX) = shuffle(targetPerCondition);
@@ -85,33 +89,19 @@
8589
% - targets cannot be on the first or last event of a block
8690
% - no more than 2 target in the same event order
8791

88-
chosenTarget = [];
89-
90-
tmpTarget = numTargetsForEachBlock(iBlock);
91-
92-
switch tmpTarget
93-
94-
case 1
95-
96-
chosenTarget = randsample(2:NB_EVENTS_PER_BLOCK - 1, tmpTarget, false);
97-
98-
case 2
99-
100-
targetDifference = 0;
101-
102-
while abs(targetDifference) <= 2
103-
chosenTarget = randsample(2:NB_EVENTS_PER_BLOCK - 1, tmpTarget, false);
104-
targetDifference = diff(chosenTarget);
105-
end
92+
nbTarget = numTargetsForEachBlock(iBlock);
10693

107-
end
94+
chosenPosition = setTargetPositionInSequence( ...
95+
NB_EVENTS_PER_BLOCK, ...
96+
nbTarget, ...
97+
[1 NB_EVENTS_PER_BLOCK]);
10898

109-
fixationTargets(iBlock, chosenTarget) = 1;
99+
fixationTargets(iBlock, chosenPosition) = 1;
110100

111101
end
112102

113103
% Check rule 3
114-
if max(sum(fixationTargets)) < 3
104+
if max(sum(fixationTargets)) < NB_REPETITIONS - 1
115105
break
116106
end
117107

@@ -150,26 +140,17 @@
150140
directions = zeros(NB_BLOCKS, NB_EVENTS_PER_BLOCK);
151141

152142
% Create a vector for the static condition
143+
NB_REPEATS_BASE_VECTOR = NB_EVENTS_PER_BLOCK / length(STATIC_DIRECTIONS);
144+
153145
static_directions = repmat( ...
154146
STATIC_DIRECTIONS, ...
155-
1, NB_EVENTS_PER_BLOCK / length(STATIC_DIRECTIONS));
147+
1, NB_REPEATS_BASE_VECTOR);
156148

157149
for iMotionBlock = 1:NB_REPETITIONS
158150

159-
% Check that we never have twice the same direction
160-
while 1
161-
tmp = [ ...
162-
shuffle(MOTION_DIRECTIONS), ...
163-
shuffle(MOTION_DIRECTIONS), ...
164-
shuffle(MOTION_DIRECTIONS)];
165-
166-
if ~any(diff(tmp, [], 2) == 0)
167-
break
168-
end
169-
end
170-
171151
% Set motion direction and static order
172-
directions(MOTION_INDEX(iMotionBlock), :) = tmp;
152+
directions(MOTION_INDEX(iMotionBlock), :) = ...
153+
repeatShuffleConditions(MOTION_DIRECTIONS, NB_REPEATS_BASE_VECTOR);
173154
directions(STATIC_INDEX(iMotionBlock), :) = static_directions;
174155

175156
end
@@ -183,15 +164,8 @@
183164
% CONSTANTS
184165
% Set directions for static and motion condition
185166

186-
STATIC_DIRECTIONS = [-1 -1 -1 -1];
187-
188-
switch cfg.design.motionType
189-
case 'translation'
190-
MOTION_DIRECTIONS = [0 0 180 180];
191-
case 'radial'
192-
STATIC_DIRECTIONS = [666 -666 666 -666];
193-
MOTION_DIRECTIONS = [666 -666 666 -666];
194-
end
167+
MOTION_DIRECTIONS = cfg.design.motionDirections;
168+
STATIC_DIRECTIONS = repmat(-1, size(MOTION_DIRECTIONS));
195169

196170
end
197171

@@ -202,25 +176,16 @@
202176
nbBlocks = length(cfg.design.names) * nbRepet;
203177
end
204178

205-
function [condition, STATIC_INDEX, MOTION_INDEX] = assignConditions(cfg)
179+
function [conditionNamesVector, STATIC_INDEX, MOTION_INDEX] = assignConditions(cfg)
206180

207181
[~, nbRepet] = getInput(cfg);
208182

209-
condition = repmat(cfg.design.names, nbRepet, 1);
183+
conditionNamesVector = repmat(cfg.design.names, nbRepet, 1);
210184

211185
% Get the index of each condition
212-
STATIC_INDEX = find(strcmp(condition, 'static'));
213-
MOTION_INDEX = find(strcmp(condition, 'motion'));
214-
215-
end
186+
STATIC_INDEX = find(strcmp(conditionNamesVector, 'static'));
187+
MOTION_INDEX = find(strcmp(conditionNamesVector, 'motion'));
216188

217-
function shuffled = shuffle(unshuffled)
218-
% in case PTB is not in the path
219-
try
220-
shuffled = Shuffle(unshuffled);
221-
catch
222-
shuffled = unshuffled(randperm(length(unshuffled)));
223-
end
224189
end
225190

226191
function diplayDesign(cfg, displayFigs)

0 commit comments

Comments
 (0)