PXLab Manual

[Home]    [Content]    [Previous Chapter]    [Next Chapter]


The Design of an Experiment

Every experiment in PXLab is defined by a design file. This is an ASCII text file which defines every aspect of the experiment. The design file is read by the PXLab control program which then builds a data structure describing the experiment. This data structure controls the experimental run. It is important to always keep in mind that the design file is not interpreted at run time but is only used to build the design tree structure for controlling the experiment. This implies that the design file does not contain implicit procedural elements but contains only declarative statements. And the sequence of these declarative statements in general does not define the sequence of execution. We will come back to this later when looking at the sequence of assignment execution.

A design file starts with the node Experiment() which contains a block with up to three subsections:

Experiment() {
   Context() {
      ...
   }
   Factors() {
      ...
   }
   Procedure() {
      ...
   }
}

The Context()-section defines all stimulus properties. The major job of the Context()-section is to define the stimulus for a single trial. This is done by binding together stimulus elements from the PXLab collection of stimulus elements and setting these elements' parameters. The Context()-section also contains parameter settings for global parameters of an experiment like randomization properties, data file destinations, and many more properties.

The Factors()-section defines the factorial design of the experiment and binds abstract factors and factor level names to stimulus properties. This section contains a list of all independent, dependent and covariate factors and also creates a connection between the combined levels of the factors and stimulus parameters.

The Procedure()-section finally contains a list of all sessions, blocks, and trials of the experiment. For each of these procedural units the list contains the factor levels, other stimulus parameter values, and place holders for experimental data.

The Stimulus Context

The Context()-section of the design file defines the experimental stimuli. In the PXLab language a stimulus is a DisplayList. Every single trial of an experiment corresponds to a single DisplayList object. Furthermore there are DisplayList objects which are shown at the start and the end of a session and at the start and the end of a block of trials. An experimental data collection session with a single subject will start with a single Procedure() DisplayList object, followed by a Session() DisplayList object, followed by a Block()DisplayList. The block contains a sequence of Trial() DisplayList objects. After the sequence of Trial() DisplayList objects is shown we may have a BlockEnd()and a SessionEnd() DisplayList or there may follow more Block(), Trial(), and BlockEnd()DisplayList objects. The data collection session is closed by a a ProcedureEnd() DisplayList.

A DisplayList is a sequence of Displayobjects. Display objects are the basic elements of a stimulus. The Display objects of a DisplayList are executed strictly in sequence. Suppose a single trial contains the following sequence of stimulus elements: First there is a fixation mark which also works as an attention signal. Then there is a short blank screen period followed by a stimulus word. The subject's task may be to decide whether the word is lexically correct or not. This may be done by pressing a key on the keyboard or a mouse button. After the response there will be a short delay before the next trial starts.

The DisplayList of such a trial will contain the following Display objects:

  1. FixationMark. A little cross at the center of the screen to indicate where the target word will appear and to catch the subject's attention.
  2. ClearScreen. A short clear screen interval between attention and target signal.
  3. Message. A text string which contains the target word.
  4. ClearScreen. another short clear screen break finishes the DisplayList.

Every Display object in a DisplayListhas a certain presentation duration. The Display object always is shown at the start of its presentation duration. The Display object never is removed explicitly but always is replaced by the next Display object in the DisplayList. Thus the FixationMark object is shown at the start of its presentation interval and is 'removed' by the presentation of the following ClearScreenobject. Clear screen periods are presentations of ClearScreen objects.

Display objects have properties. These are controlled by experimental parameters. The FixationMark as an example has parameters to control its position, size, line thickness, and color. PXLab calls properties like position, size, and line thickness the geometry properties and calls object colors the color properties. It also has properties which control its timing, the timing properties. And it has some properties which control procedural aspects which will be looked at later. The properties of a FixationMark object may be set by defining parameter values. This is done in the DisplayList declaration section of the design file.

    FixationMark() {
      LineWidth = 3;
      Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
      Duration = 300;
    }

