Skip to content

Commit

Permalink
Merge pull request #82 from Remi-Gau/remi-eeg_beh
Browse files Browse the repository at this point in the history
improve support for EEG, MEG, iEEG and eyetracking data
  • Loading branch information
Remi-Gau authored Aug 16, 2020
2 parents 97ff407 + af39717 commit f6e3b4c
Show file tree
Hide file tree
Showing 20 changed files with 856 additions and 266 deletions.
39 changes: 22 additions & 17 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,32 +1,39 @@
# Travis CI (https://travis-ci.org/)
# This will only work on your repo if you have an account on travis and you
# have set it up to run continuous integration on this this repo

# Linux distribution (bionic beaver)
dist: bionic

# Language and version
language: python
python:
- "3.6" # current default Python on Travis CI

language: c
dist: bionic # based on bionic beaver ubuntu distribution
cache:
apt: true # only works with Pro version

env:
global: # define environment variables for bash
global: # Define environment variables for bash
- OCTFLAGS="--no-gui --no-window-system --silent"

before_install:
- travis_retry sudo apt-get -y -qq update
# install octave
# Install octave
- travis_retry sudo apt-get -y install octave
- travis_retry sudo apt-get -y install liboctave-dev
# install javascript
# Install javascript node and node package manager necessary to run the validator
- travis_retry sudo apt-get -y install nodejs
- travis_retry sudo apt-get -y install npm
# install miss_hit linter
- cd .. && git clone https://github.com/florianschanda/miss_hit.git && export PATH=$PATH:`pwd`/miss_hit && cd CPP_BIDS

# Install BIDS-Validator
- sudo npm install -g bids-validator
# Install miss_hit linter
- pip3 install miss_hit

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

before_script:
# Add to src functions to path
Expand All @@ -36,11 +43,9 @@ before_script:

jobs:
include:
- stage: "BIDS validator"
name: "Create and check dataset"
- name: "BIDS validator: create and check dataset"
script: octave $OCTFLAGS --eval "test_makeRawDataset" && bids-validator `pwd`/output/rawdata/ --ignoreNiftiHeaders
- stage: "Linter"
name: "miss_hit"
script: cd .. && mh_style `pwd`


- name: "miss_hit: checking code quality"
script: mh_metric . --ci
- name: "miss_hit: checking code style"
script: mh_style .
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ Feel free to open issues to report a bug and ask for improvements.

Here are the naming templates used.

- Behavior

`sub-<label>[_ses-<label>]_task-<label>[_acq-<label>][_run-<index>]_events.tsv`
`sub-<label>[_ses-<label>]_task-<label>[_acq-<label>][_run-<index>]_events.json`
`sub-<label>[_ses-<label>]_task-<label>[_acq-<label>][_run-<index>]_beh.tsv`
`sub-<label>[_ses-<label>]_task-<label>[_acq-<label>][_run-<index>]_beh.json`

- BOLD

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

`sub-<label>[_ses-<label>]_task-<label>[_run-<index>]_eeg.<manufacturer_specific_extension>`
`sub-<label>[_ses-<label>]_task-<label>[_run-<index>]_eeg.json`

<!-- European data format (Each recording consisting of a .edf file)
BrainVision Core Data Format (Each recording consisting of a .vhdr, .vmrk, .eeg file triplet)
The format used by the MATLAB toolbox EEGLAB (Each recording consisting of a .set file with an optional .fdt file)
Biosemi data format (Each recording consisting of a .bdf file) -->



- MEG

???

- Eyetracker

current format
`<matches>_recording-eyetracking_physio.tsv.gz`

future BEP format in a dedicated eyetracker folder
`sub-<participant_label>[_ses-<label>][_acq-<label>]_task-<task_label>_eyetrack.<manufacturer_specific_extension>`

- Stim and physio

`<matches>[_recording-<label>]_physio.tsv.gz`
`<matches>[_recording-<label>]_physio.json`
`<matches>[_recording-<label>]_stim.tsv.gz`
`<matches>[_recording-<label>]_stim.json`

### 3.3. <a name='Contributors'></a>Contributors ✨

Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
Expand Down
11 changes: 11 additions & 0 deletions manualTests/dummyData/.bidsignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
*_beh.*
*_eeg.*
*_ieeg.*
*_meg.*

# *_beh.tsv
# *_eeg.edf
# *_ieeg.edf
# *_meg.fif

