From 2ad744e6fa448b63cf4e38ec7ee4729d6b61538a Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sun, 2 Aug 2020 21:39:26 +0200 Subject: [PATCH 1/5] fix issue with static dots --- lib/CPP_BIDS | 2 +- lib/CPP_PTB | 2 +- setParameters.m | 11 ++++------- subfun/doDotMo.m | 2 +- subfun/expDesign.m | 4 ++-- 5 files changed, 9 insertions(+), 12 deletions(-) diff --git a/lib/CPP_BIDS b/lib/CPP_BIDS index 7d7c052..a17e12a 160000 --- a/lib/CPP_BIDS +++ b/lib/CPP_BIDS @@ -1 +1 @@ -Subproject commit 7d7c052f22d0288d4dd469ae97a81988fba390d3 +Subproject commit a17e12a53c0063e36fb017b7a545d605e2c2ef8e diff --git a/lib/CPP_PTB b/lib/CPP_PTB index 46f990f..0f11cb8 160000 --- a/lib/CPP_PTB +++ b/lib/CPP_PTB @@ -1 +1 @@ -Subproject commit 46f990f18fe320984f71ae6ec1b02c12f604ad40 +Subproject commit 0f11cb8b197176d4a6ce3360e7f734b9e431b79a diff --git a/setParameters.m b/setParameters.m index 93aa334..629c41d 100644 --- a/setParameters.m +++ b/setParameters.m @@ -50,19 +50,16 @@ cfg.numEventsPerBlock = 12; cfg.eventDuration = 1; % second - % speed in visual angles / second + % Speed in visual angles / second cfg.dot.speed = 15; % Coherence Level (0-1) cfg.dot.coherence = 1; - % nb dots per visual angle square. - cfg.dot.density = .25; - + % Number of dots per visual angle square. + cfg.dot.density = .05; % Dot life time in seconds cfg.dot.lifeTime = 10; - % proportion of dots killed per frame - cfg.dot.proportionKilledPerFrame = .05; - + cfg.dot.proportionKilledPerFrame = 0; % Dot Size (dot width) in visual angles. cfg.dot.size = 1; cfg.dot.color = cfg.color.white; diff --git a/subfun/doDotMo.m b/subfun/doDotMo.m index 1ba4ce3..487741f 100644 --- a/subfun/doDotMo.m +++ b/subfun/doDotMo.m @@ -16,7 +16,7 @@ %% Get parameters - dots = initializeDots(cfg, thisEvent); + dots = initDots(cfg, thisEvent); % Set for how many frames this event will last framesLeft = floor(cfg.eventDuration / cfg.screen.ifi); diff --git a/subfun/expDesign.m b/subfun/expDesign.m index 2d77451..50626d1 100644 --- a/subfun/expDesign.m +++ b/subfun/expDesign.m @@ -38,8 +38,8 @@ % Set directions for static and motion condition motionDirections = [0 90 180 270]; - staticDirections = [0 90 180 270]; - % staticDirections = [-1 -1 -1 -1]; +% staticDirections = [0 90 180 270]; + staticDirections = [-1 -1 -1 -1]; %% Check inputs From 1142def534bbb005241cce4173c96b64ad555bfc Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sun, 2 Aug 2020 21:50:46 +0200 Subject: [PATCH 2/5] get expDesign from Marco's Branch --- subfun/expDesign.m | 319 ++++++++++++++++++++++++++++++++------------- 1 file changed, 227 insertions(+), 92 deletions(-) diff --git a/subfun/expDesign.m b/subfun/expDesign.m index 50626d1..1d303b0 100644 --- a/subfun/expDesign.m +++ b/subfun/expDesign.m @@ -1,20 +1,24 @@ function [cfg] = expDesign(cfg, displayFigs) % 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. + % The conditions are consecutive static and motion blocks (Gives better results than randomised). % % EVENTS % The numEventsPerBlock should be a multiple of the number of "base" % listed in the motionDirections and staticDirections (4 at the moment). + % Pseudorandomization rules: + % (1) Directions are all present in random orders in `numEventsPerBlock/nDirections` + % consecutive chunks. This evenly distribute the directions across the + % block. + % (2) No same consecutive direction (TO IMPLEMENT) % - % - % 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 + % TARGETS + % Pseudorandomization rules: + % (1) If there are 2 targets per block we make sure that they are at least 2 + % events apart. + % (2) Targets cannot be on the first or last event of a block. + % (3) Targets can not be present more than 2 times in the same event + % position across blocks. % % Input: % - ExpParameters: parameters returned by SetParameters @@ -30,7 +34,7 @@ % - 0 90 180 270 indicate the angle % - -1 indicates static % - % - ExpParameters.designSpeeds = array (nr_blocks, numEventsPerBlock) * speedEvent + % - ExpParameters.designSpeeds = array (nr_blocks, numEventsPerBlock) * speedEvent; % % - ExpParameters.designFixationTargets = array (nr_blocks, numEventsPerBlock) % showing for each event if it should be accompanied by a target @@ -38,11 +42,11 @@ % Set directions for static and motion condition motionDirections = [0 90 180 270]; -% staticDirections = [0 90 180 270]; - staticDirections = [-1 -1 -1 -1]; - + staticDirections = [-1 -1 -1 -1]; + + %% Check inputs - + % Set variables here for a dummy test of this function if nargin < 1 || isempty(cfg) cfg.names = {'static', 'motion'}; @@ -67,101 +71,232 @@ if mod(numEventsPerBlock, length(motionDirections)) ~= 0 warning('Number of events/block not a multiple of number of motion/static direction'); end - + + %% Adapt some variables according to input - - % Set directions for static and motion condition - motionDirections = repmat(motionDirections, 1, numEventsPerBlock / length(motionDirections)); - staticDirections = repmat(staticDirections, 1, numEventsPerBlock / length(staticDirections)); - + % Assign the conditions condition = repmat(names, 1, numRepetitions); nrBlocks = length(condition); + + % Assigne design parameters to be exported + cfg.designBlockNames = cell(nrBlocks, 1); + cfg.designDirections = zeros(nrBlocks, numEventsPerBlock); + cfg.designSpeeds = ones(nrBlocks, numEventsPerBlock) * speedEvent; + cfg.designFixationTargets = zeros(nrBlocks, numEventsPerBlock); + + % Create a vector for the static condition + staticDirections = repmat(staticDirections, 1, numEventsPerBlock/length(staticDirections)); + % Get the index of each condition - staticIndex = find(strcmp(condition, 'static')); - motionIndex = find(strcmp(condition, 'motion')); - + staticIndex = find( strcmp(condition, 'static') ); + motionIndex = find( strcmp(condition, 'motion') ); + + for iMotionBlock = 1:numRepetitions + + % Shuffle and set motion direction order + cfg.designDirections(motionIndex(iMotionBlock),:) = ... + [ Shuffle(motionDirections), Shuffle(motionDirections), Shuffle(motionDirections)]; + + % Set static condition + cfg.designDirections(staticIndex(iMotionBlock),:) = staticDirections; + + end + % Assign the targets for each condition rangeTargets = [1 maxNumFixTargPerBlock]; + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + % % % IT COULD BE A PROBLEM IF WE SET THE N OF TARGETS RANDOMLY (TOO CHOOSE + % % % RANDOMLY B/W 1 AND 2 FOR N TIMES) BECAUSE AT THE END EACH PARTICIPANT + % % % HAS A DIFFERENET NUMBER OF TARKETS TO GET, LMK + % Get random number of targets for one condition targetPerCondition = randi(rangeTargets, 1, numRepetitions); % Assign the number of targets for each condition after shuffling numTargets = zeros(1, nrBlocks); numTargets(staticIndex) = Shuffle(targetPerCondition); numTargets(motionIndex) = Shuffle(targetPerCondition); - - %% Give the blocks the names with condition - - cfg.design.blockNames = cell(nrBlocks, 1); - cfg.design.directions = zeros(nrBlocks, numEventsPerBlock); - cfg.design.speeds = ones(nrBlocks, numEventsPerBlock) * dotsSpeed; - cfg.design.fixationTargets = zeros(nrBlocks, numEventsPerBlock); - - for iMotionBlock = 1:numRepetitions - - cfg.design.directions(motionIndex(iMotionBlock), :) = Shuffle(motionDirections); - cfg.design.directions(staticIndex(iMotionBlock), :) = Shuffle(staticDirections); - - end - - for iBlock = 1:nrBlocks - - % Set block name - switch condition{iBlock} - case 'static' - thisBlockName = {'static'}; - case 'motion' - thisBlockName = {'motion'}; + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + %% Give the blocks the names with condition and design the task in each event + + while 1 + + for iBlock = 1:nrBlocks + + + % Set block name + switch condition{iBlock} + case 'static' + thisBlockName = {'static'}; + case 'motion' + thisBlockName = {'motion'}; + end + + cfg.designBlockNames(iBlock) = thisBlockName; + + % Set target + % - 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 + % - no more than 2 target in the same event order + + chosenTarget = []; + + tmpTarget = numTargets(iBlock); + + switch tmpTarget + + case 1 + + chosenTarget = randsample(2:numEventsPerBlock-1, tmpTarget, false); + + case 2 + + targetDifference = 0; + + + while targetDifference <= 2 + chosenTarget = randsample(2:numEventsPerBlock-1, tmpTarget, false); + targetDifference = (max(chosenTarget) - min(chosenTarget)); + end + + end + + cfg.designFixationTargets(iBlock, chosenTarget) = 1; + end - cfg.design.blockNames(iBlock) = thisBlockName; - - % set target - % 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 - - chosenTarget = []; - - tmpTarget = numTargets(iBlock); - - switch tmpTarget - - case 1 - - chosenTarget = randsample(2:numEventsPerBlock - 1, tmpTarget, false); - - case 2 - - targetDifference = 0; - - while targetDifference <= 2 - chosenTarget = randsample(2:numEventsPerBlock - 1, tmpTarget, false); - targetDifference = (max(chosenTarget) - min(chosenTarget)); - end - + + % Check rule 3 + if max(sum(cfg.designFixationTargets)) < 3 + break + else + cfg.designBlockNames = cell(nrBlocks, 1); + cfg.designFixationTargets = zeros(nrBlocks, numEventsPerBlock); end - - cfg.design.fixationTargets(iBlock, chosenTarget) = 1; - + end - + + + designSpeeds = cfg.designSpeeds'; + designDirections = cfg.designDirections'; + designFixationTargets = cfg.designFixationTargets'; + + cfg.trialList = [designDirections(:) designSpeeds(:) designFixationTargets(:)]; + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %% Visualize the design matrix if displayFigs - - uniqueNames = unique(cfg.design.blockNames) ; - - Ind = zeros(length(cfg.design.blockNames), length(uniqueNames)) ; - - for i = 1:length(uniqueNames) - CondInd(:, i) = find( ... - strcmp(cfg.design.blockNames, uniqueNames{i})) ; %#ok<*AGROW> - Ind(CondInd(:, i), i) = 1 ; + + figure(1); + + + % Shows blocks (static and motion) and events (motion direction) order + subplot(3,3,1) + + designDirection = cfg.designDirections; + designDirection(designDirection==-1) = -90; + + imagesc(designDirection) + + labelAxesBlock() + + caxis([-90-37, 270+37]) + myColorMap = lines(5); + colormap(myColorMap); + + title('Block (static and motion) & Events (motion direction)') + + + % Shows the direction position distribution in the motion blocks + % across the experiment + subplot(3,3,2) + + leftPosition = []; + for i=1:nrBlocks + leftPosition = [ leftPosition find(cfg.designDirections(i,:)==0) ]; end - - imagesc(Ind); - - set(gca, ... - 'XTick', 1:length(uniqueNames), ... - 'XTickLabel', uniqueNames); - + hist(leftPosition) + scaleAxes() + labelAxesFreq() + title('0') + + subplot(3,3,3) + + rightPosition = []; + for i=1:nrBlocks + rightPosition = [ rightPosition find(cfg.designDirections(i,:)==90) ]; + end + hist(rightPosition) + scaleAxes() + labelAxesFreq() + title('90') + + subplot(3,3,5) + + upPosition = []; + for i=1:nrBlocks + upPosition = [ upPosition find(cfg.designDirections(i,:)==180) ]; + end + hist(upPosition) + scaleAxes() + labelAxesFreq() + title('180') + + subplot(3,3,6) + + downPosition = []; + for i=1:nrBlocks + downPosition = [ downPosition find(cfg.designDirections(i,:)==270) ]; + end + hist(downPosition) + scaleAxes() + labelAxesFreq() + title('270') + + + % Shows the fixation targets design in each event (1 or 0) + subplot(3,3,7) + + imagesc(cfg.designFixationTargets) + labelAxesBlock() + title('Fixation Targets design') + + + % Shows the fixation targets position distribution in the block across + % the experimet + subplot(3,3,8) + + itargetPosition = []; + for i=1:nrBlocks + itargetPosition = [ itargetPosition find(cfg.designFixationTargets(i,:)==1) ]; + end + hist(itargetPosition) + labelAxesFreq() + title('Fixation Targets position distribution') + 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('freq.', 'Fontsize', 8); + xlabel('Events', 'Fontsize', 8); +end + +function scaleAxes() + xlim([1 12]) + ylim([0 5]) +end \ No newline at end of file From 53ccc48ca72edcc7912c337e4d74b7e44f2b5664 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Mon, 3 Aug 2020 00:32:48 +0200 Subject: [PATCH 3/5] refactor expDesign --- subfun/expDesign.m | 392 ++++++++++++++++++++++----------------------- 1 file changed, 192 insertions(+), 200 deletions(-) diff --git a/subfun/expDesign.m b/subfun/expDesign.m index 1d303b0..4da0590 100644 --- a/subfun/expDesign.m +++ b/subfun/expDesign.m @@ -4,282 +4,274 @@ % The conditions are consecutive static and motion blocks (Gives better results than randomised). % % EVENTS - % The numEventsPerBlock should be a multiple of the number of "base" - % listed in the motionDirections and staticDirections (4 at the moment). - % Pseudorandomization rules: - % (1) Directions are all present in random orders in `numEventsPerBlock/nDirections` - % consecutive chunks. This evenly distribute the directions across the - % block. - % (2) No same consecutive direction (TO IMPLEMENT) + % The numEventsPerBlock should be a multiple of the number of "base" + % listed in the motionDirections and staticDirections (4 at the moment). + % + % Pseudorandomization rules: + % (1) Directions are all present in random orders in `numEventsPerBlock/nDirections` + % consecutive chunks. This evenly distribute the directions across the + % block. + % (2) No same consecutive direction + % % % TARGETS - % Pseudorandomization rules: - % (1) If there are 2 targets per block we make sure that they are at least 2 - % events apart. - % (2) Targets cannot be on the first or last event of a block. - % (3) Targets can not be present more than 2 times in the same event - % position across blocks. + % + % Pseudorandomization rules: + % (1) If there are 2 targets per block we make sure that they are at least 2 + % events apart. + % (2) Targets cannot be on the first or last event of a block. + % (3) Targets can not be present more than 2 times in the same event + % position across blocks. % % Input: - % - ExpParameters: parameters returned by SetParameters - % - displayFigs: a boolean to decide whether to show the basic design - % matrix of the design + % - cfg: parameters returned by setParameters + % - displayFigs: a boolean to decide whether to show the basic design + % matrix of the design % % Output: - % - ExpParameters.designBlockNames = cell array (nr_blocks, 1) with the - % name for each block + % - ExpParameters.designBlockNames = cell array (nr_blocks, 1) with the + % name for each block % - % - ExpParameters.designDirections = array (nr_blocks, numEventsPerBlock) - % with the direction to present in a given block - % - 0 90 180 270 indicate the angle - % - -1 indicates static + % - cfg.designDirections = array (nr_blocks, numEventsPerBlock) + % with the direction to present in a given block + % - 0 90 180 270 indicate the angle + % - -1 indicates static % - % - ExpParameters.designSpeeds = array (nr_blocks, numEventsPerBlock) * speedEvent; + % - cfg.designSpeeds = array (nr_blocks, numEventsPerBlock) * speedEvent; % - % - ExpParameters.designFixationTargets = array (nr_blocks, numEventsPerBlock) - % showing for each event if it should be accompanied by a target + % - cfg.designFixationTargets = array (nr_blocks, numEventsPerBlock) + % showing for each event if it should be accompanied by a target % - - % Set directions for static and motion condition - motionDirections = [0 90 180 270]; - staticDirections = [-1 -1 -1 -1]; %% Check inputs - % Set variables here for a dummy test of this function - if nargin < 1 || isempty(cfg) - cfg.names = {'static', 'motion'}; - cfg.numRepetitions = 4; - cfg.dot.speed = 4; - cfg.numEventsPerBlock = 12; - cfg.target.maxNbPerBlock = 2; - end - % Set to 1 for a visualtion of the trials design order - if nargin < 2 || isempty(displayFigs) + if nargin < 2 || isempty(displayFigs) displayFigs = 0; end - - % Get the parameters - names = cfg.names; - numRepetitions = cfg.numRepetitions; - dotsSpeed = cfg.dot.speedPixPerFrame; - numEventsPerBlock = cfg.numEventsPerBlock; - maxNumFixTargPerBlock = cfg.target.maxNbPerBlock; - - if mod(numEventsPerBlock, length(motionDirections)) ~= 0 - warning('Number of events/block not a multiple of number of motion/static direction'); - end - - %% Adapt some variables according to input - - % Assign the conditions - condition = repmat(names, 1, numRepetitions); - nrBlocks = length(condition); - - % Assigne design parameters to be exported - cfg.designBlockNames = cell(nrBlocks, 1); - cfg.designDirections = zeros(nrBlocks, numEventsPerBlock); - cfg.designSpeeds = ones(nrBlocks, numEventsPerBlock) * speedEvent; - cfg.designFixationTargets = zeros(nrBlocks, numEventsPerBlock); - - % Create a vector for the static condition - staticDirections = repmat(staticDirections, 1, numEventsPerBlock/length(staticDirections)); - - % Get the index of each condition - staticIndex = find( strcmp(condition, 'static') ); - motionIndex = find( strcmp(condition, 'motion') ); - - for iMotionBlock = 1:numRepetitions - - % Shuffle and set motion direction order - cfg.designDirections(motionIndex(iMotionBlock),:) = ... - [ Shuffle(motionDirections), Shuffle(motionDirections), Shuffle(motionDirections)]; - - % Set static condition - cfg.designDirections(staticIndex(iMotionBlock),:) = staticDirections; - + % Set variables here for a dummy test of this function + if nargin < 1 || isempty(cfg) + cfg.design.names = {'static'; 'motion'}; + cfg.design.nbRepetitions = 4; + cfg.design.nbEventsPerBlock = 12; + cfg.dot.speedPixPerFrame = 4; + cfg.target.maxNbPerBlock = 2; + displayFigs = 1; end - % Assign the targets for each condition - rangeTargets = [1 maxNumFixTargPerBlock]; - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + [NB_BLOCKS, NB_REPETITIONS, NB_EVENTS_PER_BLOCK, MAX_TARGET_PER_BLOCK] = getInput(cfg); + [~, staticIndex, motionIndex] = assignConditions(cfg); - % % % IT COULD BE A PROBLEM IF WE SET THE N OF TARGETS RANDOMLY (TOO CHOOSE - % % % RANDOMLY B/W 1 AND 2 FOR N TIMES) BECAUSE AT THE END EACH PARTICIPANT - % % % HAS A DIFFERENET NUMBER OF TARKETS TO GET, LMK + RANGE_TARGETS = [1 MAX_TARGET_PER_BLOCK]; + targetPerCondition = repmat(RANGE_TARGETS, 1, NB_REPETITIONS/2); - % Get random number of targets for one condition - targetPerCondition = randi(rangeTargets, 1, numRepetitions); - % Assign the number of targets for each condition after shuffling - numTargets = zeros(1, nrBlocks); - numTargets(staticIndex) = Shuffle(targetPerCondition); - numTargets(motionIndex) = Shuffle(targetPerCondition); - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + numTargetsForEachBlock = zeros(1, NB_BLOCKS); + numTargetsForEachBlock(staticIndex) = shuffle(targetPerCondition); + numTargetsForEachBlock(motionIndex) = shuffle(targetPerCondition); %% Give the blocks the names with condition and design the task in each event - while 1 - for iBlock = 1:nrBlocks - - - % Set block name - switch condition{iBlock} - case 'static' - thisBlockName = {'static'}; - case 'motion' - thisBlockName = {'motion'}; - end - - cfg.designBlockNames(iBlock) = thisBlockName; + fixationTargets = zeros(NB_BLOCKS, NB_EVENTS_PER_BLOCK); + + for iBlock = 1:NB_BLOCKS % Set target - % - 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 - % - no more than 2 target in the same event order + % - 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 + % - no more than 2 target in the same event order chosenTarget = []; - tmpTarget = numTargets(iBlock); + tmpTarget = numTargetsForEachBlock(iBlock); switch tmpTarget case 1 - chosenTarget = randsample(2:numEventsPerBlock-1, tmpTarget, false); + chosenTarget = randsample(2:NB_EVENTS_PER_BLOCK - 1, tmpTarget, false); case 2 targetDifference = 0; - - while targetDifference <= 2 - chosenTarget = randsample(2:numEventsPerBlock-1, tmpTarget, false); - targetDifference = (max(chosenTarget) - min(chosenTarget)); + while any(targetDifference <= 2) + chosenTarget = randsample(2:NB_EVENTS_PER_BLOCK - 1, tmpTarget, false); + targetDifference = diff(chosenTarget); end end - cfg.designFixationTargets(iBlock, chosenTarget) = 1; + fixationTargets(iBlock, chosenTarget) = 1; end % Check rule 3 - if max(sum(cfg.designFixationTargets)) < 3 + if max(sum(fixationTargets)) < 3 break - else - cfg.designBlockNames = cell(nrBlocks, 1); - cfg.designFixationTargets = zeros(nrBlocks, numEventsPerBlock); end end + %% Now we do the easy stuff + cfg.design.blockNames = assignConditions(cfg); + + cfg = setDirections(cfg); + directions = cfg.design.directions; + directions = directions'; + + speeds = ones(NB_BLOCKS, NB_EVENTS_PER_BLOCK) * cfg.dot.speedPixPerFrame; + cfg.design.speeds = speeds; + speeds = speeds'; + + cfg.design.fixationTargets = fixationTargets; + fixationTargets = fixationTargets'; + + cfg.design.trialList = [directions(:) speeds(:) fixationTargets(:)]; + + %% Plot + diplayDesign(cfg, displayFigs) + +end + +function cfg = setDirections(cfg) + + % CONSTANTS + % Set directions for static and motion condition + MOTION_DIRECTIONS = [0 90 180 270]; + STATIC_DIRECTIONS = [-1 -1 -1 -1]; + + [NB_BLOCKS, NB_REPETITIONS, NB_EVENTS_PER_BLOCK] = getInput(cfg); + + [~, staticIndex, motionIndex] = 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 + static_directions = repmat( ... + STATIC_DIRECTIONS, ... + 1, NB_EVENTS_PER_BLOCK / length(STATIC_DIRECTIONS)); + + for iMotionBlock = 1:NB_REPETITIONS + + % Check that we never have twice the same direction + while 1 + tmp = [ ... + shuffle(MOTION_DIRECTIONS), ... + shuffle(MOTION_DIRECTIONS), ... + shuffle(MOTION_DIRECTIONS)]; + + if ~any(diff(tmp,[],2)==0) + break + end + end + + % Set motion direction and static order + directions(motionIndex(iMotionBlock), :) = tmp; + directions(staticIndex(iMotionBlock), :) = static_directions; + + end + + cfg.design.directions = directions; - designSpeeds = cfg.designSpeeds'; - designDirections = cfg.designDirections'; - designFixationTargets = cfg.designFixationTargets'; +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 [condition, staticIndex, motionIndex] = assignConditions(cfg) + + [~, nbRepet] = getInput(cfg); - cfg.trialList = [designDirections(:) designSpeeds(:) designFixationTargets(:)]; + condition = repmat(cfg.design.names, nbRepet, 1); + % Get the index of each condition + staticIndex = find(strcmp(condition, 'static')); + motionIndex = find(strcmp(condition, 'motion')); - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +end + +function shuffled = shuffle(unshuffled) + % in case PTB is not in the path + try + shuffled = Shuffle(unshuffled); + catch + shuffled = unshuffled(randperm(length(unshuffled))); + end +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 - subplot(3,3,1) - - designDirection = cfg.designDirections; - designDirection(designDirection==-1) = -90; + directions = cfg.design.directions; + directions(directions == -1) = -90; - imagesc(designDirection) + subplot(3, 1, 1); + imagesc(directions); - labelAxesBlock() + labelAxesBlock(); - caxis([-90-37, 270+37]) + caxis([-90 - 37, 270 + 37]); myColorMap = lines(5); colormap(myColorMap); - title('Block (static and motion) & Events (motion direction)') - - - % Shows the direction position distribution in the motion blocks - % across the experiment - subplot(3,3,2) - - leftPosition = []; - for i=1:nrBlocks - leftPosition = [ leftPosition find(cfg.designDirections(i,:)==0) ]; - end - hist(leftPosition) - scaleAxes() - labelAxesFreq() - title('0') - - subplot(3,3,3) - - rightPosition = []; - for i=1:nrBlocks - rightPosition = [ rightPosition find(cfg.designDirections(i,:)==90) ]; - end - hist(rightPosition) - scaleAxes() - labelAxesFreq() - title('90') - - subplot(3,3,5) - - upPosition = []; - for i=1:nrBlocks - upPosition = [ upPosition find(cfg.designDirections(i,:)==180) ]; - end - hist(upPosition) - scaleAxes() - labelAxesFreq() - title('180') + title('Block (static and motion) & Events (motion direction)'); - subplot(3,3,6) + % Shows the fixation targets design in each event (1 or 0) + fixationTargets = cfg.design.fixationTargets; - downPosition = []; - for i=1:nrBlocks - downPosition = [ downPosition find(cfg.designDirections(i,:)==270) ]; - end - hist(downPosition) - scaleAxes() - labelAxesFreq() - title('270') + 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); - % Shows the fixation targets design in each event (1 or 0) - subplot(3,3,7) + subplot(3, 1, 3); + hist(itargetPosition); + labelAxesFreq(); + title('Fixation Targets position distribution'); - imagesc(cfg.designFixationTargets) - labelAxesBlock() - title('Fixation Targets design') + figure(2); - % Shows the fixation targets position distribution in the block across - % the experimet - subplot(3,3,8) + MOTION_DIRECTIONS = [0 90 180 270]; - itargetPosition = []; - for i=1:nrBlocks - itargetPosition = [ itargetPosition find(cfg.designFixationTargets(i,:)==1) ]; + for iMotion = 1:length(MOTION_DIRECTIONS) + + [~, position] = find(directions == MOTION_DIRECTIONS(iMotion)); + + subplot(2, 2, iMotion); + hist(position); + scaleAxes(); + labelAxesFreq(); + title(num2str(MOTION_DIRECTIONS(iMotion))); + end - hist(itargetPosition) - labelAxesFreq() - title('Fixation Targets position distribution') - + + end end @@ -292,11 +284,11 @@ function labelAxesBlock() function labelAxesFreq() % an old viking saying because they really cared about their axes - ylabel('freq.', 'Fontsize', 8); + ylabel('Number of targets', 'Fontsize', 8); xlabel('Events', 'Fontsize', 8); end function scaleAxes() - xlim([1 12]) - ylim([0 5]) -end \ No newline at end of file + xlim([1 12]); + ylim([0 5]); +end From bc14611801c18c1f573e88a46eeeb1dcb4db3151 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Mon, 3 Aug 2020 01:07:21 +0200 Subject: [PATCH 4/5] update submodule --- lib/CPP_PTB | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CPP_PTB b/lib/CPP_PTB index 0f11cb8..a8bc6b5 160000 --- a/lib/CPP_PTB +++ b/lib/CPP_PTB @@ -1 +1 @@ -Subproject commit 0f11cb8b197176d4a6ce3360e7f734b9e431b79a +Subproject commit a8bc6b5a1457ad78b496fa89037c07f0e548e0c8 From 1d889f510b28b2e6c79f70697ad78d5e9d6ba4ed Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Mon, 3 Aug 2020 01:07:43 +0200 Subject: [PATCH 5/5] finish refactoring design --- setParameters.m | 22 +++++++++++++--------- subfun/expDesign.m | 9 +++------ visualLocTanslational.m | 8 +++----- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/setParameters.m b/setParameters.m index 629c41d..b4b031d 100644 --- a/setParameters.m +++ b/setParameters.m @@ -11,6 +11,7 @@ 'output'); %% Debug mode settings + cfg.debug.do = true; % 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 = true; % To test with trasparent full size screen @@ -32,10 +33,14 @@ cfg = setMRI(cfg); %% Experiment Design - cfg.names = {'static', 'motion'}; - cfg.possibleDirections = [-1 1]; % 1 motion , -1 static - cfg.numBlocks = size(cfg.possibleDirections, 2); - cfg.numRepetitions = 1; % AT THE MOMENT IT IS NOT SET IN THE MAIN SCRIPT + + cfg.design.names = {'static'; 'motion'}; + cfg.design.nbRepetitions = 4; + cfg.design.nbEventsPerBlock = 12; % DO NOT CHANGE + + %% Timing + + % Time between blocs in secs cfg.IBI = .5; % 8; % Time between events in secs cfg.ISI = 0.5; @@ -44,18 +49,16 @@ % Number of seconds after the end all the stimuli before ending the run cfg.endDelay = .1; - %% Visual Stimulation - - % Number of events per block (should not be changed) - cfg.numEventsPerBlock = 12; cfg.eventDuration = 1; % second + + %% Visual Stimulation % Speed in visual angles / second cfg.dot.speed = 15; % Coherence Level (0-1) cfg.dot.coherence = 1; % Number of dots per visual angle square. - cfg.dot.density = .05; + cfg.dot.density = .1; % Dot life time in seconds cfg.dot.lifeTime = 10; % proportion of dots killed per frame @@ -89,6 +92,7 @@ cfg.target.duration = 0.05; % In secs cfg.extraColumns = {'direction', 'speed', 'target', 'event', 'block'}; + end function cfg = setKeyboards(cfg) diff --git a/subfun/expDesign.m b/subfun/expDesign.m index 4da0590..fec3ffd 100644 --- a/subfun/expDesign.m +++ b/subfun/expDesign.m @@ -119,18 +119,15 @@ %% Now we do the easy stuff cfg.design.blockNames = assignConditions(cfg); + cfg.design.nbBlocks = NB_BLOCKS; + cfg = setDirections(cfg); - directions = cfg.design.directions; - directions = directions'; speeds = ones(NB_BLOCKS, NB_EVENTS_PER_BLOCK) * cfg.dot.speedPixPerFrame; cfg.design.speeds = speeds; - speeds = speeds'; cfg.design.fixationTargets = fixationTargets; - fixationTargets = fixationTargets'; - - cfg.design.trialList = [directions(:) speeds(:) fixationTargets(:)]; + %% Plot diplayDesign(cfg, displayFigs) diff --git a/visualLocTanslational.m b/visualLocTanslational.m index e14fd4c..0c17f6a 100644 --- a/visualLocTanslational.m +++ b/visualLocTanslational.m @@ -48,9 +48,7 @@ [el] = eyeTracker('Calibration', cfg); - % % % REFACTOR THIS FUNCTION [cfg] = expDesign(cfg); - % % % % Prepare for the output logfiles with all logFile.extraColumns = cfg.extraColumns; @@ -78,20 +76,20 @@ %% For Each Block - for iBlock = 1:cfg.numBlocks + for iBlock = 1:cfg.design.nbBlocks fprintf('\n - Running Block %.0f \n', iBlock); eyeTracker('StartRecording', cfg); % For each event in the block - for iEvent = 1:cfg.numEventsPerBlock + for iEvent = 1:cfg.design.nbEventsPerBlock % 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 = 'dummy'; + 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);