This file is indexed.

/usr/share/psychtoolbox-3/PsychDemos/PsychExampleExperiments/MinimumMotionExp.m is in psychtoolbox-3-common 3.0.11.20140816.dfsg1-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
function MinimumMotionExp(writeResult)
% MinimumMotionExp([writeResult=0])
%
% Minimum motion luminance measurement (Anstis and Cavanagh). Rapid,
% precise, and works well down to 1hz, or 1  cpd. Self-contained script,
% with optional gamma file input or default.
%
% Measures red/green and blue/green foveal or extrafoveal equiluminance
% using the Cavanagh, MacLeod and Anstis (1987) sinusoidal minimum motion
% stimulus, here drawn as a windmill.
%
% One component of the test stimulus is a rotary sine wave where peaks of 1
% phosphor coincide with troughs of a second phosphor. This is counterphase
% modulated in cosine phase is space and time. A "luminance lure" grating
% is added in spatial and temporal quadrature (sine phase). The stimulus
% appears in clockwise or anticlockwise motion, depending on the relative
% luminance of two phosphors. The ratio of the two phosphor intensities is
% adjusted using the mouse. The isoluminant point is the precise point
% where the motion direction changes, and there the motion is replaced by
% counterphase flicker. User selects whether to measure the red phosphor
% luminance relative to the green, the blue phosphor luminance relative to
% the green, or the blue phosphor luminance relative to the red. Reported
% values are the ratios of phosphor luminances when each phosphor is at
% maximum intensity. Average stimulus chromaticity and luminance are kept
% constant during the adjustment.
%
% The stimulus is produced by clut animation (palette mode). If BPP is
% nonzero, we assume that DVI video output is relayed to the monitor via a
% CRS BitsPlusPlus device, or via a DataPixx/ViewPixx device allowing fine
% control of modulation depths and corresponding precision in luminance
% estimates. Otherwise set BPP to zero. In either case, Psychophysics
% Toolbox 3 and Matlab 7.4 or later (or Octave 3.2 or later) are required.
%
% Note: Writing of result files is disabled, but can be enabled in a
% "production setting" by providing the optional argument 'writeResult' as
% 1.
%
% This script is contributed by Don MacLeod, UCSD. It was modified by Mario
% Kleiner for portability across all supported operating systems and for
% optimized speed.
%

% History:
% 03/03/08  Initial checking by MK on behalf of Don MacLeod.
% 12/20/09  Add support for VPixx DataPixx. (MK)
% 08/19/12  Use PsychImaging CLUT mapping mode instead of moglClutBlit().
%           This simplifies code somewhat. (MK)

clc;

disp('============================ Minimum Motion Windmill ==============================================')
disp('==A fast, precise, versatile and eternally fascinating photometric technique for the computer age==');
fprintf('\n');

% Set writeResult to 1 if you want to actually really write result files.
if nargin < 1 || isempty(writeResult)
    % It is == 0 by default to suppress writing when running as PTB demo.
    writeResult = 0;
end

useBPPString = input('Do you want to use the Bits++ box or DataPixx/ViewPixx for stimulation [y/n]? ', 's');
if strcmpi(useBPPString, 'y')
    BPP = 1;  % Use high precision display device in CLUT mode.
else
    BPP = 0;  % Use standard 8 bpc graphics card.
end

if (writeResult > 0) && ~exist('data','dir')
    mkdir(data);  % create this subdirectory if it doesn't already exist
end

% Abort script if it isn't executed on Psychtoolbox-3:
AssertOpenGL;

% Use common key mapping for all operating systems and define the escape
% key as abort key:
KbName('UnifyKeyNames');
escape = KbName('ESCAPE');

%***************************************************
%           -- Gamma compensation preparations --
% __________________________________________________
% define inverse gamma table...Note, this is what PTB calls the gamma table
% Entries are video signal levels, indexed by intensity
GAMMA = 2.7;

if BPP
    disp('Using CRS Bits++ in Bits++ CLUT mode or DataPixx/ViewPixx in L48 mode.')
    GTBLENGTH = 65536;
else
    disp('Using standard graphics card with 8 bpc framebuffer.')
    GTBLENGTH = 256;
end

disp('For default gamma compensation (gamma = 2.7), just hit enter at the following prompt: ');
invgammafilename = input...
    (['File with Nx3 InvGammaTablevariable listing the video outputs (0 to'...
    num2str(GTBLENGTH) ') producing r,g,b intensities ~ row index; will  add .mat extension)'...
    '\nOr, for default gamma compensation (gamma = ' num2str(GAMMA) '), just hit enter: '], 's');

