Skip to content

Commit f6e3b4c

Browse files
authored
Merge pull request #82 from Remi-Gau/remi-eeg_beh
improve support for EEG, MEG, iEEG and eyetracking data
2 parents 97ff407 + af39717 commit f6e3b4c

20 files changed

+856
-266
lines changed

.travis.yml

+22-17
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,39 @@
11
# Travis CI (https://travis-ci.org/)
2+
# This will only work on your repo if you have an account on travis and you
3+
# have set it up to run continuous integration on this this repo
4+
5+
# Linux distribution (bionic beaver)
6+
dist: bionic
7+
8+
# Language and version
9+
language: python
10+
python:
11+
- "3.6" # current default Python on Travis CI
212

3-
language: c
4-
dist: bionic # based on bionic beaver ubuntu distribution
513
cache:
614
apt: true # only works with Pro version
715

816
env:
9-
global: # define environment variables for bash
17+
global: # Define environment variables for bash
1018
- OCTFLAGS="--no-gui --no-window-system --silent"
1119

1220
before_install:
1321
- travis_retry sudo apt-get -y -qq update
14-
# install octave
22+
# Install octave
1523
- travis_retry sudo apt-get -y install octave
1624
- travis_retry sudo apt-get -y install liboctave-dev
17-
# install javascript
25+
# Install javascript node and node package manager necessary to run the validator
1826
- travis_retry sudo apt-get -y install nodejs
1927
- travis_retry sudo apt-get -y install npm
20-
# install miss_hit linter
21-
- cd .. && git clone https://github.com/florianschanda/miss_hit.git && export PATH=$PATH:`pwd`/miss_hit && cd CPP_BIDS
22-
28+
# Install BIDS-Validator
29+
- sudo npm install -g bids-validator
30+
# Install miss_hit linter
31+
- pip3 install miss_hit
2332

2433
install:
2534
# make octave file the JSONio submodule
2635
- cd lib/JSONio; mkoctfile --mex jsonread.c jsmn.c -DJSMN_PARENT_LINKS; cd ../..
2736
- octave $OCTFLAGS --eval "addpath (pwd); savepath ();"
28-
# Install BIDS-Validator
29-
- sudo npm install -g bids-validator
3037

3138
before_script:
3239
# Add to src functions to path
@@ -36,11 +43,9 @@ before_script:
3643

3744
jobs:
3845
include:
39-
- stage: "BIDS validator"
40-
name: "Create and check dataset"
46+
- name: "BIDS validator: create and check dataset"
4147
script: octave $OCTFLAGS --eval "test_makeRawDataset" && bids-validator `pwd`/output/rawdata/ --ignoreNiftiHeaders
42-
- stage: "Linter"
43-
name: "miss_hit"
44-
script: cd .. && mh_style `pwd`
45-
46-
48+
- name: "miss_hit: checking code quality"
49+
script: mh_metric . --ci
50+
- name: "miss_hit: checking code style"
51+
script: mh_style .

README.md

+29
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,13 @@ Feel free to open issues to report a bug and ask for improvements.
7676

7777
Here are the naming templates used.
7878

79+
- Behavior
80+
81+
`sub-<label>[_ses-<label>]_task-<label>[_acq-<label>][_run-<index>]_events.tsv`
82+
`sub-<label>[_ses-<label>]_task-<label>[_acq-<label>][_run-<index>]_events.json`
83+
`sub-<label>[_ses-<label>]_task-<label>[_acq-<label>][_run-<index>]_beh.tsv`
84+
`sub-<label>[_ses-<label>]_task-<label>[_acq-<label>][_run-<index>]_beh.json`
85+
7986
- BOLD
8087

8188
`sub-<label>[_ses-<label>]_task-<label>[_acq-<label>][_ce-<label>][_dir-<label>][_rec-<label>][_run-<index>][_echo-<index>]_<contrast_label>.nii[.gz]`
@@ -87,15 +94,37 @@ Here are the naming templates used.
8794
- EEG
8895