This sets the line width of the FixationMark object to 3 pixels, sets the object's timer to be a timer of type CLOCK_TIMER and sets its duration to 300 ms. All other properties of the FixationMark class are unmodified from their default values. The timer property will be explained later in detail. It will be sufficient to explain here that a CLOCK_TIMER is a timer with a fixed presentation interval. The duration of the presentation interval is set to 300 ms.

Now followes the ClearScreen object for the empty time interval between the FixationMark and the Message object.

    ClearScreen() {
      Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
      Duration = 300;
    }

The only parameters we have to set here is the timer type and the duration.

Finally we have the target word presentation. This is done by a Message object which shows a centered text string.

    Message() {
      Timer = de.pxlab.pxl.TimerCodes.RESPONSE_TIMER;
    }

Most of the parameters can get their default values. We only set the timer type to a RESPONSE_TIMER. Essentially a RESPONSE_TIMER waits until a response event is detected. Thus the text string will be shown until the subject responds. It then will be replaced by another ClearScreen object.

After the response has been detected the screen is cleared again and there is a short delay before the next Trial() starts.

    ClearScreen:Break() {
      Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
      Duration = 500;
    }

Note that this ClearScreen object has 'Break' as an instance name postfix in order to distinguish it from the previous ClearScreen object. Whenever there is more than a single instance of a Display object contained in the same DisplayList then the objects must get instance name modifiers. These are appended to the Display object's class name with a colon. We now have the full Trial() DisplayList being defined:

  Trial() {
    FixationMark() {
      LineWidth = 3;
      Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
      Duration = 300;
    }
    ClearScreen() {
      Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
      Duration = 300;
    }
    Message() {
      Timer = de.pxlab.pxl.TimerCodes.RESPONSE_TIMER;
    }
    ClearScreen:Break() {
      Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
      Duration = 500;
    }
  }

There is one point missing: Where is the target word being defined? The experiment will contain many trials with different target words. The target word then must be different for each trial. In this case the experimental parameter which defines the target word is entered as an argument to the Trial() DisplayList:

  Trial(Message.Text) {
    ...
  }

The experimental parameter which defines the target word is the parameter 'Text' of the Message Displayobject. The complete instance name of this parameter is 'Trial.Message.Text'. The DisplayList prefix 'Trial', however, may be omitted in the Trial() DisplayList argument list. Inserting an experimental parameter in the argument list of a DisplayList object makes this parameter a local parameter which can change its value for every instance of the respective DisplayList. Later on when Trial()instances will be defined then we will also have to define the values of this parameter for every trial. This will be done in the Procedure()-section of the design file.

We now have defined the Trial()stimulus. One thing still remains to be done: The response parameters have to be provided. The response parameters are those parameters which actually contain data. In our example the response is connected to the Message Display object. Its timer type is a RESPONSE_TIMER. This means that its presentation period is stopped when a response event is detected. In this case the Message object's parameters ResponseTime and ResponseCode are set. ResponseTime is the time when the Message object was stopped and ResponseCode is the code of the keys which has been pressed as a response. These parameters get their value when a Trial() is actually run. The value is specific for a Trial() instance and thus should be added to the argument list of the Trial().

  Trial(Message.Text, Message.ResponseCode, Message.ResponseTime) {
    ...
  }

The Trial() instance will not define these parameters but will only put place holders into the argument lists of Trial()instances. Only after the experiment will be run will these values be defined.

PXlab stores its data into a data file whose name is derived from a subject identification code. Thus every experimental run needs a subject identification code. This code is stored in the experimental parameter SubjectCode. This is not a DisplayList parameter but a global parameter of the PXLab system. These parameters may also be set in the Context()-section of a design file. A subnode of the Context()-section is the AssignmentGroup() node. This node contains global parameter value assignments. In our example case a single assignment for the parameter SubjectCode will be sufficient.

  AssignmentGroup() {
    SubjectCode = "pxlab";
  }

Since we do not really want to collect data we use the SubjectCode value 'pxlab'. This is special in that it constitutes a valid subject identification code but also tells PXLab to overwrite successive data files. If the value of SubjectCode were different from 'pxlab' successive runs with the same value would not overwrite existing data files but automatically create unique data file names.

