walkerDirectionsStaircaseDemo.m

A copy is also included in PsychBench in <PsychBench folder>/demos, so you can run it from there.

<cdcm>% WALKER DIRECTIONS STAIRCASE EXPERIMENT DEMO<cdcm>
<cdcm>% Coding method<cdcm>
<cdcm>% --------------------------------------------------<cdcm>
 
 
<cdcm>% Same as walkerDirectionsDemo.m except this demo uses a staircase to vary number<cdcm>
<cdcm>% of mask dots across trials.<cdcm>
<cdcm>%<cdcm>
<cdcm>% Staircase parameters:<cdcm>
<cdcm>% Method:                               fixed step<cdcm>
<cdcm>% Step rule:                            3 correct / 1 incorrect<cdcm>
<cdcm>% Initial value:                        35 (total number of mask dots = staircase value * 2 + 15--see below)<cdcm>
<cdcm>% Step sizes:                           +8 correct, -12 incorrect<cdcm>
<cdcm>% Minimum value:                        15<cdcm>
<cdcm>% Number of fast reversals (1/1 rule): 3<cdcm>
<cdcm>% Fast reversal step sizes:             (same)<cdcm>
<cdcm>% Maximum number of reversals:          30<cdcm>
 
 
 
<cdcm>% See walkerDirectionsDemo.m for more commenting.<cdcm>
 
 
 
newExperiment
 
 
 
<cdcm>% TASK TRIALS<cdcm>
<cdcm>% 2 trial definitions numbered 1-2: 2 directions<cdcm>
<cdcm>% ==========<cdcm>
<cdkm>for<cdkm> d = [1 2]
    <cdcm>% Walker<cdcm>
    <cdcm>% ---<cdcm>
        walker = bmlWalkerObject;
 
        <cdcm>% bmlWalkerData.mat comes with the bmlWalker element type.<cdcm>
        <cdcm>% Contains cell array "bmlWalkerData".<cdcm>
        <cdcm>% {2} contains the motion data we want to use.<cdcm>
        walker.fileName = <cdsm>"bmlWalkerData.mat"<cdsm>;
        walker.dataExpr = <cdsm>"bmlWalkerData{2}"<cdsm>;
 
    <cdkm>if<cdkm> d == 1
        <cdcm>% Facing left<cdcm>
        walker.azimuth = -90;
    <cdkm>else<cdkm>
        <cdcm>% Facing right<cdcm>
        walker.azimuth = +90;
    <cdkm>end<cdkm>
 
        <cdcm>% Default start at trial start; End at response<cdcm>
        walker.end.response = true;
 
        <cdcm>% See direction # in results<cdcm>
        walker.info.direction = d;
    <cdcm>% ---<cdcm>
 
 
    <cdcm>% Walker mask<cdcm>
    <cdcm>% ---<cdcm>
        <cdcm>% Use two bmlWalker objects superimposed: one for dots facing left, one for<cdcm>
        <cdcm>% facing right<cdcm>
        masks = bmlWalkerObject(2);
 
        <cdcm>% Mask 1 = same direction as walker, 2 = opposite<cdcm>
        masks(1).azimuth =  walker.azimuth;
        masks(2).azimuth = -walker.azimuth;
 
        <cdcm>% Staircase number of dots in each mask.<cdcm>
        <cdcm>% Mask 1 (same direction)     = staircase value for trial<cdcm>
        <cdcm>% Mask 2 (opposite direction) = staircase value for trial + 15<cdcm>
        masks(1).staircase.what = <cdsm>"numScrambleDots"<cdsm>;
        masks(2).staircase.what = <cdsm>"numScrambleDots"<cdsm>;
        masks(1).staircase.setExpr = <cdsm>"staircaseVal"<cdsm>;
        masks(2).staircase.setExpr = <cdsm>"staircaseVal+15"<cdsm>;
 
    <cdcm>% All other properties same for both objects...<cdcm>
    <cdkm>for<cdkm> n = 1:2
        <cdcm>% Same motion data as walker<cdcm>
        masks(n).fileName = <cdsm>"bmlWalkerData.mat"<cdsm>;
        masks(n).dataExpr = <cdsm>"bmlWalkerData{2}"<cdsm>;
 
        <cdcm>% .scramble = true makes it a random dot mask<cdcm>
        masks(n).scramble = true;
 
        <cdcm>% 12 deg square mask area<cdcm>
        masks(n).scrambleAreaSize = [12 12];
 
        <cdcm>% Random motion phase across dots<cdcm>
        masks(n).scramblePhases = true;
 
        <cdcm>% Start/End same as walker<cdcm>
        masks(n).end.response = true;
 
        <cdcm>% See number of dots in results.<cdcm>
        <cdcm>% Total number of mask dots = sum of them.<cdcm>
        masks(n).report = <cdsm>"numScrambleDots"<cdsm>;
    <cdkm>end<cdkm>
    <cdcm>%---<cdcm>
 
 
    <cdcm>% Key press response<cdcm>
    <cdcm>% ---<cdcm>
    response = keyPressObject;
 
    <cdcm>% Listen for left/right arrow keys.<cdcm>
    <cdcm>% You can get these key names using showKey() at the MATLAB command line.<cdcm>
    <cdcm>% Response value recorded in record property .response will be number of the name in this list, i.e. 1 = left, 2 = right.<cdcm>
    response.listenKeyNames = [<cdsm>"left"<cdsm> <cdsm>"right"<cdsm>];
 
    <cdcm>% Score correct/incorrect.<cdcm>
    <cdcm>% Response values 1 = left, 2 = right conveniently match our loop variable "d", so we can just set correct response = that.<cdcm>
    response.scoreResponse = true;
    response.correctResponse = d;
 
    <cdcm>% Default start at trial start.<cdcm>
    <cdcm>% By default keyPress elements end when they record a response -> don't need to set .end.<cdcm>
 
    <cdcm>% See response, score, latency in results<cdcm>
    response.report = [<cdsm>"response"<cdsm> <cdsm>"responseScore"<cdsm> <cdsm>"responseLatency"<cdsm>];
    <cdcm>% ---<cdcm>
 
 
    <cdcm>% Add trial definition with default numbering (1, 2, 3, ...)<cdcm>
    addTrial(walker, masks, response);
