diff --git a/README.md b/README.md
index 44a5abf..4691521 100644
--- a/README.md
+++ b/README.md
@@ -5,33 +5,30 @@
[](https://travis-ci.com/cpp-lln-lab/localizer_visual_motion)
-
-
-- 1. [Requirements](#Requirements)
-- 2. [Installation](#Installation)
-- 3. [Structure and function details](#Structureandfunctiondetails)
- _ 3.1. [visualLocTranslational](#visualLocTranslational)
- _ 3.2. [setParameters](#setParameters)
- _ 3.3. [subfun/doDotMo](#subfundoDotMo)
- _ 3.3.1. [Input:](#Input:)
- _ 3.3.2. [Output:](#Output:)
- _ 3.4. [subfun/expDesign](#subfunexpDesign)
- _ 3.4.1. [EVENTS](#EVENTS)
- _ 3.4.2. [TARGETS:](#TARGETS:)
- _ 3.4.3. [Input:](#Input:-1)
- _ 3.4.4. [Output:](#Output:-1)
-
-
-
+
+- [fMRI localizers for visual motion](#fmri-localizers-for-visual-motion)
+ - [Translational Motion](#translational-motion)
+ - [Requirements](#requirements)
+ - [Installation](#installation)
+ - [Structure and function details](#structure-and-function-details)
+ - [visualLocTranslational](#visualloctranslational)
+ - [setParameters](#setparameters)
+ - [Let the scanner pace the experiment](#let-the-scanner-pace-the-experiment)
+ - [subfun/doDotMo](#subfundodotmo)
+ - [Input](#input)
+ - [Output](#output)
+ - [subfun/expDesign](#subfunexpdesign)
+ - [EVENTS](#events)
+ - [TARGETS](#targets)
+ - [Input](#input-1)
+ - [Output](#output-1)
+
# fMRI localizers for visual motion
-# Translational Motion
+## Translational Motion
-## 1. Requirements
+## Requirements
Make sure that the following toolboxes are installed and added to the matlab / octave path.
@@ -45,7 +42,7 @@ For instructions see the following links:
| [Matlab](https://www.mathworks.com/products/matlab.html) | >=2017 |
| or [octave](https://www.gnu.org/software/octave/) | >=4.? |
-## 2. Installation
+## Installation
The CPP_BIDS and CPP_PTB dependencies are already set up as submodule to this repository.
You can install it all with git by doing.
@@ -54,9 +51,9 @@ You can install it all with git by doing.
git clone --recurse-submodules https://github.com/cpp-lln-lab/localizer_visual_motion.git
```
-## 3. Structure and function details
+## Structure and function details
-### 3.1. visualLocTranslational
+### visualLocTranslational
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)
@@ -64,7 +61,7 @@ By default it is run in `Debug mode` meaning that it does not run care about sub
Any details of the experiment can be changed in `setParameters.m` (e.g., experiment mode, motion stimuli details, exp. design, etc.)
-### 3.2. setParameters
+### setParameters
`setParameters.m` is the core engine of the experiment. It contains the following tweakable sections:
@@ -105,40 +102,40 @@ if cfg.pacedByTriggers.do
end
```
-### 3.3. subfun/doDotMo
+### subfun/doDotMo
-#### 3.3.1. Input:
+#### Input
- `cfg`: PTB/machine configurations returned by `setParameters` and `initPTB`
- `expParameters`: parameters returned by `setParameters`
- `logFile`: structure that stores the experiment logfile to be saved
-#### 3.3.2. Output:
+#### Output
- Event `onset`
- Event `duration`
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.
-### 3.4. subfun/expDesign
+### subfun/expDesign
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.
-#### 3.4.1. EVENTS
+#### EVENTS
The `numEventsPerBlock` should be a multiple of the number of "base" listed in the `motionDirections` and `staticDirections` (4 at the moment).
-#### 3.4.2. TARGETS:
+#### TARGETS
- If there are 2 targets per block we make sure that they are at least 2 events apart.
- Targets cannot be on the first or last event of a block
-#### 3.4.3. Input:
+#### Input
- `expParameters`: parameters returned by `setParameters`
- `displayFigs`: a boolean to decide whether to show the basic design matrix of the design
-#### 3.4.4. Output:
+#### Output
- `expParameters.designBlockNames` is a cell array `(nr_blocks, 1)` with the name for each block
- `expParameters.designDirections` is an array `(nr_blocks, numEventsPerBlock)` with the direction to present in a given block
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..7d8b3af
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1 @@
+# Documentation
\ No newline at end of file
diff --git a/initEnv.m b/initEnv.m
index bf4ec9c..0420ce7 100644
--- a/initEnv.m
+++ b/initEnv.m
@@ -1,18 +1,19 @@
-%
-% 1 - Check if version requirements
-% are satisfied and the packages are
-% are installed/loaded:
-% Octave > 4
-% - image
-% - optim
-% - struct
-% - statistics
-%
-% MATLAB >= R2015b
-%
-% 2 - Add project to the O/M path
+% (C) Copyright 2020 Agah Karakuzu
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers
function initEnv
+ % 1 - Check if version requirements
+ % are satisfied and the packages are
+ % are installed/loaded:
+ % Octave > 4
+ % - image
+ % - optim
+ % - struct
+ % - statistics
+ %
+ % MATLAB >= R2015b
+ %
+ % 2 - Add project to the O/M path
octaveVersion = '4.0.3';
matlabVersion = '8.6.0';
@@ -56,8 +57,8 @@
if numel(dir(libDirectory)) <= 2 % Means that the external is empty
error(['Git submodules are not cloned!', ...
- 'Try this in your terminal:', ...
- ' git submodule update --recursive ']);
+ 'Try this in your terminal:', ...
+ ' git submodule update --recursive ']);
else
addDependencies();
end
diff --git a/lib/CPP_BIDS b/lib/CPP_BIDS
index 962c947..bfa76ac 160000
--- a/lib/CPP_BIDS
+++ b/lib/CPP_BIDS
@@ -1 +1 @@
-Subproject commit 962c947fe38094da9561eeba5daa44993505f2c0
+Subproject commit bfa76acd7ad6796dbe2d353a097c9c1db94cade9
diff --git a/lib/CPP_PTB b/lib/CPP_PTB
index e7be247..0334054 160000
--- a/lib/CPP_PTB
+++ b/lib/CPP_PTB
@@ -1 +1 @@
-Subproject commit e7be247a039cfe5ed95122d5045328f770023935
+Subproject commit 03340548820285460bde9e27396a2595bb2e54af
diff --git a/lib/README.md b/lib/README.md
new file mode 100644
index 0000000..ca16653
--- /dev/null
+++ b/lib/README.md
@@ -0,0 +1 @@
+# External libraries and dependencies
\ No newline at end of file
diff --git a/miss_hit.cfg b/miss_hit.cfg
index 5a5f311..df98a43 100644
--- a/miss_hit.cfg
+++ b/miss_hit.cfg
@@ -1,8 +1,10 @@
# style 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"
+copyright_entity: "Mohamed Rezk"
+copyright_entity: "Agah Karakuzu"
+copyright_entity: "CPP visual motion localizer developpers"
# metrics limit for the code quality (https://florianschanda.github.io/miss_hit/metrics.html)
metric "cnest": limit 4
diff --git a/setParameters.m b/setParameters.m
index 6179c47..7714aeb 100644
--- a/setParameters.m
+++ b/setParameters.m
@@ -1,3 +1,5 @@
+% (C) Copyright 2020 CPP visual motion localizer developpers
+
function [cfg] = setParameters()
% VISUAL LOCALIZER
@@ -9,21 +11,21 @@
% setParamters.m file is
% change that if you want the data to be saved somewhere else
cfg.dir.output = fullfile( ...
- fileparts(mfilename('fullpath')), '..', ...
- 'output');
+ fileparts(mfilename('fullpath')), '..', ...
+ 'output');
%% Debug mode settings
cfg.debug.do = false; % To test the script out of the scanner, skip PTB sync
cfg.debug.smallWin = false; % To test on a part of the screen, change to 1
- cfg.debug.transpWin = false; % To test with trasparent full size screen
+ cfg.debug.transpWin = true; % To test with trasparent full size screen
cfg.verbose = false;
%% Engine parameters
cfg.testingDevice = 'mri';
- cfg.eyeTracker.do = true;
+ cfg.eyeTracker.do = false;
cfg.audio.do = false;
cfg = setMonitor(cfg);
@@ -34,18 +36,24 @@
% MRI settings
cfg = setMRI(cfg);
- cfg.pacedByTriggers.do = true;
+ cfg.pacedByTriggers.do = false;
%% Experiment Design
- % cfg.design.motionType = 'translation';
+ % cfg.design.localizer = 'MT_MST';
+
% cfg.design.motionType = 'radial';
cfg.design.motionType = 'translation';
+
cfg.design.motionDirections = [0 0 180 180];
cfg.design.names = {'static'; 'motion'};
cfg.design.nbRepetitions = 8;
cfg.design.nbEventsPerBlock = 12; % DO NOT CHANGE
+ if isfield(cfg.design, 'localizer') && strcmpi(cfg.design.localizer, 'MT_MST')
+ cfg.design.names = {'fixation_right'; 'fixation_left'};
+ end
+
%% Timing
% FOR 7T: if you want to create localizers on the fly, the following must be
@@ -65,6 +73,10 @@
% Number of seconds after the end all the stimuli before ending the run
cfg.timing.endDelay = 3.6;
+ if isfield(cfg.design, 'localizer') && strcmpi(cfg.design.localizer, 'MT_MST')
+ cfg.timing.IBI = 3.6;
+ end
+
% reexpress those in terms of repetition time
if cfg.pacedByTriggers.do
@@ -81,6 +93,11 @@
cfg.timing.onsetDelay = 0;
% Number of seconds after the end all the stimuli before ending the run
cfg.timing.endDelay = 2;
+
+ if isfield(cfg.design, 'localizer') && strcmpi(cfg.design.localizer, 'MT_MST')
+ cfg.timing.IBI = 2;
+ end
+
end
%% Visual Stimulation
@@ -92,11 +109,11 @@
% Number of dots per visual angle square.
cfg.dot.density = 1;
% Dot life time in seconds
- cfg.dot.lifeTime = 10;
+ cfg.dot.lifeTime = .15;
% proportion of dots killed per frame
- cfg.dot.proportionKilledPerFrame = 0;
+ cfg.dot.proportionKilledPerFrame = 0.005;
% Dot Size (dot width) in visual angles.
- cfg.dot.size = .1;
+ cfg.dot.size = .2;
cfg.dot.color = cfg.color.white;
% Diameter/length of side of aperture in Visual angles
@@ -104,9 +121,18 @@
cfg.aperture.width = []; % if left empty it will take the screen height
cfg.aperture.xPos = 0;
+ if isfield(cfg.design, 'localizer') && strcmpi(cfg.design.localizer, 'MT_MST')
+ cfg.aperture.type = 'circle';
+ cfg.aperture.width = 7; % if left empty it will take the screen height
+ cfg.aperture.xPos = 7;
+ end
+
%% Task(s)
cfg.task.name = 'visual localizer';
+ if isfield(cfg.design, 'localizer') && strcmpi(cfg.design.localizer, 'MT_MST')
+ cfg.task.name = 'mt mst localizer';
+ end
% Instruction
cfg.task.instruction = '1-Detect the RED fixation cross\n \n\n';
@@ -115,7 +141,7 @@
cfg.fixation.type = 'cross';
cfg.fixation.colorTarget = cfg.color.red;
cfg.fixation.color = cfg.color.white;
- cfg.fixation.width = .5;
+ cfg.fixation.width = .25;
cfg.fixation.lineWidthPix = 3;
cfg.fixation.xDisplacement = 0;
cfg.fixation.yDisplacement = 0;
@@ -123,16 +149,24 @@
cfg.target.maxNbPerBlock = 1;
cfg.target.duration = 0.05; % In secs
- cfg.extraColumns = {'direction', 'speed', 'target', 'event', 'block', 'keyName'};
+ cfg.extraColumns = { ...
+ 'direction', ...
+ 'speed', ...
+ 'target', ...
+ 'event', ...
+ 'block', ...
+ 'keyName', ...
+ 'fixationPosition', ...
+ 'aperturePosition'};
end
function cfg = setKeyboards(cfg)
cfg.keyboard.escapeKey = 'ESCAPE';
cfg.keyboard.responseKey = { ...
- 'r', 'g', 'y', 'b', ...
- 'd', 'n', 'z', 'e', ...
- 't'}; % dnze rgyb
+ 'r', 'g', 'y', 'b', ...
+ 'd', 'n', 'z', 'e', ...
+ 't'};
cfg.keyboard.keyboard = [];
cfg.keyboard.responseBox = [];
@@ -145,7 +179,7 @@
function cfg = setMRI(cfg)
% letter sent by the trigger to sync stimulation and volume acquisition
cfg.mri.triggerKey = 't';
- cfg.mri.triggerNb = 5;
+ cfg.mri.triggerNb = 1;
cfg.mri.repetitionTime = 1.8;
diff --git a/subfun/assignConditions.m b/subfun/assignConditions.m
new file mode 100644
index 0000000..74ef6ae
--- /dev/null
+++ b/subfun/assignConditions.m
@@ -0,0 +1,20 @@
+% (C) Copyright 2020 CPP visual motion localizer developpers
+
+function [conditionNamesVector, CONDITON1_INDEX, CONDITON2_INDEX] = assignConditions(cfg)
+
+ [~, nbRepet] = getDesignInput(cfg);
+
+ conditionNamesVector = repmat(cfg.design.names, nbRepet, 1);
+
+ % Get the index of each condition
+ nameCondition1 = 'static';
+ nameCondition2 = 'motion';
+ if isfield(cfg.design, 'localizer') && strcmpi(cfg.design.localizer, 'MT_MST')
+ nameCondition1 = 'fixation_right';
+ nameCondition2 = 'fixation_left';
+ end
+
+ CONDITON1_INDEX = find(strcmp(conditionNamesVector, nameCondition1));
+ CONDITON2_INDEX = find(strcmp(conditionNamesVector, nameCondition2));
+
+end
diff --git a/subfun/diplayDesign.m b/subfun/diplayDesign.m
new file mode 100644
index 0000000..34929bb
--- /dev/null
+++ b/subfun/diplayDesign.m
@@ -0,0 +1,81 @@
+% (C) Copyright 2020 CPP visual motion localizer developpers
+
+function diplayDesign(cfg, displayFigs)
+
+ %% Visualize the design matrix
+ if displayFigs
+
+ close all;
+
+ figure(1);
+
+ % Shows blocks (static and motion) and events (motion direction) order
+ directions = cfg.design.directions;
+ directions(directions == -1) = -90;
+
+ subplot(3, 1, 1);
+ imagesc(directions);
+
+ labelAxesBlock();
+
+ caxis([-90 - 37, 270 + 37]);
+ myColorMap = lines(5);
+ colormap(myColorMap);
+
+ title('Block (static and motion) & Events (motion direction)');
+
+ % Shows the fixation targets design in each event (1 or 0)
+ fixationTargets = cfg.design.fixationTargets;
+
+ subplot(3, 1, 2);
+ imagesc(fixationTargets);
+ labelAxesBlock();
+ title('Fixation Targets design');
+ colormap(gray);
+
+ % Shows the fixation targets position distribution in the block across
+ % the experimet
+ [~, itargetPosition] = find(fixationTargets == 1);
+
+ subplot(3, 1, 3);
+ hist(itargetPosition);
+ labelAxesFreq();
+ title('Fixation Targets position distribution');
+
+ figure(2);
+
+ [motionDirections] = getDirectionBaseVectors(cfg);
+ motionDirections = unique(motionDirections);
+
+ for iMotion = 1:length(motionDirections)
+
+ [~, position] = find(directions == motionDirections(iMotion));
+
+ subplot(2, 2, iMotion);
+ hist(position);
+ scaleAxes();
+ labelAxesFreq();
+ title(num2str(motionDirections(iMotion)));
+
+ end
+
+ end
+
+end
+
+function labelAxesBlock()
+ % an old viking saying because they really cared about their axes
+ ylabel('Block seq.', 'Fontsize', 8);
+ xlabel('Events', 'Fontsize', 8);
+end
+
+function labelAxesFreq()
+ % an old viking saying because they really cared about their axes
+ ylabel('Number of targets', 'Fontsize', 8);
+ xlabel('Events', 'Fontsize', 8);
+end
+
+function scaleAxes()
+ xlim([1 12]);
+ ylim([0 5]);
+end
diff --git a/subfun/doDotMo.m b/subfun/doDotMo.m
index 5978421..4a4a312 100644
--- a/subfun/doDotMo.m
+++ b/subfun/doDotMo.m
@@ -1,4 +1,7 @@
-function [onset, duration] = doDotMo(cfg, thisEvent)
+% (C) Copyright 2018 Mohamed Rezk
+% (C) Copyright 2020 CPP visual motion localizer developpers
+
+function [onset, duration] = doDotMo(cfg, thisEvent, thisFixation)
% Draws the stimulation of static/moving in 4 directions dots or static
%
% DIRECTIONS
@@ -30,12 +33,14 @@
[dots] = updateDots(dots, cfg);
%% Center the dots
+
% We assumed that zero is at the top left, but we want it to be
% in the center, so shift the dots up and left, which just means
% adding half of the screen width in pixel to both the x and y direction.
thisEvent.dot.positions = (dots.positions - cfg.dot.matrixWidth / 2)';
%% make textures
+
dotTexture('make', cfg, thisEvent);
apertureTexture('make', cfg, thisEvent);
@@ -46,9 +51,6 @@
apertureTexture('draw', cfg, thisEvent);
- % If this frame shows a target we change the color of the cross
- thisFixation.fixation = cfg.fixation;
- thisFixation.screen = cfg.screen;
if thisEvent.target(1) && GetSecs < (onset + cfg.target.duration)
thisFixation.fixation.color = cfg.fixation.colorTarget;
end
@@ -67,7 +69,7 @@
%% Erase last dots
- drawFixation(cfg);
+ drawFixation(thisFixation);
Screen('DrawingFinished', cfg.screen.win);
diff --git a/subfun/expDesign.m b/subfun/expDesign.m
index 912915e..1678d83 100644
--- a/subfun/expDesign.m
+++ b/subfun/expDesign.m
@@ -1,3 +1,5 @@
+% (C) Copyright 2020 CPP visual motion localizer developpers
+
function [cfg] = expDesign(cfg, displayFigs)
% Creates the sequence of blocks and the events in them
%
@@ -62,8 +64,8 @@
fprintf('\n\nCreating design.\n\n');
- [NB_BLOCKS, NB_REPETITIONS, NB_EVENTS_PER_BLOCK, MAX_TARGET_PER_BLOCK] = getInput(cfg);
- [~, STATIC_INDEX, MOTION_INDEX] = assignConditions(cfg);
+ [NB_BLOCKS, NB_REPETITIONS, NB_EVENTS_PER_BLOCK, MAX_TARGET_PER_BLOCK] = getDesignInput(cfg);
+ [~, CONDITON1_INDEX, CONDITON2_INDEX] = assignConditions(cfg);
if mod(NB_REPETITIONS, MAX_TARGET_PER_BLOCK) ~= 0
error('number of repetitions must be a multiple of max number of targets');
@@ -73,8 +75,8 @@
targetPerCondition = repmat(RANGE_TARGETS, 1, NB_REPETITIONS / MAX_TARGET_PER_BLOCK);
numTargetsForEachBlock = zeros(1, NB_BLOCKS);
- numTargetsForEachBlock(STATIC_INDEX) = shuffle(targetPerCondition);
- numTargetsForEachBlock(MOTION_INDEX) = shuffle(targetPerCondition);
+ numTargetsForEachBlock(CONDITON1_INDEX) = shuffle(targetPerCondition);
+ numTargetsForEachBlock(CONDITON2_INDEX) = shuffle(targetPerCondition);
%% Give the blocks the names with condition and design the task in each event
while 1
@@ -92,9 +94,9 @@
nbTarget = numTargetsForEachBlock(iBlock);
chosenPosition = setTargetPositionInSequence( ...
- NB_EVENTS_PER_BLOCK, ...
- nbTarget, ...
- [1 NB_EVENTS_PER_BLOCK]);
+ NB_EVENTS_PER_BLOCK, ...
+ nbTarget, ...
+ [1 NB_EVENTS_PER_BLOCK]);
fixationTargets(iBlock, chosenPosition) = 1;
@@ -123,147 +125,3 @@
diplayDesign(cfg, displayFigs);
end
-
-function cfg = setDirections(cfg)
-
- [MOTION_DIRECTIONS, STATIC_DIRECTIONS] = getDirectionBaseVectors(cfg);
-
- [NB_BLOCKS, NB_REPETITIONS, NB_EVENTS_PER_BLOCK] = getInput(cfg);
-
- [~, STATIC_INDEX, MOTION_INDEX] = assignConditions(cfg);
-
- if mod(NB_EVENTS_PER_BLOCK, length(MOTION_DIRECTIONS)) ~= 0
- error('Number of events/block not a multiple of number of motion/static direction');
- end
-
- % initialize
- directions = zeros(NB_BLOCKS, NB_EVENTS_PER_BLOCK);
-
- % Create a vector for the static condition
- NB_REPEATS_BASE_VECTOR = NB_EVENTS_PER_BLOCK / length(STATIC_DIRECTIONS);
-
- static_directions = repmat( ...
- STATIC_DIRECTIONS, ...
- 1, NB_REPEATS_BASE_VECTOR);
-
- for iMotionBlock = 1:NB_REPETITIONS
-
- % Set motion direction and static order
- directions(MOTION_INDEX(iMotionBlock), :) = ...
- repeatShuffleConditions(MOTION_DIRECTIONS, NB_REPEATS_BASE_VECTOR);
- directions(STATIC_INDEX(iMotionBlock), :) = static_directions;
-
- end
-
- cfg.design.directions = directions;
-
-end
-
-function [MOTION_DIRECTIONS, STATIC_DIRECTIONS] = getDirectionBaseVectors(cfg)
-
- % CONSTANTS
- % Set directions for static and motion condition
-
- MOTION_DIRECTIONS = cfg.design.motionDirections;
- STATIC_DIRECTIONS = repmat(-1, size(MOTION_DIRECTIONS));
-
-end
-
-function [nbBlocks, nbRepet, nbEventsBlock, maxTargBlock] = getInput(cfg)
- nbRepet = cfg.design.nbRepetitions;
- nbEventsBlock = cfg.design.nbEventsPerBlock;
- maxTargBlock = cfg.target.maxNbPerBlock;
- nbBlocks = length(cfg.design.names) * nbRepet;
-end
-
-function [conditionNamesVector, STATIC_INDEX, MOTION_INDEX] = assignConditions(cfg)
-
- [~, nbRepet] = getInput(cfg);
-
- conditionNamesVector = repmat(cfg.design.names, nbRepet, 1);
-
- % Get the index of each condition
- STATIC_INDEX = find(strcmp(conditionNamesVector, 'static'));
- MOTION_INDEX = find(strcmp(conditionNamesVector, 'motion'));
-
-end
-
-function diplayDesign(cfg, displayFigs)
-
- %% Visualize the design matrix
- if displayFigs
-
- close all;
-
- figure(1);
-
- % Shows blocks (static and motion) and events (motion direction) order
- directions = cfg.design.directions;
- directions(directions == -1) = -90;
-
- subplot(3, 1, 1);
- imagesc(directions);
-
- labelAxesBlock();
-
- caxis([-90 - 37, 270 + 37]);
- myColorMap = lines(5);
- colormap(myColorMap);
-
- title('Block (static and motion) & Events (motion direction)');
-
- % Shows the fixation targets design in each event (1 or 0)
- fixationTargets = cfg.design.fixationTargets;
-
- subplot(3, 1, 2);
- imagesc(fixationTargets);
- labelAxesBlock();
- title('Fixation Targets design');
- colormap(gray);
-
- % Shows the fixation targets position distribution in the block across
- % the experimet
- [~, itargetPosition] = find(fixationTargets == 1);
-
- subplot(3, 1, 3);
- hist(itargetPosition);
- labelAxesFreq();
- title('Fixation Targets position distribution');
-
- figure(2);
-
- [motionDirections] = getDirectionBaseVectors(cfg);
- motionDirections = unique(motionDirections);
-
- for iMotion = 1:length(motionDirections)
-
- [~, position] = find(directions == motionDirections(iMotion));
-
- subplot(2, 2, iMotion);
- hist(position);
- scaleAxes();
- labelAxesFreq();
- title(num2str(motionDirections(iMotion)));
-
- end
-
- end
-
-end
-
-function labelAxesBlock()
- % an old viking saying because they really cared about their axes
- ylabel('Block seq.', 'Fontsize', 8);
- xlabel('Events', 'Fontsize', 8);
-end
-
-function labelAxesFreq()
- % an old viking saying because they really cared about their axes
- ylabel('Number of targets', 'Fontsize', 8);
- xlabel('Events', 'Fontsize', 8);
-end
-
-function scaleAxes()
- xlim([1 12]);
- ylim([0 5]);
-end
diff --git a/subfun/getDesignInput.m b/subfun/getDesignInput.m
new file mode 100644
index 0000000..7e177bc
--- /dev/null
+++ b/subfun/getDesignInput.m
@@ -0,0 +1,8 @@
+% (C) Copyright 2020 CPP visual motion localizer developpers
+
+function [nbBlocks, nbRepet, nbEventsBlock, maxTargBlock] = getDesignInput(cfg)
+ nbRepet = cfg.design.nbRepetitions;
+ nbEventsBlock = cfg.design.nbEventsPerBlock;
+ maxTargBlock = cfg.target.maxNbPerBlock;
+ nbBlocks = length(cfg.design.names) * nbRepet;
+end
diff --git a/subfun/getDirectionBaseVectors.m b/subfun/getDirectionBaseVectors.m
new file mode 100644
index 0000000..b63d0ea
--- /dev/null
+++ b/subfun/getDirectionBaseVectors.m
@@ -0,0 +1,17 @@
+% (C) Copyright 2020 CPP visual motion localizer developpers
+
+function [CONDITION1_DIRECTIONS, CONDITION2_DIRECTIONS] = getDirectionBaseVectors(cfg)
+
+ % CONSTANTS
+
+ % Set directions for static and motion condition
+ CONDITION1_DIRECTIONS = cfg.design.motionDirections;
+ CONDITION2_DIRECTIONS = repmat(-1, size(CONDITION1_DIRECTIONS)); % static
+
+ % for for the MT / MST localizer
+ if isfield(cfg.design, 'localizer') && strcmpi(cfg.design.localizer, 'MT_MST')
+ CONDITION1_DIRECTIONS = cfg.design.motionDirections;
+ CONDITION2_DIRECTIONS = cfg.design.motionDirections;
+ end
+
+end
diff --git a/subfun/postInitializationSetup.m b/subfun/postInitializationSetup.m
index 1bbaa4e..fdafac3 100644
--- a/subfun/postInitializationSetup.m
+++ b/subfun/postInitializationSetup.m
@@ -1,3 +1,5 @@
+% (C) Copyright 2020 CPP visual motion localizer developpers
+
function varargout = postInitializationSetup(varargin)
% varargout = postInitializatinSetup(varargin)
@@ -20,8 +22,8 @@
% dots are displayed on a square with a length in visual angle equal to the
% field of view
cfg.dot.number = round(cfg.dot.density * ...
- (cfg.dot.matrixWidth / cfg.screen.ppd)^2);
+ (cfg.dot.matrixWidth / cfg.screen.ppd)^2);
- varargout = cfg;
+ varargout = {cfg};
end
diff --git a/subfun/preSaveSetup.m b/subfun/preSaveSetup.m
new file mode 100644
index 0000000..d13c65f
--- /dev/null
+++ b/subfun/preSaveSetup.m
@@ -0,0 +1,33 @@
+% (C) Copyright 2020 CPP visual motion localizer developpers
+
+function varargout = preSaveSetup(varargin)
+ % varargout = postInitializatinSetup(varargin)
+
+ % generic function to prepare structures before saving
+
+ [thisEvent, thisFixation, iBlock, iEvent, duration, onset, cfg, logFile] = ...
+ deal(varargin{:});
+
+ thisEvent.event = iEvent;
+ thisEvent.block = iBlock;
+ thisEvent.keyName = 'n/a';
+ thisEvent.duration = duration;
+ thisEvent.onset = onset - cfg.experimentStart;
+ thisEvent.fixationPosition = thisFixation.fixation.xDisplacement;
+ thisEvent.aperturePosition = cfg.aperture.xPos * sign(cfg.aperture.xPosPix);
+
+ % % this value should be in degrees / second in the log file
+ % % highlights that the way speed is passed around could be
+ % % simplified.
+ % %
+ % thisEvent.speed
+ % %
+
+ % Save the events txt logfile
+ % we save event by event so we clear this variable every loop
+ thisEvent.fileID = logFile.fileID;
+ thisEvent.extraColumns = logFile.extraColumns;
+
+ varargout = {thisEvent};
+
+end
diff --git a/subfun/preTrialSetup.m b/subfun/preTrialSetup.m
new file mode 100644
index 0000000..2c8a49a
--- /dev/null
+++ b/subfun/preTrialSetup.m
@@ -0,0 +1,37 @@
+% (C) Copyright 2020 CPP visual motion localizer developpers
+
+function varargout = preTrialSetup(varargin)
+ % varargout = postInitializatinSetup(varargin)
+
+ % generic function to prepare some structure before each trial
+
+ [cfg, iBlock, iEvent] = deal(varargin{:});
+
+ % set direction, speed of that event and if it is a target
+ thisEvent.trial_type = cfg.design.blockNames{iBlock};
+ thisEvent.direction = cfg.design.directions(iBlock, iEvent);
+ thisEvent.speed = cfg.design.speeds(iBlock, iEvent);
+ thisEvent.target = cfg.design.fixationTargets(iBlock, iEvent);
+
+ % If this frame shows a target we change the color of the cross
+ thisFixation.fixation = cfg.fixation;
+ thisFixation.screen = cfg.screen;
+
+ switch thisEvent.trial_type
+ case 'fixation_right'
+ cfg.aperture.xPosPix = -abs(cfg.aperture.xPosPix);
+
+ thisFixation.fixation.xDisplacement = cfg.aperture.xPos;
+ thisFixation = initFixation(thisFixation);
+
+ case 'fixation_left'
+ cfg.aperture.xPosPix = +abs(cfg.aperture.xPosPix);
+
+ thisFixation.fixation.xDisplacement = -cfg.aperture.xPos;
+ thisFixation = initFixation(thisFixation);
+
+ end
+
+ varargout = {thisEvent, thisFixation, cfg};
+
+end
diff --git a/subfun/saveResponsesAndTriggers.m b/subfun/saveResponsesAndTriggers.m
index 61f8e5a..bca56bd 100644
--- a/subfun/saveResponsesAndTriggers.m
+++ b/subfun/saveResponsesAndTriggers.m
@@ -1,3 +1,5 @@
+% (C) Copyright 2020 CPP visual motion localizer developpers
+
function saveResponsesAndTriggers(responseEvents, cfg, logFile, triggerString)
if isfield(responseEvents(1), 'onset') && ~isempty(responseEvents(1).onset)
diff --git a/subfun/setDirections.m b/subfun/setDirections.m
new file mode 100644
index 0000000..c6c4a04
--- /dev/null
+++ b/subfun/setDirections.m
@@ -0,0 +1,52 @@
+% (C) Copyright 2020 CPP visual motion localizer developpers
+
+function cfg = setDirections(cfg)
+
+ [CONDITION1_DIRECTIONS, CONDITION2_DIRECTIONS] = getDirectionBaseVectors(cfg);
+
+ [NB_BLOCKS, NB_REPETITIONS, NB_EVENTS_PER_BLOCK] = getDesignInput(cfg);
+
+ [~, CONDITON1_INDEX, CONDITON2_INDEX] = assignConditions(cfg);
+
+ if mod(NB_EVENTS_PER_BLOCK, length(CONDITION1_DIRECTIONS)) ~= 0
+ error('Number of events/block not a multiple of number of motion/static direction');
+ end
+
+ % initialize
+ directions = zeros(NB_BLOCKS, NB_EVENTS_PER_BLOCK);
+
+ % Create a vector for the static condition
+ NB_REPEATS_BASE_VECTOR = NB_EVENTS_PER_BLOCK / length(CONDITION2_DIRECTIONS);
+
+ static_directions = repmat( ...
+ CONDITION2_DIRECTIONS, ...
+ 1, NB_REPEATS_BASE_VECTOR);
+
+ for iMotionBlock = 1:NB_REPETITIONS
+
+ if isfield(cfg.design, 'localizer') && strcmpi(cfg.design.localizer, 'MT_MST')
+
+ % Set motion direction for MT/MST localizer
+
+ directions(CONDITON1_INDEX(iMotionBlock), :) = ...
+ repeatShuffleConditions(CONDITION1_DIRECTIONS, NB_REPEATS_BASE_VECTOR);
+
+ directions(CONDITON2_INDEX(iMotionBlock), :) = ...
+ repeatShuffleConditions(CONDITION1_DIRECTIONS, NB_REPEATS_BASE_VECTOR);
+
+ else
+
+ % Set motion direction and static order
+
+ directions(CONDITON2_INDEX(iMotionBlock), :) = ...
+ repeatShuffleConditions(CONDITION1_DIRECTIONS, NB_REPEATS_BASE_VECTOR);
+
+ directions(CONDITON1_INDEX(iMotionBlock), :) = static_directions;
+
+ end
+
+ end
+
+ cfg.design.directions = directions;
+
+end
diff --git a/tests/README.md b/tests/README.md
new file mode 100644
index 0000000..da470b5
--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,5 @@
+# Unit tests folder
+
+To be run using mox unit.
+
+Code coverage can be estimated with Mocov.
\ No newline at end of file
diff --git a/tests/test_expDesign.m b/tests/test_expDesign.m
index a224c55..5ef89da 100644
--- a/tests/test_expDesign.m
+++ b/tests/test_expDesign.m
@@ -28,8 +28,9 @@ function test_exDesignBasic()
% make sure that we have the right number of blocks of the right length
assertTrue(all(size(cfg.design.directions) == [ ...
- cfg.design.nbRepetitions * numel(cfg.design.names), ...
- cfg.design.nbEventsPerBlock]));
+ cfg.design.nbRepetitions * ...
+ numel(cfg.design.names), ...
+ cfg.design.nbEventsPerBlock]));
% check that we do not have more than the required number of targets per
% block
@@ -62,8 +63,9 @@ function test_exDesignBasicOtherSetUp()
% make sure that we have the right number of blocks of the right length
assertTrue(all(size(cfg.design.directions) == [ ...
- cfg.design.nbRepetitions * numel(cfg.design.names), ...
- cfg.design.nbEventsPerBlock]));
+ cfg.design.nbRepetitions * ...
+ numel(cfg.design.names), ...
+ cfg.design.nbEventsPerBlock]));
% check that we do not have more than the required number of targets per
% block
diff --git a/visualLocTranslational.m b/visualLocTranslational.m
index 0951228..4e40173 100644
--- a/visualLocTranslational.m
+++ b/visualLocTranslational.m
@@ -1,17 +1,14 @@
-%% Visual hMT localizer using translational motion in four directions
-% (up- down- left and right-ward)
+% (C) Copyright 2018 Mohamed Rezk
+% (C) Copyright 2020 CPP visual motion localizer developpers
-% by Mohamed Rezk 2018
-% adapted by MarcoB and RemiG 2020
-
-%%
+%% Visual motion localizer
getOnlyPress = 1;
more off;
% Clear all the previous stuff
-% clc; clear;
+clc;
if ~ismac
close all;
clear Screen;
@@ -37,7 +34,11 @@
[el] = eyeTracker('Calibration', cfg);
+ % if isfield(cfg.design, 'localizer') && strcmpi(cfg.design.localizer, 'MT_MST')
+ % [cfg] = expDesignMtMst(cfg);
+ % else
[cfg] = expDesign(cfg);
+ % end
% Prepare for the output logfiles with all
logFile.extraColumns = cfg.extraColumns;
@@ -79,50 +80,33 @@
% Check for experiment abortion from operator
checkAbort(cfg, cfg.keyboard.keyboard);
- % set direction, speed of that event and if it is a target
- thisEvent.trial_type = cfg.design.blockNames{iBlock};
- thisEvent.direction = cfg.design.directions(iBlock, iEvent);
- thisEvent.speed = cfg.design.speeds(iBlock, iEvent);
- thisEvent.target = cfg.design.fixationTargets(iBlock, iEvent);
+ [thisEvent, thisFixation, cfg] = preTrialSetup(cfg, iBlock, iEvent);
% we wait for a trigger every 2 events
if cfg.pacedByTriggers.do && mod(iEvent, 2) == 1
waitForTrigger( ...
- cfg, ...
- cfg.keyboard.responseBox, ...
- cfg.pacedByTriggers.quietMode, ...
- cfg.pacedByTriggers.nbTriggers);
+ cfg, ...
+ cfg.keyboard.responseBox, ...
+ cfg.pacedByTriggers.quietMode, ...
+ cfg.pacedByTriggers.nbTriggers);
end
% play the dots and collect onset and duraton of the event
- [onset, duration] = doDotMo(cfg, thisEvent);
-
- thisEvent.event = iEvent;
- thisEvent.block = iBlock;
- thisEvent.keyName = 'n/a';
- thisEvent.duration = duration;
- thisEvent.onset = onset - cfg.experimentStart;
-
- % % this value should be in degrees / second in the log file
- % % highlights that the way speed is passed around could be
- % % simplified.
- % %
- % thisEvent.speed
- % %
-
- % Save the events txt logfile
- % we save event by event so we clear this variable every loop
- thisEvent.fileID = logFile.fileID;
- thisEvent.extraColumns = logFile.extraColumns;
-
+ [onset, duration] = doDotMo(cfg, thisEvent, thisFixation);
+
+ thisEvent = preSaveSetup( ...
+ thisEvent, ...
+ thisFixation, ...
+ iBlock, iEvent, ...
+ duration, onset, ...
+ cfg, ...
+ logFile);
saveEventsFile('save', cfg, thisEvent);
- clear thisEvent;
-
% collect the responses and appends to the event structure for
% saving in the tsv file
responseEvents = getResponse('check', cfg.keyboard.responseBox, cfg, ...
- getOnlyPress);
+ getOnlyPress);
triggerString = ['trigger_' cfg.design.blockNames{iBlock}];
saveResponsesAndTriggers(responseEvents, cfg, logFile, triggerString);
@@ -133,11 +117,22 @@
eyeTracker('StopRecordings', cfg);
+ % "prepare" cross for the baseline block
+ % if MT / MST this allows us to set the cross at the position of the next block
+ if iBlock < cfg.design.nbBlocks
+ nextBlock = iBlock + 1;
+ else
+ nextBlock = cfg.design.nbBlocks;
+ end
+ [~, thisFixation] = preTrialSetup(cfg, nextBlock, 1);
+ drawFixation(thisFixation);
+ Screen('Flip', cfg.screen.win);
+
waitFor(cfg, cfg.timing.IBI);
% trigger monitoring
triggerEvents = getResponse('check', cfg.keyboard.responseBox, cfg, ...
- getOnlyPress);
+ getOnlyPress);
triggerString = 'trigger_baseline';
saveResponsesAndTriggers(triggerEvents, cfg, logFile, triggerString);