Skip to content

Commit 2db0595

Browse files
authored
Merge pull request #8 from Remi-Gau/dev
make it easy to adapt stimuli to the screen dimension
2 parents 80c351a + cb4e4b3 commit 2db0595

14 files changed

Lines changed: 248 additions & 167 deletions

File tree

.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/moxunit-action@v1.1
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)

README.md

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,59 @@
1+
[![](https://img.shields.io/badge/Octave-CI-blue?logo=Octave&logoColor=white)](https://github.com/cpp-lln-lab/CPP_retinotopy/actions)
2+
![](https://github.com/cpp-lln-lab/CPP_retinotopy/workflows/CI/badge.svg)
3+
4+
[![codecov](https://codecov.io/gh/cpp-lln-lab/CPP_retinotopy/branch/master/graph/badge.svg)](https://codecov.io/gh/cpp-lln-lab/CPP_retinotopy)
5+
6+
[![Build Status](https://travis-ci.com/cpp-lln-lab/CPP_retinotopy.svg?branch=master)](https://travis-ci.com/cpp-lln-lab/CPP_retinotopy)
7+
18
# RETINOTOPIC MAPPING STIMULUS
29

10+
<!-- vscode-markdown-toc -->
11+
12+
- 1. [Depdendencies](#Depdendencies) \* 1.1. [mpm (beta)](#mpmbeta)
13+
- 2. [Running the experiment](#Runningtheexperiment)
14+
_ 2.1. [Set up](#Setup)
15+
_ 2.2. [Running the retinotopy scripts](#Runningtheretinotopyscripts)
16+
- 3. [Other scripts](#Otherscripts)
17+
- 4. [Reporting a problem](#Reportingaproblem)
18+
- 5. [Contributors](#Contributors)
19+
20+
<!-- vscode-markdown-toc-config
21+
numbering=true
22+
autoSave=true
23+
/vscode-markdown-toc-config -->
24+
<!-- /vscode-markdown-toc -->
325

4-
## Depdendencies
26+
## 1. <a name='Depdendencies'></a>Depdendencies
527

628
| Dependencies | Used version |
7-
|----------------------------------------------------------|--------------|
29+
| -------------------------------------------------------- | ------------ |
830
| [Matlab](https://www.mathworks.com/products/matlab.html) | 20??? |
931
| or [Octave](https://www.gnu.org/software/octave/) | 4.? |
10-
| [Psychtoolbox](http://psychtoolbox.org/) | v3.15 |
11-
| [JSONio](https://github.com/gllmflndn/JSONio) | NA |
32+
| [Psychtoolbox](http://psychtoolbox.org/) | v3.15 |
33+
| [JSONio](https://github.com/gllmflndn/JSONio) | NA |
1234

13-
### mpm (beta)
35+
### 1.1. <a name='mpmbeta'></a>mpm (beta)
1436

1537
You can use [matlab package manager](https://github.com/mobeets/mpm) to install the dependencies.
1638

1739
```matlab
1840
mpm install -i fullpath_to_this_folder/dependencies.txt -c PTB_retinotopy --allpaths
1941
```
2042

21-
## Running the experiment
43+
## 2. <a name='Runningtheexperiment'></a>Running the experiment
2244

23-
### Set up
45+
### 2.1. <a name='Setup'></a>Set up
2446

2547
Before you can begin you will need to generate a checkerboard or ripple stimulus with the dimensions of the screen you are using for your experiment. For this, you will need to modify the script `GenCheckerboard.m` or `GenRipples.m` in the `input` folder and change the variable `width` or the `height` of your screen in pixels. After running this script the new checkerboard stimulus will be saved on your disk.
2648

2749
Some other things will need to be changed to match your needs. Most of it should be in :
2850

2951
- `SetParameters.m`
30-
- The parameter `Resolution` needs to be changed to reflect your screen dimensions (e.g. `[0 0 1024 768]` for 1024*768).
52+
- The parameter `Resolution` needs to be changed to reflect your screen dimensions (e.g. `[0 0 1024 768]` for 1024\*768).
3153
- The parameters `TR` and `Dummies` need to be adjusted for your scanner sequence.
3254

3355
In the following
56+
3457
- `Polar.m`
3558
- `Eccen.m`
3659
- `DriftingBar`
@@ -39,27 +62,25 @@ you will need to adapt the `VolsPerCycle` parameter such that you have enough vo
3962

4063
For example, if your TR is 2 seconds you would want to have 30 volumes per cycle. (Note that shorter cycles work as well, but for inexperienced participants a minute per cycle is probably optimal).
4164

42-
### Running the retinotopy scripts
65+
### 2.2. <a name='Runningtheretinotopyscripts'></a>Running the retinotopy scripts
4366

4467
Any of the other parameters probably do not need to be changed at all.
4568

4669
Calling the `Polar` and `Eccen` program without any input arguments will run a demo which is triggered manually by pressing a key.
4770

4871
The first input argument is the subject name/ID, the second (optional) input argument defines the direction the stimulus is moving (that is either clockwise vs anticlockwise or contracting vs expanding).
4972

50-
51-
## Other scripts
73+
## 3. <a name='Otherscripts'></a>Other scripts
5274

5375
There are also two scripts which can be used for population receptive field mapping. One is the standard protocol with bars traversing the visual field as in Dumoulin & Wandell (2008).
5476

5577
In addition, there is a dual phase-encoded protocol with which you could theoretically map both the polar angle and the eccentricity in the same scan. By including blank periods this could also be used in order to map population receptive fields. This still needs refactoring.
5678

57-
58-
## Reporting a problem
79+
## 4. <a name='Reportingaproblem'></a>Reporting a problem
5980

6081
Get in touch by reporting an issue or sending a pull request
6182

62-
## Contributors
83+
## 5. <a name='Contributors'></a>Contributors
6384

6485
Original version by [Sam Schwarzkopf](https://sampendu.net/sam-schwarzkopf/) (27th July 2010)
6586

@@ -68,7 +89,7 @@ Some modifications (esp for eye tracking) have been done by [Tim Rohe](https://s
6889
Code cleaning and refactoring was done by Rémi Gau.
6990

7091
The loose style guide used here:
92+
7193
- use `PascalCase`
7294
- structures that are passed around and centralize a lot of information (TARGET, BEHAVIOUR, PARAMETERS...) are in upper case
7395
- [McCabe complexity](https://en.wikipedia.org/wiki/Cyclomatic_complexity) has to be inferior to 15 (see `checkcode('foo.m', '-cyc')`)
74-

driftingBars.m

100644100755
Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,62 +5,62 @@ function driftingBars(debug, stim, emul)
55
% Subj : String with subject ID
66
% Stim : Stimulus file name e.g. 'Checkerboard'
77
% Emul : 0 = Triggered by scanner, 1 = Trigger by keypress
8-
8+
99
if nargin < 1 || isempty(debug)
1010
debug = 1;
1111
end
1212
if nargin < 2 || isempty(stim)
1313
stim = 'Ripples.mat';
1414
end
1515
if nargin < 3 || isempty(emul)
16-
emul = 1;
16+
emul = 0;
1717
end
1818

1919
initEnv();
20-
20+
2121
%% Experimental Parameters
22-
22+
2323
cfg.task.name = 'retinotopy drifting bar';
24-
24+
2525
% Stimulus type
2626
cfg.aperture.type = 'bar';
27-
27+
2828
% Stimulus conditions in each block defined by number
29-
cfg.conditions = [90 45 0 135 270 225 180 315];
30-
29+
cfg.conditions = [90 45 135 270 225 315 90 45 135 270 225 315];
30+
3131
%% Set defaults
32-
32+
3333
cfg.stim = stim;
3434
cfg.debug.do = debug;
35-
35+
3636
if ~emul
3737
cfg.testingDevice = 'mri';
3838
else
3939
cfg.testingDevice = 'pc';
4040
end
41-
41+
4242
cfg.extraColumns.bar_angle = struct( ...
4343
'length', 1, ...
4444
'bids', struct( ...
4545
'LongName', 'bar angle', ...
4646
'Description', '', ...
4747
'Units', 'degrees'));
48-
48+
4949
cfg.extraColumns.bar_width = struct( ...
5050
'length', 1, ...
5151
'bids', struct( ...
5252
'LongName', 'width of the bar', ...
5353
'Description', '', ...
5454
'Units', 'degrees'));
55-
55+
5656
cfg.extraColumns.bar_position = struct( ...
5757
'length', 1, ...
5858
'bids', struct( ...
5959
'LongName', '', ...
6060
'Description', 'bar position with respoect to the fixation cross', ...
6161
'Units', 'degrees'));
62-
62+
6363
[cfg] = setParameters(cfg);
64-
64+
6565
%% Run the experiment
6666
barsMapping(cfg);

initEnv.m

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

1515
function initEnv
1616

1717
octaveVersion = '4.0.3';
18-
matlabVersion = '9.2.0';
18+
matlabVersion = '8.6.0';
19+
20+
installlist = {'io', 'statistics', 'image'};
1921

2022
if isOctave
2123

@@ -24,27 +26,19 @@
2426
error('Minimum required Octave version: %s', octaveVersion);
2527
end
2628

27-
installlist = {'statistics'};
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)
80-
cacheval = (exist ("OCTAVE_VERSION", "builtin") > 0);
74+
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'));
89-
addpath(fullfile(pth, 'lib', 'CPP_BIDS', 'src'));
90-
addpath(fullfile(pth, 'lib', 'CPP_PTB', 'src', 'aperture'));
91-
addpath(fullfile(pth, 'lib', 'CPP_PTB'));
102+
addpath(genpath(fullfile(pth, 'lib', 'CPP_BIDS', 'src')));
103+
addpath(genpath(fullfile(pth, 'lib', 'CPP_PTB', 'src')));
92104
addpath(genpath(fullfile(pth, 'subfun')));
93105

94106
end

input/genRipples.m

100644100755
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
% Define here the height of the screen minus a few pixels
22

3-
Height = 1080;
3+
Height = 1000;
44

55
Phases = 0:10:355;
66

lib/CPP_BIDS

Submodule CPP_BIDS updated 56 files

miss_hit.cfg

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

optimRetMapping.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
ringSpeed = widthFOV ./ cycleDur;
1212
stayRingSec = widthRing ./ ringSpeed; % time ring needs to pass a voxel
13-
stickFct = mod(time, cycleDur) < stayRingSec ;
13+
stickFct = mod(time, cycleDur) < stayRingSec;
1414
[hrf, p] = spm_hrf(TR);
1515
Y = filter(hrf', 1, stickFct);
1616
Y = Y + randn(length(timeSpan), length(widthRingRange), length(cycleDurRange));
@@ -20,7 +20,7 @@
2020
cos(time(:, iWidth, iCycle) * 2 * pi / cycleDur(1, iWidth, iCycle)), ...
2121
ones(length(timeSpan), 1)];
2222
[param, dev, stats] = glmfit(X, Y(:, iWidth, iCycle), 'normal');
23-
ResVar = dev * 1 / (length(timeSpan) - 3) ;
23+
ResVar = dev * 1 / (length(timeSpan) - 3);
2424
F(iWidth, iCycle) = (sum(param(1:2).^2) / 3) / ResVar;
2525
power(iWidth, iCycle) = abs(param(2) + i * param(1));
2626
end

0 commit comments

Comments
 (0)