To complete the Context()-section we may now define DisplayListobjects for the Session()and the SessionEnd() nodes. These are stimuli which are presented at the start and the end of a session. Session()may simply be a short instruction text which also tells the subject to start the experiment by pressing any response key and SessionEnd() may be a message telling the subject that the session is done. This is done by text objects Instruction and SessionEndMessage.

  Session() {
      Instruction() {
        Text = ["Word or Not!",
		" ",
		"You will see a sequence of letters.",
                "Your task is to decide whether this is a correct word or not.",
		"Press the left mouse button or the cursor left key if
                it is a correct word and press the right mouse button
                or the right cursor key if not.",
                " ",
                "Press any key now to start!"];
      }   
    }
  }
  SessionEnd() {
    SessionEndMessage();
  }

The complete Context()-section may now be listed. It defines the stimuli for a Trial(), the Session(), and the SessionEnd(). It also tells us that every Trial() instance must have 3 arguments: The Message.Text for the stimulus and the Message.ResponseTime and Message.ResponseCode for collecting the responses.

 
Context() { 
  AssignmentGroup() {
    SubjectCode = "pxlab";
  }
  Session() {
      Instruction() {
        Text = ["Word or Not!",
		" ",
		"You will see a sequence of letters.",
                "Your task is to decide whether this is a correct word or not.",
		"Press the left mouse button or the cursor left key if
                it is a correct word and press the right mouse button
                or the right cursor key if not.",
                " ",
                "Press any key now to start!"];
      }   
    }
  }
  SessionEnd() {
    SessionEndMessage();
  }
  Trial(Message.Text, Message.ResponseCode, Message.ResponseTime) {
    FixationMark() {
      LineWidth = 3;
      Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
      Duration = 300;
    }
    ClearScreen() {
      Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
      Duration = 300;
    }
    Message() {
      Timer = de.pxlab.pxl.TimerCodes.RESPONSE_TIMER;
    }
    ClearScreen:Break() {
      Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
      Duration = 500;
    }
  }
} 

The Factorial Structure of the Experiment

The Factors()-section of the design file describes the experimental factors of the design. It declares independent, dependent, and covariate factors and factor levels. We postpone the description of the Factors()-section to a later chapter since a design file also works without a Factors()-section.

The Experimental Procedure

The Procedure()-section of the design file contains the declaration of sessions, blocks, and trials as they are presented to the subject. While the Context()-section defines the structure of each stimulus DisplayList the Procedure()-section describes the sequence of trials including the Trial()parameters. Trial, block, and session parameters are parameter values which change from one trial to the next or from one block to the next. This section lists every trial with its argument list of Trial()parameters. In our example experiment this may look like

Procedure() {
  Session() {
    Block() {
      Trial("House", ?, ?);
      Trial("Garden", ?, ?);
      Trial("Chair", ?, ?);
      Trial("Mend", ?, ?);
      Trial("Kornte", ?, ?);
      Trial("Begren", ?, ?);
      ...
    }
  }
}

We see a list of trials with three arguments corresponding to the argument list in the declaration

  Trial(Message.Text, Message.ResponseCode, Message.ResponseTime) {
    ...
  }

The first argument is the word which is presented to the subject. The second and third argument are the yet unknown parameter values of the respective response parameters. The question mark stands for 'unknown' and indicates that this parameter value changes between trials and is only valid after the data collection. The Procedure(), Session(), and Block()DisplayList objects do not have parameters in our example. The button runs the design file in a window.

The following modification enhances this little demonstration by adding feedback to the subject. This requires two major modifications: The program now must know what the correct response is for any item and the feedback must be shown on the screen. Here is the new trial declaration which includes a Feedback object.

    Trial( Message.Text, Feedback.CorrectCode, 
           Message.ResponseCode, Feedback.Response, Message.ResponseTime) {
      FixationMark() {
        LineWidth = 3;
        Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
        Duration = 300;
      }
      ClearScreen() {
        Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
        Duration = 200;
      }
      Message() {
        Timer = de.pxlab.pxl.TimerCodes.RESPONSE_TIMER;
      }
      Feedback() {
	ResponseParameter = Trial.Message.ResponseCode;
	Evaluation = de.pxlab.pxl.EvaluationCodes.COMPARE_CODE;
        CorrectText = "%Trial.Message.ResponseTime@i% ms";
        FalseText = "False!";
        Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
        Duration = 1600;
      }
      ClearScreen:Break() {
        Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
        Duration = 500;
      }
    }

