From 1e1b6be761929eb36a1535cf847811b25bc4c445 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sun, 2 Aug 2020 11:42:06 +0200 Subject: [PATCH 01/12] update draw fixation functions and defaults --- drawFixation.m | 13 +++++ drawFixationCross.m | 11 ----- initPTB.m | 116 ++++++++++++++++++++++++++------------------ setDefaultsPTB.m | 10 +++- 4 files changed, 91 insertions(+), 59 deletions(-) create mode 100644 drawFixation.m delete mode 100644 drawFixationCross.m diff --git a/drawFixation.m b/drawFixation.m new file mode 100644 index 0000000..0e436a6 --- /dev/null +++ b/drawFixation.m @@ -0,0 +1,13 @@ +function drawFixation(cfg) + % Define the parameters of the fixation cross in `cfg` and `expParameters` + + if strcmp(cfg.fixation.type, 'cross') + Screen('DrawLines', ... + cfg.screen.win, ... + cfg.fixation.allCoords, ... + cfg.fixation.lineWidthPix, ... + cfg.fixation.color, ... + [cfg.screen.center(1) cfg.screen.center(2)], 1); + end + +end diff --git a/drawFixationCross.m b/drawFixationCross.m deleted file mode 100644 index 5ce4a2f..0000000 --- a/drawFixationCross.m +++ /dev/null @@ -1,11 +0,0 @@ -function drawFixationCross(cfg, color) - % Define the parameters of the fixation cross in `cfg` and `expParameters` - - Screen('DrawLines', ... - cfg.screen.win, ... - cfg.allCoords, ... - cfg.fixation.lineWidthPix, ... - color, ... - [cfg.screen.center(1) cfg.screen.center(2)], 1); - -end diff --git a/initPTB.m b/initPTB.m index 0a15c59..de24de4 100644 --- a/initPTB.m +++ b/initPTB.m @@ -19,59 +19,62 @@ % % % - + checkPtbVersion(); - + pth = fileparts(mfilename('fullpath')); addpath(fullfile(pth, 'subfun')); - + % For octave: to avoid displaying messenging one screen at a time more off; - + % check for OpenGL compatibility, abort otherwise: AssertOpenGL; - + cfg = setDefaultsPTB(cfg); - + initKeyboard; initDebug(cfg); - + % Mouse HideCursor; - + %% Audio cfg = initAudio(cfg); - + %% Visual - + % Get the screen numbers and draw to the external screen if avaliable cfg.screen.idx = max(Screen('Screens')); - + cfg = openWindow(cfg); - + % window size info [cfg.screen.winWidth, cfg.screen.winHeight] = WindowSize(cfg.screen.win); - + % Get the Center of the Screen cfg.screen.center = [cfg.screen.winRect(3), cfg.screen.winRect(4)] / 2; - + % Computes the number of pixels per degree given the distance to screen and % monitor width % This assumes that the window fills the whole screen cfg.screen.FOV = computeFOV(cfg); cfg.screen.ppd = cfg.screen.winRect(3) / cfg.screen.FOV; - + + % Initialize visual parmaters for fixation cross or dot + cfg = initFixation(cfg); + %% Select specific text font, style and size initText(cfg); - + %% Timing % Query frame duration cfg.screen.ifi = Screen('GetFlipInterval', cfg.screen.win); cfg.screen.monitorRefresh = 1 / cfg.screen.ifi; - + % Set priority for script execution to realtime priority: Priority(MaxPriority(cfg.screen.win)); - + %% Warm up some functions % Do dummy calls to GetSecs, WaitSecs, KbCheck to make sure % they are loaded and ready when we need them - without delays @@ -79,95 +82,95 @@ KbCheck; WaitSecs(0.1); GetSecs; - + end function initDebug(cfg) - + % init PTB with different options in concordance to the debug Parameters Screen('Preference', 'SkipSyncTests', 0); if cfg.debug.do - + Screen('Preference', 'SkipSyncTests', 2); Screen('Preference', 'Verbosity', 0); Screen('Preference', 'SuppressAllWarnings', 1); - + fprintf('\n\n\n\n'); fprintf('########################################\n'); fprintf('## DEBUG MODE. TIMING WILL BE OFF. ##\n'); fprintf('########################################'); fprintf('\n\n\n\n'); - + testKeyboards(cfg); - + end - + if cfg.debug.transpWin PsychDebugWindowConfiguration; end - + end function initKeyboard - + % Make sure keyboard mapping is the same on all supported operating systems % Apple MacOS/X, MS-Windows and GNU/Linux: KbName('UnifyKeyNames'); - + % Don't echo keypresses to Matlab window ListenChar(-1); - + end function cfg = initAudio(cfg) - + if cfg.audio.do - + InitializePsychSound(1); - + cfg.audio.devIdx = []; cfg.audio.playbackMode = 1; - + if isfield(cfg.audio, 'useDevice') - + % get audio device list audioDev = PsychPortAudio('GetDevices'); - + % find output device to use idx = find( ... audioDev.NrInputChannels == cfg.audio.inputChannels && ... audioDev.NrOutputChannels == cfg.audio.channels && ... ~cellfun(@isempty, regexp({audioDev.HostAudioAPIName}, cfg.audio.deviceName))); - + % save device ID cfg.audio.devIdx = audioDev(idx).DeviceIndex; - + % get device's sampling rate cfg.audio.fs = audioDev(idx).DefaultSampleRate; - + end - + cfg.audio.pahandle = PsychPortAudio('Open', ... cfg.audio.devIdx, ... cfg.audio.playbackMode, ... cfg.audio.requestedLatency, ... cfg.audio.fs, ... cfg.audio.channels); - + % set initial PTB volume for safety (participants can adjust this manually % at the begining of the experiment) PsychPortAudio('Volume', cfg.audio.pahandle, cfg.audio.initVolume); - + cfg.audio.pushSize = cfg.audio.fs * 0.010; % ! push N ms only - + cfg.audio.requestOffsetTime = 1; % offset 1 sec cfg.audio.reqsSampleOffset = cfg.audio.requestOffsetTime * cfg.audio.fs; - + end end function cfg = openWindow(cfg) - + if cfg.debug.smallWin [cfg.screen.win, cfg.screen.winRect] = ... Screen('OpenWindow', cfg.screen.idx, cfg.color.background, ... @@ -176,17 +179,36 @@ function initDebug(cfg) [cfg.screen.win, cfg.screen.winRect] = ... Screen('OpenWindow', cfg.screen.idx, cfg.color.background); end - + % Enable alpha-blending, set it to a blend equation useable for linear % superposition with alpha-weighted source. Screen('BlendFunction', cfg.screen.win, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + +end +function cfg = initFixation(cfg) + + if strcmp(cfg.fixation.type, 'cross') + + % Convert some values from degrees to pixels + cfg.fixation = degToPix('width', cfg.fixation, cfg); + + % Prepare fixation cross + cfg.fixation.xCoords = [-cfg.fixation.widthPix cfg.fixation.widthPix 0 0] + ... + cfg.fixation.xDisplacement; + cfg.fixation.yCoords = [0 0 -cfg.fixation.widthPix cfg.fixation.widthPix] + ... + cfg.fixation.yDisplacement; + cfg.fixation.allCoords = [cfg.fixation.xCoords; cfg.fixation.yCoords]; + + end + + end function initText(cfg) - + Screen('TextFont', cfg.screen.win, cfg.text.font); Screen('TextSize', cfg.screen.win, cfg.text.size); Screen('TextStyle', cfg.screen.win, cfg.text.style); - + end diff --git a/setDefaultsPTB.m b/setDefaultsPTB.m index abddbd5..0804442 100644 --- a/setDefaultsPTB.m +++ b/setDefaultsPTB.m @@ -29,7 +29,15 @@ fieldsToSet.screen.monitorWidth = 42; fieldsToSet.screen.monitorDistance = 134; - + + % fixation cross or dot + fieldsToSet.fixation.type = 'cross'; + fieldsToSet.fixation.xDisplacement = 0; + fieldsToSet.fixation.yDisplacement = 0; + fieldsToSet.fixation.color = [255 255 255]; + fieldsToSet.fixation.width = 1; + fieldsToSet.fixation.lineWidthPix = 5; + if isfield(cfg, 'audio') && cfg.audio.do fieldsToSet.audio.fs = 44800; From 99d0977f852ed7f4c0389e2f0c48ab1636a5d124 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sun, 2 Aug 2020 11:42:24 +0200 Subject: [PATCH 02/12] add aperture texture --- apertureTexture.m | 44 ++++++++++++++++++++++++++++++++++++++++++++ setDefaultsPTB.m | 3 +++ 2 files changed, 47 insertions(+) create mode 100644 apertureTexture.m diff --git a/apertureTexture.m b/apertureTexture.m new file mode 100644 index 0000000..5bd6d7b --- /dev/null +++ b/apertureTexture.m @@ -0,0 +1,44 @@ + +function cfg = apertureTexture(action, cfg, thisEvent) + + matrixSize = 400; + + switch action + + case 'init' + + cfg.aperture.texture = Screen('MakeTexture', cfg.screen.win, ... + cfg.color.background(1) * ones(cfg.screen.winRect([4 3]))); + + case 'make' + + switch cfg.aperture.type + + case 'none' + + Screen('Fillrect', cfg.aperture.texture, [0 0 0 0]); + + case 'circle' + + Screen('FillOval', cfg.aperture.texture, [0 0 0 0], ... + CenterRectOnPoint([0 0 repmat(matrixSize, 1, 2)], ... + cfg.screen.winRect(3) / 2, cfg.screen.winRect(4) / 2)); + + end + + case 'draw' + + Screen('DrawTexture', cfg.screen.win, cfg.aperture.texture); + + % Screen('DrawTexture', cfg.screen.win, apertureTexture, ... + % cfg.screen.winRect, cfg.screen.winRect, current.apertureAngle - 90); + + + end + + + + + + +end \ No newline at end of file diff --git a/setDefaultsPTB.m b/setDefaultsPTB.m index 0804442..b3b623c 100644 --- a/setDefaultsPTB.m +++ b/setDefaultsPTB.m @@ -38,6 +38,9 @@ fieldsToSet.fixation.width = 1; fieldsToSet.fixation.lineWidthPix = 5; + % define visual apperture field + fieldsToSet.aperture.type = 'none'; + if isfield(cfg, 'audio') && cfg.audio.do fieldsToSet.audio.fs = 44800; From 751dc6717a1d902ef9cb41d680d9c63e0a6c7599 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sun, 2 Aug 2020 11:42:39 +0200 Subject: [PATCH 03/12] add dot texture function --- dotTexture.m | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 dotTexture.m diff --git a/dotTexture.m b/dotTexture.m new file mode 100644 index 0000000..d6a57be --- /dev/null +++ b/dotTexture.m @@ -0,0 +1,26 @@ +function cfg = dotTexture(action, cfg, thisEvent) + + switch action + + case 'init' + cfg.dot.texture = Screen('MakeTexture', cfg.screen.win, ... + cfg.color.background(1) * ones(cfg.screen.winRect([4 3]))); + + case 'make' + + Screen('FillRect', cfg.dot.texture, cfg.color.background); + Screen('DrawDots', cfg.dot.texture, ... + thisEvent.dot.position, ... + cfg.dot.sizePix, ... + cfg.dot.color, ... + cfg.screen.center, ... + 1); + + case 'draw' + + Screen('DrawTexture', cfg.screen.win, cfg.dot.texture); + + + end + +end \ No newline at end of file From fbd03dba41fbef10200f045de5c961593f3fe1f4 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sun, 2 Aug 2020 11:43:23 +0200 Subject: [PATCH 04/12] add wrapper functions for experiment start end farewell and stand by screen --- farewellScreen.m | 9 +++++++++ getExperimentEnd.m | 15 +++++++++++++++ getExperimentStart.m | 6 ++++++ standByScreen.m | 14 ++++++++++++++ 4 files changed, 44 insertions(+) create mode 100644 farewellScreen.m create mode 100644 getExperimentEnd.m create mode 100644 getExperimentStart.m create mode 100644 standByScreen.m diff --git a/farewellScreen.m b/farewellScreen.m new file mode 100644 index 0000000..b512788 --- /dev/null +++ b/farewellScreen.m @@ -0,0 +1,9 @@ +function farewellScreen(cfg) + + Screen('FillRect', cfg.screen.win, cfg.color.background, cfg.screen.winRect); + DrawFormattedText(cfg.screen.win, 'Thank you!', 'center', 'center', cfg.text.color); + Screen('Flip', cfg.screen.win); + WaitSecs(cfg.mri.repetitionTime * 2); + +end + diff --git a/getExperimentEnd.m b/getExperimentEnd.m new file mode 100644 index 0000000..d254ee8 --- /dev/null +++ b/getExperimentEnd.m @@ -0,0 +1,15 @@ +function cfg = getExperimentEnd(cfg) + + drawFixation(cfg); + endExpmt = Screen('Flip', cfg.screen.win); + + disp(' '); + ExpmtDur = endExpmt - cfg.experimentStart; + ExpmtDurMin = floor(ExpmtDur / 60); + ExpmtDurSec = mod(ExpmtDur, 60); + disp(['Experiment lasted ', ... + num2str(ExpmtDurMin), ' minutes', ... + num2str(ExpmtDurSec), ' seconds']); + disp(' '); + +end diff --git a/getExperimentStart.m b/getExperimentStart.m new file mode 100644 index 0000000..62c7918 --- /dev/null +++ b/getExperimentStart.m @@ -0,0 +1,6 @@ +function cfg = getExperimentStart(cfg) + % Show the fixation cross + drawFixation(cfg); + vbl = Screen('Flip', cfg.screen.win); + cfg.experimentStart = vbl; +end \ No newline at end of file diff --git a/standByScreen.m b/standByScreen.m new file mode 100644 index 0000000..a320658 --- /dev/null +++ b/standByScreen.m @@ -0,0 +1,14 @@ +function standByScreen(cfg) + + Screen('FillRect', cfg.screen.win, cfg.color.background, cfg.screen.winRect); + + DrawFormattedText(cfg.screen.win, ... + cfg.task.instruction, ... + 'center', 'center', cfg.text.color); + + Screen('Flip', cfg.screen.win); + + % Wait for space key to be pressed + pressSpaceForMe(); + +end From 26de68f9f3e1bd6479bec98b5cb83a9871c76308 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sun, 2 Aug 2020 13:16:08 +0200 Subject: [PATCH 05/12] update from visual localizer to use textures --- apertureTexture.m | 32 +++++++++++++++++--------------- degToPix.m | 2 +- dotTexture.m | 2 +- getExperimentEnd.m | 2 +- waitForTrigger.m | 2 +- 5 files changed, 21 insertions(+), 19 deletions(-) diff --git a/apertureTexture.m b/apertureTexture.m index 5bd6d7b..71cf35e 100644 --- a/apertureTexture.m +++ b/apertureTexture.m @@ -1,12 +1,18 @@ - function cfg = apertureTexture(action, cfg, thisEvent) - matrixSize = 400; + transparent = [0 0 0 0]; switch action case 'init' + % we take the screen height as maximum aperture width if not + % specified. + if ~isfield(cfg.aperture, 'width') || isempty(cfg.aperture.width) + cfg.aperture.width = cfg.screen.winRect(4) / cfg.screen.ppd; + end + cfg.aperture = degToPix('width', cfg.aperture, cfg); + cfg.aperture.texture = Screen('MakeTexture', cfg.screen.win, ... cfg.color.background(1) * ones(cfg.screen.winRect([4 3]))); @@ -16,12 +22,14 @@ case 'none' - Screen('Fillrect', cfg.aperture.texture, [0 0 0 0]); + Screen('Fillrect', cfg.aperture.texture, transparent); case 'circle' - Screen('FillOval', cfg.aperture.texture, [0 0 0 0], ... - CenterRectOnPoint([0 0 repmat(matrixSize, 1, 2)], ... + diameter = cfg.aperture.widthPix; + + Screen('FillOval', cfg.aperture.texture, transparent, ... + CenterRectOnPoint([0 0 repmat(diameter, 1, 2)], ... cfg.screen.winRect(3) / 2, cfg.screen.winRect(4) / 2)); end @@ -30,15 +38,9 @@ Screen('DrawTexture', cfg.screen.win, cfg.aperture.texture); - % Screen('DrawTexture', cfg.screen.win, apertureTexture, ... - % cfg.screen.winRect, cfg.screen.winRect, current.apertureAngle - 90); - - + % Screen('DrawTexture', cfg.screen.win, apertureTexture, ... + % cfg.screen.winRect, cfg.screen.winRect, current.apertureAngle - 90); + end - - - - - - + end \ No newline at end of file diff --git a/degToPix.m b/degToPix.m index 9dbf832..367d07a 100644 --- a/degToPix.m +++ b/degToPix.m @@ -8,6 +8,6 @@ deg = getfield(structure, fieldName); %#ok structure = setfield(structure, [fieldName 'Pix'], ... - floor(cfg.screen.ppd * deg)) ; %#ok + floor(deg * cfg.screen.ppd)) ; %#ok end diff --git a/dotTexture.m b/dotTexture.m index d6a57be..c10f320 100644 --- a/dotTexture.m +++ b/dotTexture.m @@ -10,7 +10,7 @@ Screen('FillRect', cfg.dot.texture, cfg.color.background); Screen('DrawDots', cfg.dot.texture, ... - thisEvent.dot.position, ... + thisEvent.dot.positions, ... cfg.dot.sizePix, ... cfg.dot.color, ... cfg.screen.center, ... diff --git a/getExperimentEnd.m b/getExperimentEnd.m index d254ee8..78d5d46 100644 --- a/getExperimentEnd.m +++ b/getExperimentEnd.m @@ -8,7 +8,7 @@ ExpmtDurMin = floor(ExpmtDur / 60); ExpmtDurSec = mod(ExpmtDur, 60); disp(['Experiment lasted ', ... - num2str(ExpmtDurMin), ' minutes', ... + num2str(ExpmtDurMin), ' minutes ', ... num2str(ExpmtDurSec), ' seconds']); disp(' '); diff --git a/waitForTrigger.m b/waitForTrigger.m index f4ccede..54729fe 100644 --- a/waitForTrigger.m +++ b/waitForTrigger.m @@ -27,7 +27,7 @@ function waitForTrigger(cfg, deviceNumber) if strcmpi(cfg.testingDevice, 'mri') - msg = 'Waiting for trigger'; + msg = 'Waiting for trigger...'; talkToMe(cfg, msg); while triggerCounter < cfg.mri.triggerNb From ac6fc160bfb535a08b4f7afbe35a4e48a35b2184 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sun, 2 Aug 2020 13:16:51 +0200 Subject: [PATCH 06/12] MH autolint --- apertureTexture.m | 28 +++++------ dotTexture.m | 19 ++++---- drawFixation.m | 2 +- farewellScreen.m | 5 +- getExperimentEnd.m | 6 +-- getExperimentStart.m | 2 +- initPTB.m | 109 +++++++++++++++++++++---------------------- setDefaultsPTB.m | 8 ++-- standByScreen.m | 6 +-- 9 files changed, 91 insertions(+), 94 deletions(-) diff --git a/apertureTexture.m b/apertureTexture.m index 71cf35e..c32648c 100644 --- a/apertureTexture.m +++ b/apertureTexture.m @@ -1,11 +1,11 @@ function cfg = apertureTexture(action, cfg, thisEvent) - + transparent = [0 0 0 0]; - + switch action - + case 'init' - + % we take the screen height as maximum aperture width if not % specified. if ~isfield(cfg.aperture, 'width') || isempty(cfg.aperture.width) @@ -15,15 +15,15 @@ cfg.aperture.texture = Screen('MakeTexture', cfg.screen.win, ... cfg.color.background(1) * ones(cfg.screen.winRect([4 3]))); - + case 'make' - + switch cfg.aperture.type - + case 'none' - + Screen('Fillrect', cfg.aperture.texture, transparent); - + case 'circle' diameter = cfg.aperture.widthPix; @@ -33,14 +33,14 @@ cfg.screen.winRect(3) / 2, cfg.screen.winRect(4) / 2)); end - + case 'draw' - + Screen('DrawTexture', cfg.screen.win, cfg.aperture.texture); - + % Screen('DrawTexture', cfg.screen.win, apertureTexture, ... % cfg.screen.winRect, cfg.screen.winRect, current.apertureAngle - 90); - + end -end \ No newline at end of file +end diff --git a/dotTexture.m b/dotTexture.m index c10f320..9dbeb8f 100644 --- a/dotTexture.m +++ b/dotTexture.m @@ -1,13 +1,13 @@ function cfg = dotTexture(action, cfg, thisEvent) - + switch action - + case 'init' cfg.dot.texture = Screen('MakeTexture', cfg.screen.win, ... cfg.color.background(1) * ones(cfg.screen.winRect([4 3]))); - + case 'make' - + Screen('FillRect', cfg.dot.texture, cfg.color.background); Screen('DrawDots', cfg.dot.texture, ... thisEvent.dot.positions, ... @@ -15,12 +15,11 @@ cfg.dot.color, ... cfg.screen.center, ... 1); - + case 'draw' - + Screen('DrawTexture', cfg.screen.win, cfg.dot.texture); - - + end - -end \ No newline at end of file + +end diff --git a/drawFixation.m b/drawFixation.m index 0e436a6..bb77d0d 100644 --- a/drawFixation.m +++ b/drawFixation.m @@ -9,5 +9,5 @@ function drawFixation(cfg) cfg.fixation.color, ... [cfg.screen.center(1) cfg.screen.center(2)], 1); end - + end diff --git a/farewellScreen.m b/farewellScreen.m index b512788..37e0ad2 100644 --- a/farewellScreen.m +++ b/farewellScreen.m @@ -1,9 +1,8 @@ function farewellScreen(cfg) - + Screen('FillRect', cfg.screen.win, cfg.color.background, cfg.screen.winRect); DrawFormattedText(cfg.screen.win, 'Thank you!', 'center', 'center', cfg.text.color); Screen('Flip', cfg.screen.win); WaitSecs(cfg.mri.repetitionTime * 2); - -end +end diff --git a/getExperimentEnd.m b/getExperimentEnd.m index 78d5d46..19d0ea7 100644 --- a/getExperimentEnd.m +++ b/getExperimentEnd.m @@ -1,8 +1,8 @@ function cfg = getExperimentEnd(cfg) - + drawFixation(cfg); endExpmt = Screen('Flip', cfg.screen.win); - + disp(' '); ExpmtDur = endExpmt - cfg.experimentStart; ExpmtDurMin = floor(ExpmtDur / 60); @@ -11,5 +11,5 @@ num2str(ExpmtDurMin), ' minutes ', ... num2str(ExpmtDurSec), ' seconds']); disp(' '); - + end diff --git a/getExperimentStart.m b/getExperimentStart.m index 62c7918..3d64ffd 100644 --- a/getExperimentStart.m +++ b/getExperimentStart.m @@ -3,4 +3,4 @@ drawFixation(cfg); vbl = Screen('Flip', cfg.screen.win); cfg.experimentStart = vbl; -end \ No newline at end of file +end diff --git a/initPTB.m b/initPTB.m index de24de4..4cb8097 100644 --- a/initPTB.m +++ b/initPTB.m @@ -19,62 +19,62 @@ % % % - + checkPtbVersion(); - + pth = fileparts(mfilename('fullpath')); addpath(fullfile(pth, 'subfun')); - + % For octave: to avoid displaying messenging one screen at a time more off; - + % check for OpenGL compatibility, abort otherwise: AssertOpenGL; - + cfg = setDefaultsPTB(cfg); - + initKeyboard; initDebug(cfg); - + % Mouse HideCursor; - + %% Audio cfg = initAudio(cfg); - + %% Visual - + % Get the screen numbers and draw to the external screen if avaliable cfg.screen.idx = max(Screen('Screens')); - + cfg = openWindow(cfg); - + % window size info [cfg.screen.winWidth, cfg.screen.winHeight] = WindowSize(cfg.screen.win); - + % Get the Center of the Screen cfg.screen.center = [cfg.screen.winRect(3), cfg.screen.winRect(4)] / 2; - + % Computes the number of pixels per degree given the distance to screen and % monitor width % This assumes that the window fills the whole screen cfg.screen.FOV = computeFOV(cfg); cfg.screen.ppd = cfg.screen.winRect(3) / cfg.screen.FOV; - + % Initialize visual parmaters for fixation cross or dot cfg = initFixation(cfg); - + %% Select specific text font, style and size initText(cfg); - + %% Timing % Query frame duration cfg.screen.ifi = Screen('GetFlipInterval', cfg.screen.win); cfg.screen.monitorRefresh = 1 / cfg.screen.ifi; - + % Set priority for script execution to realtime priority: Priority(MaxPriority(cfg.screen.win)); - + %% Warm up some functions % Do dummy calls to GetSecs, WaitSecs, KbCheck to make sure % they are loaded and ready when we need them - without delays @@ -82,95 +82,95 @@ KbCheck; WaitSecs(0.1); GetSecs; - + end function initDebug(cfg) - + % init PTB with different options in concordance to the debug Parameters Screen('Preference', 'SkipSyncTests', 0); if cfg.debug.do - + Screen('Preference', 'SkipSyncTests', 2); Screen('Preference', 'Verbosity', 0); Screen('Preference', 'SuppressAllWarnings', 1); - + fprintf('\n\n\n\n'); fprintf('########################################\n'); fprintf('## DEBUG MODE. TIMING WILL BE OFF. ##\n'); fprintf('########################################'); fprintf('\n\n\n\n'); - + testKeyboards(cfg); - + end - + if cfg.debug.transpWin PsychDebugWindowConfiguration; end - + end function initKeyboard - + % Make sure keyboard mapping is the same on all supported operating systems % Apple MacOS/X, MS-Windows and GNU/Linux: KbName('UnifyKeyNames'); - + % Don't echo keypresses to Matlab window ListenChar(-1); - + end function cfg = initAudio(cfg) - + if cfg.audio.do - + InitializePsychSound(1); - + cfg.audio.devIdx = []; cfg.audio.playbackMode = 1; - + if isfield(cfg.audio, 'useDevice') - + % get audio device list audioDev = PsychPortAudio('GetDevices'); - + % find output device to use idx = find( ... audioDev.NrInputChannels == cfg.audio.inputChannels && ... audioDev.NrOutputChannels == cfg.audio.channels && ... ~cellfun(@isempty, regexp({audioDev.HostAudioAPIName}, cfg.audio.deviceName))); - + % save device ID cfg.audio.devIdx = audioDev(idx).DeviceIndex; - + % get device's sampling rate cfg.audio.fs = audioDev(idx).DefaultSampleRate; - + end - + cfg.audio.pahandle = PsychPortAudio('Open', ... cfg.audio.devIdx, ... cfg.audio.playbackMode, ... cfg.audio.requestedLatency, ... cfg.audio.fs, ... cfg.audio.channels); - + % set initial PTB volume for safety (participants can adjust this manually % at the begining of the experiment) PsychPortAudio('Volume', cfg.audio.pahandle, cfg.audio.initVolume); - + cfg.audio.pushSize = cfg.audio.fs * 0.010; % ! push N ms only - + cfg.audio.requestOffsetTime = 1; % offset 1 sec cfg.audio.reqsSampleOffset = cfg.audio.requestOffsetTime * cfg.audio.fs; - + end end function cfg = openWindow(cfg) - + if cfg.debug.smallWin [cfg.screen.win, cfg.screen.winRect] = ... Screen('OpenWindow', cfg.screen.idx, cfg.color.background, ... @@ -179,36 +179,35 @@ function initDebug(cfg) [cfg.screen.win, cfg.screen.winRect] = ... Screen('OpenWindow', cfg.screen.idx, cfg.color.background); end - + % Enable alpha-blending, set it to a blend equation useable for linear % superposition with alpha-weighted source. Screen('BlendFunction', cfg.screen.win, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - + end function cfg = initFixation(cfg) - + if strcmp(cfg.fixation.type, 'cross') - + % Convert some values from degrees to pixels cfg.fixation = degToPix('width', cfg.fixation, cfg); - + % Prepare fixation cross cfg.fixation.xCoords = [-cfg.fixation.widthPix cfg.fixation.widthPix 0 0] + ... cfg.fixation.xDisplacement; cfg.fixation.yCoords = [0 0 -cfg.fixation.widthPix cfg.fixation.widthPix] + ... cfg.fixation.yDisplacement; cfg.fixation.allCoords = [cfg.fixation.xCoords; cfg.fixation.yCoords]; - + end - - + end function initText(cfg) - + Screen('TextFont', cfg.screen.win, cfg.text.font); Screen('TextSize', cfg.screen.win, cfg.text.size); Screen('TextStyle', cfg.screen.win, cfg.text.style); - + end diff --git a/setDefaultsPTB.m b/setDefaultsPTB.m index b3b623c..5a172d6 100644 --- a/setDefaultsPTB.m +++ b/setDefaultsPTB.m @@ -29,15 +29,15 @@ fieldsToSet.screen.monitorWidth = 42; fieldsToSet.screen.monitorDistance = 134; - + % fixation cross or dot fieldsToSet.fixation.type = 'cross'; - fieldsToSet.fixation.xDisplacement = 0; - fieldsToSet.fixation.yDisplacement = 0; + fieldsToSet.fixation.xDisplacement = 0; + fieldsToSet.fixation.yDisplacement = 0; fieldsToSet.fixation.color = [255 255 255]; fieldsToSet.fixation.width = 1; fieldsToSet.fixation.lineWidthPix = 5; - + % define visual apperture field fieldsToSet.aperture.type = 'none'; diff --git a/standByScreen.m b/standByScreen.m index a320658..8df6d23 100644 --- a/standByScreen.m +++ b/standByScreen.m @@ -1,5 +1,5 @@ function standByScreen(cfg) - + Screen('FillRect', cfg.screen.win, cfg.color.background, cfg.screen.winRect); DrawFormattedText(cfg.screen.win, ... @@ -7,8 +7,8 @@ function standByScreen(cfg) 'center', 'center', cfg.text.color); Screen('Flip', cfg.screen.win); - + % Wait for space key to be pressed pressSpaceForMe(); - + end From ed51eef33fc1b2771dc07f2e71501def7271e97f Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sun, 2 Aug 2020 16:11:24 +0200 Subject: [PATCH 07/12] severeral updates on some base functions --- apertureTexture.m | 13 +++++++++++-- computeFOV.m | 11 +++++++++-- decompMotion.m | 5 +++++ dotTexture.m | 4 +++- drawFixation.m | 5 ++++- initFixation.m | 19 +++++++++++++++++++ initPTB.m | 21 ++------------------- 7 files changed, 53 insertions(+), 25 deletions(-) create mode 100644 decompMotion.m create mode 100644 initFixation.m diff --git a/apertureTexture.m b/apertureTexture.m index c32648c..1b0663a 100644 --- a/apertureTexture.m +++ b/apertureTexture.m @@ -27,11 +27,20 @@ case 'circle' diameter = cfg.aperture.widthPix; + + xPos = cfg.screen.center(1); + yPos = cfg.screen.center(2); + if isfield(cfg.aperture, 'xPosPix') + xPos = cfg.screen.center(1) + cfg.aperture.xPosPix; + end + if isfield(cfg.aperture, 'yPosPix') + yPos = cfg.screen.center(2) + cfg.aperture.yPosPix; + end Screen('FillOval', cfg.aperture.texture, transparent, ... CenterRectOnPoint([0 0 repmat(diameter, 1, 2)], ... - cfg.screen.winRect(3) / 2, cfg.screen.winRect(4) / 2)); - + xPos, yPos)); + end case 'draw' diff --git a/computeFOV.m b/computeFOV.m index 1d12fef..b8b66a9 100644 --- a/computeFOV.m +++ b/computeFOV.m @@ -3,8 +3,15 @@ % % computes the number of degrees of visual angle in the whole field of view % + % δ = 2 arctan ( d / 2D ) + % + % δ is the angular diameter, and d is the actual diameter of the object, + % and D is the distance to the object. + % The result obtained is in radians. + % - FOV = 2 * ... - (180 * (atan(cfg.screen.monitorWidth / (2 * cfg.screen.monitorDistance)) / pi)); + FOV = ... + 180 / pi * ... + 2 * atan( cfg.screen.monitorWidth / (2 * cfg.screen.monitorDistance) ); end diff --git a/decompMotion.m b/decompMotion.m new file mode 100644 index 0000000..bf6283b --- /dev/null +++ b/decompMotion.m @@ -0,0 +1,5 @@ +function [horVector, vertVector] = decompMotion(angleMotion) + % decompose angle of start motion into horizontal and vertical vector + horVector = cos(pi * angleMotion / 180); + vertVector = -sin(pi * angleMotion / 180); +end diff --git a/dotTexture.m b/dotTexture.m index 9dbeb8f..2914681 100644 --- a/dotTexture.m +++ b/dotTexture.m @@ -7,6 +7,8 @@ cfg.color.background(1) * ones(cfg.screen.winRect([4 3]))); case 'make' + + dotType = 2; Screen('FillRect', cfg.dot.texture, cfg.color.background); Screen('DrawDots', cfg.dot.texture, ... @@ -14,7 +16,7 @@ cfg.dot.sizePix, ... cfg.dot.color, ... cfg.screen.center, ... - 1); + dotType); case 'draw' diff --git a/drawFixation.m b/drawFixation.m index bb77d0d..f8d7072 100644 --- a/drawFixation.m +++ b/drawFixation.m @@ -2,12 +2,15 @@ function drawFixation(cfg) % Define the parameters of the fixation cross in `cfg` and `expParameters` if strcmp(cfg.fixation.type, 'cross') + + smooth = 1; + Screen('DrawLines', ... cfg.screen.win, ... cfg.fixation.allCoords, ... cfg.fixation.lineWidthPix, ... cfg.fixation.color, ... - [cfg.screen.center(1) cfg.screen.center(2)], 1); + [cfg.screen.center(1) cfg.screen.center(2)], smooth); end end diff --git a/initFixation.m b/initFixation.m new file mode 100644 index 0000000..5d0b981 --- /dev/null +++ b/initFixation.m @@ -0,0 +1,19 @@ +function cfg = initFixation(cfg) + + if strcmp(cfg.fixation.type, 'cross') + + % Convert some values from degrees to pixels + cfg.fixation = degToPix('width', cfg.fixation, cfg); + cfg.fixation = degToPix('xDisplacement', cfg.fixation, cfg); + cfg.fixation = degToPix('yDisplacement', cfg.fixation, cfg); + + % Prepare fixation cross + cfg.fixation.xCoords = [-cfg.fixation.widthPix cfg.fixation.widthPix 0 0] + ... + cfg.fixation.xDisplacementPix; + cfg.fixation.yCoords = [0 0 -cfg.fixation.widthPix cfg.fixation.widthPix] + ... + cfg.fixation.yDisplacementPix; + cfg.fixation.allCoords = [cfg.fixation.xCoords; cfg.fixation.yCoords]; + + end + +end \ No newline at end of file diff --git a/initPTB.m b/initPTB.m index 4cb8097..5364207 100644 --- a/initPTB.m +++ b/initPTB.m @@ -59,7 +59,7 @@ % monitor width % This assumes that the window fills the whole screen cfg.screen.FOV = computeFOV(cfg); - cfg.screen.ppd = cfg.screen.winRect(3) / cfg.screen.FOV; + cfg.screen.ppd = cfg.screen.winWidth / cfg.screen.FOV; % Initialize visual parmaters for fixation cross or dot cfg = initFixation(cfg); @@ -182,28 +182,11 @@ function initDebug(cfg) % Enable alpha-blending, set it to a blend equation useable for linear % superposition with alpha-weighted source. + % Required for drwing smooth lines and screen('DrawDots') Screen('BlendFunction', cfg.screen.win, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); end -function cfg = initFixation(cfg) - - if strcmp(cfg.fixation.type, 'cross') - - % Convert some values from degrees to pixels - cfg.fixation = degToPix('width', cfg.fixation, cfg); - - % Prepare fixation cross - cfg.fixation.xCoords = [-cfg.fixation.widthPix cfg.fixation.widthPix 0 0] + ... - cfg.fixation.xDisplacement; - cfg.fixation.yCoords = [0 0 -cfg.fixation.widthPix cfg.fixation.widthPix] + ... - cfg.fixation.yDisplacement; - cfg.fixation.allCoords = [cfg.fixation.xCoords; cfg.fixation.yCoords]; - - end - -end - function initText(cfg) Screen('TextFont', cfg.screen.win, cfg.text.font); From ae4b3af956bb4a608586fa2d9fd856b311e59159 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sun, 2 Aug 2020 18:28:32 +0200 Subject: [PATCH 08/12] fix real width fixation --- initFixation.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/initFixation.m b/initFixation.m index 5d0b981..9643c1e 100644 --- a/initFixation.m +++ b/initFixation.m @@ -8,9 +8,9 @@ cfg.fixation = degToPix('yDisplacement', cfg.fixation, cfg); % Prepare fixation cross - cfg.fixation.xCoords = [-cfg.fixation.widthPix cfg.fixation.widthPix 0 0] + ... + cfg.fixation.xCoords = [-cfg.fixation.widthPix cfg.fixation.widthPix 0 0]/2 + ... cfg.fixation.xDisplacementPix; - cfg.fixation.yCoords = [0 0 -cfg.fixation.widthPix cfg.fixation.widthPix] + ... + cfg.fixation.yCoords = [0 0 -cfg.fixation.widthPix cfg.fixation.widthPix]/2 + ... cfg.fixation.yDisplacementPix; cfg.fixation.allCoords = [cfg.fixation.xCoords; cfg.fixation.yCoords]; From 1cf218fbadeba516b431c6dc0fb264a81bab9b42 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sun, 2 Aug 2020 19:17:56 +0200 Subject: [PATCH 09/12] update README --- README.md | 94 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index da29616..45fba69 100644 --- a/README.md +++ b/README.md @@ -35,44 +35,74 @@ We use the [MISS_HIT linter](https://florianschanda.github.io/miss_hit/style_che ## How to install -### Use the matlab package manager +### Download with git + +``` bash +cd fullpath_to_directory_where_to_install +# use git to download the code +git clone https://github.com/cpp-lln-lab/CPP_PTB.git +# move into the folder you have just created +cd CPP_PTB +# add the src folder to the matlab path and save the path +matlab -nojvm -nosplash -r "addpath(fullfile(pwd)); savepath ();" +``` -This repository can be added as a dependencies by listing it in a [mpm-requirements.txt file](.mpm-requirements.txt) -as follows: +Then get the latest commit: +```bash +# from the directory where you downloaded the code +git pull origin master +``` - CPP_PTB -u https://github.com/cpp-lln-lab/CPP_PTB.git +To work with a specific version, create a branch at a specific version tag number +```bash +# creating and checking out a branch that will be calle version1 at the version tag v0.0.1 +git checkout -b version1 v0.0.1 +``` -You can then use the [matlab package manager](https://github.com/mobeets/mpm), to simply download the appropriate version of those dependencies and add them to your path by running a `getDependencies` function like the one below where you just need to replace `YOUR_EXPERIMENT_NAME` by the name of your experiment. +### Add as a submodule -```matlab - function getDependencies(action) - % Will install on your computer the matlab dependencies specified in the mpm-requirements.txt - % and add them to the matlab path. The path is never saved so you need to run getDependencies() when - % you start matlab. - % - % getDependencies('update') will force the update and overwrite previous version of the dependencies. - % - % getDependencies() If you only already have the appropriate version but just want to add them to the matlab path. - - experimentName = YOUR_EXPERIMENT_NAME; - - if nargin<1 - action = ''; - end - - switch action - case 'update' - % install dependencies - mpm install -i mpm-requirements.txt -f -c YOUR_EXPERIMENT_NAME - end - - % adds them to the path - mpm_folder = fileparts(which('mpm')); - addpath(genpath(fullfile(mpm_folder, 'mpm-packages', 'mpm-collections', experimentName))); - - end +Add it as a submodule in the repo you are working on. + +``` bash +cd fullpath_to_directory_where_to_install +# use git to download the code +git submodule add https://github.com/cpp-lln-lab/CPP_PTB.git +# move into the folder you have just created +cd CPP_PTB +# add the src folder to the matlab path and save the path +matlab -nojvm -nosplash -r "addpath(fullfile(pwd))" +``` + +To get the latest commit you then need to update the submodule with the information +on its remote repository and then merge those locally. +```bash +git submodule update --remote --merge ``` +Remember that updates to submodules need to be commited as well. + +**TO DO** + + +### Direct download + +Download the code. Unzip. And add to the matlab path. + +Pick a specific version: + +https://github.com/cpp-lln-lab/CPP_PTB/releases + +Or take the latest commit (NOT RECOMMENDED): + +https://github.com/cpp-lln-lab/CPP_PTB/archive/master.zip + +**TO DO** + + ## Setting up keyboards To select a specific keyboard to be used by the experimenter or the participant, you need to know From 197c4d4ce68da1c40d0f5f504e509582301e3087 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sun, 2 Aug 2020 19:19:29 +0200 Subject: [PATCH 10/12] add functions for RDK from the visual localizer --- initializeDots.m | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ updateDots.m | 26 +++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 initializeDots.m create mode 100644 updateDots.m diff --git a/initializeDots.m b/initializeDots.m new file mode 100644 index 0000000..a8b5755 --- /dev/null +++ b/initializeDots.m @@ -0,0 +1,50 @@ +function [dots] = initializeDots(cfg, thisEvent) + + direction = thisEvent.direction(1); + + dots.lifeTime = cfg.dot.lifeTime; + + speedPixPerFrame = thisEvent.speed(1); + + % decide which dots are signal dots (1) and those are noise dots (0) + dots.isSignal = rand(cfg.dot.number, 1) < cfg.dot.coherence; + + % for static dots + if direction == -1 + speedPixPerFrame = 0; + dots.lifeTime = cfg.eventDuration; + dots.isSignal = ones(cfg.dot.number, 1); + end + + % Convert from seconds to frames + dots.lifeTime = ceil(dots.lifeTime / cfg.screen.ifi); + + % Set an array of dot positions [xposition, yposition] + % These can never be bigger than 1 or lower than 0 + % [0,0] is the top / left of the square + % [1,1] is the bottom / right of the square + dots.positions = rand(cfg.dot.number, 2) * cfg.screen.winWidth; + + % Set a N x 2 matrix that speed in X and Y + dots.speeds = nan(cfg.dot.number, 2); + + % Coherent dots + [horVector, vertVector] = decompMotion(direction); + dots.speeds(dots.isSignal,:) = ... + repmat([horVector, vertVector], sum(dots.isSignal), 1); + + % If not 100% coherence, we get new random direction for the other dots + direction = rand(sum(~dots.isSignal), 1) * 360; + [horVector, vertVector] = decompMotion(direction); + dots.speeds(~dots.isSignal, :) = [horVector, vertVector]; + + % So far we were working wiht unit vectors convert that speed in pixels per + % frame + dots.speeds = dots.speeds * speedPixPerFrame; + + % Create a vector to update to dotlife time of each dot + % Not all set to one so the dots will die at different times + dots.time = floor(rand(cfg.dot.number, 1) * cfg.eventDuration / cfg.screen.ifi); + +end + diff --git a/updateDots.m b/updateDots.m new file mode 100644 index 0000000..35ab733 --- /dev/null +++ b/updateDots.m @@ -0,0 +1,26 @@ +function [dots] = updateDots(dots, cfg) + + % Move the selected dots + dots.positions = dots.positions + dots.speeds; + + % Create a logical vector to detect any dot that has: + % - an xy position inferior to 0 + % - an x position superior to winWidth + % - an x position superior to winHeight + % - has exceeded its liftime + N = any([... + dots.positions > cfg.screen.winWidth, ... + dots.positions < 0, ... + dots.time > dots.lifeTime, ... + rand(cfg.dot.number,1) < cfg.dot.proportionKilledPerFrame ], 2) ; + + % If there is any such dot we relocate it to a new random position + % and change its lifetime to 1 + if any(N) + dots.positions(N, :) = rand(sum(N), 2) * cfg.screen.winWidth; + dots.time(N, 1) = 1; + end + + % Add one frame to the dot lifetime to each dot + dots.time = dots.time + 1; +end From 5fcf7368930765dfe7efb292bafd66dc4e029f0b Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sun, 2 Aug 2020 19:45:38 +0200 Subject: [PATCH 11/12] update unit test --- tests/test_setDefaultsPTB.m | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/test_setDefaultsPTB.m b/tests/test_setDefaultsPTB.m index fa19428..90b3584 100644 --- a/tests/test_setDefaultsPTB.m +++ b/tests/test_setDefaultsPTB.m @@ -61,6 +61,18 @@ function test_setDefaultsPTB() 'monitorWidth', 42, ... 'monitorDistance', 134)); + + % fixation cross or dot + expectedCFG.fixation.type = 'cross'; + expectedCFG.fixation.xDisplacement = 0; + expectedCFG.fixation.yDisplacement = 0; + expectedCFG.fixation.color = [255 255 255]; + expectedCFG.fixation.width = 1; + expectedCFG.fixation.lineWidthPix = 5; + + % define visual apperture field + expectedCFG.aperture.type = 'none'; + expectedCFG.keyboard.keyboard = []; expectedCFG.keyboard.responseBox = []; expectedCFG.keyboard.responseKey = {}; From 46f990f18fe320984f71ae6ec1b02c12f604ad40 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sun, 2 Aug 2020 19:51:39 +0200 Subject: [PATCH 12/12] mh autofix --- apertureTexture.m | 4 ++-- computeFOV.m | 8 ++++---- dotTexture.m | 2 +- drawFixation.m | 4 ++-- initFixation.m | 6 +++--- initializeDots.m | 31 +++++++++++++++---------------- tests/test_setDefaultsPTB.m | 1 - updateDots.m | 10 +++++----- 8 files changed, 32 insertions(+), 34 deletions(-) diff --git a/apertureTexture.m b/apertureTexture.m index 1b0663a..a695637 100644 --- a/apertureTexture.m +++ b/apertureTexture.m @@ -27,7 +27,7 @@ case 'circle' diameter = cfg.aperture.widthPix; - + xPos = cfg.screen.center(1); yPos = cfg.screen.center(2); if isfield(cfg.aperture, 'xPosPix') @@ -40,7 +40,7 @@ Screen('FillOval', cfg.aperture.texture, transparent, ... CenterRectOnPoint([0 0 repmat(diameter, 1, 2)], ... xPos, yPos)); - + end case 'draw' diff --git a/computeFOV.m b/computeFOV.m index b8b66a9..2f3cdf9 100644 --- a/computeFOV.m +++ b/computeFOV.m @@ -5,13 +5,13 @@ % % δ = 2 arctan ( d / 2D ) % - % δ is the angular diameter, and d is the actual diameter of the object, - % and D is the distance to the object. - % The result obtained is in radians. + % δ is the angular diameter, and d is the actual diameter of the object, + % and D is the distance to the object. + % The result obtained is in radians. % FOV = ... 180 / pi * ... - 2 * atan( cfg.screen.monitorWidth / (2 * cfg.screen.monitorDistance) ); + 2 * atan(cfg.screen.monitorWidth / (2 * cfg.screen.monitorDistance)); end diff --git a/dotTexture.m b/dotTexture.m index 2914681..1d5ff2c 100644 --- a/dotTexture.m +++ b/dotTexture.m @@ -7,7 +7,7 @@ cfg.color.background(1) * ones(cfg.screen.winRect([4 3]))); case 'make' - + dotType = 2; Screen('FillRect', cfg.dot.texture, cfg.color.background); diff --git a/drawFixation.m b/drawFixation.m index f8d7072..ce9d3f0 100644 --- a/drawFixation.m +++ b/drawFixation.m @@ -2,9 +2,9 @@ function drawFixation(cfg) % Define the parameters of the fixation cross in `cfg` and `expParameters` if strcmp(cfg.fixation.type, 'cross') - + smooth = 1; - + Screen('DrawLines', ... cfg.screen.win, ... cfg.fixation.allCoords, ... diff --git a/initFixation.m b/initFixation.m index 9643c1e..e85d7b3 100644 --- a/initFixation.m +++ b/initFixation.m @@ -8,12 +8,12 @@ cfg.fixation = degToPix('yDisplacement', cfg.fixation, cfg); % Prepare fixation cross - cfg.fixation.xCoords = [-cfg.fixation.widthPix cfg.fixation.widthPix 0 0]/2 + ... + cfg.fixation.xCoords = [-cfg.fixation.widthPix cfg.fixation.widthPix 0 0] / 2 + ... cfg.fixation.xDisplacementPix; - cfg.fixation.yCoords = [0 0 -cfg.fixation.widthPix cfg.fixation.widthPix]/2 + ... + cfg.fixation.yCoords = [0 0 -cfg.fixation.widthPix cfg.fixation.widthPix] / 2 + ... cfg.fixation.yDisplacementPix; cfg.fixation.allCoords = [cfg.fixation.xCoords; cfg.fixation.yCoords]; end -end \ No newline at end of file +end diff --git a/initializeDots.m b/initializeDots.m index a8b5755..0ddcbce 100644 --- a/initializeDots.m +++ b/initializeDots.m @@ -1,21 +1,21 @@ function [dots] = initializeDots(cfg, thisEvent) - + direction = thisEvent.direction(1); - + dots.lifeTime = cfg.dot.lifeTime; - - speedPixPerFrame = thisEvent.speed(1); - + + speedPixPerFrame = thisEvent.speed(1); + % decide which dots are signal dots (1) and those are noise dots (0) dots.isSignal = rand(cfg.dot.number, 1) < cfg.dot.coherence; - + % for static dots if direction == -1 speedPixPerFrame = 0; dots.lifeTime = cfg.eventDuration; dots.isSignal = ones(cfg.dot.number, 1); end - + % Convert from seconds to frames dots.lifeTime = ceil(dots.lifeTime / cfg.screen.ifi); @@ -24,27 +24,26 @@ % [0,0] is the top / left of the square % [1,1] is the bottom / right of the square dots.positions = rand(cfg.dot.number, 2) * cfg.screen.winWidth; - - % Set a N x 2 matrix that speed in X and Y + + % Set a N x 2 matrix that speed in X and Y dots.speeds = nan(cfg.dot.number, 2); - + % Coherent dots [horVector, vertVector] = decompMotion(direction); - dots.speeds(dots.isSignal,:) = ... + dots.speeds(dots.isSignal, :) = ... repmat([horVector, vertVector], sum(dots.isSignal), 1); - + % If not 100% coherence, we get new random direction for the other dots direction = rand(sum(~dots.isSignal), 1) * 360; [horVector, vertVector] = decompMotion(direction); dots.speeds(~dots.isSignal, :) = [horVector, vertVector]; - + % So far we were working wiht unit vectors convert that speed in pixels per % frame dots.speeds = dots.speeds * speedPixPerFrame; - + % Create a vector to update to dotlife time of each dot % Not all set to one so the dots will die at different times dots.time = floor(rand(cfg.dot.number, 1) * cfg.eventDuration / cfg.screen.ifi); - -end +end diff --git a/tests/test_setDefaultsPTB.m b/tests/test_setDefaultsPTB.m index 90b3584..9370eb6 100644 --- a/tests/test_setDefaultsPTB.m +++ b/tests/test_setDefaultsPTB.m @@ -61,7 +61,6 @@ function test_setDefaultsPTB() 'monitorWidth', 42, ... 'monitorDistance', 134)); - % fixation cross or dot expectedCFG.fixation.type = 'cross'; expectedCFG.fixation.xDisplacement = 0; diff --git a/updateDots.m b/updateDots.m index 35ab733..719ad64 100644 --- a/updateDots.m +++ b/updateDots.m @@ -1,5 +1,5 @@ function [dots] = updateDots(dots, cfg) - + % Move the selected dots dots.positions = dots.positions + dots.speeds; @@ -8,19 +8,19 @@ % - an x position superior to winWidth % - an x position superior to winHeight % - has exceeded its liftime - N = any([... + N = any([ ... dots.positions > cfg.screen.winWidth, ... dots.positions < 0, ... dots.time > dots.lifeTime, ... - rand(cfg.dot.number,1) < cfg.dot.proportionKilledPerFrame ], 2) ; - + rand(cfg.dot.number, 1) < cfg.dot.proportionKilledPerFrame], 2) ; + % If there is any such dot we relocate it to a new random position % and change its lifetime to 1 if any(N) dots.positions(N, :) = rand(sum(N), 2) * cfg.screen.winWidth; dots.time(N, 1) = 1; end - + % Add one frame to the dot lifetime to each dot dots.time = dots.time + 1; end