Skip to content
MAT_practice_170704.m 19.2 KiB
Newer Older
Julian Kosciessa's avatar
Julian Kosciessa committed
%_______________________________________________________________________
%
% Run random dot motion experiment defined in createDotInfo
%_______________________________________________________________________
%
% Input
%
% dotInfo | experiment configuration (struct)
%    subj | identifier of subject (string)
% condSet | conditions (cell array of strings)
%_______________________________________________________________________
%
function [thresholds] = MAT_practice_170704(dotInfo, subj, DEBUG, opacity, P)

  %%
  %% sanitize function parameters
  %%
  if nargin == 0
    eval('help dotsExperiment')
    return
  end

  if not(exist('dotInfo','var'))
    error('Missing input: dotInfo')
  elseif isempty(dotInfo) || not(isstruct(dotInfo))
    error('Invalid input: dotInfo (requires non-empty struct)')
  end

  if not(exist('subj','var'))
    error('Missing input: subj')
  elseif not(isempty(subj)) && ischar(subj)
    dotInfo.subj = subj;
  else
    error('Invalid input: subj (requires non-empty string)')
  end

  if not(isfield(dotInfo,'deployed'))
    dotInfo.deployed = true;
  end

  %%
  %% for saving behavioural data
  %%

  sessionFile = [dotInfo.dataDir, subj '_' datestr(now, 'yymmdd_HHMM'), '.mat'];
    
  %%
  %% prepare presentation
  %%
  displayList = Screen('screens');
  displayIdx = displayList(1);
  KbName('UnifyKeyNames'); % use portable key naming scheme
  oldVerbosityLevel = Screen('Preference', 'Verbosity', 2); % show errors and warnings
  arrowKeys = [dotInfo.keyLeft dotInfo.keyRight];
  baseDotInfo = dotInfo; % keep for saving later
  
    if DEBUG == 1
        Screen('Preference', 'SkipSyncTests', 1);
        PsychDebugWindowConfiguration(0, opacity);
    else
        clear Screen; %PsychDebugWindowConfiguration([], 1);
    end

  try

    %%
    %% prepare canvas
    %%
    screenInfo = openExperiment(50,50,displayIdx); clc; % open drawing canvas
    if numel(Screen('screens')) == 1 || dotInfo.deployed
      HideCursor(screenInfo.curWindow);
    end
    Screen('TextFont', screenInfo.curWindow, 'Helvetica');
    Screen('TextSize', screenInfo.curWindow, 20);
    Screen('TextStyle', screenInfo.curWindow, 0); % regular
    Screen('TextColor', screenInfo.curWindow, 255); % white
    
    ResultMat = [];
    
    ExperimentProtocol = cell(0);
    ExperimentProtocol = [ExperimentProtocol; {'SessionOnset'}, {GetSecs}, {[]}, {[]}, {[]}, {NaN}, {[]}];

    %% Intro slide

    DrawFormattedText(screenInfo.curWindow, '\nWillkommen bei der Multi-Attribut Aufgabe\n\nIm folgenden werden Sie Punktewolken sehen, die verschiedene Charakteristika aufweisen: \n\n Bewegungsrichtung \n Helligkeit \n Gre \n Farbe \n\n Im Folgenden sehen Sie ein (lngeres) Beispiel', 'center', 'center');
    Screen('Flip', screenInfo.curWindow);

    % wait for experimenter to resume session
    while true
    [exitSession, resumeSession] = checkKeys(dotInfo);
    if resumeSession
      break
    elseif exitSession
      closeExperiment;
      return
    end
    end
    
    %% Show first 5 second example
    
    dotInfo.durCue = 0;     % duration of cue
    dotInfo.durPres = 5;	% duration of presentation
    dotInfo.durResp = 0;	% duration of question
    dotInfo.durConf = 0;	% duration of confidence
    
    dotInfo.MAT.percAtt1H = .65;
    dotInfo.MAT.percAtt1L = 1-dotInfo.MAT.percAtt1H;
    dotInfo.MAT.percAtt2H = .65;
    dotInfo.MAT.percAtt2L = 1-dotInfo.MAT.percAtt2H;
    dotInfo.MAT.percAtt3H = .65;
    dotInfo.MAT.percAtt3L = 1-dotInfo.MAT.percAtt3H;
    dotInfo.MAT.percAtt4H = .65;
    dotInfo.MAT.percAtt4L = 1-dotInfo.MAT.percAtt4H;
    dotInfo.MAT.color = [255 255 255; 255 0 0];
    dotInfo.MAT.coherence = 1;  % define dot movement (coherence) [useless now!]
    dotInfo.MAT.direction = [180 0]; % left and right
    dotInfo.MAT.size = [5 8]; % define dot size
    dotInfo.MAT.luminance = [.4 .8]; % define dot luminance
    dotInfo.MAT.attNames = {'color'; 'direction'; 'size'; 'luminance'};
    
    TrialInitiation = GetSecs;
    ExperimentProtocol = [ExperimentProtocol; {'TrialInitiation'}, {TrialInitiation}, {[]}, {[]}, {[]}, {0}, {[]}];
    targets = makeDotTargets(screenInfo, dotInfo); % initialize fixation targets
    showTargets(screenInfo, targets, 1);
    DisplayInfo.CombPositionByTrial = [];
    DisplayInfo.CombSamples = [];
    [~, dotInfo, ExperimentProtocol,ResultMat,DisplayInfo] = dotsX_JQK_MAT_170704(screenInfo, dotInfo, targets, 1, ExperimentProtocol,ResultMat,DisplayInfo);              

    %% Instruction
    
    DrawFormattedText(screenInfo.curWindow, 'Nun sehen Sie die Attribute einzeln und in der spteren Anzeigedauer. \n Bitte geben Sie an, von welcher der beiden Optionen mehr vorhanden ist.', 'center', 'center');
    Screen('Flip', screenInfo.curWindow);

    pause(1);
    
    % wait for experimenter to resume session
    while true
    [exitSession, resumeSession] = checkKeys(dotInfo);
    if resumeSession
      break
    elseif exitSession
      closeExperiment;
      return
    end
    end
    
    %% Instruction
    
    DrawFormattedText(screenInfo.curWindow, 'Wir beginnen mit der Farbe. \n\n Falls die weien Punkte dominieren, drcken Sie die linke Taste. \n Falls die roten Punkte dominieren, dcken Sie die rechte Taste. \n\n Im Anschluss an Ihre Antwort erhalten Sie eine Rckmeldung.', 'center', 'center');
    Screen('Flip', screenInfo.curWindow);

    pause(1);
    
    % wait for experimenter to resume session
    while true
    [exitSession, resumeSession] = checkKeys(dotInfo);
    if resumeSession
      break
    elseif exitSession
      closeExperiment;
      return
    end
    end
    
    %% 1st dimension
    
    % intialize psi parameters
    PM = PAL_AMPM_setupPM('priorAlphaRange' , P.PM.priorAlphaRange, ...
        'stimRange', P.PM.stimRange, 'numtrials', P.PM.numtrials, ...
        'gamma', P.PM.gamma, 'priorLambdaRange', P.PM.priorLambdaRange, ...
        'marginalize', 'slope', 'marginalize', 'lambda');
     
    % intiate parameters
    indTrial = 1;
    DisplayInfo.CombPositionByTrial = [];
    DisplayInfo.CombSamples = [];
    ResultMat = [];
    % run the staircase algorithm until criterion is reached
    dotInfo = [];
    dotInfo = createDotInfo_TEST_170704(PM, 1);
    dotInfo.deployed = true;
    dotInfo.feedback = 1;
    
    ExperimentProtocol = [ExperimentProtocol; {'TrialInitiation'}, {GetSecs}, {[]}, {[]}, {[]}, {0}, {[]}];
    while ~PM.stop
        % Set the difference in amount of evidence for the target feature
        % to the adaptive value. Note that for the remaining attributes,
        % this is meaningless, as the different options are equal anyways.
        dotInfo.MAT.percAtt1H = PM.xCurrent;
        dotInfo.MAT.percAtt1L = 1-dotInfo.MAT.percAtt1H;
        targets = makeDotTargets(screenInfo, dotInfo); % initialize fixation targets
        showTargets(screenInfo, targets, 1);
        [~, dotInfo, ExperimentProtocol,ResultMat,DisplayInfo] = dotsX_JQK_MAT_170704(screenInfo, dotInfo, targets, indTrial, ExperimentProtocol,ResultMat,DisplayInfo);              
        % update accuracy for the staircase code
        trial.resp = ResultMat(indTrial,4);
        % update PM structure
        PM = PAL_AMPM_updatePM(PM, trial.resp);
        indTrial = indTrial+1;
    end
    
    Results{1,1} = dotInfo;
    Results{2,1} = ExperimentProtocol;
    Results{3,1} = ResultMat;
    Results{4,1} = PM;
    Results{5,1} = DisplayInfo;
    
    % estimate threshold
    thresholds.color = PM.threshold(end);
    %dotInfo.MAT.color75 = PM.PF([PM.threshold(end) 10.^PM.slope(end) PM.priorGammaRange PM.priorLambdaRange],75/100, 'Inverse');
    
    %% Instruction
    
    DrawFormattedText(screenInfo.curWindow, 'Als nchstes die Bewegungsrichtung. \n\n Falls die Bewegung nach links dominiert, drcken Sie die linke Taste. \n Falls die Bewegung nach rechts dominiert, dcken Sie die rechte Taste.', 'center', 'center', [255 255 255]);
    Screen('Flip', screenInfo.curWindow);

    pause(1);
    
    % wait for experimenter to resume session
    while true
    [exitSession, resumeSession] = checkKeys(dotInfo);
    if resumeSession
      break
    elseif exitSession
      closeExperiment;
      return
    end
    end
    
    %% 2nd dimension
    
    % intialize psi parameters
    PM = PAL_AMPM_setupPM('priorAlphaRange' , P.PM.priorAlphaRange, ...
        'stimRange', P.PM.stimRange, 'numtrials', P.PM.numtrials, ...
        'gamma', P.PM.gamma, 'priorLambdaRange', P.PM.priorLambdaRange, ...
        'marginalize', 'slope', 'marginalize', 'lambda');
     
    % intiate parameters
    indTrial = 1;
    DisplayInfo.CombPositionByTrial = [];
    DisplayInfo.CombSamples = [];
    ResultMat = [];
    % run the staircase algorithm until criterion is reached
    dotInfo = [];
    dotInfo = createDotInfo_TEST_170704(PM, 2);
    dotInfo.deployed = true;
    dotInfo.feedback = 1;
    
    ExperimentProtocol = [ExperimentProtocol; {'TrialInitiation'}, {GetSecs}, {[]}, {[]}, {[]}, {0}, {[]}];
    while ~PM.stop
        dotInfo.MAT.percAtt2H = PM.xCurrent;
        dotInfo.MAT.percAtt2L = 1-dotInfo.MAT.percAtt2H;
        targets = makeDotTargets(screenInfo, dotInfo); % initialize fixation targets
        showTargets(screenInfo, targets, 1);
        [~, dotInfo, ExperimentProtocol,ResultMat,DisplayInfo] = dotsX_JQK_MAT_170704(screenInfo, dotInfo, targets, indTrial, ExperimentProtocol,ResultMat,DisplayInfo);
        % update accuracy for the staircase code
        trial.resp = ResultMat(indTrial,4);
        % update PM structure
        PM = PAL_AMPM_updatePM(PM, trial.resp);
        indTrial = indTrial+1;
    end
    
    Results{1,2} = dotInfo;
    Results{2,2} = ExperimentProtocol;
    Results{3,2} = ResultMat;
    Results{4,2} = PM;
    Results{5,2} = DisplayInfo;
    
    % estimate threshold
    thresholds.direction = PM.threshold(end);
    
    %% Instruction
    
    DrawFormattedText(screenInfo.curWindow, 'Als nchstes die Punktgre. \n\n Falls die kleineren Punkte dominieren, drcken Sie die linke Taste. \n Falls die greren Punkte dominieren, dcken Sie die rechte Taste.', 'center', 'center', [255 255 255]);
    Screen('Flip', screenInfo.curWindow);

    pause(1);
    
    % wait for experimenter to resume session
    while true
    [exitSession, resumeSession] = checkKeys(dotInfo);
    if resumeSession
      break
    elseif exitSession
      closeExperiment;
      return
    end
    end
    
    %% 3rd dimension
    
    % intialize psi parameters
    PM = PAL_AMPM_setupPM('priorAlphaRange' , P.PM.priorAlphaRange, ...
        'stimRange', P.PM.stimRange, 'numtrials', P.PM.numtrials, ...
        'gamma', P.PM.gamma, 'priorLambdaRange', P.PM.priorLambdaRange, ...
        'marginalize', 'slope', 'marginalize', 'lambda');
     
    % intiate parameters
    indTrial = 1;
    DisplayInfo.CombPositionByTrial = [];
    DisplayInfo.CombSamples = [];
    ResultMat = [];
    % run the staircase algorithm until criterion is reached
    dotInfo = [];
    dotInfo = createDotInfo_TEST_170704(PM, 3);
    dotInfo.deployed = true;
    dotInfo.feedback = 1;
    
    ExperimentProtocol = [ExperimentProtocol; {'TrialInitiation'}, {GetSecs}, {[]}, {[]}, {[]}, {0}, {[]}];
    while ~PM.stop
        dotInfo.MAT.percAtt3H = PM.xCurrent;
        dotInfo.MAT.percAtt3L = 1-dotInfo.MAT.percAtt3H;
        targets = makeDotTargets(screenInfo, dotInfo); % initialize fixation targets
        showTargets(screenInfo, targets, 1);
        [~, dotInfo, ExperimentProtocol,ResultMat,DisplayInfo] = dotsX_JQK_MAT_170704(screenInfo, dotInfo, targets, indTrial, ExperimentProtocol,ResultMat,DisplayInfo);
        % update accuracy for the staircase code
        trial.resp = ResultMat(indTrial,4);
        % update PM structure
        PM = PAL_AMPM_updatePM(PM, trial.resp);
        indTrial = indTrial+1;
    end
    
    Results{1,3} = dotInfo;
    Results{2,3} = ExperimentProtocol;
    Results{3,3} = ResultMat;
    Results{4,3} = PM;
    Results{5,3} = DisplayInfo;
    
    % estimate threshold
    thresholds.size = PM.threshold(end);
    
    %% Instruction
    
    DrawFormattedText(screenInfo.curWindow, 'Als letztes die Punkthelligkeit. \n\n Falls die dunkleren Punkte dominieren, drcken Sie die linke Taste. \n Falls die helleren Punkte dominieren, dcken Sie die rechte Taste.', 'center', 'center', [255 255 255]);
    Screen('Flip', screenInfo.curWindow);

    pause(1);
    
    % wait for experimenter to resume session
    while true
    [exitSession, resumeSession] = checkKeys(dotInfo);
    if resumeSession
      break
    elseif exitSession
      closeExperiment;
      return
    end
    end
    
     %% 4th dimension
    
    % intialize psi parameters
    PM = PAL_AMPM_setupPM(...
        'priorAlphaRange' , P.PM.priorAlphaRange, ...
        'stimRange', P.PM.stimRange, ...
        'numtrials', P.PM.numtrials, ...
        'gamma', P.PM.gamma, ...
        'priorLambdaRange', P.PM.priorLambdaRange, ...
        'marginalize', 'slope', ...
        'marginalize', 'lambda');
