From 62c9f270b557b3a9cbe53bc115448a126b774db3 Mon Sep 17 00:00:00 2001 From: "J. L." Date: Wed, 9 Oct 2024 13:26:03 +0200 Subject: [PATCH 1/9] Added files to branch Added changes for JenaSMI verision to branch. A bit messy, since original PC is offline. Goal is to merge this branch into master by implementing a config variable to change between Eyelink and SMI trackers --- Task/Classes/al_eyeTracker.m | 64 ++++++++++++++----- Task/Functions/al_baselineArousal.m | 11 ++-- Task/Functions/al_sendTrigger.m | 5 +- .../commonConfetti/al_confettiLoop.m | 14 ++-- .../commonConfetti/al_saveEyelinkData.m | 25 ++++---- 5 files changed, 78 insertions(+), 41 deletions(-) diff --git a/Task/Classes/al_eyeTracker.m b/Task/Classes/al_eyeTracker.m index afc25b7..efa9a70 100644 --- a/Task/Classes/al_eyeTracker.m +++ b/Task/Classes/al_eyeTracker.m @@ -18,6 +18,7 @@ ppd % estimated pixels per degree resolutionX % x resolution (in pixels) saccThres % threshold value + el %SMI instance end @@ -47,7 +48,8 @@ function self = initializeEyeLink(self, taskParam, et_file_name_suffix) % INITIALIZEEYELINK This function initialzes the eye-tracker - % + % !! NOW ACTUALLY INITIALIZES SMI NOT EYELINK !! name kept for + % easier implementation. % Input % taskParam: Task-parameter-object instance % et_file_name_suffix: Suffix of file name @@ -58,18 +60,39 @@ self.et_file_name = sprintf('%s%s', taskParam.subject.ID, et_file_name_suffix); self.et_file_name = [self.et_file_name]; % todo: check if this is really necessary - + + + settings = SMITE.getDefaults('HiSpeed'); + settings.connectInfo = {'192.168.1.1',4444,'192.168.1.2',5555}; + settings.doAverageEyes = false; + settings.cal.bgColor = taskParam.colors.background; + settings.freq = 500; +% settings.trackMode = 'MONOCULAR'; + settings.trackEye = 'EYE_RIGHT'; + settings.logFileName = 'test_log.txt'; + settings.save.allowFileTransfer = false; + % Todo test if we can also pass object instead instead of new structure - options.dist = self.dist; - options.width = self.width; - options.height = self.height; - options.window_rect = taskParam.display.windowRect; - options.frameDur = self.frameDur; - options.frameRate = self.frameRate; - [el, ~] = ELconfig(taskParam.display.window.onScreen, self.et_file_name, options); - +% settings.dist = self.dist; +% settings.width = self.width; +% settings.height = self.height; +% settings.window_rect = taskParam.display.windowRect; +% settings.frameDur = self.frameDur; +% settings.frameRate = self.frameRate; + + % initialize SMI + self.el = SMITE(settings); + %EThndl = EThndl.setDummyMode(); + self.el.init(); + self.el.calibrate(taskParam.display.window.onScreen, false); + + + %[el, ~] = ELconfig(taskParam.display.window.onScreen, self.et_file_name, settings); + % Calibrate the eye tracker - EyelinkDoTrackerSetup(el); + % EyelinkDoTrackerSetup(el); + + end @@ -102,7 +125,9 @@ % sacc: Detected saccades % % Credit: Donner lab - + + error('this is the SMI version, no saccades implemented for now.') + % Short break pause(0.002) @@ -139,12 +164,17 @@ % % Output % taskParam: Task-parameter-object instance - - - Eyelink('StartRecording'); + + + % Eyelink('StartRecording'); + % WaitSecs(0.1); + % Eyelink('message', 'Start recording Eyelink'); + + taskParam.eyeTracker.el.startRecording() +% taskParam.eyeTracker.el.startBuffer() WaitSecs(0.1); - Eyelink('message', 'Start recording Eyelink'); - + taskParam.eyeTracker.el.sendMessage('Start recording SMI'); + % Reference time stamp taskParam.timingParam.ref = GetSecs(); end diff --git a/Task/Functions/al_baselineArousal.m b/Task/Functions/al_baselineArousal.m index dd35406..12bb6ab 100644 --- a/Task/Functions/al_baselineArousal.m +++ b/Task/Functions/al_baselineArousal.m @@ -33,8 +33,9 @@ function al_baselineArousal(taskParam, file_name_suffix) % Presenting trial number at the bottom of the eyetracker display - optional if taskParam.gParam.eyeTracker && isequal(taskParam.trialflow.exp, 'exp') - Eyelink('command', 'record_status_message "TRIAL %d/%d"', i, 3); - Eyelink('message', 'TRIALID %d', i); +% Eyelink('command', 'record_status_message "TRIAL %d/%d"', i, 3); +% Eyelink('message', 'TRIALID %d', i); + taskParam.eyeTracker.el.sendMessage(sprintf('TRIALID BaseAr %d', i)); end % Only send trigger on first interation @@ -73,8 +74,10 @@ function al_baselineArousal(taskParam, file_name_suffix) if taskParam.gParam.eyeTracker% && isequal(taskParam.trialflow.saveEtData, 'true') et_path = pwd; et_file_name=[taskParam.eyeTracker.et_file_name, '.edf']; - al_saveEyelinkData(et_path, et_file_name) - Eyelink('StopRecording'); + al_saveEyelinkData(taskParam.eyeTracker.el, et_path, et_file_name) + taskParam.eyeTracker.el.stopRecording(); +% al_saveEyelinkData(et_path, et_file_name) +% Eyelink('StopRecording'); end % if taskParam.gParam.eyeTracker diff --git a/Task/Functions/al_sendTrigger.m b/Task/Functions/al_sendTrigger.m index 23b687c..d390208 100644 --- a/Task/Functions/al_sendTrigger.m +++ b/Task/Functions/al_sendTrigger.m @@ -263,8 +263,9 @@ end % Send the pupil trigger -if taskParam.gParam.eyeTracker && isequal(taskParam.trialflow.exp, 'exp') && taskParam.gParam.eyeTracker && isequal(taskParam.trialflow.exp, 'passive') - Eyelink('message', num2str(triggerID)); +if taskParam.gParam.eyeTracker && isequal(taskParam.trialflow.exp, 'exp') +% Eyelink('message', num2str(triggerID)); + taskParam.eyeTracker.el.sendMessage(num2str(triggerID)); end % Send the EEG trigger diff --git a/Task/TaskVersions/ResearchUnit/commonConfetti/al_confettiLoop.m b/Task/TaskVersions/ResearchUnit/commonConfetti/al_confettiLoop.m index ef488f8..4243ab0 100644 --- a/Task/TaskVersions/ResearchUnit/commonConfetti/al_confettiLoop.m +++ b/Task/TaskVersions/ResearchUnit/commonConfetti/al_confettiLoop.m @@ -77,8 +77,9 @@ % Presenting trial number at the bottom of the eyetracker display - optional if taskParam.gParam.eyeTracker - Eyelink('command', 'record_status_message "TRIAL %d/%d"', i, trial); - Eyelink('message', 'TRIALID %d', i); +% Eyelink('command', 'record_status_message "TRIAL %d/%d"', i, trial); +% Eyelink('message', 'TRIALID %d', i); + taskParam.eyeTracker.el.sendMessage(sprintf('TRIALID %d', i)); end % Save constant variables on each trial @@ -432,9 +433,12 @@ if taskParam.gParam.eyeTracker && isequal(taskParam.trialflow.saveEtData, 'true') et_path = pwd; - et_file_name=[taskParam.eyeTracker.et_file_name, '.edf']; - al_saveEyelinkData(et_path, et_file_name) - Eyelink('StopRecording'); +% et_file_name=[taskParam.eyeTracker.et_file_name, '.edf']; +% al_saveEyelinkData(et_path, et_file_name) +% Eyelink('StopRecording'); + et_file_name=[taskParam.eyeTracker.et_file_name]; + al_saveEyelinkData(taskParam.eyeTracker.el, et_path, et_file_name) + taskParam.eyeTracker.el.stopRecording(); end % Save behavioral data diff --git a/Task/TaskVersions/ResearchUnit/commonConfetti/al_saveEyelinkData.m b/Task/TaskVersions/ResearchUnit/commonConfetti/al_saveEyelinkData.m index 06936a7..7184953 100644 --- a/Task/TaskVersions/ResearchUnit/commonConfetti/al_saveEyelinkData.m +++ b/Task/TaskVersions/ResearchUnit/commonConfetti/al_saveEyelinkData.m @@ -1,4 +1,4 @@ -function al_saveEyelinkData(et_path, et_file_name) +function al_saveEyelinkData(tracker_instance, et_path, et_file_name) %AL_SAVEEYELINKDATA This function saves the eye-tracking data % % Input @@ -7,17 +7,16 @@ function al_saveEyelinkData(et_path, et_file_name) % % Output % None - -fprintf('Saving EyeLink data to %s\n', et_path) + +% !! THIS IS THE EDITED SMI VERSION FOR JENA !! + +fprintf('Saving SMI data to %s\n', et_path) eyefilename = fullfile(et_path,et_file_name); -Eyelink('CloseFile'); -Eyelink('WaitForModeReady', 500); -try - status = Eyelink('ReceiveFile', et_file_name, eyefilename); - disp(['File ' eyefilename ' saved to disk']); -catch - warning(['File ' eyefilename ' not saved to disk']); -end -% Eyelink('StopRecording'); -end \ No newline at end of file +tracker_instance.stopRecording(); +tracker_instance.saveData(eyefilename); + + +% Eyelink('StopRecording'); + +end From 5ef185c4d7e44f032614cfb592864906b8889728 Mon Sep 17 00:00:00 2001 From: "J. L." Date: Wed, 9 Oct 2024 16:53:45 +0200 Subject: [PATCH 2/9] Added config folder for JenaSMI Version and changed one line in al_cannon to be compatible with Matlab2018a for our eyetracking machine --- Task/Classes/al_cannon.m | 3 +- .../JenaSMI/al_commonConfettiConfigJenaSMI.m | 69 +++ .../al_commonConfettiInstructionsJena.m | 530 ++++++++++++++++++ 3 files changed, 601 insertions(+), 1 deletion(-) create mode 100644 Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiConfigJenaSMI.m create mode 100644 Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiInstructionsJena.m diff --git a/Task/Classes/al_cannon.m b/Task/Classes/al_cannon.m index f9063b9..ec5b433 100644 --- a/Task/Classes/al_cannon.m +++ b/Task/Classes/al_cannon.m @@ -58,7 +58,8 @@ if self.defaultParticles == false self.dotCol = nan; else - self.dotCol = load('dotColDefault.mat').dotColDefault; +% self.dotCol = load('dotColDefault.mat').dotColDefault; + self.dotCol = getfield(load('dotColDefault.mat','dotColDefault'),'dotColDefault'); end end diff --git a/Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiConfigJenaSMI.m b/Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiConfigJenaSMI.m new file mode 100644 index 0000000..119164e --- /dev/null +++ b/Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiConfigJenaSMI.m @@ -0,0 +1,69 @@ +% Common Confetti Version Configuration Example +% +% Example of how to add local parameter settings as config input to the +% function that runs the task. +% +% It is recommended that you create your own script with the local +% parameter settings so that you can re-use your settings. + + +% Create config structure +config = struct(); + +% Add desired parameters +config.trialsExp = 100; %default for experiment is 200 for each noise condition +config.nBlocks = 2; %blocks per noise condition, i.e. 4 blocks +config.practTrialsVis = 10; +config.practTrialsHid = 20; +config.cannonPractCriterion = 4; % criterion cannon practice +config.cannonPractNumOutcomes = 5; % number of trials cannon practice +config.cannonPractFailCrit = 3; +config.passiveViewing = false; +config.passiveViewingPractTrials = 10; +config.baselineFixLength = 0.25; +config.blockIndices = [1 999 999 999]; % we don't have breaks within each block +config.runIntro = true; % false; +config.baselineArousal = true; % true; +config.language = 'German'; % 'English'; +config.sentenceLength = 80; +config.textSize = 32; +config.vSpacing = 1; +config.headerSize = 50; +config.screenSize = [0 0 1680 1050]*1; % get(0,'MonitorPositions')*1.0; +config.screenNumber = 1; +config.s = 83; +config.enter = 13; +config.five = 15; +config.defaultParticles = true; +config.debug = false; +config.showConfettiThreshold = false; +config.printTiming = true; +config.hidePtbCursor = true; +config.dataDirectory = 'C://Users//Matlab-User//Documents//AdaptiveLearning//DataDirectory'; +config.meg = false; +config.scanner = false; +config.eyeTracker = true; %true; +config.onlineSaccades = false; +config.saccThres = 1; +config.useDegreesVisualAngle = true; +config.distance2screen = 740; %700; % defined in mm (for degrees visual angle) and eT +config.screenWidthInMM = 580; % for degrees visual angle and ET +config.screenHeightInMM = 295; %210; % for ET +config.sendTrigger = false; +config.sampleRate = 500; % Sampling rate for EEG +config.port = hex2dec('E050'); +config.rotationRadPixel = 140; % 170 +config.rotationRadDeg = 3.16; % 2.5 +config.customInstructions = true; +config.instructionText = al_commonConfettiInstructionsJena(config.language); +config.noPtbWarnings = false; +config.predSpotCircleTolerance = 2; + +if config.sendTrigger + [config.session, ~] = IOPort( 'OpenSerialPort', 'COM3' ); +else + config.session = nan; +end + +% Run task with config input +RunCommonConfettiVersion(config); \ No newline at end of file diff --git a/Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiInstructionsJena.m b/Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiInstructionsJena.m new file mode 100644 index 0000000..315adc7 --- /dev/null +++ b/Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiInstructionsJena.m @@ -0,0 +1,530 @@ +classdef al_commonConfettiInstructionsJena + %AL_COMMONCONFETTIINSTRUCTIONSDEFAULTTEXT This class-definition file + % specifiec the properties of the instruction text. + % + % The advantage of this kind of text file is that most text is in one + % place and a local file can replace (specified in the config file) the + % default file so that local differences are not tracked on GitHub. + + properties + + language + welcomeText + introduceCannon + introduceConfetti + introduceSpot + introduceShield + introduceMiss + introduceMissBucket + introducePracticeSession + firstPracticeHeader + firstPractice + reduceShieldHeader + reduceShield + secondPracticeHeader + secondPractice + thirdPracticeHeader + thirdPractice + fourthPracticeHeader + fourthPractice + startTaskHeader + startTask + noCatchHeader + noCatch + accidentalCatchHeader + accidentalCatch + showCannonText + addCannonText + cannonFeedbackText + practiceBlockFailHeader + practiceBlockFail + cannonPracticeFail + firstPupilBaselineHeader + firstPupilBaseline + secondPupilBaselineHeader + secondPupilBaseline + introduceLowNoiseHeader + introduceLowNoise + introduceHighNoiseHeader + introduceHighNoise + dynamicFeedbackTxt + dynamicFeedbackHeader + introducePassiveViewingHeader + introducePassiveViewing + dynamicBlockTxt + + end + + methods + + function self = al_commonConfettiInstructionsJena(language) + % This function creates an object of + % class al_commonConfettiInstructionsDefaultText + % + % Input + % language: Optional parameter specifying language (default German) + % + % Output + % None + + + % Check if language parameter is provided + if ~exist('language', 'var') || isempty(language) + self.language = 'German'; + else + self.language = language; + end + + % First message when starting task + if isequal(self.language, 'German') + self.welcomeText = 'Herzlich Willkommen zur Konfetti-Kanonen-Aufgabe!'; + elseif isequal(self.language, 'English') + self.welcomeText = 'Welcome to the confetti-cannon task!'; + else + error('language parameter unknown') + end + + % Introduce cannon + if isequal(self.language, 'German') + self.introduceCannon = ['Du blicktst von oben auf eine Konfetti-Kanone, die in der Mitte eines Kreises ist. Deine Aufgabe ist es, das Konfetti mit einem Eimer zu fangen. Mit dem rosafarbenen '... + 'Punkt kannst du anklicken, wo auf dem Kreis du deinen Eimer platzieren möchtest, um das Konfetti zu fangen. Du kannst den Punkt mit der '... + 'Maus steuern.']; + elseif isequal(self.language, 'English') + self.introduceCannon = ['You are looking from above at a confetti cannon placed in the center of a circle. Your task is to catch the confetti with a bucket. Use the pink dot '... + 'to indicate where you would like to place your bucket to catch the confetti. '... + 'You can move the pink dot using the mouse.']; + else + error('language parameter unknown') + end + + % Introduce confetti + if isequal(self.language, 'German') + self.introduceConfetti = 'Das Ziel der Konfetti-Kanone wird mit der schwarzen Linie angezeigt. Steuere den rosafarbenen Punkt auf den Kreis und drücke die linke Maustaste, damit die Konfetti-Kanone schießt.'; + elseif isequal(self.language, 'English') + self.introduceConfetti = 'The aim of the cannon is indicated by the black line. Hit the left mouse button to fire the cannon.'; + else + error('language parameter unknown') + end + + % Introduce spot + if isequal(self.language, 'German') + self.introduceSpot = ['Der schwarze Strich zeigt dir die mittlere Position der letzten Konfettiwolke. Der rosafarbene Strich zeigt dir die '... + 'letzte Position deines Eimers. Steuere den rosafarbenen Punkt jetzt bitte auf das Ziel der Konfetti-Kanone und drücke die linke Maustaste.']; + elseif isequal(self.language, 'English') + self.introduceSpot = ['The black line shows the central position of the last confetti burst. The pink line shows the '... + 'last position of your bucket. Now move the pink dot to the aim of the confetti cannon and press the left mouse button.']; + else + error('language parameter unknown') + end + + % Introduce shield + if isequal(self.language, 'German') + self.introduceShield = 'Nach dem Kanonenschuss siehst du den Eimer. Wenn du mindestens die Hälfte des Konfettis im Eimer fangst, zählt es als Treffer und du erhältst einen Punkt.'; + elseif isequal(self.language, 'English') + self.introduceShield = ['After the cannon is shot you will see the bucket. '... + 'If you catch at least half of the confetti with the bucket, it is considered a "catch" and you get a point. ']; + else + error('language parameter unknown') + end + + % Introduce miss + if isequal(self.language, 'German') + self.introduceMiss = 'Versuche nun, deinen Eimer so zu plazieren, dass du das Konfetti NICHT fängst. Drücke dann die linke Maustaste.'; + elseif isequal(self.language, 'English') + self.introduceMiss = ['Now try to place the bucket so that you miss the confetti. Then press '... + 'the left mouse button. ']; + elseneedle + error('language parameter unknown') + end + + % Introduce miss with bucket + if isequal(self.language, 'German') + self.introduceMissBucket = 'In diesem Fall hast du Konfetti verfehlt.'; + elseif isequal(self.language, 'English') + self.introduceMissBucket = 'In this case you missed the confetti.'; + else + error('language parameter unknown') + end + + % Introduce practice session + if isequal(self.language, 'German') + self.introducePracticeSession = 'Im Folgenden machst du ein paar Übungsdurchgänge\nund im Anschluss zwei Durchgänge des Experiments.'; + elseif isequal(self.language, 'English') + self.introducePracticeSession = 'In the following, you will go through a few practice runs\nand then two blocks of the experiment.'; + else + error('language parameter unknown') + end + + % First practice header + if isequal(self.language, 'German') + self.firstPracticeHeader = 'Erster Übungsdurchgang'; + elseif isequal(self.language, 'English') + self.firstPracticeHeader = 'First Practice Run'; + else + error('language parameter unknown') + end + + % First practice + if isequal(self.language, 'German') + self.firstPractice = ['In diesem Durchgang ist die Konfetti-Kanone schon sehr alt und die Schüsse sind daher ziemlich ungenau. Das heißt, auch wenn '... + 'du den Eimer genau auf das Ziel der Konfetti-Kanone plazierst, kannst du das Konfetti verfehlen. Die Ungenauigkeit ist zufällig. '... + 'Dennoch fangst du am meisten Konfetti, wenn du den rosafarbenen Punkt genau auf die Stelle '... + 'steuerst, auf die die Konfetti-Kanone zielt.\n\nIn dieser Übung sollst du mit der Ungenauigkeit '... + 'der Konfetti-Kanone erst mal vertraut werden. Steuere Sie den rosafarbenen Punkt bitte immer auf die anvisierte '... + 'Stelle. \n\nAchte bitte auf Augenbewegungen und Blinzeln wie von der Versuchsleitung erklärt.']; + elseif isequal(self.language, 'English') + self.firstPractice = ['In this block, the confetti cannon is very old '... + 'and its aim therefore pretty inaccurate. Even if you move the bucket to the exact aim of the confetti cannon, '... + 'you might miss the confetti. This inaccuracy is random. '... + 'Still, your best strategy is to place the '... + 'bucket in the location where the cannon is '... + 'aimed.\n\nThe purpose of this practice session is to familiarize yourself with the inaccuracy '... + 'of the confetti cannon. Please always aim the pink dot at the '... + 'aim of the cannon.']; + else + error('language parameter unknown') + end + + % Reduced shield header + if isequal(self.language, 'German') + self.reduceShieldHeader = 'Illustration Ihres Eimers'; + elseif isequal(self.language, 'English') + self.reduceShieldHeader = 'Demonstration of your bucket'; + else + error('language parameter unknown') + end + + % Reduced + if isequal(self.language, 'German') + self.reduceShield = ['Ab jetzt sehst du den Eimer nur noch als zwei Striche. Außerdem siehst du die Aufgabe in weniger Farben. ' ... + 'Dies ist notwendig, damit wir deine Pupillengröße gut messen können. Achte daher bitte besonders darauf, '... + 'möglichst auf den Punkt in der Mitte des Kreises zu schauen. Bitte versuche Augenbewegungen und blinzeln '... + 'so gut es geht zu vermeiden.\n\n'... + 'Jetzt folgt zunächst eine kurze Demonstration, wie der Eimer mit Strichen im Vergleich zum Eimer der vorherigen Übung aussieht.']; + elseif isequal(self.language, 'English') + self.reduceShield = 'Please update if you plan to use this.'; + else + error('language parameter unknown') + end + + % Second practice header + if isequal(self.language, 'German') + self.secondPracticeHeader = 'Zweiter Übungsdurchgang'; + elseif isequal(self.language, 'English') + self.secondPracticeHeader = 'Second Practice Run'; + else + error('language parameter unknown') + end + + % Second practice + if isequal(self.language, 'German') + self.secondPractice = ['Um sicherzugehen, dass du die Aufgabe verstanden hast, machen wir jetzt eine kurze Übung:\n\n'... + 'Du wirst hintereinander fünf Schüsse der Konfetti-Kanone sehen. Danach gibst du bitte an, wo du das Ziel der Konfetti-Kanone vermutest.\n\n'... + 'Die beste Strategie ist, die mittlere Position der Schüsse anzugeben. Diese Position ist die beste Vohersage, um in der Aufgabe am meisten Konfetti zu fangen.']; + elseif isequal(self.language, 'English') + self.secondPractice = ['Add instructions please']; % update few things if planning to use this + else + error('language parameter unknown') + end + + % Third practice header + if isequal(self.language, 'German') + self.thirdPracticeHeader = 'Dritter Übungsdurchgang'; + elseif isequal(self.language, 'English') + self.thirdPracticeHeader = 'Third Practice Run'; + else + error('language parameter unknown') + end + + % Third practice + if isequal(self.language, 'German') + self.thirdPractice = ['In dieser Übung siehst du nur noch einen Schuss der Konfetti-Kanone. '... + 'Bitte gib wieder an, wo du die Konfetti-Kanone vermutest.\n\nBitte beachte, dass das Ziel der Kanone meistens gleich bleibt. Manchmal richtet sich die Kanone allerdings neu aus. '... + 'Wenn du denkst, dass die Konfetti-Kanone ihre Richtung geändert hat, solltest du auch den Eimer '... + 'dorthin bewegen.\n\nBeachte, dass du das Konfetti trotz guter Vorhersagen auch häufig nicht fangen kannst.']; + elseif isequal(self.language, 'English') + self.thirdPractice = ['Add instructions please']; % update few things if planning to use this + else + error('language parameter unknown') + end + + % Fourth practice header + if isequal(self.language, 'German') + self.fourthPracticeHeader = 'Vierter Übungsdurchgang'; + elseif isequal(self.language, 'English') + self.fourthPracticeHeader = 'Fourth Practice Run'; + else + error('language parameter unknown') + end + + % Fourth practice + if isequal(self.language, 'German') + self.fourthPractice = ['Jetzt kommen wir zur letzten Übung.\n\nDiesmal musst du mit dem rosafarbenen Punkt deinen Schild platzieren und siehst dabei die Kanone nicht mehr. Außerdem wirst du es sowohl mit einer relativ genauen '... + 'als auch einer eher ungenauen versteckten Konfetti-Kanone zu tun haben.\n\n'... + 'Achte bitte auf Augenbewegungen und Blinzeln wie von der Versuchsleitung erklärt.'... + '\n\nBeachte bitte auch, dass das Ziel der Konfetti-Kanone in manchen Fällen sichtbar sein wird. In diesen Fällen ist die beste Strategie, zum Ziel der Kanone zu gehen.']; + elseif isequal(self.language, 'English') + self.fourthPractice = ['Add instructions please']; % update few things if planning to use this + else + error('language parameter unknown') + end + + % Start task header + if isequal(self.language, 'German') + self.startTaskHeader = 'Start des Experiments'; + elseif isequal(self.language, 'English') + self.startTaskHeader = 'Beginning of the Experiment'; + else + error('language parameter unknown') + end + + % Start task + if isequal(self.language, 'German') + self.startTask = ['Toll, du hast die Übungen geschafft! Kurz zusammengefasst fängst du also das meiste Konfetti, '... + 'wenn du den Eimer (rosafarbener Punkt) auf die Stelle bewegst, auf die die Konfetti-Kanone zielt. Weil du die Konfetti-Kanone meistens nicht mehr '... + 'sehen kannst, musst du diese Stelle aufgrund der Position der letzten Konfettiwolken einschätzen. Beachte, dass du das Konfetti trotz '... + 'guter Vorhersagen auch oft nicht fangen kannst. \n\nIn wenigen Fällen wirst du die Konfetti-Kanone zu sehen bekommen und kannst deine Leistung '... + 'verbessern, indem du den Eimer genau auf das Ziel steuerst.\n\n'... + 'Achte bitte auf Augenbewegungen und Blinzeln wie von der Versuchsleitung erklärt.\n\nViel Erfolg!']; + elseif isequal(self.language, 'English') + self.startTask = ['You have completed the practice phase. To summarize, you catch the most confetti, '... + 'when you move the bucket (pink dot) to the aim of the confetti cannon. Because you can usually no longer see the cannon, '... + 'you will have to estimate the aim based on the last confetti bursts. Please note that despite '... + 'good predictions, you often wont be able to catch it. \n\nIn a few cases, you will see the confetti cannon and can improve your performance '... + 'by moving the bucket to its aim.\n\n'... + 'Please avoid eye movements and blinking during a trial. If the dot in the middle is light grey at the end of a trial, you may blink.\n\nGood luck!']; else + error('language parameter unknown') + end + + % No catch header + if isequal(self.language, 'German') + self.noCatchHeader = 'Leider nicht gefangen!'; + elseif isequal(self.language, 'English') + self.noCatchHeader = 'Unfortunately no catch!'; + else + error('language parameter unknown') + end + + % No catch + if isequal(self.language, 'German') + self.noCatch = 'Du hast leider zu wenig Konfetti gefangen. Versuche es noch mal!'; + elseif isequal(self.language, 'English') + self.noCatch = 'Unfortunately, you did not catch enough confetti. Try again!'; + else + error('language parameter unknown') + end + + % Accidental catch header + if isequal(self.language, 'German') + self.accidentalCatchHeader = 'Leider gefangen!'; + elseif isequal(self.language, 'English') + self.accidentalCatchHeader = 'A catch, unfortunately!'; + else + error('language parameter unknown') + end + + % Accidental catch + if isequal(self.language, 'German') + self.accidentalCatch = 'Du hast zu viel Konfetti gefangen. Versuche bitte, das Konfetti zu verfehlen!'; + elseif isequal(self.language, 'English') + self.accidentalCatch = 'You have caught too much confetti. Please try to miss the confetti!'; + else + error('language parameter unknown') + end + + % Show cannon + if isequal(self.language, 'German') + self.showCannonText = 'Bitte gib an, wo du die Kanone vermutest.'; + elseif isequal(self.language, 'English') + self.showCannonText = 'Please add instructions'; + else + error('language parameter unknown') + end + + % Additional show cannon text + if isequal(self.language, 'German') + self.addCannonText = ['\n\nDie grauen Striche zeigen wo das Konfetti als letztes gelandet ist.\n'... + 'Mit der Maus kannst du angeben, wo du die Kanone vermutest.']; + elseif isequal(self.language, 'English') + self.addCannonText = 'Please add instructions'; + else + error('language parameter unknown') + end + + % Cannon feedback text + if isequal(self.language, 'German') + self.cannonFeedbackText = '\n\nHier kannst du deine Angabe und die echte Konfetti-Kanone vergleichen.'; + elseif isequal(self.language, 'English') + self.cannonFeedbackText = 'Please add instructions'; + else + error('language parameter unknown') + end + + % Practice block fail header + if isequal(self.language, 'German') + self.practiceBlockFailHeader = 'Bitte noch mal probieren!'; + elseif isequal(self.language, 'English') + self.practiceBlockFailHeader = 'Please try again!'; + else + error('language parameter unknown') + end + + % Practice block fail + if isequal(self.language, 'German') + self.practiceBlockFail = ['Du hast deinen Eimer oft neben dem Ziel der Kanone platziert. Versuche im nächsten '... + 'Durchgang bitte, den Eimer direkt auf das Ziel zu steuern. Das Ziel wird durch die schwarzen Linie angezeigt.']; + elseif isequal(self.language, 'English') + self.practiceBlockFail = ['You have often placed your bucket next to the aim of the cannon. In the next '... + 'phase, please try to aim the bucket directly at the target. The aim is indicated by the black line.']; + else + error('language parameter unknown') + end + + % Practice block fail + if isequal(self.language, 'German') + self.cannonPracticeFail = ['Du hast die Konfetti-Kanone nicht genau genug eingeschätzt. Versuche im nächsten '... + 'Durchgang bitte, den Mittelpunkt der einzelnen Schüsse auszuwählen. Wenn du Fragen hast, wende dich an die Versuchsleitung.']; + elseif isequal(self.language, 'English') + self.cannonPracticeFail = ['Please add instructions']; + else + error('language parameter unknown') + end + + % First pupil baseline + if isequal(self.language, 'German') + self.firstPupilBaselineHeader = 'Erste Pupillenmessung'; + self.firstPupilBaseline = ['Du wirst jetzt für drei Minuten verschiedene Farben auf dem Bildschirm sehen. '... + 'Bitte fixiere deinen Blick währenddessen auf den kleinen Punkt in der Mitte des Bildschirms.']; + elseif isequal(self.language, 'English') + self.firstPupilBaselineHeader = 'First Pupil Assessment'; + self.firstPupilBaseline = ['Include correct instructions here']; + else + error('language parameter unknown') + end + + % Second pupil baseline + if isequal(self.language, 'German') + self.secondPupilBaselineHeader = 'Zweite Pupillenmessung'; + self.secondPupilBaseline = ['Du wirst jetzt noch mal für drei Minuten verschiedene Farben auf dem Bildschirm sehen. '... + 'Bitte fixiere deinen Blick währenddessen auf den kleinen Punkt in der Mitte des Bildschirms.']; + elseif isequal(self.language, 'English') + self.secondPupilBaselineHeader = 'Second Pupil Assessment'; + self.secondPupilBaseline = ['Include correct instructions here']; + else + error('language parameter unknown') + end + + % Indicate low noise + if isequal(self.language, 'German') + self.introduceLowNoiseHeader = 'Genauere Konfetti-Kanone'; + self.introduceLowNoise = ['Im folgenden Block wird die Konfetti-Kanone relativ genau sein.\n\n'... + 'Die Größe des Eimers kann sich von Durchgang '... + 'zu Durchgang ändern. Diese Veränderung kannst du nicht beeinflussen '... + 'und auch nicht vorhersagen. Daher ist es immer die beste Strategie, '... + 'den Eimer genau dorthin zu stellen, wo du das Ziel der Konfetti-Kanone vermutest.\n\n'... + 'Zur Erinnerung: Der rosafarbene Strich zeigt deine letzte Vorhersage. Der schwarze '... + 'Strich zeigt die Position der letzten Konfetti-Wolke.\n\n'... + 'Achte bitte auf Augenbewegungen und Blinzeln wie von der Versuchsleitung erklärt.']; + elseif isequal(self.language, 'English') + self.introduceLowNoiseHeader = 'More accurate confetti cannon'; + self.introduceLowNoise = ['In the following block, the confetti cannon will be relatively accurate.\n\n'... + 'The size of the bucket can change from trial '... + 'to trial. You cannot influence this change '... + 'nor can you predict it. Therefore, the best strategy is always to '... + 'place the bucket exactly where you think the confetti cannon will be aimed.']; + else + error('language parameter unknown') + end + + % Indicate high noise + if isequal(self.language, 'German') + self.introduceHighNoiseHeader = 'Ungenauere Konfetti-Kanone'; + self.introduceHighNoise = ['Im folgenden Block wird die Konfetti-Kanone relativ ungenau sein.\n\n'... + 'Die Größe des Eimers kann sich von Durchgang '... + 'zu Durchgang ändern. Diese Veränderung kannst du nicht beeinflussen '... + 'und auch nicht vorhersagen. Daher ist es immer die beste Strategie, '... + 'den Eimer genau dorthin zu stellen, wo du das Ziel der Konfetti-Kanone vermutest.\n\n'... + 'Zur Erinnerung: Der rosafarbene Strich zeigt deine letzte Vorhersage. Der schwarze '... + 'Strich zeigt die Position der letzten Konfetti-Wolke.\n\n'... + 'Achte bitte auf Augenbewegungen und Blinzeln wie von der Versuchsleitung erklärt.']; + elseif isequal(self.language, 'English') + self.introduceHighNoiseHeader = 'Less accurate confetti cannon'; + self.introduceHighNoise = ['In the following block, the confetti cannon will be relatively inaccurate.\n\n'... + 'The size of the bucket can change from trial '... + 'to trial. You cannot influence this change '... + 'nor can you predict it. Therefore, the best strategy is always to '... + 'place the bucket exactly where you think the confetti cannon will be aimed.']; + else + error('language parameter unknown') + end + + self.introducePassiveViewingHeader = 'Beobachtungsaufgabe'; + self.introducePassiveViewing = ['Versuche bitte in dieser Aufgabe die Mitte '... + 'des Bildschirms zu fixieren. Es ist wichtig, dass du deine Augen nicht bewegst!\n\n'... + 'Versuche nur zu blinzeln, wenn der weiße Punkt erscheint.']; + + end + + + function self = giveFeedback(self, currPoints, type) + %GIVEFEEDBACK This function displays feedback after a block or + %the task + % + % Input + % self: Instructions-text-object instance + % currPoints: Number of points + % type: single-block vs. whole task feedback + % + % Output + % self: Instructions-text-object instance + + + if isequal(type, 'block') + if isequal(self.language, 'German') + self.dynamicFeedbackTxt = sprintf('In diesem Block hast du %.0f Punkte verdient.', currPoints); + elseif isequal(self.language, 'English') + self.dynamicFeedbackTxt = sprintf('You have earned %.0f points in this block.', currPoints); + else + error('language parameter unknown') + end + + elseif isequal(type, 'task') + if isequal(self.language, 'German') + self.dynamicFeedbackHeader = 'Ende des Versuchs!'; + self.dynamicFeedbackTxt = sprintf('Vielen Dank für deine Teilnahme!\n\n\nDu hast insgesamt %i Punkte gewonnen!', currPoints); + elseif isequal(self.language, 'English') + self.dynamicFeedbackHeader = 'End of the Experiment!'; + self.dynamicFeedbackTxt = sprintf('Thank you for taking part!\n\n\nYou have won a total of %i points!', currPoints); + else + error('language parameter unknown') + end + else + error('type parameter unknown') + end + end + + + function self = giveBlockFeedback(self, nBlocks, half, currBlock) + %GIVEBLOCKFEEDBACK This function displays how many blocks have + %been completed so far + % + % Input + % self: Instructions-text-object instance + % nBlocks: Number of blocks + % half: Number of points + % currBlock: Indicates if we are in passive-viewing condition + % Output + % self: Instructions-text-object instance + + + if half == 1 + self.dynamicBlockTxt = sprintf('Kurze Pause!\n\nDu hast bereits %i von insgesamt %i Durchgängen geschafft.', currBlock, nBlocks*2); + elseif half == 2 + self.dynamicBlockTxt = sprintf('Kurze Pause!\n\nDu hast bereits %i von insgesamt %i Durchgängen geschafft.', currBlock+nBlocks, nBlocks*2); + else + error('half parameter undefined') + end + end + + end +end \ No newline at end of file From f32ecabc7a40252f725d8efcce521bd874ac81ca Mon Sep 17 00:00:00 2001 From: "J. L." Date: Thu, 10 Oct 2024 12:28:32 +0200 Subject: [PATCH 3/9] added config parameter to change between eyelink and SMI version. still needs testing --- Task/Classes/al_eyeTracker.m | 155 ++++++++++-------- Task/Functions/al_baselineArousal.m | 27 ++- Task/Functions/al_sendTrigger.m | 7 +- .../JenaSMI/al_commonConfettiConfigJenaSMI.m | 1 + .../ResearchUnit/JenaSMI/al_saveSMIData.m | 17 ++ .../commonConfetti/RunCommonConfettiVersion.m | 3 + .../al_commonConfettiConfigExample.m | 1 + .../commonConfetti/al_confettiLoop.m | 28 +++- .../commonConfetti/al_saveEyelinkData.m | 25 +-- 9 files changed, 161 insertions(+), 103 deletions(-) create mode 100644 Task/TaskVersions/ResearchUnit/JenaSMI/al_saveSMIData.m diff --git a/Task/Classes/al_eyeTracker.m b/Task/Classes/al_eyeTracker.m index efa9a70..96d9f63 100644 --- a/Task/Classes/al_eyeTracker.m +++ b/Task/Classes/al_eyeTracker.m @@ -18,7 +18,7 @@ ppd % estimated pixels per degree resolutionX % x resolution (in pixels) saccThres % threshold value - el %SMI instance + el %eyetracker instance end @@ -46,10 +46,9 @@ self.saccThres = 1; end - function self = initializeEyeLink(self, taskParam, et_file_name_suffix) +function self = initializeEyeLink(self, taskParam, et_file_name_suffix) % INITIALIZEEYELINK This function initialzes the eye-tracker - % !! NOW ACTUALLY INITIALIZES SMI NOT EYELINK !! name kept for - % easier implementation. + % % Input % taskParam: Task-parameter-object instance % et_file_name_suffix: Suffix of file name @@ -57,45 +56,51 @@ % Output: % el: Eye-link object - self.et_file_name = sprintf('%s%s', taskParam.subject.ID, et_file_name_suffix); self.et_file_name = [self.et_file_name]; % todo: check if this is really necessary - - - settings = SMITE.getDefaults('HiSpeed'); - settings.connectInfo = {'192.168.1.1',4444,'192.168.1.2',5555}; - settings.doAverageEyes = false; - settings.cal.bgColor = taskParam.colors.background; - settings.freq = 500; -% settings.trackMode = 'MONOCULAR'; - settings.trackEye = 'EYE_RIGHT'; - settings.logFileName = 'test_log.txt'; - settings.save.allowFileTransfer = false; - - % Todo test if we can also pass object instead instead of new structure -% settings.dist = self.dist; -% settings.width = self.width; -% settings.height = self.height; -% settings.window_rect = taskParam.display.windowRect; -% settings.frameDur = self.frameDur; -% settings.frameRate = self.frameRate; - - % initialize SMI - self.el = SMITE(settings); - %EThndl = EThndl.setDummyMode(); - self.el.init(); - self.el.calibrate(taskParam.display.window.onScreen, false); - - - %[el, ~] = ELconfig(taskParam.display.window.onScreen, self.et_file_name, settings); - - % Calibrate the eye tracker - % EyelinkDoTrackerSetup(el); - - + if isequal(taskParam.gparam.trackerVersion, 'eyelink') + % Todo test if we can also pass object instead instead of new structure + options.dist = self.dist; + options.width = self.width; + options.height = self.height; + options.window_rect = taskParam.display.windowRect; + options.frameDur = self.frameDur; + options.frameRate = self.frameRate; + [el, ~] = ELconfig(taskParam.display.window.onScreen, self.et_file_name, options); + + % Calibrate the eye tracker + EyelinkDoTrackerSetup(el); + + elseif isequal(taskParam.gparam.trackerVersion, 'SMI') && ~exists(taskParam.eyeTracker.el) %todo see if this really checks if SMI was already initialized + if ~exists(taskParam.eyeTracker.el) %todo see if this really checks if SMI was already initialized + settings = SMITE.getDefaults('HiSpeed'); + settings.connectInfo = {'192.168.1.1',4444,'192.168.1.2',5555}; + settings.doAverageEyes = false; + settings.cal.bgColor = taskParam.colors.background; + settings.freq = 500; + % settings.trackMode = 'MONOCULAR'; + settings.trackEye = 'EYE_RIGHT'; + settings.logFileName = 'test_log.txt'; + settings.save.allowFileTransfer = false; + + % initialize SMI + self.el = SMITE(settings); + %EThndl = EThndl.setDummyMode(); + self.el.init(); + self.el.calibrate(taskParam.display.window.onScreen, false); + elseif exists(taskParam.eyeTracker.el) && taskParam.eyeTracker.el.isInitialized == true + self.el.calibrate(taskParam.display.window.onScreen, false); + else + error('failed to initialize or calibrate SMI') + end + + else + error('Please specifiy tracker version as eyelink or SMI') + end end + function self = estimatePixelsPerDegree(self) % ESTIMATEPIXELSPERDEGREE This function estimates the number of % pixels per degree for online saccade detection @@ -126,30 +131,34 @@ % % Credit: Donner lab - error('this is the SMI version, no saccades implemented for now.') + if isequal(taskParam.gparam.trackerVersion, 'eyelink') - % Short break - pause(0.002) + % Short break + pause(0.002) + + % Extract samples from eye-link + [samples, ~, ~] = Eyelink('GetQueuedData'); + + % Extract relevant samples depending on tracked eye + if eye==0 + x = (samples(14,:)-zero(1))/self.ppd; + y = (samples(16,:)-zero(2))/self.ppd; + else + x = (samples(15,:)-zero(1))/self.ppd; + y = (samples(17,:)-zero(2))/self.ppd; + end + + % Compute deviation from fixation spot and categorize saccades + d = (x.^2 + y.^2).^.5; + a = d(2:length(d)); + if any(a>self.saccThres) + sacc = 1; + else + sacc = 0; + end - % Extract samples from eye-link - [samples, ~, ~] = Eyelink('GetQueuedData'); - - % Extract relevant samples depending on tracked eye - if eye==0 - x = (samples(14,:)-zero(1))/self.ppd; - y = (samples(16,:)-zero(2))/self.ppd; else - x = (samples(15,:)-zero(1))/self.ppd; - y = (samples(17,:)-zero(2))/self.ppd; - end - - % Compute deviation from fixation spot and categorize saccades - d = (x.^2 + y.^2).^.5; - a = d(2:length(d)); - if any(a>self.saccThres) - sacc = 1; - else - sacc = 0; + error('online saccades only implemented for eyelink') end end end @@ -164,19 +173,23 @@ % % Output % taskParam: Task-parameter-object instance + if isequal(taskParam.gparam.trackerVersion, 'eyelink') + Eyelink('StartRecording'); + WaitSecs(0.1); + Eyelink('message', 'Start recording Eyelink'); + + % Reference time stamp + taskParam.timingParam.ref = GetSecs(); + + elseif isequal(taskParam.gparam.trackerVersion, 'SMI') + taskParam.eyeTracker.el.startRecording() + WaitSecs(0.1); + taskParam.eyeTracker.el.sendMessage('Start recording SMI'); - - % Eyelink('StartRecording'); - % WaitSecs(0.1); - % Eyelink('message', 'Start recording Eyelink'); - - taskParam.eyeTracker.el.startRecording() -% taskParam.eyeTracker.el.startBuffer() - WaitSecs(0.1); - taskParam.eyeTracker.el.sendMessage('Start recording SMI'); - - % Reference time stamp - taskParam.timingParam.ref = GetSecs(); + % Reference time stamp + taskParam.timingParam.ref = GetSecs(); + end end + end end diff --git a/Task/Functions/al_baselineArousal.m b/Task/Functions/al_baselineArousal.m index 12bb6ab..5dc7935 100644 --- a/Task/Functions/al_baselineArousal.m +++ b/Task/Functions/al_baselineArousal.m @@ -33,9 +33,12 @@ function al_baselineArousal(taskParam, file_name_suffix) % Presenting trial number at the bottom of the eyetracker display - optional if taskParam.gParam.eyeTracker && isequal(taskParam.trialflow.exp, 'exp') -% Eyelink('command', 'record_status_message "TRIAL %d/%d"', i, 3); -% Eyelink('message', 'TRIALID %d', i); - taskParam.eyeTracker.el.sendMessage(sprintf('TRIALID BaseAr %d', i)); + if isequal(taskParam.gparam.trackerVersion, 'eyelink') + Eyelink('command', 'record_status_message "TRIAL %d/%d"', i, 3); + Eyelink('message', 'TRIALID %d', i); + elseif isequal(taskParam.gparam.trackerVersion, 'SMI') + taskParam.eyeTracker.el.sendMessage(sprintf('TRIALID BaseAr %d', i)); + end end % Only send trigger on first interation @@ -72,12 +75,18 @@ function al_baselineArousal(taskParam, file_name_suffix) % ----------------- if taskParam.gParam.eyeTracker% && isequal(taskParam.trialflow.saveEtData, 'true') - et_path = pwd; - et_file_name=[taskParam.eyeTracker.et_file_name, '.edf']; - al_saveEyelinkData(taskParam.eyeTracker.el, et_path, et_file_name) - taskParam.eyeTracker.el.stopRecording(); -% al_saveEyelinkData(et_path, et_file_name) -% Eyelink('StopRecording'); + if isequal(taskParam.gparam.trackerVersion, 'eyelink') + et_path = pwd; + et_file_name=[taskParam.eyeTracker.et_file_name, '.edf']; + al_saveEyelinkData(et_path, et_file_name) + Eyelink('StopRecording'); + + elseif isequal(taskParam.gparam.trackerVersion, 'SMI') + et_path = pwd; + et_file_name=[taskParam.eyeTracker.et_file_name, '.edf']; + al_saveEyelinkData(et_path, et_file_name) + taskParam.eyeTracker.el.stopRecording(); + end end % if taskParam.gParam.eyeTracker diff --git a/Task/Functions/al_sendTrigger.m b/Task/Functions/al_sendTrigger.m index d390208..bba7ea6 100644 --- a/Task/Functions/al_sendTrigger.m +++ b/Task/Functions/al_sendTrigger.m @@ -264,8 +264,11 @@ % Send the pupil trigger if taskParam.gParam.eyeTracker && isequal(taskParam.trialflow.exp, 'exp') -% Eyelink('message', num2str(triggerID)); - taskParam.eyeTracker.el.sendMessage(num2str(triggerID)); + if isequal(taskParam.gparam.trackerVersion, 'eyelink') + Eyelink('message', num2str(triggerID)); + elseif isequal(taskParam.gparam.trackerVersion, 'SMI') + taskParam.eyeTracker.el.sendMessage(num2str(triggerID)); + end end % Send the EEG trigger diff --git a/Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiConfigJenaSMI.m b/Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiConfigJenaSMI.m index 119164e..831a677 100644 --- a/Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiConfigJenaSMI.m +++ b/Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiConfigJenaSMI.m @@ -43,6 +43,7 @@ config.meg = false; config.scanner = false; config.eyeTracker = true; %true; +config.trackerVersion = 'SMI'; %set 'eyelink' or 'SMI' config.onlineSaccades = false; config.saccThres = 1; config.useDegreesVisualAngle = true; diff --git a/Task/TaskVersions/ResearchUnit/JenaSMI/al_saveSMIData.m b/Task/TaskVersions/ResearchUnit/JenaSMI/al_saveSMIData.m new file mode 100644 index 0000000..d5853d6 --- /dev/null +++ b/Task/TaskVersions/ResearchUnit/JenaSMI/al_saveSMIData.m @@ -0,0 +1,17 @@ +function al_saveSMIData(tracker_instance, et_path, et_file_name) +%AL_SAVEEYELINKDATA This function saves the eye-tracking data +% +% Input +% et_path: Data directory path +% et_file_name: File name +% +% Output +% None + +fprintf('Saving SMI data to %s\n', et_path) +eyefilename = fullfile(et_path,et_file_name); + +tracker_instance.stopRecording(); +tracker_instance.saveData(eyefilename); + +end diff --git a/Task/TaskVersions/ResearchUnit/commonConfetti/RunCommonConfettiVersion.m b/Task/TaskVersions/ResearchUnit/commonConfetti/RunCommonConfettiVersion.m index fd27314..5c47812 100644 --- a/Task/TaskVersions/ResearchUnit/commonConfetti/RunCommonConfettiVersion.m +++ b/Task/TaskVersions/ResearchUnit/commonConfetti/RunCommonConfettiVersion.m @@ -64,6 +64,7 @@ config.meg = false; config.scanner = false; config.eyeTracker = false; + config.trackerVersion = 'eyelink'; config.onlineSaccades = true; config.saccThres = 0.7; config.useDegreesVisualAngle = true; @@ -148,6 +149,7 @@ hidePtbCursor = config.hidePtbCursor; % hide cursor dataDirectory = config.dataDirectory; eyeTracker = config.eyeTracker; % doing eye-tracking? +trackerVersion = config.trackerVersion; % select whether eyelink or SMI onlineSaccades = config.onlineSaccades; % online saccades tracking? saccThresh = config.saccThres; useDegreesVisualAngle = config.useDegreesVisualAngle; % Define stimuli in degrees of visual angle @@ -291,6 +293,7 @@ gParam.dataDirectory = dataDirectory; gParam.meg = meg; gParam.eyeTracker = eyeTracker; +gParam.trackerVersion = trackerVersion; gParam.onlineSaccades = onlineSaccades; gParam.sendTrigger = sendTrigger; gParam.scanner = scanner; diff --git a/Task/TaskVersions/ResearchUnit/commonConfetti/al_commonConfettiConfigExample.m b/Task/TaskVersions/ResearchUnit/commonConfetti/al_commonConfettiConfigExample.m index 45e07ec..d7ecf8a 100644 --- a/Task/TaskVersions/ResearchUnit/commonConfetti/al_commonConfettiConfigExample.m +++ b/Task/TaskVersions/ResearchUnit/commonConfetti/al_commonConfettiConfigExample.m @@ -43,6 +43,7 @@ config.meg = false; config.scanner = false; config.eyeTracker = false; %true; +config.trackerVersion = 'eyelink'; %set 'eyelink' or 'SMI' config.onlineSaccades = true; config.saccThres = 1; config.useDegreesVisualAngle = true; diff --git a/Task/TaskVersions/ResearchUnit/commonConfetti/al_confettiLoop.m b/Task/TaskVersions/ResearchUnit/commonConfetti/al_confettiLoop.m index 4243ab0..c3fcb3a 100644 --- a/Task/TaskVersions/ResearchUnit/commonConfetti/al_confettiLoop.m +++ b/Task/TaskVersions/ResearchUnit/commonConfetti/al_confettiLoop.m @@ -77,9 +77,12 @@ % Presenting trial number at the bottom of the eyetracker display - optional if taskParam.gParam.eyeTracker -% Eyelink('command', 'record_status_message "TRIAL %d/%d"', i, trial); -% Eyelink('message', 'TRIALID %d', i); - taskParam.eyeTracker.el.sendMessage(sprintf('TRIALID %d', i)); + if isequal(taskParam.gparam.trackerVersion, 'eyelink') + Eyelink('command', 'record_status_message "TRIAL %d/%d"', i, trial); + Eyelink('message', 'TRIALID %d', i); + elseif isequal(taskParam.gparam.trackerVersion, 'SMI') + taskParam.eyeTracker.el.sendMessage(sprintf('TRIALID %d', i)); + end end % Save constant variables on each trial @@ -433,12 +436,19 @@ if taskParam.gParam.eyeTracker && isequal(taskParam.trialflow.saveEtData, 'true') et_path = pwd; -% et_file_name=[taskParam.eyeTracker.et_file_name, '.edf']; -% al_saveEyelinkData(et_path, et_file_name) -% Eyelink('StopRecording'); - et_file_name=[taskParam.eyeTracker.et_file_name]; - al_saveEyelinkData(taskParam.eyeTracker.el, et_path, et_file_name) - taskParam.eyeTracker.el.stopRecording(); + + if isequal(taskParam.gparam.trackerVersion, 'eyelink') + et_file_name=[taskParam.eyeTracker.et_file_name, '.edf']; + al_saveEyelinkData(et_path, et_file_name) + Eyelink('StopRecording'); + + elseif isequal(taskParam.gparam.trackerVersion, 'SMI') + et_file_name=[taskParam.eyeTracker.et_file_name]; + al_saveSMIData(taskParam.eyeTracker.el, et_path, et_file_name) + taskParam.eyeTracker.el.stopRecording(); + + end + end % Save behavioral data diff --git a/Task/TaskVersions/ResearchUnit/commonConfetti/al_saveEyelinkData.m b/Task/TaskVersions/ResearchUnit/commonConfetti/al_saveEyelinkData.m index 7184953..06936a7 100644 --- a/Task/TaskVersions/ResearchUnit/commonConfetti/al_saveEyelinkData.m +++ b/Task/TaskVersions/ResearchUnit/commonConfetti/al_saveEyelinkData.m @@ -1,4 +1,4 @@ -function al_saveEyelinkData(tracker_instance, et_path, et_file_name) +function al_saveEyelinkData(et_path, et_file_name) %AL_SAVEEYELINKDATA This function saves the eye-tracking data % % Input @@ -7,16 +7,17 @@ function al_saveEyelinkData(tracker_instance, et_path, et_file_name) % % Output % None - -% !! THIS IS THE EDITED SMI VERSION FOR JENA !! - -fprintf('Saving SMI data to %s\n', et_path) -eyefilename = fullfile(et_path,et_file_name); - -tracker_instance.stopRecording(); -tracker_instance.saveData(eyefilename); - -% Eyelink('StopRecording'); - +fprintf('Saving EyeLink data to %s\n', et_path) +eyefilename = fullfile(et_path,et_file_name); +Eyelink('CloseFile'); +Eyelink('WaitForModeReady', 500); +try + status = Eyelink('ReceiveFile', et_file_name, eyefilename); + disp(['File ' eyefilename ' saved to disk']); +catch + warning(['File ' eyefilename ' not saved to disk']); end +% Eyelink('StopRecording'); + +end \ No newline at end of file From 8f057fd81c75022cc3df91c837562bf5df0a6f3a Mon Sep 17 00:00:00 2001 From: "J. L." Date: Fri, 11 Oct 2024 15:37:57 +0200 Subject: [PATCH 4/9] small bugfixes --- Task/Classes/al_eyeTracker.m | 22 +++++++++---------- Task/Classes/al_gparam.m | 2 ++ Task/Functions/al_baselineArousal.m | 8 +++---- Task/Functions/al_sendTrigger.m | 4 ++-- .../JenaSMI/al_commonConfettiConfigJenaSMI.m | 2 +- .../commonConfetti/al_confettiLoop.m | 8 +++---- 6 files changed, 24 insertions(+), 22 deletions(-) diff --git a/Task/Classes/al_eyeTracker.m b/Task/Classes/al_eyeTracker.m index 96d9f63..f8d5f9f 100644 --- a/Task/Classes/al_eyeTracker.m +++ b/Task/Classes/al_eyeTracker.m @@ -59,7 +59,7 @@ self.et_file_name = sprintf('%s%s', taskParam.subject.ID, et_file_name_suffix); self.et_file_name = [self.et_file_name]; % todo: check if this is really necessary - if isequal(taskParam.gparam.trackerVersion, 'eyelink') + if isequal(taskParam.gParam.trackerVersion, 'eyelink') % Todo test if we can also pass object instead instead of new structure options.dist = self.dist; options.width = self.width; @@ -72,25 +72,25 @@ % Calibrate the eye tracker EyelinkDoTrackerSetup(el); - elseif isequal(taskParam.gparam.trackerVersion, 'SMI') && ~exists(taskParam.eyeTracker.el) %todo see if this really checks if SMI was already initialized - if ~exists(taskParam.eyeTracker.el) %todo see if this really checks if SMI was already initialized + elseif isequal(taskParam.gParam.trackerVersion, 'SMI') + if ~exist(taskParam.eyeTracker.el) %todo see if this really checks if SMI was already initialized settings = SMITE.getDefaults('HiSpeed'); settings.connectInfo = {'192.168.1.1',4444,'192.168.1.2',5555}; settings.doAverageEyes = false; settings.cal.bgColor = taskParam.colors.background; settings.freq = 500; - % settings.trackMode = 'MONOCULAR'; + settings.trackMode = 'MONOCULAR'; settings.trackEye = 'EYE_RIGHT'; settings.logFileName = 'test_log.txt'; settings.save.allowFileTransfer = false; % initialize SMI self.el = SMITE(settings); - %EThndl = EThndl.setDummyMode(); + %self.el = self.el.setDummyMode(); self.el.init(); - self.el.calibrate(taskParam.display.window.onScreen, false); - elseif exists(taskParam.eyeTracker.el) && taskParam.eyeTracker.el.isInitialized == true - self.el.calibrate(taskParam.display.window.onScreen, false); + self.el.calibrate(taskParam.display.window.onScreen); % was calibrate(taskParam.display.window.onScreen, false) + elseif exist(taskParam.eyeTracker.el) && taskParam.eyeTracker.el.isInitialized == true + self.el.calibrate(taskParam.display.window.onScreen); else error('failed to initialize or calibrate SMI') end @@ -131,7 +131,7 @@ % % Credit: Donner lab - if isequal(taskParam.gparam.trackerVersion, 'eyelink') + if isequal(taskParam.gParam.trackerVersion, 'eyelink') % Short break pause(0.002) @@ -173,7 +173,7 @@ % % Output % taskParam: Task-parameter-object instance - if isequal(taskParam.gparam.trackerVersion, 'eyelink') + if isequal(taskParam.gParam.trackerVersion, 'eyelink') Eyelink('StartRecording'); WaitSecs(0.1); Eyelink('message', 'Start recording Eyelink'); @@ -181,7 +181,7 @@ % Reference time stamp taskParam.timingParam.ref = GetSecs(); - elseif isequal(taskParam.gparam.trackerVersion, 'SMI') + elseif isequal(taskParam.gParam.trackerVersion, 'SMI') taskParam.eyeTracker.el.startRecording() WaitSecs(0.1); taskParam.eyeTracker.el.sendMessage('Start recording SMI'); diff --git a/Task/Classes/al_gparam.m b/Task/Classes/al_gparam.m index 5a1788c..42c79b6 100644 --- a/Task/Classes/al_gparam.m +++ b/Task/Classes/al_gparam.m @@ -65,6 +65,7 @@ scanner % indicates if experiment takes place in scanner meg % indicates if experiment takes place with MEG eyeTracker % indicates if experiment takes place with eyeTracker + trackerVersion % selects whether we want eyelink or SMI version onlineSaccades % indicates if we track saccades during task uke % indicates uke fMRI scanner joy % potentially temporary joystick variable @@ -134,6 +135,7 @@ self.scanner = false; self.meg = false; self.eyeTracker = false; + self.trackerVersion = 'eyelink'; self.uke = false; self.joy = nan; self.useResponseThreshold = false; diff --git a/Task/Functions/al_baselineArousal.m b/Task/Functions/al_baselineArousal.m index 5dc7935..8b6f264 100644 --- a/Task/Functions/al_baselineArousal.m +++ b/Task/Functions/al_baselineArousal.m @@ -33,10 +33,10 @@ function al_baselineArousal(taskParam, file_name_suffix) % Presenting trial number at the bottom of the eyetracker display - optional if taskParam.gParam.eyeTracker && isequal(taskParam.trialflow.exp, 'exp') - if isequal(taskParam.gparam.trackerVersion, 'eyelink') + if isequal(taskParam.gParam.trackerVersion, 'eyelink') Eyelink('command', 'record_status_message "TRIAL %d/%d"', i, 3); Eyelink('message', 'TRIALID %d', i); - elseif isequal(taskParam.gparam.trackerVersion, 'SMI') + elseif isequal(taskParam.gParam.trackerVersion, 'SMI') taskParam.eyeTracker.el.sendMessage(sprintf('TRIALID BaseAr %d', i)); end end @@ -75,13 +75,13 @@ function al_baselineArousal(taskParam, file_name_suffix) % ----------------- if taskParam.gParam.eyeTracker% && isequal(taskParam.trialflow.saveEtData, 'true') - if isequal(taskParam.gparam.trackerVersion, 'eyelink') + if isequal(taskParam.gParam.trackerVersion, 'eyelink') et_path = pwd; et_file_name=[taskParam.eyeTracker.et_file_name, '.edf']; al_saveEyelinkData(et_path, et_file_name) Eyelink('StopRecording'); - elseif isequal(taskParam.gparam.trackerVersion, 'SMI') + elseif isequal(taskParam.gParam.trackerVersion, 'SMI') et_path = pwd; et_file_name=[taskParam.eyeTracker.et_file_name, '.edf']; al_saveEyelinkData(et_path, et_file_name) diff --git a/Task/Functions/al_sendTrigger.m b/Task/Functions/al_sendTrigger.m index bba7ea6..fce2dbb 100644 --- a/Task/Functions/al_sendTrigger.m +++ b/Task/Functions/al_sendTrigger.m @@ -264,9 +264,9 @@ % Send the pupil trigger if taskParam.gParam.eyeTracker && isequal(taskParam.trialflow.exp, 'exp') - if isequal(taskParam.gparam.trackerVersion, 'eyelink') + if isequal(taskParam.gParam.trackerVersion, 'eyelink') Eyelink('message', num2str(triggerID)); - elseif isequal(taskParam.gparam.trackerVersion, 'SMI') + elseif isequal(taskParam.gParam.trackerVersion, 'SMI') taskParam.eyeTracker.el.sendMessage(num2str(triggerID)); end end diff --git a/Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiConfigJenaSMI.m b/Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiConfigJenaSMI.m index 831a677..fb3f706 100644 --- a/Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiConfigJenaSMI.m +++ b/Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiConfigJenaSMI.m @@ -39,7 +39,7 @@ config.showConfettiThreshold = false; config.printTiming = true; config.hidePtbCursor = true; -config.dataDirectory = 'C://Users//Matlab-User//Documents//AdaptiveLearning//DataDirectory'; +config.dataDirectory = 'C://Users//Matlab-User//Documents//AdaptiveLearning//DataDirectory'; %'C://Users//Matlab-User//Documents//AdaptiveLearning//DataDirectory' config.meg = false; config.scanner = false; config.eyeTracker = true; %true; diff --git a/Task/TaskVersions/ResearchUnit/commonConfetti/al_confettiLoop.m b/Task/TaskVersions/ResearchUnit/commonConfetti/al_confettiLoop.m index c3fcb3a..fdf7351 100644 --- a/Task/TaskVersions/ResearchUnit/commonConfetti/al_confettiLoop.m +++ b/Task/TaskVersions/ResearchUnit/commonConfetti/al_confettiLoop.m @@ -77,10 +77,10 @@ % Presenting trial number at the bottom of the eyetracker display - optional if taskParam.gParam.eyeTracker - if isequal(taskParam.gparam.trackerVersion, 'eyelink') + if isequal(taskParam.gParam.trackerVersion, 'eyelink') Eyelink('command', 'record_status_message "TRIAL %d/%d"', i, trial); Eyelink('message', 'TRIALID %d', i); - elseif isequal(taskParam.gparam.trackerVersion, 'SMI') + elseif isequal(taskParam.gParam.trackerVersion, 'SMI') taskParam.eyeTracker.el.sendMessage(sprintf('TRIALID %d', i)); end end @@ -437,12 +437,12 @@ if taskParam.gParam.eyeTracker && isequal(taskParam.trialflow.saveEtData, 'true') et_path = pwd; - if isequal(taskParam.gparam.trackerVersion, 'eyelink') + if isequal(taskParam.gParam.trackerVersion, 'eyelink') et_file_name=[taskParam.eyeTracker.et_file_name, '.edf']; al_saveEyelinkData(et_path, et_file_name) Eyelink('StopRecording'); - elseif isequal(taskParam.gparam.trackerVersion, 'SMI') + elseif isequal(taskParam.gParam.trackerVersion, 'SMI') et_file_name=[taskParam.eyeTracker.et_file_name]; al_saveSMIData(taskParam.eyeTracker.el, et_path, et_file_name) taskParam.eyeTracker.el.stopRecording(); From 92289a3cdf13e3b6deeb0d53f84ff6f7feaa4465 Mon Sep 17 00:00:00 2001 From: "J. L." Date: Tue, 15 Oct 2024 11:09:05 +0200 Subject: [PATCH 5/9] added try catch for calibration --- Task/Classes/al_eyeTracker.m | 15 ++++++++++++--- Task/Functions/al_baselineArousal.m | 2 +- .../JenaSMI/al_commonConfettiConfigJenaSMI.m | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Task/Classes/al_eyeTracker.m b/Task/Classes/al_eyeTracker.m index f8d5f9f..116855a 100644 --- a/Task/Classes/al_eyeTracker.m +++ b/Task/Classes/al_eyeTracker.m @@ -73,13 +73,13 @@ EyelinkDoTrackerSetup(el); elseif isequal(taskParam.gParam.trackerVersion, 'SMI') - if ~exist(taskParam.eyeTracker.el) %todo see if this really checks if SMI was already initialized + if ~exist(taskParam.eyeTracker.el) settings = SMITE.getDefaults('HiSpeed'); settings.connectInfo = {'192.168.1.1',4444,'192.168.1.2',5555}; settings.doAverageEyes = false; settings.cal.bgColor = taskParam.colors.background; settings.freq = 500; - settings.trackMode = 'MONOCULAR'; + %settings.trackMode = 'MONOCULAR'; settings.trackEye = 'EYE_RIGHT'; settings.logFileName = 'test_log.txt'; settings.save.allowFileTransfer = false; @@ -90,7 +90,16 @@ self.el.init(); self.el.calibrate(taskParam.display.window.onScreen); % was calibrate(taskParam.display.window.onScreen, false) elseif exist(taskParam.eyeTracker.el) && taskParam.eyeTracker.el.isInitialized == true - self.el.calibrate(taskParam.display.window.onScreen); + tires = 0; + while tries < 7 + try + self.el.calibrate(taskParam.display.window.onScreen); + break + catch + WaitSecs(5); + tries = tries + 1; + end + end else error('failed to initialize or calibrate SMI') end diff --git a/Task/Functions/al_baselineArousal.m b/Task/Functions/al_baselineArousal.m index 8b6f264..4439722 100644 --- a/Task/Functions/al_baselineArousal.m +++ b/Task/Functions/al_baselineArousal.m @@ -84,7 +84,7 @@ function al_baselineArousal(taskParam, file_name_suffix) elseif isequal(taskParam.gParam.trackerVersion, 'SMI') et_path = pwd; et_file_name=[taskParam.eyeTracker.et_file_name, '.edf']; - al_saveEyelinkData(et_path, et_file_name) + al_saveSMIData(et_path, et_file_name) taskParam.eyeTracker.el.stopRecording(); end end diff --git a/Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiConfigJenaSMI.m b/Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiConfigJenaSMI.m index fb3f706..53ba972 100644 --- a/Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiConfigJenaSMI.m +++ b/Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiConfigJenaSMI.m @@ -39,7 +39,7 @@ config.showConfettiThreshold = false; config.printTiming = true; config.hidePtbCursor = true; -config.dataDirectory = 'C://Users//Matlab-User//Documents//AdaptiveLearning//DataDirectory'; %'C://Users//Matlab-User//Documents//AdaptiveLearning//DataDirectory' +config.dataDirectory = 'C://Users//te65luf//Documents//AdaptiveLearning//DataDirectory'; %'C://Users//Matlab-User//Documents//AdaptiveLearning//DataDirectory' config.meg = false; config.scanner = false; config.eyeTracker = true; %true; From df3ab6aed75b6168e3d7b9e1c53d1b6d95e33ee6 Mon Sep 17 00:00:00 2001 From: "J. L." Date: Thu, 17 Oct 2024 17:41:41 +0200 Subject: [PATCH 6/9] fixed some bugs and changed task configuration to shorten the task to 304 trials --- Task/Classes/al_eyeTracker.m | 53 +++++++++---------- Task/Functions/al_baselineArousal.m | 2 +- .../JenaSMI/al_commonConfettiConfigJenaSMI.m | 14 ++--- 3 files changed, 32 insertions(+), 37 deletions(-) diff --git a/Task/Classes/al_eyeTracker.m b/Task/Classes/al_eyeTracker.m index 116855a..473aa6d 100644 --- a/Task/Classes/al_eyeTracker.m +++ b/Task/Classes/al_eyeTracker.m @@ -73,38 +73,33 @@ EyelinkDoTrackerSetup(el); elseif isequal(taskParam.gParam.trackerVersion, 'SMI') - if ~exist(taskParam.eyeTracker.el) - settings = SMITE.getDefaults('HiSpeed'); - settings.connectInfo = {'192.168.1.1',4444,'192.168.1.2',5555}; - settings.doAverageEyes = false; - settings.cal.bgColor = taskParam.colors.background; - settings.freq = 500; - %settings.trackMode = 'MONOCULAR'; - settings.trackEye = 'EYE_RIGHT'; - settings.logFileName = 'test_log.txt'; - settings.save.allowFileTransfer = false; - - % initialize SMI - self.el = SMITE(settings); - %self.el = self.el.setDummyMode(); - self.el.init(); - self.el.calibrate(taskParam.display.window.onScreen); % was calibrate(taskParam.display.window.onScreen, false) - elseif exist(taskParam.eyeTracker.el) && taskParam.eyeTracker.el.isInitialized == true - tires = 0; - while tries < 7 - try - self.el.calibrate(taskParam.display.window.onScreen); - break - catch - WaitSecs(5); - tries = tries + 1; - end + settings = SMITE.getDefaults('HiSpeed'); + settings.connectInfo = {'192.168.1.1',4444,'192.168.1.2',5555}; + settings.doAverageEyes = false; + settings.cal.bgColor = taskParam.colors.background; + settings.freq = 500; + %settings.trackMode = 'MONOCULAR'; + settings.trackEye = 'EYE_RIGHT'; + settings.logFileName = 'test_log.txt'; + settings.save.allowFileTransfer = false; + + % initialize SMI + self.el = SMITE(settings); + %self.el = self.el.setDummyMode(); + %try a few times in case connection breaks + tries = 0; + while tries < 7 + try + self.el.init(); + self.el.calibrate(taskParam.display.window.onScreen, false); % was calibrate(taskParam.display.window.onScreen, false) + break + catch + WaitSecs(5); + tries = tries + 1; end - else - error('failed to initialize or calibrate SMI') end - else + error('Please specifiy tracker version as eyelink or SMI') end end diff --git a/Task/Functions/al_baselineArousal.m b/Task/Functions/al_baselineArousal.m index 4439722..81de6ab 100644 --- a/Task/Functions/al_baselineArousal.m +++ b/Task/Functions/al_baselineArousal.m @@ -84,7 +84,7 @@ function al_baselineArousal(taskParam, file_name_suffix) elseif isequal(taskParam.gParam.trackerVersion, 'SMI') et_path = pwd; et_file_name=[taskParam.eyeTracker.et_file_name, '.edf']; - al_saveSMIData(et_path, et_file_name) + al_saveSMIData(taskParam.eyeTracker.el, et_path, et_file_name) taskParam.eyeTracker.el.stopRecording(); end end diff --git a/Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiConfigJenaSMI.m b/Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiConfigJenaSMI.m index 53ba972..715480b 100644 --- a/Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiConfigJenaSMI.m +++ b/Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiConfigJenaSMI.m @@ -6,15 +6,15 @@ % It is recommended that you create your own script with the local % parameter settings so that you can re-use your settings. - +%PsychDebugWindowConfiguration(0,0.5); % Create config structure config = struct(); % Add desired parameters -config.trialsExp = 100; %default for experiment is 200 for each noise condition -config.nBlocks = 2; %blocks per noise condition, i.e. 4 blocks -config.practTrialsVis = 10; -config.practTrialsHid = 20; +config.trialsExp = 38; %default for experiment is 100 for 4 blocks (400), trying 38 trials for 8 blocks (304) +config.nBlocks = 4; %blocks per noise condition, default is 2 +config.practTrialsVis = 10; %10 +config.practTrialsHid = 20; %20 config.cannonPractCriterion = 4; % criterion cannon practice config.cannonPractNumOutcomes = 5; % number of trials cannon practice config.cannonPractFailCrit = 3; @@ -22,7 +22,7 @@ config.passiveViewingPractTrials = 10; config.baselineFixLength = 0.25; config.blockIndices = [1 999 999 999]; % we don't have breaks within each block -config.runIntro = true; % false; +config.runIntro = true; % true config.baselineArousal = true; % true; config.language = 'German'; % 'English'; config.sentenceLength = 80; @@ -39,7 +39,7 @@ config.showConfettiThreshold = false; config.printTiming = true; config.hidePtbCursor = true; -config.dataDirectory = 'C://Users//te65luf//Documents//AdaptiveLearning//DataDirectory'; %'C://Users//Matlab-User//Documents//AdaptiveLearning//DataDirectory' +config.dataDirectory = 'C://Users//Matlab-User//Documents//AdaptiveLearning//DataDirectory'; %'C://Users//Matlab-User//Documents//AdaptiveLearning//DataDirectory' config.meg = false; config.scanner = false; config.eyeTracker = true; %true; From 023201ffcbabce1d7d943ca839f7953ca0504a47 Mon Sep 17 00:00:00 2001 From: "J. L." Date: Mon, 28 Oct 2024 17:11:46 +0100 Subject: [PATCH 7/9] included SMI in keys function and small fix in al_eyeTracker --- Task/Classes/al_eyeTracker.m | 12 +++++++++--- Task/Classes/al_keys.m | 13 +++++++++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/Task/Classes/al_eyeTracker.m b/Task/Classes/al_eyeTracker.m index 473aa6d..42a257e 100644 --- a/Task/Classes/al_eyeTracker.m +++ b/Task/Classes/al_eyeTracker.m @@ -88,14 +88,20 @@ %self.el = self.el.setDummyMode(); %try a few times in case connection breaks tries = 0; - while tries < 7 + while tries <= 6 try self.el.init(); + tries self.el.calibrate(taskParam.display.window.onScreen, false); % was calibrate(taskParam.display.window.onScreen, false) break - catch - WaitSecs(5); + catch ME + WaitSecs(7); tries = tries + 1; + if tries <= 6 + continue + else + rethrow(ME); + end end end else diff --git a/Task/Classes/al_keys.m b/Task/Classes/al_keys.m index cc5a797..ecf87e9 100644 --- a/Task/Classes/al_keys.m +++ b/Task/Classes/al_keys.m @@ -107,11 +107,20 @@ et_path = pwd; et_file_name = [et_file_name, '.edf']; - al_saveEyelinkData(et_path, et_file_name) - Eyelink('StopRecording'); + if isequal(taskParam.gParam.trackerVersion, 'eyelink') + al_saveEyelinkData(et_path, et_file_name) + Eyelink('StopRecording'); + + elseif isequal(taskParam.gParam.trackerVersion, 'SMI') + + al_saveSMIData(taskParam.eyeTracker.el, et_path, et_file_name) + taskParam.eyeTracker.el.stopRecording(); + end + end + % Behavioral if isequal(taskParam.trialflow.saveData, 'true') && exist('taskData', 'var') == true al_saveData(taskData) From 3ac57253370210c1a5d7abf2997ddb6d31acd90f Mon Sep 17 00:00:00 2001 From: "J. L." Date: Wed, 13 Nov 2024 11:48:08 +0100 Subject: [PATCH 8/9] updated Trials and blocks --- .../ResearchUnit/JenaSMI/al_commonConfettiConfigJenaSMI.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiConfigJenaSMI.m b/Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiConfigJenaSMI.m index 715480b..a151564 100644 --- a/Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiConfigJenaSMI.m +++ b/Task/TaskVersions/ResearchUnit/JenaSMI/al_commonConfettiConfigJenaSMI.m @@ -11,8 +11,8 @@ config = struct(); % Add desired parameters -config.trialsExp = 38; %default for experiment is 100 for 4 blocks (400), trying 38 trials for 8 blocks (304) -config.nBlocks = 4; %blocks per noise condition, default is 2 +config.trialsExp = 47; %47; %default for experiment is 100 for 4 blocks (400), trying 38 trials for 8 blocks (304) +config.nBlocks = 3; %blocks per noise condition, default is 2 config.practTrialsVis = 10; %10 config.practTrialsHid = 20; %20 config.cannonPractCriterion = 4; % criterion cannon practice From aa8e3d12a012c7090bfbaec2911bfa84ad8ad025 Mon Sep 17 00:00:00 2001 From: "J. L." Date: Mon, 3 Feb 2025 14:51:22 +0100 Subject: [PATCH 9/9] Changed Relevant Files to SMI Version so updated Confetti Version works with SMI tracker --- Task/Functions/al_baselineArousal.m | 23 +++++++++++++++++------ Task/Functions/al_sendTrigger.m | 6 +++++- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/Task/Functions/al_baselineArousal.m b/Task/Functions/al_baselineArousal.m index bb03ad9..697953f 100644 --- a/Task/Functions/al_baselineArousal.m +++ b/Task/Functions/al_baselineArousal.m @@ -37,8 +37,12 @@ function al_baselineArousal(taskParam, file_name_suffix) % Presenting trial number at the bottom of the eyetracker display - optional if taskParam.gParam.eyeTracker && isequal(taskParam.trialflow.exp, 'exp') - Eyelink('command', 'record_status_message "TRIAL %d/%d"', i, 3); - Eyelink('message', 'TRIALID %d', i); + if isequal(taskParam.gParam.trackerVersion, 'eyelink') + Eyelink('command', 'record_status_message "TRIAL %d/%d"', i, 3); + Eyelink('message', 'TRIALID %d', i); + elseif isequal(taskParam.gParam.trackerVersion, 'SMI') + taskParam.eyeTracker.el.sendMessage(sprintf('TRIALID BaseAr %d', i)); + end end % Only send trigger on first interation @@ -75,8 +79,15 @@ function al_baselineArousal(taskParam, file_name_suffix) % ----------------- if taskParam.gParam.eyeTracker - et_path = pwd; - et_file_name=[taskParam.eyeTracker.et_file_name, '.edf']; - al_saveEyelinkData(et_path, et_file_name) - Eyelink('StopRecording'); + if isequal(taskParam.gParam.trackerVersion, 'eyelink') + et_path = pwd; + et_file_name=[taskParam.eyeTracker.et_file_name, '.edf']; + al_saveEyelinkData(et_path, et_file_name) + Eyelink('StopRecording'); + elseif isequal(taskParam.gParam.trackerVersion, 'SMI') + et_path = pwd; + et_file_name=[taskParam.eyeTracker.et_file_name, '.edf']; + al_saveSMIData(taskParam.eyeTracker.el, et_path, et_file_name) + taskParam.eyeTracker.el.stopRecording(); + end end diff --git a/Task/Functions/al_sendTrigger.m b/Task/Functions/al_sendTrigger.m index 6ba465c..b9df01e 100644 --- a/Task/Functions/al_sendTrigger.m +++ b/Task/Functions/al_sendTrigger.m @@ -264,7 +264,11 @@ % Send the pupil trigger if taskParam.gParam.eyeTracker && isequal(taskParam.trialflow.exp, 'exp') || taskParam.gParam.eyeTracker && isequal(taskParam.trialflow.exp, 'passive') - Eyelink('message', num2str(triggerID)); + if isequal(taskParam.gParam.trackerVersion, 'eyelink') + Eyelink('message', num2str(triggerID)); + elseif isequal(taskParam.gParam.trackerVersion, 'SMI') + taskParam.eyeTracker.el.sendMessage(num2str(triggerID)); + end end % Send the EEG trigger