if (~isempty(invgammafilename))
    eval(['load ' invgammafilename '.mat -mat']);
else
    if BPP
        InvGammaTable=  repmat(   floor((GTBLENGTH-.01)*(linspace(0,1,GTBLENGTH)'.^(1/GAMMA))), 1,3); % 0:65535 integer entries
    else
        InvGammaTable=  repmat(   (linspace(0,1,GTBLENGTH)'.^(1/GAMMA)), 1,3); % normalized entries, 0 to 1
    end
end

%***************************************
%       -- Parameter setting --
% ______________________________________

nblocks =input('Number of settings per condition: ');
subjectsname = input('Name of subject (only internal use!): ','s');
fname = input('File name prefix for storing new data (relative to data subdirectory, will  add .mat and .txt extensions):  ','s');
sectorspercycle = 80;  % per cycle, 20 or so is generally plenty; 254 is limit for 256 long Clut (1 is background and 256 is fixation)
ncycles = 18;   % number of windmill segments; spatial frequency in cpd is sectorspercycle/(pi*(avg of inner, outer diameters))
% ...might alternatively define spatial frequency first, derive ncycles, or else make these and/or drift rate condition-dependent. 
% ncycles must be integer (to allow the stimulus to be drawn only once, repeated in frame-dependent colorings.) 
currentcols=zeros(256,3);
DateOfExperiment = date; %#ok<*NASGU>
MAXCOL = 1; % Palette rgb intensity values will range from zero to MAXCOL
ADAPTRGB = MAXCOL*[ 0.5000  0.31   0.24 ]
lurecontrast = 0.08; % Michelson contrast of lure; .05 for precision, more for easy capture
% reasonable guess for CRT, half of max possible equal energy white, on
% scale from 0 to 1 for phosphor intensity; alternatively, request input or
% use your color calibrations etc. Palette entries are normalized below to
% max of 1 before reference to gamma table.
% Condition-dependent parameters in this script: innerdiameter, outerdiam, whichluminance (r/g:1, b/g:2, b/r:3)
conditionset = [ 11 15 3; 0 2 3]; 
nconds = size(conditionset, 1);
disp('condition-dependent parameters in this script: innerdiameter, outerdiam, whichluminance (r:1, b:2, both:3); values, 1 row per condition  are');
conditionset
VIEWDIST = 40; % cm
SCREENCM = 40; % Width in centimeters of full screen image on monitor....40 for Iiyama
disp('Spatial dimensions displayed and quoted assume that monitor screen width = viewing distance; if not, modify constants in script.')
disp('Move trackball or mouse leftward if motion is clockwise, rightward if anticlockwise, to find the balance point.');
disp('Hit escape to save and quit early; to register a setting, press any other keyboard key or a mouse button. Setting is acknowledged by a beep');
disp('Press any key to continue...'); % to avoid overwriting with PTB warnings/stimulus screen 
pause;

%***************************************************
% -- Initialise BitsPlusPlus/ install gamma table, set screen  onstants--
% -- For BPP, the actual gamma compensation comes when defining currentcols below --
% __________________________________________________

try
    % Choose screen for display: As usual, we assume the secondary display
    % is the stimulation display:
    screenid = max(Screen('Screens')); %   usual guess % 1+BPP; % if 2 screens with BPP on 2nd;     

    % Backup current graphics card gammatable, so we can restore it at
    % the end of the session:
    BackupCluts(screenid);

    % Prepare imaging pipeline:
    PsychImaging('PrepareConfiguration');
    
    % Use Bits++ for stimulation?
    if BPP
        % Use PTB's built-in setup code for Bits++ or DataPixx/Viewpixx
        % mode: This will load an identity gammatable into the graphics
        % card, so Bits++ T-Lock codes pass through unmodified. It will
        % also enable automatic updates of the Bits++ / DataPixx CLUT via
        % Screen('LoadNormalizedGammatable', ...) - PTB will automatically
        % convert CLUT's into proper T-Lock codes and draw them in the top
        % line of the frame at each Screen('Flip') - or perform the
        % equivalent action for a DataPixx or ViewPixx.
        useDPIXXString = input('Do you want to use the DataPixx box instead of Bits++ box for stimulation [y/n]? ', 's');
        if strcmpi(useDPIXXString, 'y')
            PsychImaging('AddTask', 'General', 'EnableDataPixxL48Output');
        else
            PsychImaging('AddTask', 'General', 'EnableBits++Bits++Output');
        end
    else
        % Enable CLUT animation by CLUT mapping, using a 8bpc, 256 slot clut:
        PsychImaging('AddTask', 'AllViews', 'EnableCLUTMapping');        

        % Load InvGammaTable immediately into graphics card for gamma correction:
        Screen('LoadNormalizedGammaTable', screenid, InvGammaTable);
    end

    % Open screen window with default background and imaging pipeline setup
    % for Bits+/Datapixx/Regular CLUT animation:
    wptr = PsychImaging('OpenWindow', screenid);
    
    % Open the offscreen window into which we draw the "index color image"
    % which defines the appearance of the "color wheel":
    [offwptr,screenRect] = Screen('OpenOffscreenWindow',wptr, 0); % open offscreen window

    % Perform initial flip to set display to well defined initial display
    % with background color:
    Screen('Flip', wptr);
    
    % Query duration of a video refresh interval: Technically it's the
    % minimum possible time delta between two flips, but in all setups,
    % except a few special frame-sequential stereo setups, this is the same
    % as the duration of a video refresh interval:
    flipinterval = Screen('GetFlipInterval',wptr);
    
    driftratehz = 2      % hz
    ASSUMEDREFRESHRATE = 1/flipinterval %#ok<*NOPRT> % assumedrefresh rate
    period = 1/driftratehz; % sec
    nframes = round(ASSUMEDREFRESHRATE*period); % frames (vertical retraces) per cycle
    if mod(ASSUMEDREFRESHRATE,driftratehz)
        warning('Drift period is not a multiple of monitor refresh interval: rounding'); %#ok<*WNTAG> % avoidable if each frame were drawn freshly
        fprintf('Stipulated drift rate: %f Hz, actual %f Hz\n', driftratehz, 1/(flipinterval*nframes));
    end
    
    % Hide mouse cursor:
    HideCursor;
    
    % Compute display dimensions:
    Width=RectWidth(screenRect);
    Height=RectHeight(screenRect);
    ppd=(Width/2)/(atan(SCREENCM/2/VIEWDIST) * 180.0 / pi); % pixels per degree, averaged over screen width;  24 ppd for Iiyama 1280x1024) at 40cm
    BACKINDEX = 1;  % Clut index for background

    %***************************************************
    %           -- Schedule conditions and begin experiment --
    % __________________________________________________
    datalist = [];
    for i=1:nblocks % create a list of condition indexes (to the conditionset list) in random order
        condindex(:,i)=randperm(nconds)'; %#ok<*AGROW> % =(1:nconds);%
    end
    
    % Preallocate some matrices needed later to speedup Matlabs processing:
    rphosvals = zeros(nframes, 256);
    gphosvals = zeros(nframes, 256);
    bphosvals = zeros(nframes, 256);
    currentdvivals = zeros(256,3);

    % Switch to realtime priority:
    Priority(MaxPriority(wptr));
    
    NTrials=nblocks*nconds;
    % nblocks = Number of settings per condition
    % nconds = Number of conditions
    for trial = 1:nblocks*nconds  % each 'trial' ends with a setting
        inrad = conditionset(condindex(trial),1)/2; % first condition-dependent parameter is inner diamete
        outrad = conditionset(condindex(trial),2)/2;
        whichluminance = conditionset(condindex(trial),3);
        keyisdown = 0; keycode=0;
        disp(['Trial ' ,num2str(trial),' of ', num2str(NTrials)]);
        disp(['Outer Radius: ', num2str(outrad), 'degrees,  Mean spatial frequency in cpd: ', num2str(ncycles/(outrad+inrad)/pi)])

        % Compute the spatiotemporal cosine for test stimuli, sine for lure
        sectorindex = 1:sectorspercycle; % index for the segments in the annulus
        spacesine = sin(2*pi*sectorindex./sectorspercycle);
        spacecosine = cos(2*pi*sectorindex./sectorspercycle);
        frameindex = 1:nframes; % index for time frames of stimulus
        timesine = sin(2*pi*frameindex./nframes);
        timecosine = cos(2*pi*frameindex./nframes);
        stsine = timesine'*spacesine;       % luminance lure amplitude, = spatiotemporal standing sine wave
        stcosine = timecosine'*spacecosine; % test amplitude, spatiotemporal standing cosine wave
        luredeltas(:,:,1) = (lurecontrast*stsine).*ADAPTRGB(1);  % nframe by sectorspercycle matrix of r values
        luredeltas(:,:,2) = (lurecontrast*stsine).*ADAPTRGB(2);  % g values
        luredeltas(:,:,3) = (lurecontrast*stsine).*ADAPTRGB(3);  % b values
        testdeltas(:,:,1) = ADAPTRGB(1)*ones(size(stcosine));  % nframe by sectorspercycle matrix of r values; phase compensation, if needed, could go here
        testdeltas(:,:,2) = ADAPTRGB(2)*ones(size(stcosine));  % nframe by sectorspercycle matrix of g values
        testdeltas(:,:,3) = ADAPTRGB(3)*ones(size(stcosine));  % nframe by sectorspercycle matrix of b values

        % Draw stimuli to offscreen window once: about 12ms for 180 arcs.
        Screen('FillRect', offwptr, BACKINDEX-1, screenRect); 
        Arc = 360/(ncycles*sectorspercycle);
        for i = 0: round(360/Arc) - 1 % =ncycl*nsect/2-1 rounding should not be needed if ncycles*sectorspercycle is submultiple of 360
            Screen('FillArc', offwptr, (mod(i,sectorspercycle)+1), [Width/2-(outrad*ppd) Height/2-(outrad*ppd) Width/2+(outrad*ppd) Height/2+(outrad*ppd)], Arc*i, Arc); 
        end
        
        % a small inner region of radius ncycles/pi pixels will show aliasing, so take it out (reduce ncycles if you need it smaller):
        centerradius = max(inrad*ppd, ncycles/pi);  
        Screen('FillOval', offwptr, BACKINDEX-1, [Width/2-(centerradius) Height/2-(centerradius) Width/2+(centerradius) Height/2+(centerradius)]); % inner gray circle
        Screen('FillRect', offwptr, 255, [Width/2-(.1*ppd) Height/2-(.1*ppd) Width/2+(.1*ppd) Height/2+(.1*ppd)]); % 6 min arc fixation point
        
        % Query mouse x position 'xi' to get initial setting for first
        % animation frame:
        loopcount=0; whichframe=0;
        [xi yi button] = GetMouse(wptr);

        % Perform flip at start of trial to sync us to retrace and get a
        % timestamp 'vbl' of start of trial:
        vbl = Screen('Flip', wptr);

        while ~keyisdown && ~any(button) % loop to display motion, with adjustable luminances, until a key or mouse button is pressed: about 1 msec
            loopcount=loopcount+1;
            whichframe=mod(whichframe+1,nframes);
            tstart = GetSecs;
            
            % Mouse and keyboard queries:
            oldxi = xi;
            [xi yi button] = GetMouse(wptr);       % gets mouse coordinates and button state.
            
            % We check the keyboard only every 10'th redraw, as KbCheck is
            % relatively expensive at least on OS/X (about 1 msec):
            if mod(loopcount, 10) == 0
                [keyisdown secs keycode] = KbCheck;    % is a key pressed of the keyboard, keyisdown is a logical variable if key is pressed
            end
            
            % if mouse has moved, get new lum value, update colors (not
            % just the current frame) - recompute all CLUT's for all
            % frames:
            if( loopcount == 1 || xi ~= oldxi)
                if whichluminance==1    % rlum (fraction of max g that is equiluminous with max r)
                    incphos = 2;    % g is incremented with mouse xi
                    refphos = 1;    % red phosphor is "standard", generally maximal modulation but can be reduced if necessary
                    staticphos = 3; % third phosphor is unmodulated, just used to make adaptation stimulus white
                elseif whichluminance==2 % blum (fraction of max g that is equiluminous with max b)
                    incphos = 3;    % b is incremented with mouse xi
                    refphos = 2;    % green phosphor is "standard", generally maximal modulation but can be reduced if necessary
                    staticphos = 1; % third phosphor is unmodulated, just used to make adaptation stimulus white
                elseif whichluminance==3 % compare red with blue to get fraction of max r that is equiluminous with max b
                    incphos = 1;    % r is incremented with mouse xi
                    refphos = 3;    % blue phosphor is "standard", generally maximal modulation but can be reduced if necessary
                    staticphos = 2; % third phosphor is unmodulated, just used to make adaptation stimulus white
                end

                refdeltalimit = min([MAXCOL-ADAPTRGB(refphos) ADAPTRGB(refphos)]); % maximum available modulation around ADAPTRGB for "ref" phosphor
                incdeltalimit = min([MAXCOL-ADAPTRGB(incphos) ADAPTRGB(incphos)]); % maximum available modulation around ADAPTRGB for "inc" phosphor

                % lum is the luminance of the full intensity of red or
                % blue phosphor as a fraction of that of the green
                % phosphor, or (if rorblum = 3) of the blue relative to
                % the red
                lum = exp(8*(xi./Width -.5));  % new rlum or blum value from mouse, range exp(-4) to exp(4) or about .02: 50, log spacing

                rangescaler = min(refdeltalimit, incdeltalimit/lum);    % neither phosphor will go out of range
                testdeltas(:,:,refphos) = (rangescaler*stcosine);       % nframe by sectorspercycle matrix of 'refphos' values
                testdeltas(:,:,incphos) = (-lum*rangescaler*stcosine);  % incphos values, OK for low lum but can exceed range, so...
                testdeltas(:,:,staticphos) = zeros(size(stcosine));     % nframe by sectorspercycle matrix of b values: put outside loop?

                rphosvals(:,BACKINDEX+[1:sectorspercycle]) =   max(0,ADAPTRGB(1) + testdeltas(:,:,1) + luredeltas(:,:,1)); %#ok<*NBRAK> % '-lure' to make direction of response to mouse intuitive
                gphosvals(:,BACKINDEX+[1:sectorspercycle]) =   max(0,ADAPTRGB(2) + testdeltas(:,:,2) + luredeltas(:,:,2));
                bphosvals(:,BACKINDEX+[1:sectorspercycle]) =   max(0,ADAPTRGB(3) + testdeltas(:,:,3) + luredeltas(:,:,3));
                rphosvals(:,256) = 0; % fixation point
                gphosvals(:,256) = 0;
                bphosvals(:,256) = 0;
                rphosvals(:,BACKINDEX) = ADAPTRGB(1); % background
                gphosvals(:,BACKINDEX) = ADAPTRGB(2);
                bphosvals(:,BACKINDEX) = ADAPTRGB(3);
            end

            % Select CLUT to use for next video refresh:
            currentcols = [rphosvals(whichframe+1,:)'  gphosvals(whichframe+1,:)'  bphosvals(whichframe+1,:)']; % list of rgb triplets for current frame

            % Timing check:
            if whichframe == 0 && loopcount > 1   % report actual cycletime if not nframes*flipinterval
                t_end = GetSecs;
                cycletime = t_end - tstart;
                if cycletime > (nframes +1) * flipinterval
                    fprintf('Requested drift rate not attained on cycle %f: reduce sectorspercycle (per spatial cycle?) %fHz vs. %fHz\n', loopcount, 1/cycletime, driftratehz)
                end
            end

            % Drawing of windmill image is only needed during first two
            % draw cycles to put the image into the two buffers of our
            % double-buffered window. As the window isn't cleared after
            % a flip, no need to redraw at all following cycles:
            if loopcount <= 2
                % Draw offscreen window with stimulus "windmill" index image to framebuffer.
                % Set filterMode == 0 to disable any kind of interpolation.
                % We want the pixels exactly as defined in the offscreen
                % window, so CLUT palette animation works:
                Screen('DrawTexture', wptr, offwptr, [], [], [], 0);
            end
            
            % now transform the desired intensities (0...1) into digital
            % video card output (DVI) values for R,G,B, using the provided
            % gammatable or else the default one
            
            % Different methods for Bits++ vs. standard graphics:
            if BPP
                % Bits++ CLUT animation: One unified Bits++ CLUT for both,
                % CLUT animation and display gamma correction:
                
                %-------------------------------------------
                % Derive needed DVI values, currentdvivals, by
                % Gamma-compensating the currentcols palette, and draw it as a
                % Clut to line 1 of the on-screen window of the frame buffer
                %-------------------------------------------
                tmpcols = max(ceil(65536*currentcols/MAXCOL),1); % currentcols, rescaled 1 to 65536
                
                % Gamma correction via table lookup in InvGammaTable:
                for gun = 1:3
                    currentdvivals(:,gun) = floor(InvGammaTable(tmpcols(:,gun),gun)); % 0 to 65535; use uint16 table, intensities?
                end
                
                % Final Clut needs to be in normalized range [0.0 - 1.0] for Screen's
                % builtin Bits++ CLUT update function to work:
                Clut = currentdvivals / 65535;

                % Tell Screen() to upload this 'Clut' to the Bits++ box at
                % next invocation of Screen('Flip') -- it will draw the
                % proper T-Lock line at the top of the stimulus image. This
                % is faster than doing it with the old Matlab encoding
                % functions! The flag '2' enables this special mode,
                % whereas a flag of 0 or 1 would affect the standard gamma
                % tables of the graphics card:
                Screen('LoadNormalizedGammaTable', wptr, Clut, 2);                
            else
                % Standard graphics: Gamma correction is already done by
                % the hardware gammatables of the graphics card (see setup
                % at top of script). We just need to apply the current CLUT
                % 'currentcols' to the color index image in the onscreen
                % window to convert it into the final true-color RGB
                % stimulus image for this frame.
                %
                % We ask the imaging pipeline to apply itto the framebuffer
                % at next 'Flip', similar to what the Bits++ path does:
                Screen('LoadNormalizedGammaTable', wptr, currentcols, 2);
            end
            
            % Show updated stimulus image (or same image with updated
            % Bits++ CLUT) at next display vertical retrace (when = []),
            % disable clearing of framebuffer after flip ( == 2), because
            % we totally overwrite the framebuffer anyway at next draw
            % cycle:
            vbl = Screen('Flip', wptr, vbl, 2 ); % 2 = on next screen refresh, and don't clear the frame buffer

            % End of this redraw cycle...
        end % exit from 'while' loop on mouse or keyboard keypress

        % acknowledge keypress received, setting will be recorded.
        beep;

        % Need a drawnow, so beep() works properly with all Matlav versions.
        drawnow; 

        % Abort if ESCape key pressed:
        if keyisdown && keycode(escape)
            break;
        end
        
        % wait until key is released, to avoid multiple saves of one setting
        while keyisdown || any(button)
            keyisdown = KbCheck;
            [xi, yi, button] = GetMouse(wptr); %#ok<*ASGLU>
        end
   
        % Modification for PTB demo: Only write results if writeResult = 1:
        if writeResult
            % Save results of this trial to file:
            datalist(trial,:)=[conditionset(condindex(trial),:) lum]; % append the luminosity implied by this setting to the list in datalist
            if whichluminance==1
                disp(['rlum, luminance of red phosphor relative to green =' num2str(lum)]) % show result for this setting
            elseif whichluminance==2
                disp(['blum, luminance of blue phosphor relative to green =' num2str(lum)])
            elseif whichluminance==3
                disp(['Luminance of blue phosphor relative to red =' num2str(lum)])
            end
            % just about everything to .mat file
            eval(['save ./data/' fname '.mat', ' whichluminance', ' VIEWDIST', ' subjectsname', ... 
                 ' datalist', ' screenRect', ' ADAPTRGB', ' NTrials', ' conditionset' , ' ASSUMEDREFRESHRATE',...
                 ' period', ' sectorspercycle', ' nframes', ' ncycles', ' nconds', ' nblocks', ' DateOfExperiment',...
                 ' ppd', ' rangescaler', ' refdeltalimit',  ...
                 ' fname', ' driftratehz', ' lurecontrast', ' inrad', ' outrad', ' incdeltalimit']);
            eval(['save ./data/' fname '.txt datalist /ascii']); % save datalist matrix to chosen .txt ascii file
        end

        keyisdown=0;
        
        % Reposition invisible mouse cursor to set new random offset:
        SetMouse(round(Width*rand),round(Height*rand),wptr) % random offset for next setting
        
        % End of this trial - Next trial...
    end

    %***************************************************
    %                 -- Clean up --
    % __________________________________________________
    
    if BPP
        % Load identity mapping CLUT into Bits++ / DataPixx, so it works
        % again as a normal display:
        BitsPlusPlus('LoadIdentityClut', wptr);
    end
    
    % Do a single flip to clear out display to background color:
    Screen('Flip', wptr);
    
    % Switch back to normal priority:
    Priority(0);

    % Restore pre-session gammatable into graphics card:
    RestoreCluts;

    % Close window and release all ressources:
    Screen('CloseAll');

    % Done!
    return;
    
catch %#ok<*CTCH>
    % Cleanup in case of error and error handling:
    
    % Switch back to normal priority:
    Priority(0);

    % Load identity mapping CLUT into Bits++, so it works as a normal
    % display:
    if BPP && exist('wptr', 'var')
        BitsPlusPlus('LoadIdentityClut', wptr);
    end
    
    % Restore pre-session gammatable into graphics card:
    RestoreCluts;
    
    % Close window and release all ressources:
    Screen('CloseAll');

    % Rethrow the error for Matlabs error reporting to kick in:
    psychrethrow(psychlasterror);
end

return;