rmeddis@28: function [nextStep, msg]=Levitt2 (decision, currentValue) rmeddis@28: global LevittControl withinRuns rmeddis@28: % descision is a string: 'hit' or 'miss' rmeddis@28: % current value is the value of the variable parameter (e.g. level) rmeddis@28: % msg: rmeddis@28: % msg='' indicates trial progressing successfully rmeddis@28: % msg='done' indicates that a threshold estimate has been found rmeddis@28: % msg='maximum level exceeded' rmeddis@28: % msg='maximum no of trials exceeded' rmeddis@28: rmeddis@28: % Initialize by running Levitt2 without arguments rmeddis@28: % later LevittControl will be set in aReadAndCheckParameterBoxes in expGUI_MT rmeddis@28: if nargin==0 rmeddis@28: rmeddis@28: LevittControl.peakTroughValues=[]; rmeddis@28: LevittControl.trialRunning=0; rmeddis@28: LevittControl.sequence='****'; rmeddis@28: LevittControl.LevittValuesUsed=[NaN NaN NaN NaN]; rmeddis@28: LevittControl.TurnsToSmallSteps=4; % 2 peaks and 2 troughs rmeddis@28: LevittControl.direction='same'; rmeddis@28: LevittControl.prevDirection='easier'; rmeddis@28: LevittControl.Nreversals=0; rmeddis@28: rmeddis@28: LevittControl.trialRunning=1; rmeddis@28: LevittControl.meanPeakTrough=NaN; rmeddis@28: LevittControl.sd=NaN; rmeddis@28: msg=''; rmeddis@28: return rmeddis@28: end rmeddis@28: rmeddis@28: % apply Levitt rules to find next stimulus value rmeddis@28: rule=LevittControl.rule; rmeddis@28: sequence=LevittControl.sequence; rmeddis@28: meanPT=LevittControl.meanPeakTrough; rmeddis@28: sdPT=LevittControl.sd; rmeddis@28: rmeddis@28: % response sequence: '+' is a hit and '-' is a miss. rmeddis@28: if strcmp(decision,'hit') rmeddis@28: sequence=[sequence '+']; rmeddis@28: else rmeddis@28: sequence=[sequence '-']; rmeddis@28: end rmeddis@28: LevittControl.LevittValuesUsed=[LevittControl.LevittValuesUsed withinRuns.levelList(end)]; rmeddis@28: rmeddis@28: switch rule rmeddis@28: case '++' rmeddis@28: peakCriterion='-++'; rmeddis@28: troughCriteria={'++ -', '++ +-'}; rmeddis@28: case '+++' rmeddis@28: peakCriterion='-+++'; rmeddis@28: troughCriteria={'+++ -', '+++ +-', '+++ ++-'}; rmeddis@28: otherwise rmeddis@28: error('Levitt:') rmeddis@28: end rmeddis@28: rmeddis@28: troughs=[]; allTroughPtrs=[]; rmeddis@28: for i=1:length(troughCriteria) % do all trough criteria rmeddis@28: troughCriterion=char(troughCriteria(i));% one criterion at a time rmeddis@28: % identify the location of a trough rmeddis@28: troughPtrs=findstr(sequence, troughCriterion) + length(troughCriterion)-1; rmeddis@28: % identify the level at which it occurred rmeddis@28: troughLevels=LevittControl.LevittValuesUsed(troughPtrs); rmeddis@28: % archive the list rmeddis@28: withinRuns.troughs=troughLevels; rmeddis@28: troughs=[troughs troughLevels]; rmeddis@28: allTroughPtrs=[allTroughPtrs troughPtrs]; rmeddis@28: end rmeddis@28: % only one peak criterion used rmeddis@28: withinRuns.troughs=troughs; rmeddis@28: peakPtrs=findstr(sequence,peakCriterion)+length(peakCriterion)-1; rmeddis@28: peakLevels=LevittControl.LevittValuesUsed(peakPtrs); rmeddis@28: withinRuns.peaks=peakLevels; rmeddis@28: rmeddis@28: % almagamate and sort into date order rmeddis@28: peakTroughList=[peakLevels troughs]; rmeddis@28: peakTroughPtrs=[peakPtrs allTroughPtrs]; rmeddis@28: [peakTroughPtrs idx]=sort(peakTroughPtrs); rmeddis@28: % it needs to be sequenced so that the algorithm can take the last peaks rmeddis@28: % and troughs rmeddis@28: peakTroughList=peakTroughList(idx); rmeddis@28: rmeddis@28: % adjust step size as the trial progresses rmeddis@28: % a positive step size indicates the 'harder' direction rmeddis@28: if length(peakTroughList)>=LevittControl.TurnsToSmallSteps rmeddis@28: currentStep=LevittControl.steadyLevittStep; rmeddis@28: else rmeddis@28: currentStep=LevittControl.startLevelStep; rmeddis@28: end rmeddis@28: rmeddis@28: % base next stimulus on the basis of the sequence rmeddis@28: % any miss requires an 'easier' stimulus next time. rmeddis@28: if strcmp(sequence(end),'-') rmeddis@28: nextStep= -currentStep; rmeddis@28: rmeddis@28: % success requires 2 or more successive hits rmeddis@28: elseif strcmp(sequence(end-length(rule)+1:end),rule) rmeddis@28: sequence=[sequence ' ']; % add space to prevent success on successive trials rmeddis@28: LevittControl.LevittValuesUsed=[LevittControl.LevittValuesUsed NaN]; rmeddis@28: nextStep=currentStep; rmeddis@28: rmeddis@28: % not enough hits to provoke a change rmeddis@28: else rmeddis@28: nextStep=0; rmeddis@28: end rmeddis@28: rmeddis@28: LevittControl.sequence=sequence; rmeddis@28: rmeddis@28: LevittControl.Nreversals=length(peakTroughList); rmeddis@28: % compute threshold estimate rmeddis@28: % only if minReversals exceeded and even number of peaks and troughs rmeddis@28: if LevittControl.Nreversals>=LevittControl.minReversals && rem(length(peakTroughList),2)==0 rmeddis@28: % use only the peaks and troughs at the end of the sequence rmeddis@28: peaksAndTroughs=peakTroughList(end-LevittControl.useLastNturns+1:end); rmeddis@28: disp(['peak/trough sequence= ' num2str(peaksAndTroughs, '%6.0f') ] ) rmeddis@28: rmeddis@28: meanPT=mean(peaksAndTroughs); rmeddis@28: sdPT=std(peaksAndTroughs); rmeddis@28: LevittControl.meanPeakTrough=meanPT; rmeddis@28: LevittControl.sd=sdPT; rmeddis@28: fprintf('Levitt, mean, sd= %6.1f,%6.1f\n', meanPT, sdPT) rmeddis@28: % final check that the sd is low enough rmeddis@28: if sdPTLevittControl.maxLevittValue rmeddis@28: msg='maximum level exceeded' rmeddis@28: return rmeddis@28: end rmeddis@28: rmeddis@28: % required for later use rmeddis@28: LevittControl.trialRunning=LevittControl.trialRunning+1; rmeddis@28: LevittControl.peakTroughValues=peakTroughList; rmeddis@28: rmeddis@28: if LevittControl.trialRunning>LevittControl.maxTrials rmeddis@28: msg='maximum no of trials exceeded' rmeddis@28: return rmeddis@28: end rmeddis@28: rmeddis@28: % Trial continues rmeddis@28: msg='';