8996
`sub-<label>[_ses-<label>]_task-<label>[_run-<index>]_eeg.<manufacturer_specific_extension>`
97+
`sub-<label>[_ses-<label>]_task-<label>[_run-<index>]_eeg.json`
98+
99+
<!-- European data format (Each recording consisting of a .edf file)
100+
101+
BrainVision Core Data Format (Each recording consisting of a .vhdr, .vmrk, .eeg file triplet)
102+
103+
The format used by the MATLAB toolbox EEGLAB (Each recording consisting of a .set file with an optional .fdt file)
104+
105+
Biosemi data format (Each recording consisting of a .bdf file) -->
106+
107+
90108

91109
- MEG
92110

93111
???
94112

95113
- Eyetracker
96114

115+
current format
116+
`<matches>_recording-eyetracking_physio.tsv.gz`
117+
118+
future BEP format in a dedicated eyetracker folder
97119
`sub-<participant_label>[_ses-<label>][_acq-<label>]_task-<task_label>_eyetrack.<manufacturer_specific_extension>`
98120

121+
- Stim and physio
122+
123+
`<matches>[_recording-<label>]_physio.tsv.gz`
124+
`<matches>[_recording-<label>]_physio.json`
125+
`<matches>[_recording-<label>]_stim.tsv.gz`
126+
`<matches>[_recording-<label>]_stim.json`
127+
99128
### 3.3. <a name='Contributors'></a>Contributors ✨
100129

101130
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):

manualTests/dummyData/.bidsignore

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
*_beh.*
2+
*_eeg.*
3+
*_ieeg.*
4+
*_meg.*
5+
6+
# *_beh.tsv
7+
# *_eeg.edf
8+
# *_ieeg.edf
9+
# *_meg.fif
10+
11+
# *_bold.nii.gz

manualTests/test_createBoldJson.m renamed to manualTests/test_createJson.m

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
function test_suite = test_createBoldJson %#ok<*STOUT>
1+
function test_suite = test_createJson %#ok<*STOUT>
22
try % assignment of 'localfunctions' is necessary in Matlab >= 2016
33
test_functions = localfunctions(); %#ok<*NASGU>
44
catch % no problem; early Matlab versions can use initTestSuite fine
55
end
66
initTestSuite;
77
end
88

9-
function test_createBoldJsonExtra()
9+
function test_createJsonExtra()
1010

1111
outputDir = fullfile(fileparts(mfilename('fullpath')), 'output');
1212

@@ -29,7 +29,7 @@ function test_createBoldJsonExtra()
2929

3030
extraInfo = struct('extraInfo', struct('nestedExtraInfo', 'something extra'));
3131

32-
createBoldJson(cfg, extraInfo);
32+
createJson(cfg, extraInfo);
3333

3434
%% check content
3535
fileName = strrep(cfg.fileName.events, '_events', '_bold');

manualTests/test_makeRawDataset.m

+139-22
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ function test_makeRawDataset()
2222
cfg.subject.runNb = 1;
2323

2424
cfg.task.name = 'testtask';
25+
cfg.task.instructions = 'do this';
2526

2627
logFile.extraColumns.Speed.length = 1;
2728
logFile.extraColumns.LHL24.length = 3;
@@ -30,7 +31,7 @@ function test_makeRawDataset()
3031
cfg = createFilename(cfg);
3132

3233
extraInfo = struct('extraInfo', struct('nestedExtraInfo', 'something extra'));
33-
createBoldJson(cfg, extraInfo);
34+
createJson(cfg, extraInfo);
3435

3536
createDatasetDescription(cfg);
3637

@@ -82,49 +83,165 @@ function test_makeRawDataset()
8283
saveEventsFile('save', cfg, stimLogFile);
8384
saveEventsFile('close', cfg, stimLogFile);
8485

