3CV1 Playback Program

[From Bruce Abbott (950128.1140 EST)]

The 3CV1 program I posted earlier saves data to an ASCII text file for later
analysis. However, wouldn't it be nice to be able to look over the
shoulders of your participants to see exactly what they were doing during a
run? Watching the shifting cursors may reveal dynamic aspects of their
behavior that are difficult to extract from static graphical displays of
variables. The program below allows you to "play back" a run. It
reproduces the targets and cursors as the participant saw them, and adds
cursors showing the disturbances and the participant's handle (mouse) movements.

In the data I have examined thus far, the jerkiness of the mouse movements
(caused by friction) is quite apparent in the display, suggesting that I
should try to find a way to reduce this problem (perhaps by reducing mouse
sensitivity, for example). Even so, correlations between model and handle
have been very high (around 0.99) and RMS error has been low (around 3-6
pixels). Given the insights that can be gained by examining the action
during a run, I would suggest including a playback mode as part of the
standard PCT analysis toolkit. Try it!

Regards,

Bruce

···

*********************************************************************
program CV1PLAY; { VERSION OF 950128 }

{
This program "plays back" a run created and saved to disk by the 3CV1.PAS
compensatory tracking demonstration program. The display shows the three
cursors and their targets, which were visible to the participant during
the original run, plus additional cursors representing the three
disturbances (green) and the screen-equivalent of the participant's
handle position (yellow).

Program adapted by B. Abbott from THREECV1 created by Bill Powers.
}

Uses Dos,Crt,Graph,grUtils;

const
  MAXDATA = 3600;

type datalist = array[1..MAXDATA] of integer;
     listptr = ^datalist;
     dataarray = array[1..3] of datalist;
     dataptr = ^dataarray;

var i,j,maxx,maxy,NPoints,oldh: integer;
    slow,d1,d2,d3: real;
    maxcolor: word;
    dist: dataptr;
    mc,c: dataptr;
    mh,handle: listptr;
    r, oldc, oldd: array[1..3] of integer;
    ch: char;
    k: real;
    st,num: string[80];
    FileRead: boolean;
    Filename: Pathstr;

procedure InitScreen;
begin
  ClrScr;
  InitGraphics;
  MaxX := GetMaxX; MaxY := GetMaxY;
  maxColor := getmaxcolor;
end;

procedure labelscreen;
begin
  setcolor(white);
  outtextxy(0,0,'COMPENSATORY TRACKING: PLAYING BACK ' + Filename);
  setcolor(lightred); outtextxy(0,40,'Targets');
  setcolor(white); outtextxy(0,60,'Cursors');
  setcolor(lightgreen); outtextxy(0,80,'Disturbances');
  setcolor(yellow); outtextxy(0,100,'Handle');
  setcolor(white);
end;

procedure drawcursor(c,n,init: integer);
begin
  if init = 0 then
  line(oldc[n], maxy div 2 - 60 + 25*(n - 1),
       oldc[n], maxy div 2 - 45 + 25*(n - 1));
  oldc[n] := c;
  line(oldc[n], maxy div 2 - 60 + 25*(n - 1),
       oldc[n], maxy div 2 - 45 + 25*(n - 1));
end;

procedure drawhandle(h,init: integer);
begin
  setcolor(yellow);
  if init = 0 then
    line(oldh, maxy div 2 + 15,
         oldh, maxy div 2 + 30);
    oldh := h + maxx div 2;
    line(oldh, maxy div 2 + 15,
         oldh, maxy div 2 + 30);
  setcolor(white);
end;

procedure drawdisturb(d,n,init: integer);
begin
  setcolor(lightgreen);
  if init = 0 then
  line(oldd[n], maxy div 2 - 60 + 25*(n - 1),
       oldd[n], maxy div 2 - 45 + 25*(n - 1));
  oldd[n] := d + maxx div 2;
  line(oldd[n], maxy div 2 - 60 + 25*(n - 1),
       oldd[n], maxy div 2 - 45 + 25*(n - 1));
  setcolor(white);
end;

procedure drawtargets;
begin
setcolor(lightred);
for i := 1 to 3 do
  begin
    line(maxx div 2, maxy div 2 - 60 + 25*(i - 1),
         maxx div 2, maxy div 2 - 45 + 25*(i - 1));
  end;
setcolor(white);
end;

function FileExists(Filename: PathStr): Boolean;
var
  TextFile: Text;