Julian Kosciessa's avatar
Julian Kosciessa committed
     
    % intiate parameters
    indTrial = 1;
    DisplayInfo.CombPositionByTrial = [];
    DisplayInfo.CombSamples = [];
    ResultMat = [];
    % run the staircase algorithm until criterion is reached
    dotInfo = [];
    dotInfo = createDotInfo_TEST_170704(PM, 4);
    dotInfo.deployed = true;
    dotInfo.feedback = 1;
    
    ExperimentProtocol = [ExperimentProtocol; {'TrialInitiation'}, {GetSecs}, {[]}, {[]}, {[]}, {0}, {[]}];
    while ~PM.stop
        dotInfo.MAT.percAtt4H = PM.xCurrent;
        dotInfo.MAT.percAtt4L = 1-dotInfo.MAT.percAtt4H;
        targets = makeDotTargets(screenInfo, dotInfo); % initialize fixation targets
        showTargets(screenInfo, targets, 1);
        [~, dotInfo, ExperimentProtocol,ResultMat,DisplayInfo] = dotsX_JQK_MAT_170704(screenInfo, dotInfo, targets, indTrial, ExperimentProtocol,ResultMat,DisplayInfo);        % update accuracy for the staircase code
        trial.resp = ResultMat(indTrial,4);
        % update PM structure
        PM = PAL_AMPM_updatePM(PM, trial.resp);
        indTrial = indTrial+1;
    end
    
    Results{1,4} = dotInfo;
    Results{2,4} = ExperimentProtocol;
    Results{3,4} = ResultMat;
    Results{4,4} = PM;
    Results{5,4} = DisplayInfo;
    
    % estimate threshold
    thresholds.luminance = PM.threshold(end);
    
    %% Instruction
    
    DrawFormattedText(screenInfo.curWindow, 'Nun kennen Sie alle Attribute. \n  Diese werden Ihnen im Folgenden gemeinsam prsentiert. \n Bitte geben Sie wie oben immer an, welche der beiden Optionen eines Attributs den Eindruck dominiert. \n Das relevante Attribut wird Ihnen dabei nach der Prsentation prsentiert.', 'center', 'center', [255 255 255]);
    Screen('Flip', screenInfo.curWindow);

    pause(1);
    
    % wait for experimenter to resume session
    while true
    [exitSession, resumeSession] = checkKeys(dotInfo);
    if resumeSession
      break
    elseif exitSession
      closeExperiment;
      return
    end
    end
        
    %% realistic without cue
    
    dotInfo = createDotInfo_JQK_170704(thresholds); dotInfo.deployed = true;
    dotInfo.totalTrials = 10;
    dotInfo.durCue = 0; % duration of cue
    dotInfo.feedback = 1;
    dotInfo.durConf = 0;

    ExperimentProtocol = [ExperimentProtocol; {'TrialInitiation'}, {GetSecs}, {[]}, {[]}, {[]}, {0}, {[]}];
    for indTrial = 1:dotInfo.totalTrials
        targets = makeDotTargets(screenInfo, dotInfo); % initialize fixation targets
        showTargets(screenInfo, targets, 1);
        [~, dotInfo, ExperimentProtocol,ResultMat,DisplayInfo] = dotsX_JQK_MAT_170704(screenInfo, dotInfo, targets, indTrial, ExperimentProtocol,ResultMat,DisplayInfo);
    end
    
    Results{1,5} = dotInfo;
    Results{2,5} = ExperimentProtocol;
    Results{3,5} = ResultMat;
    Results{5,5} = DisplayInfo;
    
    %% Instruction 1
    
    DrawFormattedText(screenInfo.curWindow, 'Whrend mancher Trials werden Sie vorher erfahren, welches Attribut abgefragt wird. \n Falls ein "?" erscheint, kann wie gerade eben jedes Attribut abgefragt werden. \n Diese Information erscheint in blau. \n Sie werden nun nicht mehr erfahren, ob Ihre Antworten korrekt waren.', 'center', 'center', [255 255 255]);
    Screen('Flip', screenInfo.curWindow);

    pause(1);
    
    % wait for experimenter to resume session
    while true
    [exitSession, resumeSession] = checkKeys(dotInfo);
    if resumeSession
      break
    elseif exitSession
      closeExperiment;
      return
    end
    end
    
    %% Instruction 2
    
    DrawFormattedText(screenInfo.curWindow, 'Zudem bitten wir Sie im Anschluss um eine Einschtzung, wie sicher Sie sich bei der Entscheidung sind. \n  Bitte drcken Sie dann die linke Taste, falls sie sich eher unsicher sind. \n Bitte drcken Sie dann die rechte Taste, falls sie sich eher sicher sind.', 'center', 'center', [255 255 255]);
    Screen('Flip', screenInfo.curWindow);

    pause(1);
    
    % wait for experimenter to resume session
    while true
    [exitSession, resumeSession] = checkKeys(dotInfo);
    if resumeSession
      break
    elseif exitSession
      closeExperiment;
      return
    end
    end
    
    %% realistic with cue
    
    dotInfo = createDotInfo_JQK_170704(thresholds); dotInfo.deployed = true;
    dotInfo.totalTrials = 10;
    
    ExperimentProtocol = [ExperimentProtocol; {'TrialInitiation'}, {GetSecs}, {[]}, {[]}, {[]}, {0}, {[]}];
    for indTrial = 1:dotInfo.totalTrials
        targets = makeDotTargets(screenInfo, dotInfo); % initialize fixation targets
        showTargets(screenInfo, targets, 1);
        [~, dotInfo, ExperimentProtocol,ResultMat,DisplayInfo] = dotsX_JQK_MAT_170704(screenInfo, dotInfo, targets, indTrial, ExperimentProtocol,ResultMat,DisplayInfo);
    end
    
    Results{1,6} = dotInfo;
    Results{2,6} = ExperimentProtocol;
    Results{3,6} = ResultMat;
    Results{5,6} = DisplayInfo;
    
    save(sessionFile, 'Results', 'thresholds');
    
    %% inform the subject that the experiment is over
    
    DrawFormattedText(screenInfo.curWindow, 'Sie haben das Training erfolgreich beendet!', 'center', 'center', [255 255 255]);
    Screen('Flip', screenInfo.curWindow);

    pause(1);
    
    %% plot threshold data for threshold optimization of MAT task

    h = figure;
    subplot(3,1,1);
    plot(Results{4,1}.threshold)
    hold on; plot(Results{4,2}.threshold)
    hold on; plot(Results{4,3}.threshold)
    hold on; plot(Results{4,4}.threshold)
    ylim([.5 .8]), xlim([1 Results{4,1}.numTrials])
    title('Threshold estimates');
    legend({'Color'; 'Direction'; 'Size'; 'Luminance'});

    subplot(3,1,2);
    plot(Results{4,1}.x)
    hold on; plot(Results{4,2}.x)
    hold on; plot(Results{4,3}.x)
    hold on; plot(Results{4,4}.x)
    ylim([.5 .8]), xlim([1 Results{4,1}.numTrials])
    title('Presented values');
    legend({'Color'; 'Direction'; 'Size'; 'Luminance'});

    subplot(3,1,3);
    plot(Results{4,1}.response)
    hold on; plot(Results{4,2}.response)
    hold on; plot(Results{4,3}.response)
    hold on; plot(Results{4,4}.response)
    ylim([0 1]), xlim([1 Results{4,1}.numTrials])
    title('Accuracy');
    legend({'Color'; 'Direction'; 'Size'; 'Luminance'});
    
    % Maybe this should be saved as output? Then again, we can always
    % reproduce it.
    
    %% wait for experimenter to close screen
    while true
      [exitKeyPressed, resumeKeyPressed] = checkKeys(dotInfo);
      if exitKeyPressed || resumeKeyPressed
        break
      end
    end

  catch exception
    getReport(exception) % show stack trace
  end

  closeExperiment; % close drawing canvas
  Screen('Preference', 'Verbosity', oldVerbosityLevel); % restore verbosity

end