85-
% add dummy functional data
86-
funcDir = fullfile(cfg.dir.output, 'source', 'sub-001', 'ses-001', 'func');
87-
boldFilename = 'sub-001_ses-001_task-testtask_run-001_bold.nii.gz';
88-
89-
copyfile( ...
90-
fullfile('dummyData', 'dummyData.nii.gz'), ...
91-
fullfile(funcDir, boldFilename));
92-
9386
%% MRI bold rest data and fancy suffixes
9487
clear cfg;
9588

9689
cfg.dir.output = outputDir;
9790

9891
cfg.testingDevice = 'mri';
9992

100-
cfg.subject.subjectNb = 2;
101-
cfg.subject.sessionNb = 3;
102-
cfg.subject.runNb = 4;
93+
cfg.subject.subjectNb = 1;
94+
cfg.subject.runNb = 1;
10395

10496
% deal with MRI suffixes
105-
cfg.mri.reconstruction = 'fast recon';
106-
cfg.mri.contrastEnhancement = 'test';
107-
cfg.mri.phaseEncodingDirection = 'y pos';
108-
cfg.mri.echo = '1';
109-
cfg.mri.acquisition = ' new tYpe';
97+
cfg.suffix.reconstruction = 'fast recon';
98+
cfg.suffix.contrastEnhancement = 'test';
99+
cfg.suffix.phaseEncodingDirection = 'y pos';
100+
cfg.suffix.echo = '1';
101+
cfg.suffix.acquisition = ' new tYpe';
102+
110103
cfg.mri.repetitionTime = 1.56;
111104

112105
cfg.task.name = 'rest';
113106

114107
cfg = createFilename(cfg);
115108

116-
createBoldJson(cfg);
109+
extraInfo = struct('extraInfo', struct('nestedExtraInfo', 'something extra'));
110+
createJson(cfg, extraInfo);
111+
112+
%% EEG data and fancy suffixes
113+
clear cfg;
114+
115+
cfg.dir.output = outputDir;
116+
117+
cfg.testingDevice = 'eeg';
118+
119+
cfg.subject.subjectNb = 1;
120+
cfg.subject.runNb = 1;
121+
122+
cfg.task.name = 'target practice';
123+
cfg.task.instructions = 'do this';
124+
125+
cfg.bids.eeg.EEGReference = 'Cz';
126+
cfg.bids.eeg.SamplingFrequency = 2400;
127+
cfg.bids.eeg.PowerLineFrequency = 50;
128+
cfg.bids.eeg.SoftwareFilters = 'n/a';
129+
130+
cfg = createFilename(cfg);
131+
132+
extraInfo = struct('extraInfo', struct('nestedExtraInfo', 'something extra'));
133+
createJson(cfg, extraInfo);
134+
135+
%% iEEG data and fancy suffixes
136+
clear cfg;
137+
138+
cfg.dir.output = outputDir;
139+
140+
cfg.testingDevice = 'ieeg';
141+
142+
cfg.subject.subjectNb = 1;
143+
cfg.subject.runNb = 1;
144+
145+
cfg.task.name = 'implanted target practice';
146+
cfg.task.instructions = 'do this';
147+
148+
cfg.bids.ieeg.iEEGReference = 'Cz';
149+
cfg.bids.ieeg.SamplingFrequency = 2400;
150+
cfg.bids.ieeg.PowerLineFrequency = 50;
151+
cfg.bids.ieeg.SoftwareFilters = 'n/a';
152+
153+
cfg = createFilename(cfg);
154+
155+
extraInfo = struct('extraInfo', struct('nestedExtraInfo', 'something extra'));
156+
createJson(cfg, extraInfo);
157+
158+
%% MEG data and fancy suffixes
159+
clear cfg;
160+
161+
cfg.dir.output = outputDir;
162+
163+
cfg.testingDevice = 'meg';
164+
165+
cfg.subject.subjectNb = 1;
166+
cfg.subject.runNb = 1;
167+
168+
cfg.task.name = 'magnetic target practice';
169+
cfg.task.instructions = 'do this';
170+
171+
cfg.bids.meg.SamplingFrequency = 2400;
172+
cfg.bids.meg.PowerLineFrequency = 60;
173+
cfg.bids.meg.DewarPosition = 'upright';
174+
cfg.bids.meg.SoftwareFilters = 'n/a';
175+
cfg.bids.meg.DigitizedLandmarks = false;
176+
cfg.bids.meg.DigitizedHeadPoints = false;
177+
178+
cfg = createFilename(cfg);
179+
180+
extraInfo = struct('extraInfo', struct('nestedExtraInfo', 'something extra'));
181+
createJson(cfg, extraInfo);
182+
183+
%% beh data and fancy suffixes
184+
clear cfg;
185+
186+
cfg.dir.output = outputDir;
187+
188+
cfg.testingDevice = 'pc';
189+
190+
cfg.subject.subjectNb = 1;
191+
cfg.subject.runNb = 1;
192+
193+
cfg.task.name = 'easy target practice';
194+
cfg.task.instructions = 'do this';
195+
196+
cfg = createFilename(cfg);
197+
198+
extraInfo = struct('extraInfo', struct('nestedExtraInfo', 'something extra'));
199+
createJson(cfg, extraInfo);
117200