# *_bold.nii.gz
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
function test_suite = test_createBoldJson %#ok<*STOUT>
function test_suite = test_createJson %#ok<*STOUT>
try % assignment of 'localfunctions' is necessary in Matlab >= 2016
test_functions = localfunctions(); %#ok<*NASGU>
catch % no problem; early Matlab versions can use initTestSuite fine
end
initTestSuite;
end

function test_createBoldJsonExtra()
function test_createJsonExtra()

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

Expand All @@ -29,7 +29,7 @@ function test_createBoldJsonExtra()

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

createBoldJson(cfg, extraInfo);
createJson(cfg, extraInfo);

%% check content
fileName = strrep(cfg.fileName.events, '_events', '_bold');
Expand Down
161 changes: 139 additions & 22 deletions manualTests/test_makeRawDataset.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ function test_makeRawDataset()
cfg.subject.runNb = 1;

cfg.task.name = 'testtask';
cfg.task.instructions = 'do this';

logFile.extraColumns.Speed.length = 1;
logFile.extraColumns.LHL24.length = 3;
Expand All @@ -30,7 +31,7 @@ function test_makeRawDataset()
cfg = createFilename(cfg);

extraInfo = struct('extraInfo', struct('nestedExtraInfo', 'something extra'));
createBoldJson(cfg, extraInfo);
createJson(cfg, extraInfo);

createDatasetDescription(cfg);

Expand Down Expand Up @@ -82,49 +83,165 @@ function test_makeRawDataset()
saveEventsFile('save', cfg, stimLogFile);
saveEventsFile('close', cfg, stimLogFile);

% add dummy functional data
funcDir = fullfile(cfg.dir.output, 'source', 'sub-001', 'ses-001', 'func');
boldFilename = 'sub-001_ses-001_task-testtask_run-001_bold.nii.gz';

copyfile( ...
fullfile('dummyData', 'dummyData.nii.gz'), ...
fullfile(funcDir, boldFilename));

%% MRI bold rest data and fancy suffixes
clear cfg;

cfg.dir.output = outputDir;

cfg.testingDevice = 'mri';

cfg.subject.subjectNb = 2;
cfg.subject.sessionNb = 3;
cfg.subject.runNb = 4;
cfg.subject.subjectNb = 1;
cfg.subject.runNb = 1;

% deal with MRI suffixes
cfg.mri.reconstruction = 'fast recon';
cfg.mri.contrastEnhancement = 'test';
cfg.mri.phaseEncodingDirection = 'y pos';
cfg.mri.echo = '1';
cfg.mri.acquisition = ' new tYpe';
cfg.suffix.reconstruction = 'fast recon';
cfg.suffix.contrastEnhancement = 'test';
cfg.suffix.phaseEncodingDirection = 'y pos';
cfg.suffix.echo = '1';
cfg.suffix.acquisition = ' new tYpe';

cfg.mri.repetitionTime = 1.56;

cfg.task.name = 'rest';

cfg = createFilename(cfg);

createBoldJson(cfg);
extraInfo = struct('extraInfo', struct('nestedExtraInfo', 'something extra'));
createJson(cfg, extraInfo);

%% EEG data and fancy suffixes
clear cfg;

cfg.dir.output = outputDir;

cfg.testingDevice = 'eeg';

cfg.subject.subjectNb = 1;
cfg.subject.runNb = 1;

cfg.task.name = 'target practice';
cfg.task.instructions = 'do this';

cfg.bids.eeg.EEGReference = 'Cz';
cfg.bids.eeg.SamplingFrequency = 2400;
cfg.bids.eeg.PowerLineFrequency = 50;
cfg.bids.eeg.SoftwareFilters = 'n/a';

cfg = createFilename(cfg);

extraInfo = struct('extraInfo', struct('nestedExtraInfo', 'something extra'));
createJson(cfg, extraInfo);

%% iEEG data and fancy suffixes
clear cfg;

cfg.dir.output = outputDir;

cfg.testingDevice = 'ieeg';

cfg.subject.subjectNb = 1;
cfg.subject.runNb = 1;

cfg.task.name = 'implanted target practice';
cfg.task.instructions = 'do this';

cfg.bids.ieeg.iEEGReference = 'Cz';
cfg.bids.ieeg.SamplingFrequency = 2400;
cfg.bids.ieeg.PowerLineFrequency = 50;
cfg.bids.ieeg.SoftwareFilters = 'n/a';