<cdkm>end<cdkm>
<cdcm>% ==========<cdcm>
 
 
 
<cdcm>% TRAINING TRIALS<cdcm>
<cdcm>% ==========<cdcm>
<cdcm>% WALKER WITH NO MASK<cdcm>
<cdcm>% 2 trial definitions numbered 101, 102: 2 directions, no mask<cdcm>
<cdkm>for<cdkm> d = [1 2]
        walker = bmlWalkerObject;
        walker.fileName = <cdsm>"bmlWalkerData.mat"<cdsm>;
        walker.dataExpr = <cdsm>"bmlWalkerData{2}"<cdsm>;
    <cdkm>if<cdkm> d == 1
        walker.azimuth = -90;
    <cdkm>else<cdkm>
        walker.azimuth = +90;
    <cdkm>end<cdkm>
        walker.end.response = true;
 
 
    response = keyPressObject;
    response.listenKeyNames = [<cdsm>"left"<cdsm> <cdsm>"right"<cdsm>];
    response.scoreResponse = true;
    response.correctResponse = d;
 
 
    <cdcm>% Add trial definition to group 100 (101, 102, ...)<cdcm>
    addTrial(walker, response, 100);
<cdkm>end<cdkm>
 
 
<cdcm>% WALKER WITH MINIMAL MASK<cdcm>
<cdcm>% 2 trial definitions numbered 201, 202: 2 directions, same number of mask dots in each<cdcm>
<cdkm>for<cdkm> d = [1 2]
        walker = bmlWalkerObject;
        walker.fileName = <cdsm>"bmlWalkerData.mat"<cdsm>;
        walker.dataExpr = <cdsm>"bmlWalkerData{2}"<cdsm>;
    <cdkm>if<cdkm> d == 1
        walker.azimuth = -90;
    <cdkm>else<cdkm>
        walker.azimuth = +90;
    <cdkm>end<cdkm>
        walker.end.response = true;
 
 
    <cdcm>%Mask = 15 dots facing opposite direction 15-dot walker<cdcm>
    mask = bmlWalkerObject;
    mask.azimuth = -walker.azimuth;
    mask.numScrambleDots = 15;
    mask.fileName = <cdsm>"bmlWalkerData.mat"<cdsm>;
    mask.dataExpr = <cdsm>"bmlWalkerData{2}"<cdsm>;
    mask.scramble = true;
    mask.scrambleAreaSize = [12 12];
    mask.scramblePhases = true;
    mask.end.response = true;
 
 
    response = keyPressObject;
    response.listenKeyNames = [<cdsm>"left"<cdsm> <cdsm>"right"<cdsm>];
    response.scoreResponse = true;
    response.correctResponse = d;
 
 
    <cdcm>% Add trial definition to group 200 (201, 202, ...)<cdcm>
    addTrial(walker, mask, response, 200);