118-
%% add dummy functional data
119-
funcDir = fullfile(cfg.dir.output, 'source', 'sub-002', 'ses-003', 'func');
120-
boldFilename = ['sub-002_ses-003_task-rest', ...
201+
%% add dummy data
202+
subjectDir = fullfile(cfg.dir.output, 'source', 'sub-001', 'ses-001');
203+
funcDir = fullfile(subjectDir, 'func');
204+
205+
boldFilename = 'sub-001_ses-001_task-testtask_run-001_bold.nii.gz';
206+
207+
copyfile( ...
208+
fullfile('dummyData', 'dummyData.nii.gz'), ...
209+
fullfile(funcDir, boldFilename));
210+
211+
boldFilename = ['sub-001_ses-001_task-rest', ...
121212
'_acq-newTYpe_ce-test_dir-yPos_rec-fastRecon', ...
122-
'_run-004_echo-1_bold.nii.gz'];
213+
'_run-001_echo-1_bold.nii.gz'];
123214

124215
copyfile( ...
125216
fullfile('dummyData', 'dummyData.nii.gz'), ...
126217
fullfile(funcDir, boldFilename));
127218

219+
eegDir = fullfile(subjectDir, 'eeg');
220+
megDir = fullfile(subjectDir, 'meg');
221+
ieegDir = fullfile(subjectDir, 'ieeg');
222+
behDir = fullfile(subjectDir, 'beh');
223+
224+
eegFilename = 'sub-001_ses-001_task-targetPractice_run-001_eeg.edf';
225+
megFilename = 'sub-001_ses-001_task-magneticTargetPractice_run-001_meg.fif';
226+
ieegFilename = 'sub-001_ses-001_task-implantedTargetPractice_run-001_ieeg.edf';
227+
behFilename = 'sub-001_ses-001_task-easyTargetPractice_run-001_beh.tsv';
228+
229+
copyfile( ...
230+
fullfile('dummyData', 'dummyData.nii.gz'), ...
231+
fullfile(eegDir, eegFilename));
232+
233+
copyfile( ...
234+
fullfile('dummyData', 'dummyData.nii.gz'), ...
235+
fullfile(megDir, megFilename));
236+
237+
copyfile( ...
238+
fullfile('dummyData', 'dummyData.nii.gz'), ...
239+
fullfile(ieegDir, ieegFilename));
240+
241+
copyfile( ...
242+
fullfile('dummyData', 'dummyData.nii.gz'), ...
243+
fullfile(behDir, behFilename));
244+
128245
%% actually do the conversion of the source data thus created
129246
clear;
130247

miss_hit.cfg

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1+
# styly 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"
4-
exclude_dir: "lib"
5+
exclude_dir: "lib"
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 6

0 commit comments

Comments
 (0)