Skip to content

Commit d5252a6

Browse files
authored
Merge pull request #20 from Remi-Gau/remi-randomize_trigger_paced
apply randomization, pacing by trigger from visual localizer and do some refactoring
2 parents 2cc7ab9 + f9fae5e commit d5252a6

File tree

10 files changed

+235
-143
lines changed

10 files changed

+235
-143
lines changed

.github/workflows/moxunit.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
pull_request:
8+
branches: '*'
9+
10+
jobs:
11+
build:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v2
15+
with:
16+
submodules: true
17+
fetch-depth: 1
18+
- name: MOxUnit Action
19+
uses: joergbrech/[email protected]
20+
with:
21+
tests: tests
22+
src: subfun
23+
with_coverage: true
24+
cover_xml_file: coverage.xml
25+
- name: Code coverage
26+
uses: codecov/codecov-action@v1
27+
with:
28+
token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos
29+
file: coverage.xml # optional
30+
flags: unittests # optional
31+
name: codecov-umbrella # optional
32+
fail_ci_if_error: true # optional (default = false)

audioLocTranslational.m

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,8 @@
4040
[cfg] = initPTB(cfg);
4141

4242
[el] = eyeTracker('Calibration', cfg);
43-
44-
% % % REFACTOR THIS FUNCTION % % %
45-
46-
[cfg] = expDesign(cfg);
4743

48-
cfg.design.blockNames
49-
50-
% % % REFACTOR THIS FUNCTION % % %
44+
[cfg] = expDesign(cfg);
5145

5246
% Prepare for the output logfiles with all
5347
logFile.extraColumns = cfg.extraColumns;
@@ -69,7 +63,7 @@
6963

7064
getResponse('start', cfg.keyboard.responseBox);
7165

72-
WaitSecs(cfg.timing.onsetDelay);
66+
waitFor(cfg, cfg.timing.onsetDelay);
7367

7468
%% For Each Block
7569

@@ -90,13 +84,19 @@
9084
thisEvent.direction = cfg.design.directions(iBlock, iEvent);
9185
% thisEvent.speed = cfg.design.speeds(iBlock, iEvent);
9286
thisEvent.target = cfg.design.fixationTargets(iBlock, iEvent);
87+
88+
% we wait for a trigger every 2 events
89+
if cfg.pacedByTriggers.do && mod(iEvent, 2) == 1
90+
waitForTrigger( ...
91+
cfg, ...
92+
cfg.keyboard.responseBox, ...
93+
cfg.pacedByTriggers.quietMode, ...
94+
cfg.pacedByTriggers.nbTriggers);
95+
end
9396

9497
% % % REFACTOR THIS FUNCTION % % %
95-
9698
% play the sounds and collect onset and duration of the event
97-
[onset, duration] = doAudMot(cfg, thisEvent);
98-
99-
% % % REFACTOR THIS FUNCTION % % %
99+
[onset, duration] = doAuditoryMotion(cfg, thisEvent);
100100

101101
thisEvent.event = iEvent;
102102
thisEvent.block = iBlock;
@@ -122,13 +122,13 @@
122122
saveResponsesAndTriggers(responseEvents, cfg, logFile, triggerString);
123123

124124
% wait for the inter-stimulus interval
125-
WaitSecs(cfg.timing.ISI);
125+
waitFor(cfg, cfg.timing.ISI);
126126

127127
end
128128

129129
eyeTracker('StopRecordings', cfg);
130130

131-
WaitSecs(cfg.timing.IBI);
131+
waitFor(cfg, cfg.timing.IBI);
132132