cfg = createFilename(cfg);

extraInfo = struct('extraInfo', struct('nestedExtraInfo', 'something extra'));
createJson(cfg, extraInfo);

%% MEG data and fancy suffixes
clear cfg;

cfg.dir.output = outputDir;

cfg.testingDevice = 'meg';

cfg.subject.subjectNb = 1;
cfg.subject.runNb = 1;

cfg.task.name = 'magnetic target practice';
cfg.task.instructions = 'do this';

cfg.bids.meg.SamplingFrequency = 2400;
cfg.bids.meg.PowerLineFrequency = 60;
cfg.bids.meg.DewarPosition = 'upright';
cfg.bids.meg.SoftwareFilters = 'n/a';
cfg.bids.meg.DigitizedLandmarks = false;
cfg.bids.meg.DigitizedHeadPoints = false;

cfg = createFilename(cfg);

extraInfo = struct('extraInfo', struct('nestedExtraInfo', 'something extra'));
createJson(cfg, extraInfo);

%% beh data and fancy suffixes
clear cfg;

cfg.dir.output = outputDir;

cfg.testingDevice = 'pc';

cfg.subject.subjectNb = 1;
cfg.subject.runNb = 1;

cfg.task.name = 'easy target practice';
cfg.task.instructions = 'do this';

cfg = createFilename(cfg);

extraInfo = struct('extraInfo', struct('nestedExtraInfo', 'something extra'));
createJson(cfg, extraInfo);

%% add dummy functional data
funcDir = fullfile(cfg.dir.output, 'source', 'sub-002', 'ses-003', 'func');
boldFilename = ['sub-002_ses-003_task-rest', ...
%% add dummy data
subjectDir = fullfile(cfg.dir.output, 'source', 'sub-001', 'ses-001');
funcDir = fullfile(subjectDir, 'func');

boldFilename = 'sub-001_ses-001_task-testtask_run-001_bold.nii.gz';

copyfile( ...
fullfile('dummyData', 'dummyData.nii.gz'), ...
fullfile(funcDir, boldFilename));

boldFilename = ['sub-001_ses-001_task-rest', ...
'_acq-newTYpe_ce-test_dir-yPos_rec-fastRecon', ...
'_run-004_echo-1_bold.nii.gz'];
'_run-001_echo-1_bold.nii.gz'];

copyfile( ...
fullfile('dummyData', 'dummyData.nii.gz'), ...
fullfile(funcDir, boldFilename));

eegDir = fullfile(subjectDir, 'eeg');
megDir = fullfile(subjectDir, 'meg');
ieegDir = fullfile(subjectDir, 'ieeg');
behDir = fullfile(subjectDir, 'beh');

eegFilename = 'sub-001_ses-001_task-targetPractice_run-001_eeg.edf';
megFilename = 'sub-001_ses-001_task-magneticTargetPractice_run-001_meg.fif';
ieegFilename = 'sub-001_ses-001_task-implantedTargetPractice_run-001_ieeg.edf';
behFilename = 'sub-001_ses-001_task-easyTargetPractice_run-001_beh.tsv';

copyfile( ...
fullfile('dummyData', 'dummyData.nii.gz'), ...
fullfile(eegDir, eegFilename));

copyfile( ...
fullfile('dummyData', 'dummyData.nii.gz'), ...
fullfile(megDir, megFilename));

copyfile( ...
fullfile('dummyData', 'dummyData.nii.gz'), ...
fullfile(ieegDir, ieegFilename));

copyfile( ...
fullfile('dummyData', 'dummyData.nii.gz'), ...
fullfile(behDir, behFilename));

%% actually do the conversion of the source data thus created
clear;

Expand Down
9 changes: 8 additions & 1 deletion miss_hit.cfg
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
# styly guide (https://florianschanda.github.io/miss_hit/style_checker.html)
line_length: 100
regex_function_name: "[a-z]+(([A-Z]){1}[A-Za-z]+)*"
suppress_rule: "copyright_notice"
exclude_dir: "lib"
exclude_dir: "lib"

# metrics limit for the code quality (https://florianschanda.github.io/miss_hit/metrics.html)
metric "cnest": limit 4
metric "file_length": limit 500
metric "cyc": limit 15
metric "parameters": limit 6
Loading

0 comments on commit f6e3b4c

Please sign in to comment.