begin
{$I-}
  Assign(TextFile, Filename);
  Reset(TextFile);
  Close(TextFile);
{$I+}
  FileExists := (IOResult = 0);
end;

procedure ReadData(var FileRead: boolean);
var
  DataFile: Text;
  i, j, result: integer;
begin
  Filename := ParamStr(1);
  if Filename = '' then
    repeat
      gotoXY(1, 5); Write('Enter Data Filename or type QUIT to exit: ');
      Readln(Filename);
    until Filename <> '';
  for i := 1 to length(Filename) do
    Filename[i] := upcase(filename[i]);
  if Filename = 'QUIT' then
    begin
      FileRead := False;
      writeln('QUIT: No files read or written');
      Exit;
    end;
  gotoXY(1, 6); write('Filename = ', Filename);
  if FileExists(Filename) then
  begin
    i := 0;
{$I-}
    Assign(DataFile, Filename);
    Reset(DataFile);
    Result := IOResult;
{$I+}
    if Result = 0 then
      begin
        gotoXY(1, 7); write('Reading ',Filename);
        if (NOT EOF(DataFile)) then
          Readln(DataFile, r[1], r[2], r[3], handle^[1]);
        while (NOT EOF(DataFile)) do
          begin
            inc(i);
            Readln(DataFile, c^[1,i], c^[2,i], c^[3,i], handle^[i]);
            dist^[1,i] := c^[1,i] - r[1] - handle^[i];
            dist^[2,i] := c^[2,i] - r[2] - handle^[i];
            dist^[3,i] := c^[3,i] - r[3] - handle^[i];
            gotoXY(1, 8); write(i:5, ' data points read');
          end;
        Close(DataFile);
        FileRead := true;
        if i > MAXDATA then NPoints := MAXDATA else NPoints := i;
      end
    else
      begin
        FileRead := false;
        gotoXY(1, 8); write('Unable to read data file...');
      end;
  end
  else
    begin
      FileRead := false;
      gotoXY(1, 8); write('Unable to find ', Filename);
    end;
end;

procedure RunExpt;
var h,k,i: integer;
begin
for k := 1 to 3 do
   begin
{ oldc[k] := c^[k,1]; }
     drawcursor(oldc[k],k,1);
     drawdisturb(oldd[k],k,1);
     drawhandle(handle^[i],1);
   end;
for i := 1 to NPoints do
   begin
   retrace;
   for k := 1 to 3 do
    begin
     drawcursor(c^[k,i],k,0);
     drawdisturb(dist^[k,i],k,0);
     drawhandle(handle^[i],0);
    end;
   if keypressed then break;
  end;
  if keypressed then ch := readkey;
end;

begin
ClrScr;
InitScreen;
RestoreCrtMode;
new(dist);
new(c);
new(mh);
new(mc);
new(handle);
ReadData(FileRead);
if FileRead then
   begin
     setgraphmode(graphmode);
     clearviewport;
     labelscreen;
     setwritemode(XORPUT);
     drawtargets;
     RunExpt;
     RestoreCrtMode;
     closegraph;
   end;
dispose(handle);
dispose(mc);
dispose(mh);
dispose(c);
dispose(dist);
end.

Tom Bourbon [950131.1708]

[From Bruce Abbott (950128.1140 EST)]

The 3CV1 program I posted earlier saves data to an ASCII text file for later
analysis. However, wouldn't it be nice to be able to look over the
shoulders of your participants to see exactly what they were doing during a
run? Watching the shifting cursors may reveal dynamic aspects of their
behavior that are difficult to extract from static graphical displays of
variables. The program below allows you to "play back" a run. It
reproduces the targets and cursors as the participant saw them, and adds
cursors showing the disturbances and the participant's handle (mouse) movements

.

. . .Given the insights that can be gained by examining the action
during a run, I would suggest including a playback mode as part of the
standard PCT analysis toolkit. Try it!

Excellent idea, Bruce.

I've been doing someting like that whenever I run programs in which two
PCT models, or a PCT model and person, interact in real time. That lets me
show representations of handles, cursors, and targets on the screen. It's
easy then to stop the action at any time and point out certain facts, like
the agreement between handle and cursor when there is no disturbance on the
cursor, or the disparities between handle and cursor when there is a
disturbance, or the various relationships that occur during different kinds
of interactions between controllers (humans or models).

Do you think you will attend the CSG meeting in Durango, Colorado, in July?
If you do, we can all have a big show-and-tell session for our programs.
That might be a productive session. :slight_smile:

Later,

Tom