133133
% trigger monitoring
134134
triggerEvents = getResponse('check', cfg.keyboard.responseBox, cfg, ...
@@ -140,7 +140,7 @@
140140
end
141141

142142
% End of the run for the BOLD to go down
143-
WaitSecs(cfg.timing.endDelay);
143+
waitFor(cfg, cfg.timing.endDelay);
144144

145145
cfg = getExperimentEnd(cfg);
146146

initEnv.m

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
% - struct
99
% - statistics
1010
%
11-
% MATLAB > R2017a
11+
% MATLAB >= R2015b
1212
%
1313
% 2 - Add project to the O/M path
1414

@@ -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

@@ -58,7 +52,9 @@
5852

5953
% If external dir is empty throw an exception
6054
% and ask user to update submodules.
61-
if numel(dir('lib')) <= 2 % Means that the external is empty
55+
libDirectory = fullfile(fileparts(mfilename('fullpath')), 'lib');
56+
57+
if numel(dir(libDirectory)) <= 2 % Means that the external is empty
6258
error(['Git submodules are not cloned!', ...
6359
'Try this in your terminal:', ...
6460
' git submodule update --recursive ']);
@@ -70,25 +66,41 @@
7066

7167
end
7268

73-
%%
74-
%% Return: true if the environment is Octave.
75-
%%
7669
function retval = isOctave
70+
% Return: true if the environment is Octave.
7771
persistent cacheval % speeds up repeated calls
7872

7973
if isempty (cacheval)
8074
cacheval = (exist ('OCTAVE_VERSION', 'builtin') > 0);
8175
end
8276

8377
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+
8497
end
8598

8699
function addDependencies()
87100

88101
pth = fileparts(mfilename('fullpath'));
89102
addpath(genpath(fullfile(pth, 'lib', 'CPP_BIDS', 'src')));
90-
addpath(fullfile(pth, 'lib', 'CPP_PTB'));
103+
addpath(genpath(fullfile(pth, 'lib', 'CPP_PTB', 'src')));
91104
addpath(fullfile(pth, 'subfun'));
92-
addpath(fullfile(pth, 'input'));
93105

94106
end

lib/CPP_BIDS

setParameters.m

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,17 @@
3131

3232
% MRI settings
3333
cfg = setMRI(cfg);
34+
35+
cfg.pacedByTriggers.do = true;
3436

3537
%% Experiment Design
3638

3739
% cfg.design.motionType = 'translation';
3840
% cfg.design.motionType = 'radial';
3941
cfg.design.motionType = 'translation';
4042
cfg.design.names = {'static'; 'motion'};
41-
cfg.design.possibleDirections = [-1 1]; % 1 motion , -1 static %NOT IN USE AT THE MOMENT
42-
% cfg.design.nbBlocks = size(cfg.design.names, 2); % TO CHECK
43-
cfg.design.nbRepetitions = 14; % AT THE MOMENT IT IS NOT SET IN THE MAIN SCRIPT
43+
cfg.design.motionDirections = [-1 -1 1 1];
44+
cfg.design.nbRepetitions = 14;
4445
cfg.design.nbEventsPerBlock = 12;
4546

4647
%% Timing
@@ -51,15 +52,35 @@
5152
% IBI
5253
% block length = (cfg.eventDuration + cfg.ISI) * cfg.design.nbEventsPerBlock
5354

55+
cfg.timing.eventDuration = 0.850; % second
56+
5457
% Time between blocs in secs
55-
cfg.timing.IBI = 1.8; % 8;
58+
cfg.timing.IBI = 1.8;
5659
% Time between events in secs
5760
cfg.timing.ISI = 0;
5861
% Number of seconds before the motion stimuli are presented
59-
cfg.timing.onsetDelay = .1;
62+
cfg.timing.onsetDelay = 0;
6063
% Number of seconds after the end all the stimuli before ending the run
6164
cfg.timing.endDelay = 3.6;
6265

66+
% reexpress those in terms of repetition time
67+
if cfg.pacedByTriggers.do
68+
69+
cfg.pacedByTriggers.quietMode = true;
70+
cfg.pacedByTriggers.nbTriggers = 1;
71+
72+
cfg.timing.eventDuration = cfg.mri.repetitionTime / 2 - 0.04; % second
73+
74+
% Time between blocs in secs
75+
cfg.timing.IBI = 1;
76+
% Time between events in secs
77+
cfg.timing.ISI = 0;
78+
% Number of seconds before the motion stimuli are presented
79+
cfg.timing.onsetDelay = 0;
80+
% Number of seconds after the end all the stimuli before ending the run
81+
cfg.timing.endDelay = 2;
82+
end
83+
6384
%% Auditory Stimulation
6485

6586
cfg.audio.channels = 2;
@@ -80,7 +101,7 @@
80101
cfg.fixation.xDisplacement = 0;
81102
cfg.fixation.yDisplacement = 0;
82103

83-
cfg.target.maxNbPerBlock = 0;
104+
cfg.target.maxNbPerBlock = 2;
84105
cfg.target.duration = 0.5; % In secs
85106

86107
cfg.extraColumns = {'direction', 'speed', 'target', 'event', 'block', 'keyName'};

subfun/doAudMot.m renamed to subfun/doAuditoryMotion.m

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
function [onset, duration] = doAudMot(cfg, thisEvent)
1+
function [onset, duration] = doAuditoryMotion(cfg, thisEvent)
22

33
% Play the auditopry stimulation of moving in 4 directions or static noise bursts
44
%
@@ -11,48 +11,36 @@
1111
% - thisEvent: structure that the parameters regarding the event to present
1212
%
1313
% Output:
14-
% -
14+
% - onset in machine time
15+
% - duration in seconds
1516
%
1617

1718
%% Get parameters
1819

19-
sound = [];
20-
2120
direction = thisEvent.direction(1);
2221
isTarget = thisEvent.target(1);
2322
targetDuration = cfg.target.duration;
2423

2524
soundData = cfg.soundData;
2625

27-
% if isTarget == 0
26+
switch direction
27+
case -1
28+
fieldName = 'S';
29+
case 90
30+
fieldName = 'U';
31+
case 270
32+
fieldName = 'D';
33+
case 0
34+
fieldName = 'R';
35+
case 180
36+
fieldName = 'L';
37+
end
2838

29-
if direction == -1
30-
sound = soundData.S;
31-
elseif direction == 90
32-
sound = soundData.U;
33-
elseif direction == 270
34-
sound = soundData.D;
35-
elseif direction == 0
36-
sound = soundData.R;
37-
elseif direction == 180
38-
sound = soundData.L;
39+
if isTarget == 1
40+
fieldName = [fieldName '_T'];
3941
end
4042

41-
% elseif isTarget == 1
42-
%
43-
% if direction == -1
44-
% sound = soundData.S_T;
45-
% elseif direction == 90
46-
% sound = soundData.U_T;
47-
% elseif direction == 270
48-
% sound = soundData.D_T;
49-
% elseif direction == 0
50-
% sound = soundData.R_T;
51-
% elseif direction == 180
52-
% sound = soundData.L_T;
53-
% end
54-
%
55-
% end
43+
sound = soundData.(fieldName);
5644

5745
% Start the sound presentation
5846
PsychPortAudio('FillBuffer', cfg.audio.pahandle, sound);

0 commit comments

Comments
 (0)