We have added the Feedback object to the display list immediately after the subject has responded. The Feedback object is a little bit more complicated than the other Display objects of this declaration. It watches a certain response parameter and evaluates the respective ResponseCode value according to a given method. It defines text for correct and false responses and shows this text for the given time. The trial argument list now also contains the parameters Feedback.CorrectCode and Feedback.Response. Feedback.CorrectCode contains the codes of responses which are considered to be correct. The value of this parameter has to be declared for each trial in the Procedure()-section:

  Procedure() {
    Session() {
      Block() {
        Trial( "House", yes, ?, ?, ?);
        Trial( "Garden", yes, ?, ?, ?);
        Trial( "Chair", yes, ?, ?, ?);
        Trial( "Mend", no, ?, ?, ?);
        Trial( "Kornte", no, ?, ?, ?);
        Trial( "Begren", no, ?, ?, ?);
      }
    }
  }

To simplify writing we have defined two new parameters named 'yes' and 'no' which actually contain the respective response codes for 'yes' and 'no' responses. These declarations are contained in the global AssignmentGroup()-section:

    AssignmentGroup() {
      SubjectCode = "pxlab";
      new yes = [de.pxlab.pxl.KeyCodes.LEFT_KEY, de.pxlab.pxl.KeyCodes.LEFT_BUTTON];
      new no = [de.pxlab.pxl.KeyCodes.RIGHT_KEY, de.pxlab.pxl.KeyCodes.RIGHT_BUTTON];
    }

Note that we not only allow keyboard cursor key responses but we also allow mouse button responses. Here is the complete design file:

Experiment() {
  Context() {
    AssignmentGroup() {
      SubjectCode = "pxlab";
      new yes = [de.pxlab.pxl.KeyCodes.LEFT_KEY, de.pxlab.pxl.KeyCodes.LEFT_BUTTON];
      new no = [de.pxlab.pxl.KeyCodes.RIGHT_KEY, de.pxlab.pxl.KeyCodes.RIGHT_BUTTON];
    }
    Session() {
      Instruction() {
        Text = ["Word or Not!",
		" ",
		"You will see a sequence of letters. Your task is to decide whether this is a correct word or not.",
		"Press the left mouse button or the cursor left key if it is a correct word and press the right mouse button or the right cursor key if not.",
                " ",
                "Press any key now to start!"];
      }   
    }
    Trial( Message.Text, Feedback.CorrectCode, Message.ResponseCode, Feedback.Response, Message.ResponseTime) {
      FixationMark() {
        LineWidth = 3;
        Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
        Duration = 300;
      }
      ClearScreen() {
        Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
        Duration = 200;
      }
      Message() {
        Timer = de.pxlab.pxl.TimerCodes.RESPONSE_TIMER;
      }
      Feedback() {
	ResponseParameter = Trial.Message.ResponseCode;
	Evaluation = de.pxlab.pxl.EvaluationCodes.COMPARE_CODE;
        CorrectText = "%Trial.Message.ResponseTime@i% ms";
        FalseText = "False!";
        Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
        Duration = 1600;
      }
      ClearScreen:Break() {
        Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
        Duration = 500;
      }
    }
    SessionEnd() {
      SessionEndMessage();
    }
  }
  Procedure() {
    Session() {
      Block() {
        Trial( "House", yes, ?, ?, ?);
        Trial( "Garden", yes, ?, ?, ?);
        Trial( "Chair", yes, ?, ?, ?);
        Trial( "Mend", no, ?, ?, ?);
        Trial( "Kornte", no, ?, ?, ?);
        Trial( "Begren", no, ?, ?, ?);
      }
    }
  }
}

[This file was last updated on July 15, 2010, 12:07:01.]

[Home]    [Content]    [Previous Chapter]    [Next Chapter]