<cdkm>end<cdkm>
 
 
<cdcm>% PAUSE TRIAL<cdcm>
<cdcm>% ---<cdcm>
<cdcm>% dialogue object to show text until subject presses any key.<cdcm>
<cdcm>% Can just set text and leave all properties at default.<cdcm>
pauseText = dialogueObject;
pauseText.text = <cdsm>"Press any key to continue..."<cdsm>;
 
 
<cdcm>% Add trial definition with name "pause"<cdcm>
addTrial(pauseText, <cdsm>"pause"<cdsm>);
<cdcm>% ---<cdcm>
<cdcm>% ==========<cdcm>
 
 
 
<cdcm>% Set trial list:<cdcm>
<cdcm>%<cdcm>
<cdcm>% - 3 x 2 walker only training trial definitions in random order<cdcm>
<cdcm>% - pause<cdcm>
<cdcm>% - 3 x 2 walker + mask training trial definitions in random order<cdcm>
<cdcm>% - pause<cdcm>
<cdcm>% - 40 x 2 task trial definitions in random order<cdcm>
<cdcm>%<cdcm>
<cdcm>% Random order just to randomize left/right<cdcm>
nn = {randomOrder(rep(101:102, 3)) <cdsm>"pause"<cdsm> randomOrder(rep(201:202, 3)) <cdsm>"pause"<cdsm> randomOrder(rep(1:2, 40))};
setTrialList(nn)
 
 
 
<cdcm>% Staircase<cdcm>
<cdcm>% ---<cdcm>
staircase = fixedStepStaircaseObject;
 
<cdcm>% 3 correct / 1 incorrect rule<cdcm>
staircase.stepRule = [3 1];
 
<cdcm>% Staircase value will apply to number of mask dots.<cdcm>
<cdcm>% Need initial number of mask dots + any combination of steps = integer, don't step below 0, so mask .numDots always = valid value.<cdcm>
<cdcm>% Staircase value step sizes after correct/incorrect response trials = increase number of mask dots by 4, decrease by 6.<cdcm>
<cdcm>% These values translate to val1 = 35, step sizes = [+8 -12], min = 15 dots in overall display given how staircase applied above.<cdcm>
staircase.val1 = 10;
staircase.stepSizes = [4 -6];
staircase.min = 0;
 
<cdcm>% 3 fast (1/1 rule) reversals at start to get near subject's threshold fast<cdcm>
staircase.numFastReversals = 3;
 
<cdcm>% Staircase ends after 30 reversals<cdcm>
staircase.maxNumReversals = 30;
 
<cdcm>% Report staircase value, reversed true/false, threshold estimate, number of trials run, number of reversals in experiment results.<cdcm>
<cdcm>% For staircases these record properties report at each trial.<cdcm>
staircase.report = [<cdsm>"val"<cdsm> <cdsm>"reversed"<cdsm> <cdsm>"threshold"<cdsm> <cdsm>"numTrialsRan"<cdsm> <cdsm>"numReversals"<cdsm>];
 
 
addToExperiment(staircase)
<cdcm>% ---<cdcm>
 
 
 
<cdcm>% You can call "viewExperiment" and "viewExperiment -d" to visualize trials<cdcm>
 
 
[results, resultsMatrix] = runExperiment;