PXLab Manual

     

  1. Installation
  2. Three Steps to Run PXLab Applications
    1. Make sure that you have the latest Java software installed. You can check this by going to java.com, the download page for the Java runtime environment (JRE). Select 'Verify Installation' (German: 'Installation überprüfen') from the choices offered on this page and press the button labeled 'Verify Installation' on the subsequent page. If the response tells you that you have the latest Java then goto step 2. If you do not have the latest Java software, then go to the download section of java.com and download the latest Java runtime environment. Use the 'Typical setup' installation option and use 'Verify installation' to check whether the installation was successful. [This step is sufficient to run most of the demo applets contained in this manual and on the demo pages.]
    2. Download the file PXLabRT.zip and unzip it into a PXLab installation directory of your choice, c:\pxlab say. You will need a program to unzip the archive PXLabRT.zip. If you do not have such a program installed then Windows will offer you to open the zip archive directly. Create your destination directory c:\pxlab with the file explorer in this case and copy the archive content into this directory.
    3. Use your file explorer to find the file named pxlab.jar in the PXLab installation directory. Double click on this file and the PXLab ExRun control program starts with its Open Design File dialog. Go into the subdirectory of your installation root named pxd and choose a design file (a file with extension pxd) from this subdirectory and the experiment will start.

    Overview

    This chapter covers the steps which are necessary to install PXLab for running experiments as local applications.

    Running local PXLab applications requires two installation steps:

    1. Get and install a Java runtime environment, and
    2. install the PXLab software package.

    Of course, you can skip the first step if you already have a working Java runtime environment installed. The current version of PXLab requires Java version 6. For optimal performance you should always use the latest version of Java.

    The Java Runtime Environment

    Running PXLab applications and creating experimental design files requires a working Java runtime environment. The Sun Microsystem Java runtime environment (JRE) may be downloaded from Sun Microsystems' Java download site for several different operating systems.

    Sun's download site also contains information on how to install the software. Please always download the latest version and install the Java runtime environment (JRE) for your operating system according to these instructions. It simplifies things if you choose the 'Typical setup' option and use the 'Verify installation' option to check your installation.

    If the JRE is installed then you should be able to run the applets from the page of PXLab Demonstration Experiments.

    The Sun JRE installer installs at least one public command: 'java' which can be used to start Java applications. This command can also be used to start PXLab experiments. On the Windows operating system the JRE installer will also register Java Archive ('jar') files to be executable by double clicking on them. This feature is used by PXLab to make its archive file pxlab.jar executable.

    The PXLab Runtime Environment

    The minimal PXLab runtime environment package is contained in the archive PXLabRT.zip which contains the following directories:

    .
    the root directory contains the PXLab Java classes archive named pxlab.jar. This archive contains the complete PXLab software package. This directory also contains a file named pxlab.properties. This file can be used to tell the PXLab system where local startup initialization files may be found. This feature will be treated later.
    pxd
    contains the experimental design files which also may be found on www.pxlab.de as demos and as a lab course. Subdirectories of this directory contain support files for several demo experiments. Most of these are images needed by some experiments.
    bin
    contains some executable batch files and dynamic link libraries (DLL) for Windows. These libraries support some native extensions for Windows but are not necessary for standard PXLab applications.

    This directory structure is already contained in the zip-file PXLabRT.zip. So the only thing you have to do is to unpack the zip-file PXLabRT.zip into a single directory of your system while preserving the zip-file's directory structure. We suggest to use a directory named pxlab for installing PXLab.

    If the JRE is installed properly then double clicking on the file pxlab.jar starts the PXLab experimental runtime controller ExRun. There are several other ways to start PXLab experiments. Some of these methods are used by the batch files in subdirectory bin of the PXLab installation directory. Most of these methods require that the JRE 'knows' where the file pxlab.jar may be found.

    There are several ways to tell the JRE where the PXLab classes are. We describe the most simple one only:

    Figure out where the JRE installer has installed the software. If you choose the 'Typical setup' option then the current version (JRE 1.6.0) installs the software into a directory like C:\Program Files\Java\jre1.6.0. This directory contains a subdirectory named lib\ext for Java library extensions. Move or copy the file pxlab.jar into this directory and the Java runtime environment will always find the PXLab packages.

    Running PXLab Applications

    The subdirectory bin of the directory where you have extracted the file PXLabRT.zip into contains some batch files for starting PXLab demos and applications. These are prepared to run under Windows using the latest Sun Microsystem Java runtime environment with pxlab.jar being installed in the JRE's library extensions directory as described in the previous section.

    So in this case you may simply open a command window, change to the bin subdirectory of your PXLab installation root and run any of the following command files:

    ex.bat
    to start an experiment,
    ed.bat
    to start the PXLab Experimental Design Editor,
    vd.bat
    to start the Vision Demonstrations program,
    de.bat
    to start the PXLab Display Editor,
    caltool.bat
    to start the PXLab Color Calibration Tool.

    This only works if the location of these batch files is your current working directory and the file pxlab.jar has been copied to the JRE lib/ext directory.

    In order to run PXLab applications from every working directory you have to tell the operating system where the executable files are. This is done by adding the PXLab bin path to the system path list contained in the environment variable PATH.

    Here is how to modify PATH under Windows XP:

    From the Start-menu open the Control Panel, open 'System' and activate the panel named 'Extended'. Open the dialog 'Environment Variables'. Select the variable 'Path' of the panel 'System Variables' and press the 'Modify button. Then add 'C:\pxlab\bin' to your current definition of PATH, assuming that you did install PXLab into directory 'C:\pxlab'. Note that the various paths in PATH have to be separated by a colon. Her is an example how the final Path should look like:

       ...;C:\WINNT\System32;...;C:\pxlab\bin
    

    The chapter 'Running an Experiment' contains more detailed information on how to run PXLab applications and also on how to integrate experiments into HTML-pages.

    Data Files

    Note that the demonstration experiments in the PXLab subdirectory pxd are configured such that they write their data files into subdirectories named dat and dtr of that directory from where the PXLab application had been started. The data files will have the root name pxlab with an extension depending on the type of data file.

    System Properties

    You may skip this section at first reading.

    For some experimental applications it is quite useful to tell PXLab something about the physical system it is running on. An example is gamma correction for device independent color. In these cases PXLab needs some information about the physical screen. This type of information should be contained in files named system.pxd and screen.pxd. The location of these files must be defined in a file named pxlab.properties. A prototype properties fiel is contained in the PXLab installation directory. See chapter 'Running an Experiment' where PXLab searches for the properties file.

    The file pxlab.properties should at least contain two lines of text like

     
       pxlab.home=c:/pxlab 
       pxlab.local=c:/pxlab 
    
    telling PXLab that both its global home and its local installation directory is c:/pxlab. If the file pxlab.properties is not found then c:/pxlab is assumed to be the PXLab home directory. pxlab.home is the global PXLab installation directory and pxlab.local is a local client installation directory. The differece between the two is necessary for network installations where pxlab.home is installed on a server and pxlab.local contains information about the local client which actualla runs the experiments.

    The archive PxlabRT.zip contains this default version of pxlab.properties. If you do not install PXLab into c:/pxlab then you have to manually edit the properties file and enter the correct installation directory.

    Native Code Extensions

    You may skip this section at first reading or if you do not want to use native code extensions for Windows.

    The basic PXLab runtime package PXLabRT.zip includes some native code extensions for Windows. These are not necessary for running experiments but they will provide vertical retrace synchronization for time critical displays and DirectInput methods for special input devices. Vertical retrace synchronization requires that Microsoft DirectX is being installed which usually will be the case. The native code is contained in the dynamically linked library file pxlab.dll and some additional DLLs. This file will only be found by the operating system library loader if it is contained in a directory which is included in the PATH list of directories. Extraction from PXLabRT.zip moves the DLL files into the bin subdirectory of the installation path. If you include this directory into PATH as described earlier then everything is OK and PXLab will automatically use the native extensions. If you do not include the bin subdirectory into PATH then you should move pxlab.dll and the other DLL files into your Windows system directory which usually will be WINDOWS\System32.

    Sorry, but currently vertical retrace synchronization is available for Windows only.

    Further Archives

    The PXLab distribution contains the archive files listed below. Their internal directory structure is such that all of them should be extracted into the PXLab root installation directory.
    PXLabRT.zip contains the complete runtime environment for running PXLab experiments as local applications or applets.
    PXLabRTX.zip also contains the complete runtime environment for running PXLab experiments and some extensions which are mainly useful for running PXLab applets. This version contains two archive files: pxlabrt.jar and pxlabrtx.jar. The two together have the same content as pxlab.jar. pxlabrt.jar is optimized for applets in the sense that it contains only those parts of PXLab which are necessary to run an experiment as an applet. It does not contain the GUI components which can't be used with applets. Thus pxlabrt.jar can be much smaller than pxlab.jar. The archive PXLabRTX.zip contains additional files which are useful for implementing PXLab applet pages. This includes an optimized starter button and a PHP script for sending collected data to an arbitrary E-Mail address.
    PXLabAPI.zip contains the PXLab API documentation. Will be installed into the subdirectory doc of your PXLab root directory.
    PXLabSrc.zip contains the complete source tree to rebuild PXLab from source. If you intend to develop your own PXLab applications or extend PXLab classes by writing your own Java source code then you probably should install the source tree into a separate development directory. See the chapter named The PXLab Software API for more information on using the PXLab source code.

    If you want to download this manual then use the link which shows the complete manual as a single HTML page and use your browser's 'save as...' command to save the manual files.

     

  3. The Design of an Experiment
  4. 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, ?, ?, ?);
          }
        }
      }
    }
    

     

  5. Experimental Stimuli
  6. Stimuli are composed of Display objects. These stimuli are called DisplayList in PXLab. There are 7 DisplayList types: Procedure(), ProcedureEnd(), Session(), SessionEnd(), Block(), BlockEnd(), and Trial(). These Stimuli are presented at the respective procedural point. An exception is the Block() DisplayList which by default is not shown for the first and the BlockEnd() DisplayList which by default is not shown for the last block in a session. This behavior may be changed by setting the global parameter SkipBoundingBlockDisplays to 0.

    An experimental design may contain multiple definitions of a DisplayList type by adding an instance modifier to the declaration:

        Trial:Study(...) {
          ...
        }
        Trial:Test(...) {
          ...
        }
    

    This defines two types of Trial() DisplayLists, one called 'Trial:Study' and one called 'Trial:Test'. These definitions are completely independent. They may be used later in the Procedure()-section of the design at any point where a Trial() is admissible. The same mechanism is available for other DisplayList types.

    Sequential Presentation

    The Display objects in a DisplayList are executed strictly in sequence. A Display object is painted to the screen at the start of its presentation interval and remains there until it is replaced by the next Display object. Visual Display objects always have a background area as one of their elements. Thus every visual Display object erases the preceding visual Display object. An exception to this rule is a Display object which has the Overlay flag set. This will be treated later.

    Display objects themselves are composed from a series of DisplayElement objects. The first object in this series is the background DisplayElement. The following DisplayElement objects depend on the type of Display object. It may be a text string, a geometric object, an image, or anything else. In most cases the DisplayElement objects of a Display are shown simultanously. A subset of DisplayElement objects of a Display object which is always shown simultanously is called a timing group. The description of the respective Display object contains information about the timing groups of a Display if there are more than a single timing group.

    The Timer parameter of a Display object determines its presentation duration. There are three general types of timers: the NO_TIMER, the CLOCK_TIMER and the RESPONSE_TIMER. If a Display has a NO_TIMER Timer value then the presentation interval is 0. This means that the subsequently following Display object is shown immediately after this object has been presented. This only makes sense in combination with the Overlay flag of subsequent Display objects which is described later. A CLOCK_TIMER is a timer with a fixed duration. The duration is defined by the parameter Duration. A RESPONSE_TIMER is a timer which is stopped by some response event. Thus if a Timer is set to RESPONSE_TIMER then the presentation of the respective Display object runs until a response event is detected. The actual duration is then stored in the parameter ResponseTime. The parameter ResponseCode then contains a code which identifies the type of the response event which stopped the timer. A more detailed description of stimulus timing will be given later.

    Conditional Execution

    Every Display object has an Execute parameter which by default has the value 1. This enables Display execution. If the parameter is set to 0 at runtime then the respective Display object is not shown. This feature may be used for two purposes. One is testing where it might be useful to exclude a Display object from execution. The other one is conditional Display execution. The value of the parameter Execute may be set to a variable which contains the result of some earlier event. In this case the execution of a Display is conditional upon the result of an earlier Display object. Here is an example:

        Trial:Test(TrialCounter, Message:ON.Text, NounType, Feedback:Item.CorrectCode, Message:ON.ResponseCode, TextParagraphMultiple:RK.ResponseCode) {
          TextParagraphMultiple() {
            Timer = de.pxlab.pxl.TimerCodes.NO_TIMER;
    	LocationY = 200;
            Text = ["<= old", "new =>"];
    	FontSize = commonFontSize;
          }
          Message:ON() {
    	Overlay = de.pxlab.pxl.OverlayCodes.JOIN;
            Timer = de.pxlab.pxl.TimerCodes.RESPONSE_TIMER;
            ResponseSet = [de.pxlab.pxl.KeyCodes.LEFT_KEY, de.pxlab.pxl.KeyCodes.RIGHT_KEY];
    	FontSize = commonFontSize;
          }
          Feedback:Item() {
            ResponseParameter = "Trial:Test.Message:ON.ResponseCode";
            Evaluation = de.pxlab.pxl.EvaluationCodes.COMPARE_CODE;
    	Visible = 0;
            Timer = de.pxlab.pxl.TimerCodes.NO_TIMER;
    	FontSize = commonFontSize;
          }
          TextParagraphMultiple:RK() {
    	Execute = Trial:Test.Message:ON.ResponseCode == 0;
            Timer = de.pxlab.pxl.TimerCodes.RESPONSE_TIMER;
    	LocationY = 200;
    	Width = 800;
            Text = ["<= remember", "know =>"];
            ResponseSet = [de.pxlab.pxl.KeyCodes.LEFT_KEY, de.pxlab.pxl.KeyCodes.RIGHT_KEY];
    	FontSize = commonFontSize;
          }
        }
    

    In this example from a remember/know memory task the Display object named TextParagraphMultiple:RK is executed only if the ResponseCode parameter of the earlier Message:ON object is 0. Since Message:ON creates a 0 ResponseCode value only if the subject's response was 'old' the question for 'remember or know' is shown only if the subject responds with the 'old' key to the target item presentation in Message:ON.

    Another example may be found in an adaptive method demonstration experiment where conditional execution is used for deciding whether a subject simulation display object should be used or real data should be collected:

            PsychometricFunctionSimulator() {
    	  Execute = Simulation;
    	  pse = 320;
              jnd = 10;
              StimulusParameter = "Trial.HorizontalVerticalIllusion.CutLine";
            }
            ClearScreen() {
    	  Execute = !Simulation;
              Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
              Duration = 200;
            }
    	SetParameter:A() {
              Execute = AdaptiveProcedure == de.pxlab.pxl.AdaptiveProcedureCodes.UP_1_DOWN_1;
              Parameter = "result1";
    	  Value = Trial.HorizontalVerticalIllusion.CutLine;
            }
    

    Here the value of parameter Simulation decides whether PsychometricFunctionSimulator is executed or not. Also note that the following ClearScreen object is executed only if PsychometricFunctionSimulator is not executed. Note that the parameter Execute is a property of the display object PsychometricFunctionSimulator. this implies that the properties of this display object have to be computed even if it is not run. This means that all display object properties must have valid states in this case also.

    Display Composition: Overlays

    The Display elements of a DisplayList are in general shown strictly sequentially and every Display object has a background object which automatically destroys the previous Display. However, Display objects may be joined by setting the Overlay parameter of class Display to the value JOIN. The result is such that the DisplayElement objects of a Display are painted on top of any previously shown object and are shown simultanously. No background field is drawn for Display objects having the Overlay parameter set to JOIN.

    Thus we may show multiple Display objects simultaneously by setting the Overlay parameter of every Display object but the first to JOIN and setting the Timer of every Display object to NO_TIMER except for the last Display in the sequence. Here is an example from a design file which is a simple slide show:

        Trial(TrialCounter, Picture.FileName) {
          Picture() {
    	Duration = 2000;
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
    	Directory = "/images";
          }
        }
    

    This trial does nothing but present an image file for 2 seconds. Now suppose we want to add the file name as a title line on top of the image. We can do this by preceding the Picture object by a Message object which does have a NO_TIMER value of Timer and set the Overlay parameter of the Picture object to JOINsuch that the message text is not erased when the image is drawn:

        Trial(TrialCounter, Picture.FileName) {
          Message() {
    	Text = Trial.Picture.FileName;
            Timer = de.pxlab.pxl.TimerCodes.NO_TIMER;
    	LocationY = -160;
          }
          Picture() {
    	Overlay = de.pxlab.pxl.OverlayCodes.JOIN;
    	Duration = 2000;
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
    	Directory = "/images";
          }
        } 
    

    There are some restriction to observe for JOIN-overlay objects:

    1. The object preceding a JOIN-overlay object must have its Timer parameter set to NO_TIMER.
    2. The overlay object(s) have to have their Overlayparameter set to JOIN in order to prevent erasing of previously drawn screen elements.
    3. JOIN-overlay objects may contain only a single timing group.
    4. If a JOIN-overlay object has the parameter JustInTime set to 1 then the preceding object also must have set JustInTime to 1.
    5. If a Display object is followed by one or more JOIN-overlay objects and the Display object has its parameter Execute set to 0 then the whole chain of Display objects is not presented. The Execute parameters of the JOIN-overlay objects are ignored in this case.
    6. If a Display object with Execute set to 1 is followed by one or more JOIN-overlay objects and any of the JOIN-overlay Display objects has its parameter Executeset to 0 then this respective Display object is not presented. Thus if the non-JOIN-overlay Display object which precedes a JOIN-overlay has its Execute parameter set to 1 then the Execute parameters of subsequent JOIN-overlay objects are observed.

    It is not possible to JOIN-overlay an object which has a timer not equal to NO_TIMER. The reason for this is that PXLab's fast image preloading technique creates the joined display image in a single back buffer before the display is shown. Thus all joined components are actually shown simultanously and the timing for this joined display object actually is the timing of he last display element in a sequence of JOIN-overlay objects.

    A simpler version of an overlay without the restrictions of JOIN is the Overlay value TRANSPARENT. Display objects which have the Overlay parameter being set to TRANSPARENT behave exactly like any other display object and do not have any timing restrictions. The only visible effect of the TRANSPARENT code is to supress drawing of the background. TRANSPARENT-overlay objects, however, can not use fast image preloading. Thus they should be avoided if optimal timing precision is required.

    The effect of JOIN-overlaying an object with non-zero timer may be achived by a sequence of the original object and a duplicate with a JOIN-overlay. Here is the previous example modified such that the file name alone is shown first for 1 second and then the image is added:

        Trial(TrialCounter, Picture.FileName) {
          Message:first() {
    	Text = Trial.Picture.FileName;
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
    	Duration = 1000;
    	LocationY = -160;
          }
          Message:second() {
    	Text = Trial.Picture.FileName;
            Timer = de.pxlab.pxl.TimerCodes.NO_TIMER;
    	LocationY = Trial.Message:first.LocationY;
          }
          Picture() {
    	Overlay = de.pxlab.pxl.OverlayCodes.JOIN;
    	Duration = 2000;
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
    	Directory = "/images";
          }
        } 
    

    Note that the parameter LocationY of the second Message instance is copied from the first instance in order to have only a single place where the text location is being defined.

    The same functionality may be achieved simpler by using the TRANSPARENT code for the Picture object:

        Trial(TrialCounter, Picture.FileName) {
          Message {
    	Text = Trial.Picture.FileName;
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
    	Duration = 1000;
    	LocationY = -160;
          }
          Picture() {
    	Overlay = de.pxlab.pxl.OverlayCodes.TRANSPARENT;
    	Duration = 2000;
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
    	Directory = "/images";
          }
        } 
    

    List Overlays

    In some cases it might be useful to have an overlay span multiple Display objects which themselves are not overlays. An example being a test string telling the subject which trial number he/she is currently working at. This may be done by setting a Display object's Overlay parameter to DISPLAY_LIST. In this case the respective Display becomes an overlay for every following Display object of the DisplayList. Here is an example from a previous section:

      Trial(Message.Text, Message.ResponseCode, Message.ResponseTime) {
        Message:Counter() {
          Timer = de.pxlab.pxl.TimerCodes.NO_TIMER;
          FontSize = 20;
          Text = "Trial %TrialCounter%";
          LocationY = 300;
          Overlay = de.pxlab.pxl.OverlayCodes.DISPLAY_LIST;
        }
        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;
          FontSize = 40;
        }
        ClearScreen:Break() {
          Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
          Duration = 500;
        }
      }
    

    The Display object Message:Counter is defined as a display list overlay. It will be shown with every Display object in the list. The text shown is the string 'Trial' followed by the current value of the parameter TrialCounter. The location of the text is on the bottom of the screen.

    The overlay is shown for every Display in the list. If it should be removed earlier then the respective Display object must have the Overlay parameter set to CLEAR_DISPLAY_LIST:

         Overlay = de.pxlab.pxl.OverlayCodes.CLEAR_DISPLAY_LIST;
    

    removes the overlay from the display list beginning at the current Display object.

    Just in Time Computing

    The Display objects in a DisplayList have to be computed at runtime. This includes the preparation of text strings and other geometric properties which may be time consuming. To optimize Display presentation most of the computing is done at the start of a DisplayList. However, in some cases it may be necessary to delay computing up to that point in time when the Display has to be shown. This is true for cases where one Display content depends on the results of an earlier Display in the same DisplayList. An example might be a feedback element where the response time to a previous Display object is presented in the same DisplayList.

    Here is an example where immediately after the response a text string is shown which contains the value of the parameter ResponseTime of the Message object:

    
      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;
          FontSize = 40;
        }
        Message:Feedback() {
          Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
          Duration = 1000;
          Text = "%Trial.Message.ResponseTime@i% ms";
          JustInTime = 1;
          FontSize = 40;
        }
        ClearScreen:Break() {
          Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
          Duration = 500;
        }
      }
    

    The Message:Feedback() Display object has the parameter JustInTime set to 1. This has the effect that the parameters of this object are computed only immediately before it is shown. If the default value of 0 for JustInTime were used, then the text string of Message:Feedback() would contain the value of ResponseTime at the beginning of the DisplayList. Since JustInTime is set to 1 the computation of the text string is done after the Message object has been run and its ResponseTime parameter has been set. And thus the text string of Message:Feedback() shows the content of the ResponseTime parameter correctly.

    There exists a general feedback object named Feedback which behaves similar to Message, has its parameter JustInTime activated by default and also can do more complicated response evaluation.

    Adjustable Display Objects

    An adjustable display is a display which is shown repeatedly while one of its parameters is being changed depending on the response key being pressed. This type of adjustment is 1-dimensional such that there has to be a single pair of an up- and a down-key being defined. There also must be a stop key being defined which tells the program to accept the adjusted value.

    A display object becomes an adjustable display object if one of its local parameter assignments contains the modifier "adjustable" like in the following example of a horizontal-vertical illusion experiment:

          HorizontalVerticalIllusion()
          {
            BaseLine = 400;
            adjustable CutLine = 400;
            Timer = de.pxlab.pxl.TimerCodes.RESPONSE_TIMER;
          }
    

    The modifier marks the display object as an adjustable object and simultanously defines the respective parameter as the adjustable parameter. The timer associated with the adjustable display's timing group should be a RESPONSE_TIMER otherwise the input keys will not be accepted for adjustment and for signalling the program to finish adjustment.

    The respective adjustment keys and the adjustment step size should be explicitly defined in the global parameter assignment section:

     
          AdjustmentUpKey = de.pxlab.pxl.KeyCodes.RIGHT_KEY;
          AdjustmentDownKey = de.pxlab.pxl.KeyCodes.LEFT_KEY;
          AdjustmentStopKey = de.pxlab.pxl.KeyCodes.SPACE_KEY;
          AdjustableStep = 2;
    

    The example shows the default key definitions for adjustment. Note that in order for the above key codes to work properly the parameter ResponseSet must not be defined since it implicitly changes the response key codes!

    How does adjustment work? An adjustable display does not alter the way of showing a display list but only affects how the adjustable display itself is presented. Actually the only special thing is that the adjustable display is shown repeatedly depending on which response key code is found. If the response key code is equal to one of the up- or down key codes then the respective adjustable parameter is changed by the current adjustment step size and the display is asked to recompute its properties and is shown again. If the response code is the adjustment stop key then the next display object in the display list is shown.

    This procedure is somewhat different from the case where a STOP_KEY_TIMER is being used for a display's timing group. In this case the respective single timing group of this display is shown repeatedly until the stop key defined by the global parameter StopKey is found. For each non-stop key the display object's call-back method keyResponse() is called giving the Display itself a chance to make some modifications. This requires that the Display object has been programmed to handle the changes. The previously defined adjustment method, however, does not need any special modification of the Display object's code. It is applicable to every Display class.

    Quick Guide for Setting up an Adjustment Method

    1. Define the adjustment keys and the stopping key you want to use by defining the parameters AdjustmentUpKey, AdjustmentDownKey, and AdjustmentStopKey in the global assignment section. The defaults for these keys are the cursor up/down key and the space-key. You don't need to define these parameters if you accept the defaults.
    2. Also define a proper AdjustableStep which is the size of a single adjustment step which follows a single response key entry. This must also being done in the global parameter assignment section.
    3. Add the modifier prefix "adjustable" to the adjustable parameter's assignment statement in the display declaration section.
    4. Set the adjustable display object's Timer parameter to RESPONSE_TIMER.
    5. Make sure that the Display parameter ResponseSet is undefined and that the adjustable parameter is contained in the trial argument list.

    Here is an example for the adjustment methods. It shows the horizontal-vertical illusion pattern of an invertet T and the subject's task is to adjust the vertical line such that it looks equal in length to the horizontal line. Adjustment is done with the cursor left and right keys. The space-key signals satisfaction with the adjusted length. There are six trials in this demo.

    Where to Get Display Objects

    All Display objects are subclasses of class Display and are all contained in the package de.pxlab.pxl.display. Finding a Display object suitable for a certain experiment may be difficult since there are so many Display objects available. The starting point for finding a Display object may be the PXlab display editor DisplayEditor which contains a selection menu of all available display objects and also is able to show and interactively edit every Display object. The PXLab design editor ExDesignEditor is another way to find Display objects since ExDesignEditor also allows efficient paging through all available Display objects. If none of the available Display objects fits your plans then it is possible to write your own subclass of Display in Java and use it. There is an extra chapter about how to write new Display objects using the PXLab API. This manual also contains a chapter which describes all available Display objects.

    Which Parameters Does a Display Object Have?

    The parameters of a display are described in the PXLab API doumentation. Special display objects may be found as subclasses of class Display. Whenever ExDesignEditorimports a Display instance it also creates an assignment node for every parameter of the display. Thus the design editor always shows every parameter of a Display object as a subnode of this Display object.

    The display editor DisplayEditor also shows every parameter of a Display object in one of its display property panels. There are 3 property panels which cover three mutually exclusive groups of parameters: color, geometry, and timing. The display editor also has a menu entry in its popup menu which can show a textual list of all parameters of a Display object.

     

  7. Stimulus Elements
  8. The main building blocks for defining experimental stimuli are objects of the class Display. Every Display contains a set of primitive stimulus elements. These are elements like geometric objects (bars, disks, arrows), text paragraph objects, images or other more complicated elements. Actually the primitive stimulus elements can be rather complicated since every primitive stimulus element corresponds to a Java class whose properties are only limited by what is possible using the Java programming language. Note, however, that all stimulus primitives must be derived from the class DisplayElement which gives them some common properties.

    General Properties of Display Objects

    Every Display object has some parameters which describe its timing and its response properties. There also are some procedural parameters which control execution, computation, and the display mode. Here is a list of those experimental parameters which are properties of every Display object.

    Timer
    defines the timer type which controls the duration of the Display object's main timing group. If the Display object has more than a single timing group then each timing group will usually have its own timer being defined. Possible Timer values are defined in class TimerCodes.
    Duration
    is the intended display duration of the Display object's timing group which has Timer as its timer type.
    TimeControl
    contains the actual times when this display's timing groups have been shown. This parameter is set after the display has been shown and uses PXLab' high resolution clock.
    TimeError
    contains the timing error for every timing group of this display object which has a CLOCK_TIMER. The timing error is derived from the value of Duration and TimeControl.
    ResponseTime
    stores the actual display duration. For response driven timers this will also be the response time. For clock driven timers this parameter is derived from TimeControl.
    ResponseCode
    is a code generated by the event which stopped the display time interval. For response driven timers this will be a code generated by the response device. These codes are defined in class KeyCodes. For clock driven timers the code will be TIME_OUT. For media timers like MEDIA_START_TIMER the code will be one defined in class MediaEventCodes. If the parameter ResponseSet is defined then the response codes correspond to the position of the respective response code in the ResponseSet array.
    ResponsePosition
    contains an integer array with the xy-position of the response device when a response was detected.
    ResponseSet
    gives the set of response codes (see ResponseCode) which are acceptable as responses for stopping the respective timer. If this parameter is defined then it also installs a code conversion table such that the actual ResponseCode values are no longer the response device codes but the indices of the respective device code in the ResponseSet array.
    Execute
    is a flag which by default is set to a non-zero value. If non-zero then this Display object is actually computed and shown during stimulus display. If the flag is zero then this Display object is not shown. This flag may be used to switch off parts of a stimulus definition without changing the stimulus definition itself. Thus it is a mechanism which makes it possible to modify display lists at runtime depending on parameter values. This enables conditional execution of display objects. Note that if the execution of a display depends on a preceding display in the same display list then the display must have its JustInTimeparameter being set. See an earlier section for a more detailed description and some examples. The section 'Response Collection During A Clock Timer Interval' in chapter 'Stimulus and Response Timing' contains another example of how to use this feature.
    Overlay
    makes a Display object transparent when it is non-zero. In general this parameter will be zero. Since every Display object contains a background element which covers the whole display screen any previous Display object is erased when a new Display is shown. This makes it impossible to simultaneously show more than a single Display object. Thus all primitives which are to be visible simultaneously must be contained in a single Display object. However, if we set the Overlay flag of a Display object then its background is not drawn and thus the screen is not erased before its primitives are shown. This makes it possible to simultaneously show the primitive elements of more than a single Display object. See an earlier section for a more detailed description.
    JustInTime
    controls the point in time when this display object's properties are computed. If true (has a non-zero value) then this is done immediately before the Display object is shown. If this is false (is zero) then most properties are computed before the DisplayList is started. Use this flag only if necessary, since it may reduce timing precision. Setting this flag is necessary whenever a Display parameter value depends on the current value of a parameter of a Display contained in the same DisplayList. See an earlier section for a more detailed description.
    Screen
    selection code for the screen where this Display object should be shown in multiple screen systems. Possible code values are defined in class ScreenSelectionCodes. This parameter is ignored on single screen systems.

    Available Display Objects

    This section gives an overview on the Display objects which currently are available. The Display objects are collected into small groups of objects which have something in common. Note, however, that several Display objects have overlapping functionality such that it makes sense to check more than a single group of Display objects if a certain property is required. All the classes described herein are subclasses of the class Display which is the top level class of Display objects.

    Text Paragraph Objects

    The class TextParagraph is the superclass of several subclasses which contain paragraphs of text. Some of them contain more than a single paragraph and some have special methods to compose the paragraph from single elements which may be read from a file.

    TextParagraph
    is the top level class for paragraph of text. The paragraph's width, position, line skip and alignment may be defined by experimental parameters. A single string of text is broken into lines at carriage return characters. If line wrapping is switched on then the text is broken into lines such that it fits the given width but explicit line breaks are preserved.
    TextFile
    allows the subject to page up and down in a text file. Pages in the text file are marked by some marker string. This is intended for presenting multiple page instructions where the subject should have the possibility to page backwards.
    TextParagraphMultiple
    shows multiple paragraphs of text. The paragraphs' text content and positions are independent and may be defined by setting the respective parameters to array valued objects. Note that in this case an array valued parameter Text is interpreted to mean different paragraphs instead of different lines as it is done by the class TextParagraph.

    TextParagraphComposed
    makes it possible to read the text from a separate file. Different elements of the text may be composed by simply using codes which represent these elements.

    Message
    is a subclass of TextParagraph which by default uses a rather large font and centers the single lines of text at the screen's center.
    Instruction
    is a subclass of TextParagraph which by default shows a paragraph of text left aligned and prints the first line in bold face. Font and size are set such that it fits most instruction texts.
    TextInput
    for text input responses which are closed by the Return key.
    SessionStartMessage
    default message for starting a session.
    SessionEndMessage
    default message for the end of a session.
    BlockStartMessage
    default message for starting a block.
    BlockEndMessage
    default message for the end of a block.
    TrialMessage
    default message for a trial.
    CmdMessage
    a text message which is not shown on the subject's display screen but in the command line window which started the experiment. May be used to print messages for the experimenter while a trial is running.

    Letter Matrix Objects

    Objects which contain a matrix of letters as their major stimulus element are subclasses of LetterMatrix. Additional elements are a cue and a mask. Also included are matrices which collect responses and show feedback.

    LetterMatrix
    a matrix of letters.
    CuedLetterMatrix
    a matrix of letters witha cue.
    MaskedLetterMatrix
    a matrix of letters followed by a mask.
    LetterMatrixResponse
    a matrix of letter positions for letter responses.
    LetterMatrixFeedback
    a matrix of letters for feedback.
    NavonPattern
    a pattern of letters which itself forms a large letter.

    Sequences of Text Objects

    A series of single line text objects which are presented sequentially or with a certain SOA. These objects have more than a single timing group and thus have additional timer and duration parameters.

    SerialLearningList
    is a serially presented sequence of items.
    TwoStrings
    are two strings possibly presented with an SOA.
    RapidSerialPresentation
    a fast sequence of text strings.
    GrowingText
    an animated Display with a growing line of text which is used for delayed recognition experiments.

    Questionnaire Objects

    Multiple choice and rating scale type of lists.

    MultipleChoiceQuestion
    is a subclass of TextParagraph which shows the paragraph of text and also shows a list of selectable alternatives to chose from.
    RatingScaleQuestion
    is a subclass of TextParagraph which shows the paragraph of text and also shows a numbered scale with an adjustable pointer.
    ChoiceResponse
    contains only a multiple choice response element which may be overlayed on top of other Display objects whenever a multiple choice response is required.
    ItemRanking
    is a series of single lines of text which constitute 'items' which are shown simultaneously. Single items may be selected in order to create a ranking of the items.

    Geometric Objects

    These are simple and complex geometric objects. Some of them have multiple elements which all have their own color parameters.
    Arrow
    is an arrow with arbitrary orientation.
    MaskedArrow
    is an arrow which is followed by a mask.
    SimpleBar
    a rectangular bar.
    SimpleDisk
    a filled disk.
    PolyArea
    a filled polygon area.
    Chevron
    shows a Chevron type of circularly arranged wedges.
    MunsellColorBar
    is a bar whose color may be defined by Munsell notation for the reflectance function and an illumination device.
    DeviceColorBar
    is a bar whose color is defined by device coordinates.
    CIELabMaximumChroma
    shows two adjacent bars comparing a color with another color having the same lightness and hue but having maximum chroma in CIELab.
    ShowWhitePoint
    shows and sets the current color device's white point.
    DisplaySizeMeasurement
    is a pattern to visually measure the size of a display screen.
    HorizontalVerticalIllusion
    an inverted T figure to show the horizontal-vertical illusion.
    MuellerLyer
    the Müller-Lyer-Illusion figure.
    RandomDotMotionField
    shows a field of random dots which has a moving subset.
    RandomDotMotionThreshold
    shows a field of random dots where a certain proportion will move in the same direction while the rest of the dots moves at random.
    RandomDotMotionContrast
    shows a set of static dots mixed with a set of moving dots which induce virtual motion for the static dots.
    SpatialAttentionFrames
    A Left and a right square frame with a fixation mark as it is used for spatial attention experiments.
    SpatialAttentionTarget
    A Left and a right square frame with a fixation mark and an attention cue or target as it is used for spatial attention experiments.
    GenericDisplay
    a Display whose content is determined at runtime.

    Colorimetry and Photometry

    Methods for doing photometric measurement.

    FlickeringBarsPhotometry
    photometry with flickering bars.
    TeemingPhotometry
    photometry with a flickering random line pattern.
    VisualGammaTarget
    shows a pattern for visual gamma measurement by adjustment.

    Color and Brightness Contrast Objects

    Objects which generally have some target and some context element.
    BrightnessInductionDisks
    BrightnessInductionSquares
    InductionMatching
    CrispeningEffect
    ComplexColorContext
    ComplexColorContext2
    ComplexColorContextAnimated
    CrossContextMatching
    CrossContext3DMatching
    CrossContextMatchingMixed
    CrossContextMatchingRectMixed
    MunsellColorBoard
    RandomContextColorMatching

    Color Discrimination

    Color discrimination objects contain multiple color fields and the task usually is to select a single color field with a certain property.

    RandomTilesColorVisionTest
    shows a color vision screening test pattern.
    ColorDiscrimination
    shows two adjacent rectangles whose colors are to be discriminated.
    ColorSelection
    contains a small number of circular color patches and the subject's task is to select one of them like in the odd man out paradigm.
    PointLightsMixed
    simulates point light sources mixed on a projection screen.
    PointLightsAnimation
    simulates point light sources mixed on a projection screen with amplitude animation.

    Color Samples and Selection

    Selection of one or more colors from a larger sample.

    ColorSampleSelection
    allows selection from an arbitrary sample of color patches defined by their Yxy-coordinates.
    CIELabColorSampleSelection
    shows a rectangular pattern of color patches which contains a CIELab color sample of constant lightness and allows for selection of a subset of color patches.
    MunsellColorSampleSelection
    shows a rectangular pattern of color patches which contains a sample from the Munsell Book of Colors with maximum Chroma and allows for selection of a subset of color patches.
    RandomColorSampleSelection
    shows a random sample of colors and allows selection from the sample.
    ColorCircleSelection

    Search patterns

    Patterns which are used for search tasks in attention research. They include position randomization and independent definitions of target and distractor properties.
    SearchPattern
    is a superclass for pop out search patterns with various target and distractor types.
    LetterSearch
    searching for letters.
    GeometrySearch
    searching for geometric objects.

    Apparent Motion Stimuli

    Objects which may be animated and show some type of motion.

    MovingDot
    apparent motion of a dot.
    MovingDotPair
    apparent motion of a pair of dots.
    TrackingTarget
    a tracking target.
    WertheimerBar
    the classical Wertheimer bar which demonstrates apparent motion.
    ContractingBar
    GrowingText
    an animated Display with a growing line of text which is used for delayed recognition experiments.

    Pictures

    PXLab can read all bitmap image types which are supported by the package javax.imageio. This includes JPEG, PNG, BMP, WBMP, and GIF. Also supported are static Scalable Vector Graphics images. These need the Apache Software Foundation Batik package being installed.

    Picture
    shows a picture contained in a file.
    PicturePages
    allows the subject to page forward and backward through a series of pictures. Mainly intended for presenting complicated instructions which mix text and images.
    PictureAnimation
    presents a short movie made up from a series of image files.
    PictureRating
    is a subclass of RatingScaleQuestion and adds a rating scale to Picture.
    PictureComparison
    is a subclass of PictureRating and allows for simultaneous rating or comparison of two images.
    PictureMatrix
    is a subclass of Picture and simultaneously shows multiple images. It also allows image selection responses.
    PictureMasked
    is a subclass of Picture and adds an image mask to the picture.
    PictureCache
    stores a list of pictures in an internal image cache. Pictures contained in the image cache can be presented much faster than reading them from the file system.
    TextImage
    is a text string drawn as a bitmap image which may be rotated.
    ColorTransformedPicture
    shows a bitmap image and its transformation into a color subspace.
    TwoColorImageProjection
    simulates the projection of two independent bitmap images onto a common projection screen with differently colored projection light sources for the two images.
    MondrianMixturePattern
    simulation of a Mondrian pattern made up from 1, 2, or 3 light source mixtures.

    Images are usually specified by experimental parameters named Picture.Directory and Picture.FileName. The Directory may be a local file system directory, an arbitrary URL on the internet or any archive in the Java system's CLASSPATH. If the full access path of an image file is prefixed by an '@'-character then the image is cached in an image cache in memory for later re-use. This may be useful for applications or applets which need short retrieval times for multiple presentations of the same images.

    Gratings

    Grating patterns which mostly are subclasses of class ModelFest. This is a collection of standardized stimuli suggested by the ModelFest group to investigate basic properties of spatial vision. See http://vision.arc.nasa.gov/modelfest/ or http://www.neurometrics.com/projects/Modelfest/IndexModelfest.htm for a description of this set of stimuli.

    All of these define the pattern as a convex mixture of two arbitrary colors A and B. The pattern is a spatial distribution of mixture weights which satisfy 0.0 <= w(x,y) <= 1.0. At every point (x,y) in the pattern the resulting color is w(x,y)*A + (1-w(x,y))*B. All computations are done in the CIE 1931 chromaticity space.

    GaborFest
    a one-dimensional sinusoidal with phase, amplitude and orientation controlled by experimental parameters. The pattern has a 2-dimensional Gaussian envelope which may be animated such that the maximum amplitude follows a temporal Gaussian.
    CollinearGaborFest
    a collinear series of Gabor patches.
    GaussianFest
    a spatial Gaussian with a temporal Gaussian envelope. No underlying wave is present.
    ImageFest
    an achromatic image whose contrast is multiplied by a spatial Gaussian with a temporal Gaussian envelope.
    MultipoleFest
    mixture patterns which mostly ar binary: a zero contrast pattern, edge, line, and dipole patterns, a concentric disk. a checkerboard, and static and dynamic white noise. All of these have spatial and temporal Gaussian envelopes.

    The following grating patterns are not derived from ModelFest and differ from these mainly by two aspects: The may include harmonic components and they provide spatial motion classes.

    GaborPattern
    a one-dimensional, complex sinusoidal pattern multiplied by a two-dimensional Gaussian envelope. The pattern's phase, amplitude, orientation and harmonic content are controlled by parameters.
    GaborPatternAnimation
    same as GaborPattern but the sinusoid's position or amplitude may be animated.
    PlaidPattern
    a sum of two sinusoidal patterns like those of GaborPattern multiplied by a two-dimensional gaussian envelope.
    PlaidPatternAnimation
    same as PlaidPattern but the sinusoids may be animated.

    Problem Solving and Games

    This group contains some very special types of Display objects which are used mainly in problem solving experiments. Some of these are subclasses of the GameBoard class.

    Anagram
    a sequence of letters which has to be rearranged in order to make it a valid word.
    TowerOfHanoi
    the well known multi-disk problem.
    LinearSystem
    requires the subject to control a multi-state system which is described by some linear system model. The structure of the linear system is described by the class LinearSystemModel.
    MCGame
    the well known river crossing problem.
    WaterJugs
    the water jugs problem.
    VierGewinnt
    shows a state of the well known 'Vier Gewinnt' game.
    ChineseRings
    shows the chinese rings puzzle.
    FifteenPuzzle
    shows a field of n*m tiles making the tile shift puzzle.

    Attention Signals

    These are Display objects which usually are used as attention signals.

    CountDown
    is a blinking dot.
    Counter
    an animated numerical counter.
    FixationMark
    is a simple fixation mark.
    RandomNumber
    is a text element which only contains a random number.

    Feedback Objects

    Feedback
    is a Message object whose content may be derived from other Display objects' parameter values. These messages are computed just in time such that their parameter values show the real time state of the involved Display objects.
    Smiley
    is a graphic object which shows a smiley face and has a Smiley.Mood parameter.

    Sound Display Objects

    PXLab's capability to create sound stimuli is somewhat limited. Currently there are only a few sound objects.

    Beep
    presents the system beep sound.
    SoundFile
    plays an uncompressed wave file.
    SyntheticSound
    plays various synthetic sounds with defined envelopes and wave types.
    SoundRecorder
    records sound from an arbitrary sound input channel.
    SoundRecorderControl
    controls recording as a background task which runs concurrently.
    VoiceKey
    records a voice sound from a microphone and measures the time to threshold.

    Sound display objects do not affect screen content. This means that while a sound display object is running the screen remains in the state it had before the sound object started.

    General Media Player Objects

    These objects use a concurrently running media player to play arbitrary media files. They require that the Java Media Framework (JMF) package is installed on the client. The media type is recognized from media file properties. File location my be specified by any arbitrary URL.

    Movie
    starts playing a movie and waits until the movie is finished.
    MediaPlayerOpen
    opens a media player for a given media file.
    MediaPlayerStart
    starts a media player which has been opened earlier.
    MediaPlayerSync
    waits until a running media player arrives at a certain media time.
    MediaPlayerStop
    stops a currently running media player.

    Clearing the Screen

    Since PXLab's display objects never are explicitly removed from the screen we need display objects which explicitly clear the screen if an empty screen is required. These objects do not contain any DisplayElement objects.

    ClearScreen
    shows an empty screen with the color being defined by the global parameter ScreenBackgroundColor.
    FillScreen
    shows an arbitrary full screen color.
    ClearScreenRandomTime
    shows an empty screen for some random duration.

    Communication with External Devices

    PXLab can communicate with some external devices which are connected to the serial communication port, a parallel port or a USB port. In general these devices require special drivers being installed. We use the label 'External Control Box' for a device connected to the control lines of the serial communication port. See chapter 'Connecting External Devices' for more information about external devices.

    ExternalSignalOn
    switches on an external control box signal.
    ExternalSignalOff
    switches off an external control box signal.
    Trigger
    sends a trigger puls to an external control box signal line.
    IlluminationDeviceControl
    controls an illumination device connected to a parallel port.
    SerialCommunicationDeviceControl
    controls a device connected to a serial port.
    ColorMeasurement
    controls a color measurement device for measuring color tristimulus values from the screen. See chapter 'Color Calibration' for more information about color calibration.

    Response and State Control Objects

    Control objects can count errors and control response and procedure states. Some of these also can simulate certain types of responses.

    Nothing
    does not change the screen content. Its purpose is to provide a new set of response parameters.
    StartClock
    starts a high resolution clock.
    ReadClock
    reads a running high resolution clock.
    InitCounter
    initializes a counter.
    IncrementCounter
    increments and reads an initialized counter.
    SetParameter
    sets the value of an arbitrary experimental parameter.
    RandomGenerator
    computes a random response code.
    ResponseRobot
    a response simulation robot.
    ErrorControl
    counts errors and controls series of error free trials.
    ConditionalWaitResponse
    conditionally waits for a subject response.
    PsychometricFunctionSimulator
    simulates yes/no responses according to a psychometric function model.
    ResponseControlStart
    starts a response control interval where spurious subject responses are collected.
    ResponseControlStop
    stops a response control interval
    ResponseControlCheck
    checks for spurious responses during an active response control interval.
    SessionStateControl
    controls the parameter SessionState and allows for response dependent session state control.
    SystemProcess
    executes a system process of the operating system.
    ScreenCapture
    captures the display screen of the previous Display object and writes the image to a file.
    WhitePointControl
    sets the current color device's white point.

     

  9. Sound and Video
  10. Currently PXLab's capability to present sound stimuli is somewhat limited. The following sound objects can play sound files, can create and play synthetic sounds, and are able to record sound. Included is a voice key object which may also be used as a timer.

    SoundFile
    plays a wave file.
    SyntheticSound
    plays various synthetic sounds with defined envelopes and wave types.
    SoundRecorder
    records sound from an arbitrary sound input channel.
    SoundRecorderControl
    controls recording as a background task which runs across multiple displays.
    VoiceKey
    records sound from a microphone and measures the time to threshold.

    Sound display objects do not affect the screen content. This means that while a sound display object is running the screen remains in the state it had before the sound object started. Here is an example for using synthetic sound objects in a paired comparison task trial:

        Trial(TrialCounter, SyntheticSound:A.WavePars, SyntheticSound:B.WavePars, Message:C.ResponseCode) {
          Message:A() {
            Timer = de.pxlab.pxl.TimerCodes.NO_TIMER;
    	Text = "<==";
          }
          SyntheticSound:A() {
            Timer = de.pxlab.pxl.TimerCodes.END_OF_MEDIA_TIMER;
            Duration = 600;
            Gain = 0.8;
    	Envelope = de.pxlab.pxl.SoundEnvelopeCodes.SINUSOIDAL;
    	Wave =  de.pxlab.pxl.SoundWaveCodes.PURE_TONE;
          }
          Message:B() {
            Timer = de.pxlab.pxl.TimerCodes.NO_TIMER;
    	Text = "==>";
          }
          SyntheticSound:B() {
            Timer = de.pxlab.pxl.TimerCodes.END_OF_MEDIA_TIMER;
            Duration = 600;
            Gain = 0.8;
    	Envelope = de.pxlab.pxl.SoundEnvelopeCodes.SINUSOIDAL;
    	Wave =  de.pxlab.pxl.SoundWaveCodes.PURE_TONE;
          }
          Message:C() {
            Timer = de.pxlab.pxl.TimerCodes.RESPONSE_TIMER;
    	Text = "?";
    	ResponseSet = [0, de.pxlab.pxl.KeyCodes.LEFT_KEY, de.pxlab.pxl.KeyCodes.RIGHT_KEY];
          }
          ClearScreen() {
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
            Duration = 300;
          }
        }
    

    There are two sound objects presented in sequence and each has its own screen content which is defined by a preceding Message() object. For the first sound the screen shows a left arrow and for the second sound the screen shows a right arrow. The subject's response is only collected after the second sound object is finished and while the Message:C() object shows a question mark on the screen. No responses are accepted as long as the sounds are playing.

    Synthetic Sounds

    The button below starts a demo of the various synthetic sound types.

    A synthetic sound object SyntheticSound is defined by an envelope function modulating a base wave type. Envelope functions may be defined independently for the two available channels.

    Envelope typs are defined by parameter SyntheticSound.Envelope and the envelope parameters are defined by SyntheticSound.EnvelopePars. If the sound signal has two channels then the channels my have independent enevlope functions. In this case SyntheticSound.Envelope may be an array with two envelope type values. If two independent enevlopes are to be defined then the parameter SyntheticSound.EnvelopePars contains parameters of both channels. The parameters for the left channel come first and the parameters for the right channels follow.

    The following envelope functions are supported:

    CONSTANT
    A constant function.
    GAUSSIAN
    A Gaussian envelope whose maximum is at half of the sound duration. The parameter SyntheticSound.EnvelopePars contains only a single value, the standard deviation of the Gaussian.
    GAUSSIAN2
    A Gaussian envelope whose maximum position depends on a parameter. The parameter SyntheticSound.EnvelopePars contains the maximum position and the standard deviation.
    SINUSOIDAL
    A sinusoidal envelope which has no parameters.
    LINEAR_ASR
    An envelope which has a linear attack, sustain, and release period. The parameters are the attack and the release duration. The sustain duration is computed from the sound's total duration.
    GAUSSIAN_ASR
    An envelope which has a Gaussian attack and release period. The parameters are the attack duration, the standard deviation of the attack Gaussian, the release duration, and the standard deviation of the release Gaussian.
    SINUSOIDAL_ASR
    An envelope which has a sinusoidal attack and release period. The parameters are the attack and the release duration. The sustain duration is computed from the sound's total duration.

    The following wave types are supported:

    PURE_TONE
    A sine wave with the frequency as its only parameter.
    FOURIER_SERIES
    A Fourier series. The Fourier series is defined by

    S(t) = Sum(k = 1, ..., M) of [ak * cos(k * 2 * Pi * f0 * t) + bk * sin(k * 2 * Pi * f0 * t)].

    The respective SyntheticSound.WavePars array contains the following values:

    WavePars = [M, f0, a1, b1, a2, b2, ..., aM, bM]

    The parameters must be such that the series strictly remains in the range (-2 <= y <= +2). Here is an example for a square wave with Gaussian envelope:

            Trial("Gaussian Envelope\nSynthetic Square Wave", 1, 0, 
    		de.pxlab.pxl.SoundEnvelopeCodes.GAUSSIAN, [200.0],
                    de.pxlab.pxl.SoundWaveCodes.FOURIER_SERIES, 
    		[9.0, 400.0, 
    		0.0, 1.0, 
    		0.0, 0.0, 
    		0.0, 0.3333, 
    		0.0, 0.0, 
    		0.0, 0.2, 
    		0.0, 0.0, 
    		0.0, 0.14286, 
    		0.0, 0.0, 
    		0.0, 0.11111], ?);
    

    WHITE_NOISE_SOUND
    Pure white noise. The parameter SyntheticSound.WavePars is not used in this case.

    In every case a channel's signal is the product of the channel's envelope function with the wave function.

    Synthetic sound objects always have to use a timer of type END_OF_MEDIA_TIMER.

    Playing Sound Files

    The sound object SoundFile plays uncompressed wave ('WAV') files. The duration of this display object is the duration of the wave file. Here is an example which plays two WAV-files in every single Trial:

    The complete design file shows how simple it is to include sound file playing:

          SoundFile:A() {
            Timer = de.pxlab.pxl.TimerCodes.END_OF_MEDIA_TIMER;
    	Directory = ".";
          }
    

    At least a proper Timer and the Directory where the sound files reside have to be given.

    Responses to Sound Objects

    Objects which play sound have a fixed duration and use a media timer because the sound object's native duration determines the presentation duration. This may pose a problem when responses have to be collected while a sound object is still playing. Consider the following scenario: The stimulus is a synthetic sound which plays for 3000 ms. The subject may respond within these 3000 ms but also may only respond after the sound has finished. In this case we need a display object following the sound object which will wait until there is a response. Her is a somewhat complex solution to this problem:

    Experiment() {
    
      Context() {
    
        AssignmentGroup() {
          ExperimentName = "Collect Responses While Playing Sound";
          SubjectCode = "pxlab";
          DataFileTrialFormat = "%SubjectCode% %FinalResponseTime%";
          TrialFactor = 3;
          ResponseTime = 0;
          new FinalResponseTime = (ResponseTime > 0)? ResponseTime: Trial.SyntheticSound.ResponseTime;
        }
    
        Session() {
          Instruction() {
            Text = ["Respond while a Sound is Playing",
    		" ",
    		"Press a response key while the sound is playing or wait until the sound is finished and then press a response key.",
                    "The response time will be shown correctly in both cases.",
    		" ",
    		"Press any key now to start!"];
          }
        }
    
        Trial(SyntheticSound.ResponseTime, ResponseTime) {
          Message() {
            Timer = de.pxlab.pxl.TimerCodes.NO_TIMER;
    	Text = "?";
          }
          SyntheticSound() {
            Timer = de.pxlab.pxl.TimerCodes.WATCHING_END_OF_MEDIA_TIMER | de.pxlab.pxl.TimerCodes.START_RESPONSE_TIMER;
            Duration = 3000;
            Gain = 0.8;
    	Envelope = de.pxlab.pxl.SoundEnvelopeCodes.GAUSSIAN;
    	EnvelopePars = 200.0;
    	Wave = de.pxlab.pxl.SoundWaveCodes.PURE_TONE;
    	WavePars = 1000.0;
          }
          Nothing() {
            Execute = Trial.SyntheticSound.ResponseCode==de.pxlab.pxl.ResponseCodes.CLOSE_MEDIA;
            Timer = de.pxlab.pxl.TimerCodes.LIMITED_RESPONSE_TIMER | de.pxlab.pxl.TimerCodes.STOP_RESPONSE_TIMER;
            Duration = 3000;
          }
          Feedback() {
            Evaluation = de.pxlab.pxl.EvaluationCodes.COMPARE_CODE;
            CorrectCode = de.pxlab.pxl.ResponseCodes.CLOSE_MEDIA;
            ResponseParameter = "Trial.SyntheticSound.ResponseCode";
    	FalseText = "ResponseTime = %Trial.SyntheticSound.ResponseTime@i%\nCode = %Trial.SyntheticSound.ResponseCode%";
    	CorrectText = "ResponseTime = %ResponseTime@i%\nCode = %Trial.Nothing.ResponseCode%";
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
            Duration = 2000;
          }
        }
      }
    
      Procedure() {
        Session() {
          Block() {
            Trial(?, ?);
          }
        }
      }
    }
    

    The trial starts with a Message object showing a question mark an immediately followed by the sound object. The sound object's timer is defined to be

       Timer = de.pxlab.pxl.TimerCodes.WATCHING_END_OF_MEDIA_TIMER | de.pxlab.pxl.TimerCodes.START_RESPONSE_TIMER_BIT;
    

    The WATCHING_END_OF_MEDIA_TIMER activates response event watching during the sound object's display interval and the START_RESPONSE_TIMER_BIT starts response time measurement across multiple display objects. If the response event happens while the sound object is being shown then the WATCHING_END_OF_MEDIA_TIMER mechanism stores the result in the sound object's response parameters.

    The sound object is followed by a Nothing object which does not change the screen content. However, this object's Execute parameter is defined to be

       Execute = Trial.SyntheticSound.ResponseCode==de.pxlab.pxl.ResponseCodes.TIME_OUT;
    

    such that it is execute only if the sound object had TIME_OUT as its ResponseCode value which means that there was no subject response. If the Nothing object is executed then it waits for a response and also stops the response timer:

       Timer = de.pxlab.pxl.TimerCodes.LIMITED_RESPONSE_TIMER | de.pxlab.pxl.TimerCodes.STOP_RESPONSE_TIMER_BIT;
    

    The Feedback object finally shows the proper response code and the new defined parameter FinalResponseTime contains the resulting response time:

      new FinalResponseTime = (ResponseTime > 0)? ResponseTime: Trial.SyntheticSound.ResponseTime;
    

    The global parameter ResponseTime is nonzero only if the Nothing object had been executed and stopped the response timer. If not then the sound object's parameter ResponseTime contains the actual response time.

    The button below shows this demo.

    Recording Sound

    Both SoundRecorder and VoiceKey record sound from a source which should be defined at the operating system level as the system's sound input channel. Here is a view of a sound device mixer which selects the microphone as its input source:

    The voice key object VoiceKey records sound until an input value is found which is larger than the value of the parameter VoiceKey.Threshold. The voice key then stops recording and finishes the display interval. An alternative way to use a voice key response is by setting an arbitray object's Timer parameter to VOICE_KEY_TIMER. A more detailed description of the voice key is contained in chapter ,Stimulus and Response Timing'.

    Recorded sound is stored in a file whose name by default is derived from the subject code, a session and a trial counter:

       FileName = "%SubjectCode%_%SessionCounter%_%TrialCounter%.wav";
    

    The VoiceKey object also can store the recorded sound into a sound file. This is done if the parameter VoiceKey.Directory is defined which by default is undefined for the VoiceKey object.

    The SoundRecorderControl object is slightly different from SoundRecorder in that it may be used to control a single sound recorder which runs as a background task while other display objects may be shown. Background sound recording may be started by setting the parameter SoundRecorderControl.Command to STARTand it may be stopped by another instance of SoundRecorderControl which sets the parameter SoundRecorderControl.Command to STOP. All instances of SoundRecorderControl refer to the same sound recorder and the recording parameters and the file name must be set by that instance which starts recording.

    It is not possible to demonstrate the voice key and sound recording here since the Java security manager does not allow applets to record sound.

    Showing Movies and Other Time Based Media

    Showing movies requires the Java Media Framework (JMF) package being installed on the client. Be sure to follow the installation instructions of the JMF package carefully in order to make JMF work properly. It seems to be important that the order of installation is correct: First install the browser, then install the Java Runtime Environment, and then install the Java Media Framework.

    Here is an example which plays a short MPEG-1 movie containing video and sound. The demo only works if the JMF package has been installed properly.

    Playing a movie is rather simple. Here is the previous demo's design file:

    Experiment() {
      Context() {
        AssignmentGroup() {
          SubjectCode = "pxlab";
        }
        Session() {
          Message() {
            Text = "PXLab plays a movie";
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
            Duration = 500;
          }
        }
        Trial(Message.Text, Movie.FileName) {
          Message() {
            Width = 1024;
            FontSize = 32;
            LocationY = 300;
            Timer = de.pxlab.pxl.TimerCodes.NO_TIMER;
          }
          Movie() {
    	Directory = AppletSystem? ".": "c:/jpxl/www/doc/manual";
          }
        }
      }
      Procedure() {
        Session() {
          Block() {
            Trial("This is a PXLab movie", "pxlab-mpeg1.mpg");
          }
        }
      }
    }
    
    
    

    Playing the movie is done by the Movie display object. It accepts a directory and file name which contains the movie. The actual movie player is based on Sun's Java Media Framework software. The player used here is capable of playing many time based media files. Thus the Movie object can play not only movie files but also sound files using various types of compression methods depending on the codecs and JMF plugins being installed. This is different from the SoundFile object which can only play uncompressed wave files. Look into the JMF documentation for supported formats.

    Here is a demo where the Movie object is used to play a sound file:

    Experiment() {
      Context() {
        AssignmentGroup() {
          SubjectCode = "pxlab";
        }
        Trial(Movie.FileName) {
          Message() {
            Timer = de.pxlab.pxl.TimerCodes.NO_TIMER;
    	Text = "File:  '%Trial.Movie.FileName%'";
          }
          Movie() {
    	Directory = AppletSystem? ".": "c:/jpxl/doc/tutorial";
          }
        }
      }
      Procedure() {
        Session() {
          Block() {
            Trial("dieFrage.wav");
          }
        }
      }
    }
    
    
    

    The Movie display object accepts any URL as its media file name. Note, however, that for security reasons applets may only play media data which are located in the applet's location tree. This restriction does not hold for applications. The Movieobject has the following restrictions:

    • It must have its Timer parameter being set to MEDIA_STOP_TIMER which is done by default. This restriction makes it impossible to have an overlay display object follow a Movie object, since display objects which are followed by overlay objects must have its Timer parameter being set to NO_TIMER. The Movie object itself, however, may be used as an overlay.
    • It does not have a background element. Thus showing a movie does not destroy the screen content of the previous display object at areas not covered by the movie frame. It does of course destroy all objects within the movie frame area.

    Here is another MPEG1 movie which has been created from a sequence of screen captures using the ScreenCapture display object. It simply shows a running sequence of numbers which exactly number the single frames of the movie. The frame rate is 25 Hz and there exactly are 25 frames.

    In some cases it might be necessary to have better control of movie timing than is available with the Movie object. This is possible with three more media player objects:

    1. MediaPlayerOpen prepares the media player for playing a given media file. Preparation includes searching and opening the media file, caching media data and preparing the display screen for showing the media window. MediaPlayerOpen has the same experimental parameters as Movie since all visual parameters are set here.
    2. MediaPlayerStart actually starts the media player. Starting the player is as fast as possible given the way the media data have been prepared by MediaPlayerOpen. If this object's parameter MediaPlayer.FastOpen has been set then opening the player already shows a static view of the first movie frame and MediaPlayerStart can start the movie with a delay of down to 2 ms (at least on a 3 GHz PC). If MediaPlayer.FastOpen is not set then the delay may go up to 150 ms on a 3 GHz PC.
    3. MediaPlayerSync synchronizes display processing to the media stream. Two types of synchronization are possible: (1) wait until a certain media time is reached and (2) wait until the end of the media data file has been found. In order to wait for the end of the media data stream the Timer parameter of the MediaPlayerSync object must be set to MEDIA_STOP_TIMER if the synchronization object should wait until a certain media time has been reached its Timer must be set to MEDIA_SYNC_TIMERand the parameter MediaPlayerSync.MediaTime has to be set to the respective media time. The media time is given in milliseconds relative to the start of the media stream. Thus in order to synchronize to the start of the 16th frame of a 25 Hz video stream MediaPlayerSync.MediaTime must be set to 15*40 = 600 ms.

    Here is an example of a 2 second MPEG1 movie which shows a drifting grating and also counts the video frames.

    And here is the design file of this demo:

    Experiment() {
      Context() {
        AssignmentGroup() {
          SubjectCode = "pxlab";
        }
        Session() {
          ClearScreen() {
            Timer = de.pxlab.pxl.TimerCodes.NO_TIMER;
          }
        }
        Trial(MediaPlayerOpen.FileName, MediaPlayerStart.StartDelay) {
          MediaPlayerOpen() {
    	Directory = AppletSystem? ".": "c:/jpxl/www/doc/manual";
            FastStart = 1;
          }
          Message:Start() {
            Text = "Press the SPACE bar\n to start an MPEG1 grating movie";
            Width = 1024;
            LocationY = -240;
            FontSize = 32;
            Timer = de.pxlab.pxl.TimerCodes.RESPONSE_TIMER;
          }
          MediaPlayerStart() {
            Timer = de.pxlab.pxl.TimerCodes.NO_TIMER;
          }
          MediaPlayerSync:Stop() {
            Timer = de.pxlab.pxl.TimerCodes.END_OF_MEDIA_TIMER;
          }
          Feedback() {
            Text = "Start delay = %Trial.MediaPlayerStart.StartDelay% ms";
            FontSize = 32;
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
            Duration = 2000;
          }
        }
      }
    
      Procedure() {
        Session() {
          Block() {
            Trial("grating_352x288x25_mpeg1.mpg", ?);
          }
        }
      }
    }
    
    
    

    The final example uses media synchronization in order to switch the screen's background color in synchronization with the movie content. The movie again is the 25 frame numbers movie with a small change: starting from frame 16 the background is white and the numbers are black. If synchronization works then the screen background should switch its color in synchronization with the movie background.

    Here is the design file of this demo:

    Experiment() {
      Context() {
        AssignmentGroup() {
          SubjectCode = "pxlab";
        }
        Session() {
          ClearScreen() {
            Timer = de.pxlab.pxl.TimerCodes.NO_TIMER;
          }
        }
        Trial(MediaPlayerOpen.FileName, MediaPlayerStart.StartDelay) {
          MediaPlayerOpen() {
    	Directory = AppletSystem? ".": "c:/jpxl/www/doc/manual";
            FastStart = 0;
          }
          Message:Start() {
            Text = "Press the SPACE bar\n to start the MPEG1 'Numbers' Movie";
            Width = 1024;
            LocationY = -240;
            FontSize = 32;
            Timer = de.pxlab.pxl.TimerCodes.RESPONSE_TIMER;
          }
          MediaPlayerStart() {
            Timer = de.pxlab.pxl.TimerCodes.NO_TIMER;
          }
          MediaPlayerSync() {
            MediaTime = 15 * 40;
          }
          FillScreen() {
            Timer = de.pxlab.pxl.TimerCodes.NO_TIMER;
    	Color = White;
          }
          MediaPlayerSync:Stop() {
            Timer = de.pxlab.pxl.TimerCodes.END_OF_MEDIA_TIMER;
          }
          Feedback() {
            Text = "Start delay = %Trial.MediaPlayerStart.StartDelay% ms";
            FontSize = 32;
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
            Duration = 2000;
          }
        }
      }
    
      Procedure() {
        Session() {
          Block() {
            Trial("numbers_sync_352x288x25_mpeg1.mpg", ?);
          }
        }
      }
    }
    
    
    

    Note that movie timing in applets seems to be strongly affected by browser properties and the system environment. Thus in order to really test PXLab's timing properties these demos should be run as applications and not as applets. Experience shows that when running as an application the media player provides satisfactory timing precision for most applications. And also note that several video formats which are supported for applications are not supported by the JMF when running as an applet.

     

  11. Parameter Values and Expressions
  12. The PXLab programs are controlled by a large set of experimental parameters. A subset of these are global in the sense that they are available in every experiment and exist independent of Display objects. Non-global experimental parameters are parameters of Display objects. Every experimental parameter has a default value which is defined in the PXLab code. New values may be assigned to experimental parameters by assignment statements. If the experimental parameter is a global parameter, then the respective assignment must be contained in an AssignmentGroup() node of the design file. A global parameter is a parameter which is not a parameter of a subclass of class Display. Most global parameters are defined in class ExPar. Currently the only additional classes which define global parameters are the classes PrimaryScreen and SecondaryScreen which define experimental parameters for a primary and a secondary display screen. Global experimental prameters which are not defined in class ExPar are currently not allowed in expressions.

    There may be multiple AssignmentGroup() nodes, but all of them must be contained in the Context()-section of the design file. If an assigment refers to an experimental parameter of a Display object, then it must be contained in the Display object's declaration.

    Experimental parameter values appear at two places in a design file:

    • in parameter value assignments, and
    • as argument values in the Procedure()-section.

    Here is an example for the first case where experimental parameters of a Display object are set:

        Message() {
          FontSize = 40;
          Timer = de.pxlab.pxl.TimerCodes.RESPONSE_TIMER;
          ResponseSet = [de.pxlab.pxl.KeyCodes.LEFT_KEY, de.pxlab.pxl.KeyCodes.RIGHT_KEY];
        }
    

    The first assignment sets the parameter Message.FontSize to the literal value '40'. The second assignment contains a symbolic class constant as a parameter value and the third assignment assigns an array value to an experimental parameter.

    Here is an example for parameter values in argument lists:

        Procedure() {
          Session() {
            Block() {
              Trial("House", ?, ?);
              Trial("Garden", ?, ?);
              ...
            }
          }
        }
    

    In this case the first argument of a Trial is a string valued parameter and the second and third argument are undefined.

    Value Types

    Experimental parameter values may be

    • literal values like the numbers '40' or '1.23', or string values like "Hello";
    • symbolic class constants like 'de.pxlab.pxl.TimerCodes.RESPONSE_TIMER' or simply 'RESPONSE_TIMER' as a shorthand;
    • full instance names of experimental parameters;
    • the question mark as long as this is used in an argument list;
    • arrays of literal values or class constants like '[13.2, 0.313, 0.329]';
    • replicator arrays in argument lists like '<2, 4, 6>';
    • expressions containing literal values, class constants and experimental parameter names.

    Literals

    Literals are of type integer, float or string. Integer, float, and string literals in PXLab expressions are defined identical to their definition in the Java programming language with the exception, that PXLab's float literals correspond to Java's double type.

    Class Constants

    Whenever the grammar allows a literal value then it also allows a named constant from a Java class or interface. These constants must be declared as

     
        public static final int 
    

    within a class or interface file and are retrieved at runtime by their full name. Thus we may use an assignment like

     
       Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER; 
    

    to assign the value of the constant CLOCK_TIMER which is defined in interface de.pxlab.pxl.TimerCodes to parameter Timer. Only class constants from the packages de.pxlab, java, or javax are allowed. In general class constants must be written with their full access name. An exception are class constants which are defined in an interface of the package de.pxlab.pxl whose name ends in 'Codes'. These constants may be used with their constant name only. Thus the above assignment may also be written as

     
       Timer = CLOCK_TIMER; 
    

    Names of Experimental Parameters

    In most cases the values of parameters may contain the names of other parameters. Since parameters may also be created new this allows variables as parameter values. As an example we may define one Timer to get the value of another timer by

       Timer = Trial.FixationMark.Timer;
    

    Variables may appear within any expression. The only exceptions are array and replicator values. These may only contain literal values and named constants. Parameter names as values must be full instance names. The form of a full instance name depends on where the respective parameter is defined. Global parameters defined in the class ExPar exist only once such that their full instance name is identical to their variable name. An example is SubjectCode which is a global parameter defined in class ExPar. Its full instance name is 'SubjectCode'.

    Experimental parameters of Display objects have a three part full instance name. The first part is the name of the DisplayList instance which contains the respective Display object. The second part is the Display instance name and the third part is the parameter name. Suppose we have declared a DisplayList named 'Trial:Study' which contains a Display object instance named 'Message:Attention' and the Message object contains a Parameter named 'Text'. Then the full instance name of this parameter is

        Trial:Study.Message:Attention.Text
    

    Here 'Trial' is the DisplayList class name, 'Trial:Study' is the DisplayList instance name, 'Message' is the Display class name and 'Message:Attention' is the Display object instance name, and 'Text' is the parameter name. If the respective DisplayList or Display objects were unique children of their parents in the declaration then their instance suffixes may be missing and the full instance name of the Text parameter were 'Trial.Message.Text'.

    Unknown Parameter Values

    Parameters whose values are unknown before runtime and which appear in Procedure()-section argument lists may be assigned the value "?":

          Trial("Garden", ?, ?);
    

    Unknown parameter values must not be used uin assignments but are only used in argument lists of procedure units. The respective parameter inherits the value of the next higher procedure unit or has its default value.

    Expressions

    Expressions are numeric or string expressions generated from literals or identifiers combined with simple operations like addition, subtraction, multiplication, division or string concatenation. Expressions must evaluate to an integer, float, or string value. Here is an example where a new parameter is defined with an expression as its value:

          new Correct = 100 * (TrialCounter - ResponseErrorCount) / TrialCounter;
    

    This definition assigns the declared expression to the new parameter 'Correct'. This means that whenever the value of the parameter 'Correct' is computed then the expression is evaluated. Thus the expression value is the result of its component parameters at evaluation time.

    Expression Evaluation Time

    In order to understand expression evaluation it is important to known when expressions are evaluated. The Context()-section of a design file is purely declarative. The order of display list declarations does not have any effect on the order of evaluations. The order of assignments in an assignment section does have an effect and also is the order of evaluations. In general, expressions are evaluated only when they are needed. Thus suppose the previously defined parameter 'Correct' is used in a stimulus object. This means that the value of 'Correct' is evaluated only when the stimulus object's properties are computed. This may be at the begin of a display list presentation or, if the respective display object has the JustInTime parameter set, immediately before the stimulus object is shown. Also note that the parameter value has to be evaluated every time when it is used, since the expression is never replaced by its value in the declaration. This also is a consequence of the fact that design files are declarative and do not contain procedural properties. A good example for the power of PXLab's expression system is contained in the demo file monty_hall.pxd.

    Chapter 'Design File Grammar' contains a complete description of PXLab expressions and the file ExDesignTreeParser.html contains a Backus-Naur-Form of the syntax.

    Simple Expressions

    Simple PXLab expressions and parameters do actually not have types but simultaneously have an integer, float, and string representation. If a parameter is assigned an expression which evaluates to a number, then the parameter has this numeric value. If the string representation of this parameter's value is accessed, then it is a string representation of this number. Thus if a parameter has been assigned the value 3.14:

        m = 3.14;
    

    then the float representation of m is 3.14, the integer representation is 3 and the string representation is "3.14".

    If a parameter is assigned an expression which does not evaluate to a number then the parameter holds the respective string as its string representation and its integer and float representations are 0 and 0.0 respectively. Thus if a parameter has been assigned the value "a24":

        m = "a24";
    

    then the float representation of m is 0.0, the integer representation is 0 and the string representation is "a24".

    Expression operations include the usual arithmetic operators as applied to numbers. Here are some examples:

        LocationY = messageLocationY + 116;
        WorkingTime = (Trial:Multiply.ClearScreen.TimeControl - BlockTime) / 1000;
        CorrectCode = Trial:Multiply.RandomGenerator:A.ResponseCode * Trial:Multiply.RandomGenerator:B.ResponseCode;
        Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER | de.pxlab.pxl.TimerCodes.START_RESPONSE_TIMER;
    

    The division operator is always computed with floating point values. The integer result is the floating point result rounded to the nearest integer. Use the function idiv() if you need integer division. The operator '|' is not a logical operator but a binary 'or' operator which combines binary bit patterns.

    Logical Expressions

    PXLab allows the usual logical expression operators. A parameter value is considered 'true' if it is nonzero and is considered 'false' if it zero. Many experimental parameters are flags. This means that they expect binary values. An example is the parameter Execute which has been explained earlier. These flag parameters are 'true' if nonzero and 'false' if zero. Here are some examples of logical expressions:

          Execute = Trial:Test.Message:ON.ResponseCode == 0;
          Execute = !Trial.Message.Execute;
    

    Conditional Expressions

    A conditional expression is a value which depends on the state of an experimental parameter at runtime. The general format is

        m = p? x: y;
    

    The experimental parameter m is assigned the value x if the experimental parameter p has a nonzero integer value and m is assigned the value y if p has integer value zero. Here is an example

      Text = AppletSystem? "Start the Applet": "Start the Application";
    

    The experimental parameter AppletSystem has the integer value 1 if the current program is running as an applet. Thus the experimental parameter Text is assigned the value "Start the Applet" if parameter AppletSystem is nonzero. This is the case if the program is running as an applet. If the program is running as an application the parameter Text gets the value "Start the Application". Here is another example using multiple conditional expressions:

        selectionSet = (firstChoice == 0)? set12: ((firstChoice == 1)? set02: set01);
    

    Array Expressions

    An array expression is a list of literals or named constants enclosed in square brackets:

        [0.313, 0.329, 60.0] 
    

    Array expressions may be used as parameter values but array expressions cannot be elements of expressions. Thus it is not allowed to use array expressions in arbitrary expressions. It is, however, allowed to use a variable in an arbitrary expression even if the variable has an array as its value. This must be used carefully since whenever the expression is evaluated, it must make sense to have an array valued variable involved. Here is an example where array valued variables are used meaningfully in an expression:

        new set01 = [0, 1];
        new set02 = [0, 2];
        new set12 = [1, 2];
        new selectionSet = (firstChoice == 0)? set12: ((firstChoice == 1)? set02: set01);
    

    The variable selectionSet is assigned an array value here which depends on the current value of the parameter firstChoice. In this case the expression does not access the internal value of the variables set01, set02, and set12.

    Replicator Expressions

    A replicator expression is similar to an array expression but uses angle brackets instead:

        <3, 6, 9> 
    

    Replicator expressions may be used at all those places where array expressions may be used. Replicator expressions are expanded during run time by replicating the data collection unit whose argument list contains the replicator such that multiple copies of the block are created. In the list of units created the parameter with a replicator expression as its original value gets all the different values of the literal list in the replicator as its successive value. Thus

        Trial("a", <3, 6, 9>);
    

    is equivalent to writing

        Trial("a", 3);
        Trial("a", 6);
        Trial("a", 9);
    

    A data collection unit argument list may contain multiple replicators which generates copies for every possible combination of parameter values in the replicators.

    A full description of the PXLab expression syntax is contained in the PXLab design file grammar.

    Functions

    PXLab knows some functions which may be used in expressions.
    Screen Position Functions
    screenTop()
    returns the screen top border y-coordinate.
    screenRight()
    returns the screen right border x-coordinate.
    screenBottom()
    returns the screen bottom border y-coordinate.
    screenLeft()
    returns the screen left border x-coordinate.
    screenWidth()
    returns the screen width in pixels.
    screenHeight()
    returns the screen height in pixels.
    Color Related Functions

    The PXLab color software internally works with XYZ-coordinates but its main color object class PxlColor always simultanously stores the xy-chromaticity values also. Whenever internal color coordinates are stored into an experimental parameter then PXLab stores the Yxy-coordinates and whenever an internal color value is set by an experimental parameter then it expects to get Yxy-coordinates. This means that the user will only have to deal with Yxy-coordinates.

    All functions named from...() convert a color specification into the Yxy-coordinates. This means that an assignment like

      Color = fromCIELab([80, 20, 20]);
    
    will set the experimental parameter Color to a double array which contains the Yxy-coordinates of the color given in the CIELab coordinates argument. Correspondingly all functions named to...() convert from Yxy-coordinates to the target coordinate system specifeid by the function's name.
    colorAtLum(a, b)
    returns a color value which has the chromaticity of argument a and has argument b as its luminance.
    fromCIELab(a)
    converts color coordinates from the CIELab 1976 system to the CIE 1931 system.
    toCIELab(a)
    converts color coordinates from the CIE 1931 system to the CIELab 1976 system.
    fromCIELabLCh(a)
    converts color coordinates from the CIELab 1976 LCh system to the CIE 1931 system.
    toCIELabLCh(a)
    converts color coordinates from the CIE 1931 system to the CIELab 1976 LCh system.

    The CIELab conversion uses the currently defined white point as its reference. The current white point may be defined by the global parameter CIEWhitePoint. If this parameter is not defined then the current color device's white point is used as a reference for the conversion.

    fromDevRGB(a)
    converts an array of device RGB-values to Yxy color coordinates.
    toDevRGB(a)
    converts color coordinates to an array of device RGB-values.
    toDevRGBHex(a)
    converts color coordinates to an array of device RGB-values in hexadecimal notation as it is used in HTML.

    The device color conversions use the currently defined color device for computing the conversion. The color device(s) may be defined as described in the section on color calibration.

    fromXYZ(a)
    converts the XYZ-coordinates of its argument to Yxy-chromaticity coordinates.
    addColors(a, b)
    compute the additive mixture of two colors. The resultr is (a + b).
    mixColors(a, b)
    compute the convex mixture of two colors with equal weights. The result is (0.5*a + 0.5*b).

    The following color functions return color values which are independent of the currently installed gamma correction and color calibration. They always refer to device coordinates for the currently active color device.

    white()
    returns the system color white.
    lightGray()
    returns the system color lightGray.
    gray()
    returns the system color gray.
    darkGray()
    returns the system color darkGray.
    black()
    returns the system color black.
    yellow()
    returns the system color yellow.
    cyan()
    returns the system color cyan.
    magenta()
    returns the system color magenta.
    green()
    returns the system color green.
    red()
    returns the system color red.
    blue()
    returns the system color blue.
    String Functions
    runtimeID()
    returns a uniqe runtime identifier string.
    cat(a, b)
    returns the concatenation of the string value of a and the string value of b.
    lang2(a, b)
    returns a if the currently active message language is English and b if the currently active language is German. The currently active language is determined automatically or can be set on the command line.
    lang3(a, b, c)
    returns a if the currently active message language is English, b if the currently active language is German, and c if the currently active language is anything else.
    Array and Expression Functions
    arrayOf2(a, b)
    returns an array which has the values a and b as its components.
    arrayOf3(a, b, c)
    returns an array which has the values a, b, and c as its components.
    valueOf(a, i)
    returns the literal value at index i of the value of experimental parameter a. It is a runtime error if a does not have a value at index i.
    expressionFile(dir, file)
    returns the expression value defined by the expression contained in 'file' in directory 'dir'. The file must contain a valid expression as defined by the non-terminal 'assignableParameterValue' of the PXLab design file grammar.
    undefined()
    returns an undefined value. This may be used in cases where a parameter should be reset to its undefined state. The function replaces the '?'-character in assignments. It is not allowed to assign the placeholder '?' to a parameter in an assignment.
    Mathematical Functions
    idiv(a, b)
    returns the integer division of a by b. The numerical operator '/' always computes the floating point division and rounds the result to the nearest integer for the parameter's integer value.
    sin(a)
    cos(a)
    tan(a)
    atan(b, a)
    atan(a)
    sqrt(a)
    log(a)
    log10(a)
    exp(a)
    pow(a)
    abs(a)
    ceil(a)
    floor(a)
    round(a)
    randomInt(n)
    returns a random integer number x with 0 <= x < n with equal distribution. Note that this function returns a new random number on every call.
    nextNumber()
    returns an integer which is incremented by one at every call.

    Constant Parameter Values

    There exist some parameters whose values should be treated as constant values, since they are set at startup time and should not be changed by the user. Here is a list of these:
    PXLabVersion
    PXLab version string.
    Date
    Date string.
    AppletSystem
    Applet system flag. This flag is 1 if the experiment runs as an applet and is 0 if the experiment is running as an application. This parameter may be used for setting other parameters to runtime system dependent values.

    The colors defined by the following color parameters are the default screen colors as they result from device coordinates. Thus as an example the color 'White' is that white which results from setting all three device channels to their maximum value. Thus these color values are independent of the currently installed gamma correction and color calibration.

    White
    White color coordinates.
    LightGray
    Light gray color coordinates.
    Gray
    Gray color coordinates.
    DarkGray
    Dark gray color coordinates.
    Black
    Black color coordinates.
    Red
    Red color coordinates.
    Yellow
    Yellow color coordinates.
    Green
    Green color coordinates.
    Magenta
    Magenta color coordinates.
    Cyan
    Cyan color coordinates.
    Blue
    Blue color coordinates.

    String Substitution

    Whenever a string parameter value contains the name of another experimental parameter enclosed between percent signs then the current value of the respective parameter replaces the parameter within the string.

        Text = "%Trial.Message.ResponseTime% ms";
    

    defines the feedback text string in a previous example. Here the string '%Trial.Message.ResponseTime%' is replaced at runtime by the actual value of the parameter Trial.Message.ResponseTime.

    String substitution can use a formatting specification defined by class StringSubstitutionFormat. This is done by inserting a '@'-character followed by a formatting character at the end of the parameter name. Currently only these formatting characters are supported:

    • The character 'b' controls the formatting of arrays. Suppose we have defined

          Color = [15.0, 0.313, 0.329];
      

      Then "%Color%" results in

          [15.0, 0.313, 0.329]
      

      being printed. while "%Color@b%" results in

          15.0 0.313 0.329
      

      being printed.

    • The character 'i' forces scalar values to be shown as integer values.

    The default formatting method tries to make a conjecture about the type of the parameter and adapts the format accordingly.

     

  13. Stimulus and Response Timing
  14. Every Display object has at least the following timing and response parameters:

    Timer
    defines the timer type which controls the duration of the Display object's main timing group. If the Display object has more than a single timing group then each timing group will usually have its own timer being defined. Possible Timer values are defined in class TimerCodes.
    Duration
    is the intended display duration of one of that Display object's timing groups which has Timer as its timer type.
    TimeControl
    contains the actual times when this display's timing groups have been shown. This parameter is set after the display has been shown and uses PXLab's high resolution clock.
    TimeError
    contains the timing error for every timing group of this display object which has a CLOCK_TIMER. The timing error is derived from the value of Duration and TimeControl.
    ResponseTime
    stores the actual display duration. For response driven timers this will also be the response time. For clock driven timers this parameter is derived from TimeControl.
    ResponseCode
    is a code generated by the event which stopped the display time interval. For response driven timers this will be a code generated by the response device. These codes are defined in class KeyCodes. For clock driven timers the code will be TIME_OUT. For media timers like MEDIA_START_TIMER the code will be one defined in class MediaEventCodes. If the parameter ResponseSet is defined then the response codes correspond to the position of the respective response code in the ResponseSet array.
    ResponsePosition
    contains an integer array with the xy-position of the response device when a response was detected.
    ResponseSet
    gives the set of response codes (see ResponseCode) which are acceptable as responses for stopping the respective timer. If this parameter is defined, then it also installs a code conversion function such that the actual ResponseCode values are no longer the response device codes but the indices of the respective device code in the ResponseSet array.

    Defining a Timer

    There basically are two different timing mechanisms: clock controlled timers with fixed durations and event controlled timers whose durations depend on the subject or other external properties. The clock controlled timer is based on PXLab's timing clock whose precision depends on the underlying hardware. The resolution is guaranteed by the Java Virtual Machine and based on the method System.nanoTime(). Note, however, that the timing resolution of the Java Virtual Machine is implementation dependent and may differ between the various operating systems. An event controlled timer is used to control timing by non-clock based events which mainly are subject response events or media events.

    The timer for a timing group of a Display is defined by setting the value of the respective timer parameter. If the Display class has only a single timing group then Timer will be its timer parameter. If the Display has more than a single timing group then the timer of each group will be controlled by additional timer parameters whose names end in 'Timer'. A valid timer parameter value is a binary combination of the following timer property codes:

    NO_TIMER_BIT
    do not use any timer.
    CLOCK_TIMER_BIT
    use a clock timer.
    MOUSE_BUTTON_TIMER_BIT
    use a mouse button response as a timer.
    KEY_TIMER_BIT
    use a keyboard response as a timer.
    STOP_KEY_TIMER_BIT
    code for using a repeated keyboard response with a special stop key as a timer. The stop-key code is stored in the parameter StopKey. Timing group presentation is repeated until the stop key is detected. The Display object's call-back method keyResponse() is called after every key giving the Display a chance to make modifications.
    XBUTTON_TIMER_BIT
    code for using an external button box as a response timer. The external button box must be connected to the serial port control lines.
    DOWN_TIMER_BIT
    Response direction code for key or button press responses.
    UP_TIMER_BIT
    Response direction code for key or button release responses.
    MOUSE_TRACKING_TIMER_BIT
    Indicates that the response event includes mouse motion tracking. Some Display objects require this for continuous updating of their properties. The Display's call-back methods pointerActivated(), pointerReleased(), pointerDragged() , or pointerMoved() are called whenever one of the respective events is detected.
    STORE_TIMER_BIT
    Indicates that this timer's results are stored back into experimental parameters. Must be set for all timers which collect data.
    START_RESPONSE_TIMER_BIT
    Used for response intervals which span more than a single Display object. It indicates that response time measurement is started when this timer starts.
    STOP_RESPONSE_TIMER_BIT
    Used for response intervals which span more than a single Display object. Indicates that response time measurement is stopped when this timer stops.
    VIDEO_SYNCHRONIZATION_BIT
    Synchronizes the display start of the timing group controlled by this timer with the start of the next vertical blanking interval of the video system.
    WATCH_SPURIOUS_RESPONSES_BIT
    If set then a spurious response which happens during a clock timer interval is stored and replaces the timer duration and stopping code parameter.
    SERIAL_LINE_TIMER_BIT
    If set then we expect a serial line input response. This is a sequence of characters received across an activated serial port. The sequence is finished by a RETURN character. This timer should only be used for hardware devices which are connected to a serial input channel. It uses serial data transmission while XBUTTON_TIMER_BIT uses only control lines of the serial port and does not use serial data transmission.
    VOICE_KEY_TIMER_BIT
    Set for using the voice key timer.
    SYNC_TO_MEDIA_TIMER_BIT
    If set then we wait until a certain media time has been reached.
    END_OF_MEDIA_TIMER_BIT
    If set then we are waiting for an end of media event.

    In order to make it easier to define useful timer properties some commonly used combinations have predefined names:

    NO_TIMER
    code for using no timer. Timing groups which do not have a timer are shown but are immediately replaced by the next timing group. This is useful if the next timing group works as a JOIN-overlay. This is a method to simultanously show different Display objects.
    CLOCK_TIMER
    This describes a timer which is controlled by a clock with a fixed delay period and whose actual timing data are stored.
    RAW_CLOCK_TIMER
    This describes a timer which is controlled by a clock with a fixed delay period and whose actual timing data are not stored.
    VS_CLOCK_TIMER
    A timer which is controlled by a clock which only starts to run at the start of the next vertical retrace of the video system. It thus creates a timing interval whose begin is synchronized to the vertical retrace of the video system.
    WATCHING_CLOCK_TIMER
    This describes a timer which is controlled by a clock with a fixed delay period. The display element group controlled by this timer remains unchanged until the respective time period has passed. The timer also watches for spurious responses during the timing interval. The actual time passed is stored in the respective response time parameter. If there is a spurious response detected then this response's time and code are stored in the parameters ResponseTime and ResponseCode instead of the timer's actual time and stopping code.
    KEY_RESPONSE_TIMER
    This timer uses a keyboard press response to stop its delay interval. The timing and response code are stored.
    RELEASE_KEY_RESPONSE_TIMER
    This timer uses a keyboard release response to stop its delay interval. The timing and response code are stored.
    RESPONSE_TIMER
    This timer uses a mouse, a keyboard, or an external button press response to stop its delay interval. The actual timing and response code are stored.
    VS_RESPONSE_TIMER
    This timer uses a mouse, a keyboard, or an external button press response to stop its delay interval. The actual timing and response code are stored. The start of this timing interval is synchronized to the vertical retrace period.
    RELEASE_RESPONSE_TIMER
    This timer uses a mouse, a keyboard, or an external button release response to stop its delay interval. The timing and response code are stored.
    LIMITED_RESPONSE_TIMER
    This element's duration is controlled either by a subject response action as defined by RESPONSE_TIMER or by a time out clock period whatever comes first. The timing and response code are stored.
    GO_TIMER
    This is a timer which waits for any go-signal of the subject. No clock timing or time out control is involved. The subject's go-response is any of a keyboard, mouse button, or external button release. Usually this response is only used for proceeding within the display. No timing protocol is stored.
    LIMITED_GO_TIMER
    This is a timer which waits for any go-signal of the subject or of a clock. The subject's go-response is any of a keyboard, mouse button, or external button release. Same as a GO_TIMER timer but with a limited waiting interval.
    MOUSE_TRACKING_TIMER
    This timer uses a continuous mouse response and stops on mouse button release. The timing and response code are stored. The Display's call-back methods pointerActivated(), pointerReleased(), pointerDragged() , or pointerMoved() are called whenever one of the respective events is detected.

    Some Display objects implement a screen button extension of the mouse buttons. This is a rectangular button on the screen which has a text label and acts like a response key while mouse tracking is active. If a mouse button is pressed or released on this button while mouse tracking is activated it generates a mouse button event. The key code for this button is SCREEN_BUTTON. Thus the following definitions will activate a mouse tracking timer and will set the SCREEN_BUTTON as the only acceptable response 'key':

    	Timer = MOUSE_TRACKING_TIMER;
    	ResponseSet = SCREEN_BUTTON;
    
    The effect is that the subject can move the mouse and the response timer is stopped when a button is released on the screen button area.

    A Display object will show a screen button only if two conditions are satisfied: (1) the respective Display object must implement it and (2) it must be activated by setting the global experimental parameter ScreenButtonSize to a nonzero positive value.

    STOP_KEY_TIMER
    Uses a response which is only stopped by the current stop-key. Other keys may result in some update or adjustment action of the display. The stop-key code is stored in the parameter StopKey. The timing and response code are stored. The Display object's call-back method keyResponse() is called after every key giving the Display a chance to make modifications.
    MOUSE_TRACKING_STOP_KEY_TIMER
    combines the properties of a MOUSE_TRACKING_TIMER and a STOP_KEY_TIMER
    MOUSE_TRACKING_KEY_TIMER
    combines the properties of a MOUSE_TRACKING_TIMER and a KEY_RESPONSE_TIMER
    MOUSE_TRACKING_RELEASE_KEY_TIMER
    combines the properties of a MOUSE_TRACKING_TIMER and a RELEASE_KEY_RESPONSE_TIMER
    SERIAL_LINE_INPUT_TIMER
    If set then we expect a serial line input response. This is an input across a serial line channel which is stopped by detection of a carriage return character. The input string is stored in the respective timing group's ResponseCode parameter.
    VOICE_KEY_TIMER
    Set this to use a voice key timer for any arbitrary non-sound display object. Do not use this timer for the VoiceKey display object.
    START_RESPONSE_TIMER
    Used for response intervals which span more than a single Display object. It indicates that response time measurement is started when this timer starts.
    STOP_RESPONSE_TIMER
    Used for response intervals which span more than a single Display object. Indicates that response time measurement is stopped when this timer stops.
    END_OF_MEDIA_TIMER
    Use this timer to wait until a playing or recording media object is finished. The stop event is created when the media processor has stopped playing media data because the end of the data stream has been detected.
    WATCHING_END_OF_MEDIA_TIMER
    Use this timer to wait until a playing or recording media object is finished and record spurious responses while the media object plays or records its data.
    SYNC_TO_MEDIA_TIMER
    Use this timer to synchronize to a media object. The sync event is created when the media processor has arrived at a certain media time in playing the media data. The media time is given by the timing group's Duration parameter. Media time is the time relative to the start of the media.

    The timer codes are defined in class TimerCodes of the package de.pxlab.pxl of PXLab. Thus in order to assign the respective timer code to a parameter we may write

       Timer = de.pxlab.pxl.TimerCodes.RELEASE_RESPONSE_TIMER;
    

    which assigns the code RELEASE_RESPONSE_TIMER to the parameter Timer. For timer codes this may be simplified to

       Timer = RELEASE_RESPONSE_TIMER;
    

    since class TimerCodes is a member of the package de.pxlab.pxl.

    Response Time and Event Data

    Every Display object has its own timer and duration parameter. Every Display object also has parameters called ResponseTime, ResponseCode, and ResponseSet. The first two are data parameters which means that their values must not be defined within the design file but are set during run time. These actually are data collected by the experimental run. The parameter named 'ResponseTime' contains the actual duration as measured during runtime. For timers of type CLOCK_TIMER comparing the parameter Duration and the parameter ResponseTime gives some information about the precision of the timing process during runtime. If timing is perfect then these should have the same value. For every timing group of a display the difference between these two parameters is stored in parameter TimeError.

    The parameter called 'ResponseSet' restricts the set of admissable response codes to its values. All responses which generate codes not contained in the array are ignored. If the parameter is set like this:

       ResponseSet = [de.pxlab.pxl.KeyCodes.LEFT_BUTTON, de.pxlab.pxl.KeyCodes.RIGHT_BUTTON];
    

    then the only responses which are accepted are the left or the right mouse buttons as these have - as we will see later - the corresponding response codes de.pxlab.pxl.KeyCodes.LEFT_BUTTON and de.pxlab.pxl.KeyCodes.RIGHT_BUTTON.
    The parameter ResponseSet does not only restrict the acceptable response set, it also enables a translation mechanism which translates the response codes mentioned above to device independent codes. These are values which correspond to the position of a code value within the array ResponseSet. This means that if the response set is defined as above then the response code corresponding to the left mouse button will be 0 and the response code corresponding to the right button will be 1 which are the respective positions in the array. These translated codes will then be stored in parameter ResponseCode.

    The parameter ResponseCode contains information about the event which stopped the respective timer. It will be TIME_OUT for a timer which is stopped by an internal clock. It will be some other value for other timers, like response timesr which are stopped by subject responses. In this case the ResponseCode is a code of the button, key, or device which stopped the timer. These are the valid mouse button codes:

    LEFT_BUTTON
    left mouse button
    MIDDLE_BUTTON
    center button
    RIGHT_BUTTON
    right mouse button

    The valid keyboard codes correspond to the virtual key codes as they are defined in class de.pxlab.pxl.KeyCodes. For character keys these are the ASCII codes of the respective characters. Function keys are special:

    ESCAPE_KEY
    Escape key
    SPACE_KEY
    Space bar
    PAGE_UP_KEY
    Page up key
    PAGE_DOWN_KEY
    Page down key
    END_KEY
    End key
    HOME_KEY
    Home or Pos 1 key
    LEFT_KEY
    Cursor left
    UP_KEY
    Cursor up
    RIGHT_KEY
    Cursor right
    DOWN_KEY
    Cursor down

    See class de.pxlab.pxl.KeyCodes for more key codes.

    Timing Groups

    Display objects are built from groups of DisplayElement objects. These are the primitive objects like bars, disks, or text elements which make up a Display. Multiple DisplayElement objects are grouped together to form a DisplayElement timing group of a Display. This is a group of DisplayElementobjects which have the same timing properties and thus are always shown simultaneously. Thus timing parameters of Displayobjects always refer to DisplayElement timing groups and not to single DisplayElement objects. Most Display objects have only a single timing group and thus the Timer parameter refers to the whole Display object.

    A Timing Group Example: The Stroop Experiment

    We look at a Stroop experiment as an example. Every trial of this experiment starts with an attention mark which is a cross at the center of the screen. The cross is shown until the subject presses and releases any of the mouse buttons. Following the cross the screen is cleared and remains so for some time. For PXLab this means that a clear screen object is shown for some time. The next object visible is a single text string which usually is a color name and whose letters are drawn in color. While this string remains visible a second string appears only a short time interval later at a position below the first string. The subject's task is to decide whether the second probe string names the color of the letters of the first test string. The two strings remain visible until the subject responds by pressing a mouse button. Immediately after the button has been pressed there appears a feedback text which tells the subject what the response time was. The text is visible for some time and then the screen is cleared until the next trial starts with the attention signal.

    The trial of this experiment starts with an attention mark which is visible as long as the subject presses and then releases a response button. This can be achieved by using a RELEASE_RESPONSE_TIMER. The class FixationMark has a single timer parameter called Timer which controls its duration. The Stroop example contains only a single instance of FixationMark such that the instance name need not be used:

        FixationMark() {
          Timer = de.pxlab.pxl.TimerCodes.RELEASE_RESPONSE_TIMER;
        }
    

    After the subject has released the response button in order to start the trial there is a first clear screen period with a fixed duration of 500 ms. Since the Stroop experiments uses more than a single instance of class ClearScreen we have to use an instance name also:

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

    This defines the instance named 'wait1' of class ClearScreen and sets its parameter Timer to a clock controlled timer and its duration to 500 ms.

    The next Display object is an instance of subclass TwoStrings. This class has two timer parameters: TwoStrings.SOATimer and Timer. The TwoStrings.SOATimercontrols the time interval between the onset of the test and the onset of the probe string. This interval corresponds to the first timing group of this Display object. The second timer simply named Timer controls the duration of the second timing group which contains both the test and the probe string. The first timing group has a fixed time interval of 100 ms. The second timing group is response controlled but has a time out limit of 5000 ms. The response set of admissible response codes contains only the codes which correspond to the left and the right mouse button respectively:

        TwoStrings() {
          SOATimer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
          SOADuration = 100;
          Timer = de.pxlab.pxl.TimerCodes.LIMITED_RESPONSE_TIMER;
          Duration = 5000;
          ResponseSet = [de.pxlab.pxl.KeyCodes.LEFT_BUTTON, de.pxlab.pxl.KeyCodes.RIGHT_BUTTON];
        }
    

    Note that this example measures the response time as starting from the begin of the probe signal. If the response interval should start with the start of the test signal and last until the end of the probe signal then the TwoStrings.SOATimer must be given the attribute START_RESPONSE_TIMER and the Timer must correspondingly have the attribute STOP_RESPONSE_TIMER:

        TwoStrings() {
          SOATimer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER 
                     | de.pxlab.pxl.TimerCodes.START_RESPONSE_TIMER;
          SOADuration = 100;
          Timer = de.pxlab.pxl.TimerCodes.LIMITED_RESPONSE_TIMER
                     | de.pxlab.pxl.TimerCodes.STOP_RESPONSE_TIMER;
          Duration = 5000;
          ResponseSet = [de.pxlab.pxl.KeyCodes.LEFT_BUTTON, de.pxlab.pxl.KeyCodes.RIGHT_BUTTON];
        } 
    

    In this case the actual response time will be stored in a global parameter named ResponseTime and will not be stored in any of the parameters of the TwoStrings object. This is necessary since the parameters of TwoStrings already store the response data of their respective timing groups.

    Finally the Feedback object has fixed duration of 1200 ms and there is a fixed duration ClearScreen object separating one trial from the next:

        Feedback() {
          Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
          Duration = 1200;
        }
        ClearScreen.wait2() {
          Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
          Duration = 1000;
        }
      }
    

    Note that the second ClearScreen object has the instance name 'wait2' in order to discriminate it from the first instance named 'wait1'. Here is the Stroop example with a small number of trials:

    The Voice Key Timer

    The voice key timer for any non-sound stimulus object is activated by setting the Timer parameter to VOICE_KEY_TIMER. This timer uses microphone input. It waits until the voice key threshold has been passed and sets the parameter ResponseTime to the respective response time. The voice key timer uses the following global parameters:
    VoiceKeyThreshold
    the threshold value for the voice key.
    VoiceKeyRecordingBufferSize
    the size of the low level sound input data buffer. This parameter should not be changed. Its default value is 44 and it must be a multiple of 4.
    VoiceKeyStopImmediately
    a flag which stops voice recording immediately after the voice key threshold has been passed. The default is 'true'. This flag may be set 'false' if the recorded voice is written to a file and analyzed later.

    The design file Voice_Key_Timer.pxd contains an example for using the voice key timer.

    There also is a special voice key display object named VoiceKey. This works like a sound recording object whose duration is controlled by the voice input signal. This display object allows for full recording of the voice key duration period. Recording is switched on by setting this object's parameter VoiceKey.Directory to a non-zero string. If the global parameter VoiceKeyStopImmediately is 'false' then the full duration voice signal will be recorded. If VoiceKeyStopImmediately is 'true' then recording stops after the threshold has been passed. The VoiceKey object also has a parameter to control the recording sample rate. The design file Voice_Key.pxd contains an example for using the VoiceKey object. Chapter ,Sound and Video' contains some hints how to control the sound input channel for the voice key.

    Video System Synchronization

    Whenever we have very short video display objects we may want to synchronize the display of these stimuli with the vertical refresh cycle of the video system. PXLab contains a Windows native library which allows video synchronization under Windows using DirectX methods. This does not work for other runtime environments and it does not work for applets since it both violates strict device independence and the security restrictions for applets.

    Display synchronization of the objects of a single timing group is achieved by adding the flag VIDEO_SYNCHRONIZATION_BIT to the property mask of a timer. This makes the display controller wait until the next vertical blanking interval starts before showing the objects of the timing group which is controlled by the respective timer. A consequence of this is that the timing interval immediately before the synchronized timer may be longer than requested. Up to one video display cycle may be added.

    There also exist timer codes for video synchronization which behave as described in the previous paragraph and may be used fór clock and response timers. These are VS_CLOCK_TIMER and VS_RESPONSE_TIMER. These constitute timers whose start is synchronized with the vertical retrace signal of the video system. Note that if the complete display duration of an object should be synchronized to the vertical retrace then it is not sufficient to synchronize the target object's timer, since this only synchronizes its start time. We also have to synchronize the target object's stop time and this must be done by synchronizing the Display object which immediately follows the target object.

    Display List Control Objects

    A display list control object is a special type of Display which does not show any objects on the screen but executes some influence on how and which Display objects of a DisplayList are shown. The response timing mechanism uses display list control objects to control a response gate which can define intervals where certain responses are forbidden or certain responses are allowed. This mechanism is also used to check the response state at certain points within display list presentation.

    ResponseControlStart
    starts a response control interval where spurious subject responses are collected. This is required for cases where it must be guaranteed that the subject does not press any response button before a certain Display object is shown or that the response buttons must be in a certain state before a stimulus can be shown. When this Display is inserted into a DisplayList then all response events which happen during the following CLOCK_TIMER intervals are stored.
    ResponseControlStop
    stops a response control interval. This Display stops watching for response events during CLOCK_TIMER intervals.
    ResponseControlCheck
    checks for spurious responses during an active response control interval. This Display object checks whether a certain response event has happened during the most recent response control interval. The following parameters are used to define the response event to be checked for:

    These parameters define a response event group to look for, the expected responses. The Display object reports its state to the DisplayList controller via a return flag. If this flag is set then the DisplayList controller immediately stops the current DisplayList.

    The actual working of ResponseControlCheck is controlled by the parameter Type which accepts values of class ResponseControlCodes. If the value is ANY_RESPONSE then any spurious response sets the return flag. If the value is LAST_RESPONSE then only the last spurious response is checked and if the value is FIRST_RESPONSEthen only the first spurious response is checked. If the parameter is nonzero and there is a spurious response then the respective response time is stored in the global parameter ResponseTime and the respective response code is stored in the global parameter ResponseCode. If the response is one of the expected responses as defined by the parameters Device, Direction, and Codes, then the return flag is set. If the parameter is not expected or there is no spurious response then the return flag is not set.

    Here is an example for using the ResponseControlCheck Display. This is a simple response time measurement experiment. The subject sees an attention signal and then has to press a mouse button until the signal appears. The response is to release the mouse button as fast as possible.

        Trial( TrialCounter, Luminance, ResponseTime) {
          ClearScreen:Pause() {
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
            Duration = 2000;
          }
          ResponseControlStart();
          FixationMark() {
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
            Duration = 1000;
          }
          ClearScreen:WaitSignal() {
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
            Duration = 500;
          }
          ClearScreenRandomTime() {
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
            ExpectedWait = 1000;
            WaitingTimeLimit = 3500;
            Duration = 3000;
          }
          ResponseControlCheck() {
            Type = de.pxlab.pxl.ResponseControlCodes.LAST_RESPONSE;
            Device = de.pxlab.pxl.TimerBitCodes.MOUSE_BUTTON_TIMER_BIT;
            Direction = de.pxlab.pxl.TimerBitCodes.DOWN_TIMER_BIT;
            Codes = ?;
            Text = ["Error!",
    		" ",
    		"Please hold down the response button after the fixation mark appears!"];
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
    	Duration = 2000;
          }
          SimpleDisk() {
            Size = 300;
            Duration = 100;
            Timer = de.pxlab.pxl.TimerCodes.VS_CLOCK_TIMER | de.pxlab.pxl.TimerCodes.START_RESPONSE_TIMER;
          }
          ClearScreen:WaitResponse() {
            Timer = de.pxlab.pxl.TimerCodes.RELEASE_RESPONSE_TIMER | de.pxlab.pxl.TimerCodes.STOP_RESPONSE_TIMER;
          }
          ResponseControlStop();
          Feedback() {
            Text = "%ResponseTime@i% ms";
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
            Duration = 1000;
          }
        }
    

    The trial starts with a ClearScreen shown for 2000 ms and then the ResponseControlStart Display object starts spurious response control. Then there is an attention signal FixationMark which tells the subject to press a mouse button and hold it down until the target signal shows up. The attention signal is shown for 1000 ms followed by a ClearScreen with fixed duration of 500 ms and a ClearScreenRandomTime with random duration of up to 3500 ms and an expected duration of 1000 ms. The subject has to hold down the mouse button during this time and any button release were a spurious response which should lead to stopping the trial. The ResponseControlCheck Display checks for spurious responses immediately before the signal, a SimpleDisk, appears. The ResponseControlCheck has Type set to FIRST_RESPONSE such that the first spurious response is checked and the target is any mouse button response since Direction and Codes are not set to any specific value. If a spuriuos response is found then The error message is shown for 2000 ms and the Trial is stopped. Otherwise the target signal is shown and a Feedback object shows the resulting response time. Check out the trials in the following demo. Remember to hold down the button when then fixation mark appears and release the button as fast as possible when the target signal is shown.

    Timing Across Multiple Displays

    The preceding example contains one more timing feature of interest here. The target stimulus is SimpleDisk which has a fixed duration of 100 ms. The SimpleDisk is followed by a ClearScreen which actually contains the response timer. Thus the ResponseTime parameter of the ClearScreen object contains the response time but this time interval is reduced by the duration of the SimpleDisk Object. The correct response time interval should start with the start of the SimpleDisk object and end with the following ClearScreen object. This can be achieved by adding the flag START_RESPONSE_TIMER to the Timer of the SimpleDisk object and the flag STOP_RESPONSE_TIMER to the Timer of the following ClearScreen object.

          ...
          SimpleDisk() {
            Size = 300;
            Duration = 100;
            Timer = de.pxlab.pxl.TimerCodes.VS_CLOCK_TIMER | de.pxlab.pxl.TimerCodes.START_RESPONSE_TIMER;
          }
          ClearScreen:WaitResponse() {
            Timer = de.pxlab.pxl.TimerCodes.RELEASE_RESPONSE_TIMER | de.pxlab.pxl.TimerCodes.STOP_RESPONSE_TIMER;
          }
          ResponseControlStop();
          Feedback() {
            Text = "%ResponseTime@i% ms";
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
            Duration = 1000;
          }
    
        }
    

    The effect is that a response interval is measured which starts with the SimpleDisk object and ends with the ClearScreen object. The resulting time interval is not stored in any of the Display object parameters but in the global parameter ResponseTime and the respective event code is stored in the parameter ResponseCode. This is why the Feedback object has the global ResponseTimeparameter contained in the value of its Text parameter.

    Response Collection During A Clock Timer Interval

    The standard behaviour of PXLab is to not collect any subject response while a timer of type CLOCK_TIMER is running. As an example look at

          Message() {
    	Text = "Press any key now!";
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
    	Duration = 500;
          }
    

    This display object is shown for 500 milliseconds and the response keys are inactive while this time interval is running. Now suppose that we want to collect a subject response while a fixed duration time interval is running. In this case we have to use a timer of type WATCHING_CLOCK_TIMER. This timer behaves exactly like a CLOCK_TIMER but response keys are active while the clock is running. If there is a response event during the timing interval then the respective response time and response code are stored into the Display object's parameters ResponseTime and ResponseCode. Only the first response event in the timing interval is stored!

    This mechanism may also be used to control execution of a subsequent display object which only has to be shown when no response event has happened. Here is an example:

          Message() {
    	Text = "Press any key now!";
            Timer = de.pxlab.pxl.TimerCodes.WATCHING_CLOCK_TIMER;
    	Duration = 2000;
          }
          Message:Why() {
    	Execute = Trial.Message.ResponseCode == de.pxlab.pxl.ResponseCodes.TIME_OUT;
    	Text = "I am still waiting!";
            Timer = de.pxlab.pxl.TimerCodes.RESPONSE_TIMER;
          }
          Feedback() {
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
    	Duration = 2000;
            Text = Trial.Message:Why.Execute? 
                   "Response Code = %Trial.Message:Why.ResponseCode%":
                   "Response Code = %Trial.Message.ResponseCode%";
          }
    

    The first Message object has a WATCHING_CLOCK_TIMER of 2000 ms duration. If there is any response during the display interval then this object's parameter ResponseCode contains the respective response or key code. If there is no response then the response code is TIME_OUT. This is the default response code for CLOCK_TIMER intervals terminating normally. The subsequent display object has ist Execute parameter defined to an expression:

    	Execute = Trial.Message.ResponseCode == de.pxlab.pxl.ResponseCodes.TIME_OUT;
    

    This expression is non-zero if the Message object's response code is TIME_OUT. In this case the second Message object is shown if and only if the first Message object did not find any spurious response. Finally the Feedback object uses the second Message object's Execute parameter to decide, which is the response code to use as data:

            Text = Trial.Message:Why.Execute? 
                   "Response Code = %Trial.Message:Why.ResponseCode%":
                   "Response Code = %Trial.Message.ResponseCode%";
    

    A slight modification is necessary in order to also collect the response time across optional displays as used in this example. We have to use the timer properties START_RESPONSE_TIMER and STOP_RESPONSE_TIMER here in order to get a response time which possibly spans the two successive Message objects:

        Trial() {
          Message() {
    	Text = "Press any key now!";
            Timer = de.pxlab.pxl.TimerCodes.WATCHING_CLOCK_TIMER 
    		| de.pxlab.pxl.TimerCodes.START_RESPONSE_TIMER 
    		| de.pxlab.pxl.TimerCodes.STOP_RESPONSE_TIMER;
    	Duration = 2000;
          }
          Message:Why() {
    	Execute = Trial.Message.ResponseCode == de.pxlab.pxl.ResponseCodes.TIME_OUT;
    	Text = "I am still waiting!";
            Timer = de.pxlab.pxl.TimerCodes.RESPONSE_TIMER 
    		| de.pxlab.pxl.TimerCodes.STOP_RESPONSE_TIMER;
          }
          Feedback() {
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
    	Duration = 2000;
            Alignment = de.pxlab.pxl.AlignmentCodes.LEFT;
            Text = "ResponseCode = %ResponseCode%\nResponseTime = %ResponseTime@i% ms";
          }
        }
    

    The first Message object has set both START_RESPONSE_TIMER and STOP_RESPONSE_TIMER and the second Message object has set STOP_RESPONSE_TIMER. Thus the global parameters ResponseCode and ResponseTime are set by which STOP_RESPONSE_TIMER becomes effective last. Note that we use the global ResponseCode here also to show the response code.

    How to Check Proper Timing

    In many experiments precise timing is essential. Programs like PXLab which run in a multitasking and non-realtime environment can never guarantee a precision in timing which is high enough for all possible psychological experiments. However, given the current speed of laboratory computers, there will not be many experiments which really need more precision than is available. Although initially PXLab has not been developed with high precision in timing as one of its primary design goals, considerable effort went into the code of PXLab version 2 to make timing as precise as possible. Actually timing precision is the major advance to version 2.

    Given the multitasking environment with its timing uncertainty the optimal way to handle timing problems seems simply to be trying things out and check whether they work in an acceptable way. 'Trying out and check' means to run the experiment and look at the timing as it may be measured. Timing measurement may be done externally using special hardware as suggested by De Clercq, Crombez, Buysse, & Roeyers, (2003) or Plant, Hammond & Turner (2004). However it my also be done internally with high precision by using an internal high resolution timer. The high resolution timer used by PXLab is based on the Java method System.nanoTime() which is available in Java version 1.5. This method reports time intervals with nanosecond resolution from the most precise available timer on the system. The Timing Environment Dialog menu entry of the ExDesignEditor application may be used to investigate which of several possible high resolution timers PXLab is actually using.

    This example shows a configuration where PXLab uses the Java 5 nanoTime() method of class System as its high resolution timing tool. Note that PXLab limits its timer resolution to 1 ms. The dialog also shows that the built in timing method currentTimeMillis() of class System has a resolution of 16 ms only. The number of 'Checks per Step' gives the number of calls to PXLab's high resolution timing method which have been executed to determine a single resolution step of the respective timer. This number will strongly depend on CPU speed. This example has been run on a 3 GHz CPU running Windows XP.

    Timer debugging my be activated by using the debug option 'timing' when starting ExRun:

        java de.pxlab.pxl.run.ExRun -D timing
    

    When this debugging option is activated a timing protocol is printed after every display list. This protocol contains three entries for every timing group of every display object in the display list. Here is an example section of such a protocol:

    Trial.FixationMark[0]
    1067441475
    1067441476
    1067441876
    Trial.Message[0]
    1067441876
    1067441879
    1067442311
    Trial.ClearScreen[0]
    1067442311
    1067442311
    1067442612
    
    Trial.FixationMark[0]
    1067442648
    1067442649
    1067443048
    ...
    

    Each entry starts with the instance name of the respective display object followed by the timing group index. In this example only display objects with a single timing group have neen used such that the timing group index always is '0'. The timing values following are times in milliseconds. Their meaning is as follows: The first value is the time immediately before computation of the display elements of a display timing group is started. The second value is the time immediately after the display elements have been painted to the screen and have become visible and the third value is the time when the timer delay assigned to the respective timing group has passed. Here is the trial definition for this example:

        Trial(TrialCounter, Message.Text, Message.ResponseTime) {
          FixationMark() {
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
            Duration = 400;
          }
          Message() {
            Timer = de.pxlab.pxl.TimerCodes.RESPONSE_TIMER;
          }
          ClearScreen() {
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
            Duration = 300;
          }
        }
    

    In this definition we see that the intended duration of the fixation mark is 400 ms. The actual value measured is (1067441879 - 1067441476) = 403 ms. This is the second number of Trial.Message minus the second number of Trial.FixationMark. The delay between the point in time when the Message was visible and the Message object's time interval was stopped is (1067442311 - 1067441879) = 432 ms. This is exactly the same value as found in the first trial of the respective response data file:

    pxlab 0 432
    pxlab 1 262
    pxlab 2 268
    ...
    

    The difference between the second value of Message and the second value of ClearScreen is the actual visiblity duration of the Message object. This is (1067442311 - 1067441879) = 432 ms which is the same as the measured response time when working with millisecond precision.

    The difference between the first and second time value of each entry always is the time for computing and drawing needed by PXLab. This time varies depending on the complexity of the display element. The difference between the third and the second time value either corresponds to the response time or should be equal to the intended duration value of a CLOCK_TIMER timer. This interval should not deviate much from the intended duration as long as a CLOCK_TIMER timer type is used. The time between successive display lists represents the time PXLab needs to organize a display list. In our example this is (1067442648 - 1067442612) = 36 ms which seems acceptable. These measuerements have been made on a completely standard Windows XP system with a 3 GHz Intel Pentium 4 CPU.

    If instead of 'timing' the option name 'hrtiming' is used for the debug option, then all time values are printed with nanosecond resolution instead of millisecond resolution.

    Timing Adjustment for Complex Stimulus Calculations

    The timer debugging option may be used to adjust time intervals for complex stimulus calculations. Here is an example for a complex stimulus, a moving Gabor pattern:

        Trial(GaborPatternAnimation.HighColor, 
              GaborPatternAnimation.AngleOfRotation, 
              GaborPatternAnimation.ResponseTime) {
          GaborPatternAnimation() {
            Timer = de.pxlab.pxl.TimerCodes.RESPONSE_TIMER;
    	Width = 300;
    	Height = 300;
        	FramesPerCycle = 12;
    	FrameDuration = 40;
    	AmplitudeModulation = 0;
          }
          Feedback() {
    	Text = "Time = %Trial.GaborPatternAnimation.ResponseTime%";
    	Duration = 1500;
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
          }
        }
    

    The subject's task in this example may be to detect the direction of motion and there is a feedback of the response time for 1500 ms. Now if we look at the timing protocol, we see that the computation of the Gabor images takes about 1300 ms. This is the time interval between the end of a Feedback and the start of the following GaborPatternAnimation object:

    --- Trial.Feedback[0]
    1086078800
    1086078834
    1086080334
    
    --- Trial.GaborPatternAnimation
    1086081620
    1086081620
    1086082926
    --- Trial.Feedback[0]
    1086082927
    1086082929
    1086084428
    

    This time adds to the display duration of the Feedback object which effectively becomes (1086081620 - 1086078834) = 2786 ms. Knowing this, we may decrease the Feedback Duration by 1300 ms such that the effective feedback duration becomes approximately 1500 ms as intended:

          Feedback() {
    	Text = "Time = %Trial.GaborPatternAnimation.ResponseTime%";
    	Duration = 1500 - 1300;
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
          }
    

    This gives us the following timing protocoll:

    --- Trial.Feedback[0]
    1086289774
    1086289808
    1086290008
    
    --- Trial.GaborPatternAnimation
    1086291405
    1086291405
    1086292832
    

    We have an effective Feedback duration here of (1086291405 - 1086289808) which is 1597 ms in this case. This is much nearer to the intended value of 1500 ms as without adjustment.

    References

    De Clercq, A., Crombez, G., Buysse, A., & Roeyers, H. (2003). A simple and sensitive method to measure timing accuracy. Behavior Research Methods, Instruments, & Computers, 35, 109-115.

    Plant, R. R., Hammond, N., & Turner, G. (2004). Self-validating presentation and response timing in cognitive paradigms: How and why? Behavior Research Methods, Instruments, & Computers, 36, 291-303.

     

  15. Adaptive Procedure Control
  16. An adaptive procedure is a method for changing the stimuli during an experiment depending on subject responses such that the response probability for a certain response approximates a predefined value after a sufficiently long sequence of trials have been run. Adaptive procedures are used in psychometric experiments where the procedure's purpose is to search for a stimulus that elicits a certain response with a fixed probability. The most simple situation is given by the problem of finding a stimulus x, such that x in context a is in some respect equivalent to stimulus y in context b:

    (x,a) = (y,b).

    As an example let x and y be small patches of light on a video screen with x and y being the respective luminance values, and let a and b be the respective background luminance values that surround x and y. The subject's task might be to adjust stimulus intensity x such that x on the background a looks as bright as the patch y on background b. An adjustment procedure like this, however, has several disadvantages. Among these are the sequential dependencies that are introduced by letting the subject do successive adjustments. In addition to that the adjustment procedure requires that the subject decides when to stop adjusting and thus the final precision strongly depends on the subject's motivation to do precise adjustments.

    Using a psychometric method for finding the equivalent stimulus x in the above example assumes that there exists a strictly monotone increasing function for the probability that x in context a is judged to be brighter than y in context b. Let F(x,a,y,b) be that function. Then we may define x in context a to be equivalent to y in context b if F(x,a,y,b) = 0.5. This means that if we present (x,a) and (y,b) to the subject and ask if (x,a) looks brighter than (y,b) the subject will answer "yes" in approximately half of the presentations and "no" in the other half.

    Since we don't know the response probability for any value of x in advance it may be very time consuming to find the proper value of x since it already takes some repeated responses to estimate the response probability at any point x. Here come the adaptive procedures. Suppose a subject responds with "yes" to the presentation of (x0 ,a) and (y,b). This means that x0 seems to be brighter than the equivalence point we are looking for. Thus on the next trial we use a luminance x1 that is a little bit less than x0. If the subject still responds with "yes" we go on decreasing the stimulus intensity until we get a "no" response. After getting a "no" response we increase the stimulus intensity. Thus we finally will get a series of continuously changing responses as the stimulus intensity xt approximates the equivalence point.

    Adaptive procedures are very economic since they make optimal use of the information contained in the subject's responses. However, special care has to be taken in order to avoid the sequential effects of adjustment procedures mentioned above. Designing an adaptive procedure requires the solution of four major problems:

    1. What is a good starting value x0?
      It is clear that any sequentially dependent procedure somehow depends on the initial value that starts the sequence. It should be taken care that the starting value is good enough to ensure proper conversion of the whole sequence within the bounds given by the other parameters, especially the stepsize and the length of the procedure.

    2. How should each stimulus be changed, what is a good stepsize?
      Usually the stimulus change after each trial is not constant but is decreasing with increasing trial number. Thus at the beginning a large stepsize is chosen because this allows easy convergence into the range near the final point. At the end of the procedure a small stepsize is preferred since this results in a greater precision at the end of the sequence.

    3. When should the procedure be ended?
      The easiest way to define the stopping rule is to present a fixed number of trials and then stop. More refined rules make stopping depend on the number of response direction changes or on the stepsize reached.

    4. How should we define the result of an adaptive procedure?
      After stopping the data a sequence of stimuli and responses are available. The final stimulus value is an easy choice for the procedure result. Another way to compute the result is to average the stimuli in the final section of the sequence or to average the turning points in the sequence as suggested by Wetherill (1963). The turning points may also be used to compute a crude approximation for the final stimulus value's variance.

    Procedure Types

    PXLab uses an extended version of the transformed up-down methods of Levitt (1971). These methods may be described as finite state machines. Consider the simple 1-up-1-down procedure described earlier. Each adaptive sequence starts from a state named start. If the subject responds "yes" the sequence goes into state Y and if the first response is "no" then the procedure enters state N. Table 1 gives this simple state transition function. Each but the starting state is a simple description of the subject's response history.

    Table 1: A simple state transition function for adaptive procedures. The set of possible states is start, N, Y. The first column shows the state in trial t with t0 = start. The second column shows the state in trial t+1, if the response in trial t was "No" and the third column shows the state in trial t+1 when the response in trial t was "yes".
    State "no" "yes"
    start N Y
    N N Y
    Y N Y

    The main purpose of the set of states is to record the response history in one single number. The current state of a sequence may be used to compute the next stimulus value: if the sequence is in state N, then the stimulus intensity has to be increased, if it is in state Y it has to be decreased. The state transition table given in Table 1 is somewhat trivial. So assume that we want to have a more complicated procedure, where the stepsize by which the stimulus is changed should remain constant as long the subject always gives the same response. If the subject changes the response direction, the stepsize should be decreased.

    Figure 1: Example for an adaptive sequence of stimuli in the simple 1-up-1-down procedure.

    The following list of stimulus-response pairs might result from such a sequence: (80, N), (100, N), (120, N), (140, Y), (130, N), (136.6, Y) .... Figure 1 shows the stimulus values for this example. The initial stimulus intensity is 80. Since there is a "no" response the stimulus is increased by the initial stepsize of 20, such that the second stimulus is 100. The second response is again "no". Thus the stepsize remains 20 and the next stimulus becomes 120. The same holds for the third response, the fourth stimulus is 140. Now there is a "yes" response. This makes a turning point of the adaptive sequence and we reduce the stepsize to half of its previous value. The stepsize becomes 10 and the next stimulus is 130. The response is another change of direction and the stepsize becomes one third of the original size. The general schema is to hold the stepsize constant as long as the subject's response remains constant. If there is a change of the response, the stepsize is reduced. We start out with an initial stepsize T0. The actual step in trial t is computed by the formula Tt = T0 /St where S1 = 1. If the stepsize has to be reduced, we increase St by an amount D. In our example D = 1. Thus the example shows the sequence S1 = 1, S2 = 1, S3 = 1, S4 = 2, S5 = 3. With T0 = 20 we thus get T1 = 20, T2 = 20, T3 = 20, T4 = 10, T5 = 6.6. The actual stimulus value depends on the initial stimulus, the stepsize and the response. If the response is "yes", we decrease the stimulus by the stepsize, if it is "no" we increase the stimulus.

    Figure 2: Example for an adaptive sequence of stimuli in the simple 1-up-2-down procedure.

    Figure 3: State transition diagram for a transformed 1-up-1-down adaptive procedure
    which also detects changes in response direction.

    Write P for the type of adaptive procedure. Let St be the state of the procedure in trial t and Rt be the response in trial t. Then there is a function FP that maps pairs of states and responses into the set of states:

    St+1 = FP (St , Rt ).

    The function FP is the state-transition table of the procedure P. PXLab currently has 3 different state transition functions available, they are shown in Figs. 3, 4, and 5.

    Figure 3 corresponds to the simple 1-up-1-down procedure of the previous section. However, it uses a somewhat extended table of states in order to remember whether the subject's response has changed or has remained constant within the last two trials. This makes it possible to decrease the step size on response changes and to increase it if the response remains constant. Table 2 shows the corresponding parameter transformation function. This function has an additional parameter AdaptiveUpwardStepFactor which multiplies upward step sizes. Thus up and down step sizes may be different. Procedures like this have been used by Kaernbach (1990) to find points of the psychometric function with response probabilities different from 0.5.

    Figure 4: State transition diagram for a transformed 1-up-2-down adaptive procedure.

    Figure 4 is a transformed up-down method. The idea is to consider sequences of trials with constant stimulus values and to change the stimulus only if certain sequences are observed. The 1-up-2-down procedure uses pairs of trials where there are 4 possible sequences: YY, YN, NY, NN. These are partitioned into the up-group: YN, NY, NN and the down-group: YY. Thus the stimulus is decreased only if both trials elicit "yes"-responses. In all other cases the stimulus is increased. Since both the up- and the down-group arrive at a probability of 0.5 for the asymptotic stimulus value we have

    P(down) = 0.5 = P(Y) * P(Y)

    such that P(Y) = 0.707. Thus we get P(Y) = 0.707 for the asymptotic stimulus value. Note that there is no need to present the second trial of a pair if the subject responds "no" in the first trial, since both NY and NN are in the up-group. The state transition diagram 4 uses an extended set of states which also keeps track of the change history such that sequences of ups and downs may be treated different from sequences containing changes.

    Figure 5: State transition diagram for a transformed 1-up-3-down adaptive procedure.

    PXLab's 3rd adaptive procedure looks at triples of trials and decreases the stimulus only if there are 3 "yes"-responses. Figure 5 shows the state transition schema. This procedure arrives at a response probability of P(Y) = 0.795.

    The purpose of the state machine is to code the response history such that the stimulus for the current trial may be computed from the last trial's stimulus, the current state, and the stepsize control parameters. Let Tt be the stepsize for computing xt, the stimulus in trial t. The formula for computing xt is

    xt = xt-1 + sign(Tt ) max(|Tt |, Tmin ).

    Here Tmin is a lower limit of the stepsize. If the computed stepsize Tt becomes smaller than Tmin , we take Tmin as the actual stepsize.

    The stepsize Tt is a function of the current state, the initial stepsize T0 and a step dividend St

    Tt = HP (T0 , St , St ).

    The functions HP for the different procedure types are given in the third columns of Tables 2, 3, 4, and 5. The step dividend St is the main control for changes of the step size. It is a function of the procedure's state, the change parameters D and G and the step dividend of the previous trial:

    St = GP (St-1 , St , D, G).

    The Tables 2, 3, 4, and 5 contain the functions GP for PXL's adaptive procedures in the second column.

    Table 2: Parameter transformation table for the 1-up-1-down adaptive procedure. This is the most simple type of adaptive procedure. It only responds to the most recent response but may be modified by additional parameters which allow various modifications of step sizes. This makes it possible to also implement the so called weighted up-down procedure also. If AdaptiveUpwardStepFactor is set to 1 and no weighted up-down-procedure is used then the simple 1-up-1down procedure should arrive at a response probability of 0.5 both for Y and N responses.
    State AdaptiveStepDivisor(t) AdaptiveStepSize(t)
    YN AdaptiveStepDivisor(t-1) + AdaptiveStepDivisorIncrement AdaptiveUpwardStepFactor * AdaptiveStepSize(0) / AdaptiveStepDivisor(t)
    NY AdaptiveStepDivisor(t-1) + AdaptiveStepDivisorIncrement - AdaptiveStepSize(0) / AdaptiveStepDivisor(t)
    NN AdaptiveStepDivisor(t-1) - AdaptiveStepDivisorDecrement AdaptiveUpwardStepFactor * AdaptiveStepSize(0) / AdaptiveStepDivisor(t)
    YY AdaptiveStepDivisor(t-1) - AdaptiveStepDivisorDecrement - AdaptiveStepSize(0) / AdaptiveStepDivisor(t)

    Table 3: Parameter transformation table for the delayed 1-up-1-down adaptive procedure. This procedure keeps the step divisor as long as there is no change of response direction. After a change of response direction, however, the step divisor is incremented by the value which would have been active if there were a change at every trial.
    State AdaptiveStepDivisor(t) AdaptiveStepSize(t)
    YN 1 + AdaptiveTrialCounter * AdaptiveStepDivisorIncrement AdaptiveStepSize(0) / AdaptiveStepDivisor(t)
    NY 1 + AdaptiveTrialCounter * AdaptiveStepDivisorIncrement - AdaptiveStepSize(0) / AdaptiveStepDivisor(t)
    NN AdaptiveStepDivisor(t-1) AdaptiveStepSize(0) / AdaptiveStepDivisor(t)
    YY AdaptiveStepDivisor(t-1) - AdaptiveStepSize(0) / AdaptiveStepDivisor(t)

    Table 4: Parameter transformation table for the 1-up-2-down adaptive procedure. This procedure should arrive at a response probability of 0.707 for Yes responses.
    State AdaptiveStepDivisor(t) AdaptiveStepSize(t)
    YYN AdaptiveStepDivisor(t-1) + AdaptiveStepDivisorIncrement AdaptiveStepSize(0) / AdaptiveStepDivisor(t)
    YY AdaptiveStepDivisor(t-1) + AdaptiveStepDivisorIncrement - AdaptiveStepSize(0) / AdaptiveStepDivisor(t)
    NN AdaptiveStepDivisor(t-1) - AdaptiveStepDivisorDecrement AdaptiveStepSize(0) / AdaptiveStepDivisor(t)
    YYYY AdaptiveStepDivisor(t-1) - AdaptiveStepDivisorDecrement - AdaptiveStepSize(0) / AdaptiveStepDivisor(t)
    NYN AdaptiveStepDivisor(t-1) AdaptiveStepSize(0) / AdaptiveStepDivisor(t)
    NY AdaptiveStepDivisor(t-1) 0
    YYY AdaptiveStepDivisor(t-1) 0

    Table 5: Parameter transformation table for the 1-up-3-down adaptive procedure. This procedure should arrive at a response probability of 0.795 for Yes responses.
    State AdaptiveStepDivisor(t) AdaptiveStepSize(t)
    YYYN AdaptiveStepDivisor(t-1) + AdaptiveStepDivisorIncrement AdaptiveStepSize(0) / AdaptiveStepDivisor(t)
    NYYY AdaptiveStepDivisor(t-1) + AdaptiveStepDivisorIncrement - AdaptiveStepSize(0) / AdaptiveStepDivisor(t)
    NN AdaptiveStepDivisor(t-1) - AdaptiveStepDivisorDecrement AdaptiveStepSize(0) / AdaptiveStepDivisor(t)
    YYYYYY AdaptiveStepDivisor(t-1) - AdaptiveStepDivisorDecrement - AdaptiveStepSize(0) / AdaptiveStepDivisor(t)
    NYN AdaptiveStepDivisor(t-1) AdaptiveStepSize(0) / AdaptiveStepDivisor(t)
    NY AdaptiveStepDivisor(t-1) 0
    NYY AdaptiveStepDivisor(t-1) 0
    NYYYY AdaptiveStepDivisor(t-1) 0
    NYYYYY AdaptiveStepDivisor(t-1) 0

    Stopping Rules

    Up to now there are three valid stopping rules in PXLab: The first one is an empty condition and will never stop. It is selected by setting the variable AdaptiveStoppingRule to DontStop. The second stopping rule TurnPoints uses AdaptiveTurnPointCounter and AdaptiveTurnPointLimit to decide on the length of an adaptive sequence. If AdaptiveTurnPointCounter becomes equal to AdaptiveTurnPointLimit, then the sequence will be stopped. The third rule TurnPointsAtMinimum works similar to the second, but in this case counting of the turns does not start before the actual stepsize Tt is smaller than the minimal step size Tmin. Thus the procedure stops if there are at least AdaptiveTurnPointLimit turn points with minimal step size.

    Computing the Results

    Most PXLab programs are experimental only. They do not contain data analysis functions. Some adaptive applications are an exception since they are able to compute the results of adaptive procedures. The method used is the one proposed by Wetherill (1963). The idea is to take pairs of turning points of the adaptive sequence and to use them as an estimate of the final value and its variance. PXLab computes the arithmetic mean and standard deviation of the turning points in an adaptive sequence. It uses at most the last AdaptiveComputingPoints turning points of a sequence to compute the results. If less than AdaptiveComputingPoints turning points are available, then an even number of turning points is used. The results of the computations are entered into the experimental variable AdaptiveResults. The actual number of turning points used for computing the results is entered into AdaptiveTurnPointCounter.

    The parameter AdaptiveResultComputation determines the actual method used for results computation. If its value is TailMean then the previously described method is used. NoResults results in no computation at all. There are more methods for computing results of adaptive procedures. The respective parameter values are found in AdaptiveResultCodes.

    Mixing Different Adaptive Sequences in a Single Block

    One single block may contain more than one adaptive sequence for a target. Each sequence is defined by a trial which contains all parameters necessary to define the sequence. The following parameters are needed. AdaptiveTrialCounter (t) counts the number of trials within a sequence. AdaptiveState (St ) stores the current state of the sequence and AdaptiveResponseParameter (Rt ) saves the response. AdaptiveStepDivisor(St ) is the current value of the step size divisor. These parameters must be trial arguments. If results computations are to be saved for each sequence then AdaptiveResults and AdaptiveTurnPointCounter also have to be trial arguments. Aside from these procedural parameters there has to be a stimulus parameter in each trial. This parameter's name is AdaptiveParameter. There still are some more parameters required to define an adaptive procedure, but these need not always be trial arguments. The procedure type is controlled by AdaptiveProcedure (P). If more than a single procedure type is used within a block then AdaptiveProcedure has to be a trial argument. AdaptiveStepDivisorIncrement (D) and AdaptiveStepDivisorDecrement (G) control how the factor AdaptiveStepDivisor(St ) changes. AdaptiveStepSizeMinimum (Tmin ) is the smallest and AdaptiveStepSize(T0 ) the initial step size. AdaptiveStoppingRule determines the stopping rule, AdaptiveTurnPointLimit gives the maximum number of turn points and AdaptiveComputingPoints gives the number of turning points used for computing the results.

    There must be exactly one trial definition for each target stimulus in a block. The factor TrialFactor creates copies of the trial which constitute the trials of the adaptive sequence for the respective target. Thus at the start of an experiment each block contains a fixed number of trials for each target. If the stopping condition is satisfied then the remaining trials of the respective target may be deleted by setting AdaptiveRemoveTrailingTrials to ON. If AdaptiveRemoveTrailingTrials is OFF then no trials are deleted and there are exactly TrialFactor trials for each sequence. This may be desirable in order to keep proper randomization of multiple targets. If each sequence is stopped when the individual stopping criterion is satisfied then there will be only trials for a single target at the end of a block.

    If there are more than one adaptive sequences in a block then the program must be able do classify each trial according to the target it belongs to. This is done by using some of the trial parameter values. Two trials are considered to belog to the same adaptive sequence if they have the same values of AdaptiveSequenceID.

    Examples

    A horizontal and a vertical line are shown. One of them divides the other at a certain ratio. The task is to select the line which is the longer one. The cursor up/down keys correspond to the vertical the left/right keys to the horizontal line. The point of subjective equality of a horizontal and a vertical line is determined by an adaptive procedure.

    Experimental Parameters for Adaptive Procedures

    AdaptiveProcedure
    Adaptive procedure type.
    AdaptiveStoppingRule
    Stopping rule for adaptive procedures.
    AdaptiveResultComputation
    Adaptive procedure result computation method.
    AdaptiveSequenceID
    ID Code for trials of a single adaptive sequence.
    AdaptiveState
    Adaptive procedure state.
    AdaptiveParameter
    Name of the adaptive parameter.
    AdaptiveResponseParameter
    Name of the adaptive procedure response parameter.
    AdaptiveTrialCounter
    Counter which counts trials within an adaptive sequence.
    AdaptiveTurnPointCounter
    Counter which counts turn points within an adaptive sequence.
    AdaptiveTurnPointLimit
    Limit for the number of turn points used by the appropriate stopping rule.
    AdaptiveComputingPoints
    Number of points used for computing the result of an adaptive sequence.
    AdaptiveStepSize
    Initial step size in an adaptive procedure. The actual step size is limited by the value of AdaptiveStepSizeMinimum. If some transition rule requires to set the step size smaller than AdaptiveStepSizeMinimum, then it is set to the value of AdaptiveStepSizeMinimum and a flag is set which tells the adaptive procedure that the process has arrived at the minimum step size.
    AdaptiveStepSizeMinimum
    Minimum step size in an adaptive procedure.
    AdaptiveStepDivisor
    Divisor for changing the step size during an adaptive procedure. The parameter AdaptiveStepDivisor never gets smaller than the value of AdaptiveStepDivisorDecrement. If any transformation requires setting AdaptiveStepDivisor smaller than AdaptiveStepDivisorDecrement then it is set to the value of AdaptiveStepDivisorDecrement itself.
    AdaptiveStepDivisorDecrement
    Decrement for the step size divisor of an adaptive procedure.
    AdaptiveStepDivisorIncrement
    Increment for the step size divisor of an adaptive procedure.
    AdaptiveUpwardStepFactor
    Upward step size factor for weighted up-down procedures.
    AdaptiveRemoveTrailingTrials
    Flag to remove trailing trials after an adaptive sequence is finished.
    AdaptiveProtocol
    Flag to switch on a detailed protocol of the adaptive procedure.
    AdaptiveMLEstimation
    If this flag is true then maximum likelihood estimation is used by the estimation procedures of the adaptive stimulus control system. If the flag is false then least square error minimization is used.
    AdaptiveGuessingRate
    Assumed guessing rate for psychometric functions estimation.
    AdaptiveLapsingRate
    Assumed lapsing rate for psychometric functions estimation.
    AdaptiveQuantiles
    The quantiles which should be returned after psychometric function estimation. The quantiles are returned in AdaptiveResults.
    AdaptiveResults
    The results array for parameter estimates after adaptive procedures.

    References

    Kaernbach, C. (1990). A single-interval adjustment-matrix (SIAM) procedure for unbiased adaptive testing. The Journal of the Acoustical Society of America, 88, 2645--2655.

    Levitt, H. (1971). Transformed up-down methods in psychoacoustics. The Journal of the Acoustical Society of America, 49, 467-476.

    Wetherill, G. B. (1963). Sequential estimation of quantal response curves. Journal of the Royal Statistical Society, B25, 1--48.

     

  17. Colors and Color Calibration
  18. Colors in PXLab

    All colors in PXLab are specified in device independent color coordinates. PXLab uses the CIE 1931 XYZ System as its internal system for color coordinates. All design file color specifications are given with three coordinates: [Lxy], with
    1. L the luminance in candela per square meter [cd/m2],
    2. x the CIE 1931 x-chromaticity, and
    3. y the CIE 1931 y-chromaticity.

    Thus an example for defining a color is

       Color = [60.0, 0.3127, 0.3291];
    
    which assigns an array to the experimental parameter 'Color' and the color has a luminance of 60.0 cd/m2 and has the chromaticity (0.3127, 0.3291), which corresponds to D65 white.

    In many experiments it may not be necessary to have device independent colors and exact color specifications. For those cases we have a set of color functions which default to the basic device colors and may be used to assign color values to parameters like

     
       Color = white(); 
    
    which assigns a white color to the parameter 'Color'. Note, however, that the colors defined by these functions are not device independent.

    In some cases it may also be desirable to use color coordinates which differ from the CIE 1931 XYZ-System. These cases can be handled by using built in color transformations. The chapter on parameter values contains a list of all available transformations. Using these one may, as an example, use CIELab-Coordinates for color definition:

     
       Color = fromCIELab([40, 0, 0]);
    
    This defines a color which has a CIELab Lightness L*-value of 40, and the chromaticity (a*,b*) of (0, 0).

    The internal PXLab color objects are instances of class PxlColor. These store color coordinates as CIE 1931 XYZ-coordinates. This class also contains many static transformations into different color systems: CIE L*a*b* 1976, CIE L'u'v' 1976, CIEL*u*v* 1976, CIECAM 1997, the Smith & Pokorny Fundamental Cone Excitation codes, the MacLeod & Boynton Relative Cone Excitation codes, linear device RGB, and gamma dependent device RGB.

    The CIE L*a*b* related systems need a white point for defining their transformation. This white point is defined by the global experimental parameter CIEWhitePoint. By default this parameter is undefined and the system uses the current color device's white point as its reference. In order to define a 80 cd/m2 white point for a D65 illuminant we may set

       CIEWhitePoint = [80.0, 0.3127, 0.3291];
    

    Color transformations related to RGB-Systems need to know the current color device primaries. The following sections describe how these may be defined and how the necessary color calibration may be achieved.

    Color Calibration

    Color calibration of CRT devices can be rather complicated. Brainard, Pelli, and Robson (2002) give a detailed discussion of all problems involved. The PXLab software package provides simple calibration tools which will satisfy most applications. The basic assumptions are:
    • The color device is perfectly homogenous. This means that calibration is independent of screen position.
    • The color channels are independent and no additivity failures exist. This means that calibration can be done for each color channel independently.
    • The gamma function for each channel follows the function suggested by Berns, Motta, and Gorzynski (1993):
          Luminance = maxLuminance * (gain * DACValue/maxDACValue + offset)^gamma
      
      where 'gain', 'offset', and 'gamma' are the three parameters of each channel's gamma function, 'maxLuminance' is the maximum luminance of the respective color channel and 'maxDACValue is the largest possible DAC output value.

    In order to use the PXLab color calibration tools the Yxy-coordinates of the display's color primaries at maximum luminance must be known. Here the Y-coordinate refers to the maximum luminance which can be achived by the respective color channel and the xy-chromaticities describe the device's phosphor chromaticities. These coordinates may be set by defining the experimental parameters RedPrimary, GreenPrimary, and BluePrimary. Here is an example for defining the primaries of a Sony Trinitron monitor which creates a maximum luminance of 100 cd/qm at full white:

       RedPrimary = [21.26, 0.621, 0.340];
       GreenPrimary = [71.52, 0.281, 0.606];
       BluePrimary = [7.22, 0.152, 0.067];
    

    Colors are controlled by writing integer values into certain video memory slots. The relation of actual luminance at each screen position and for each color channel to these integer values is nonlinear. This function is usually called 'gamma function'. It relates video digital-to-analog converter entries to output luminance. Berns, Motta, and Gorzynski (1993) have suggested a gamma function of the following form:

       L = (a*E - s)^g
    
    which is widely accepted in color vision laboratories. Brainard, Pelli, and Robson (2002) propose a different formula, which, however, is equivalent. This function has three parameters: the gain parameter a, the offset parameter s and the gamma parameter g. The offset parameter is not independent of the gain but is s = (1-a), such that it is sufficient to define g and a. These parameters may be set in PXLab by defining the experimental parameters RedGamma, GreenGamma, and BlueGamma. Here is an example which uses the default settings of PXLab which correspond to the sRGB color space:
       RedGamma = [2.4, 1.0];
       GreenGamma = [2.4, 1.0];
       BlueGamma = [2.4, 1.0];
    

    Where can one get the gamma parameters from? The best way to get the gamma parameters is to measure luminance output for sufficiently many digital-to-analog converter entries and compute estimates for the gamma function parameters. This is done by the PXlab application ColorCalibrationTool. It requires that one has a photometer available and the device chromaticities are known.

    Experimental Parameters for Color Calibration

    PXlab can access multiple display screens simultanously. Thus the color calibration parameters must be screen specific. Currently there are two screen classes: PrimaryScreen and SecondaryScreen These classes contain the following experimental parameters for defining the screen color properties:
    ColorDevice
    A code for identifying the color display device. If this code is 0 then the color device properties are initialized from the experimental parameters below. If the code is non-zero then it should identify one of the known standard devices. In this case the device's primaries are read from an internal table.
    ColorDeviceDACRange
    Gives the upper limit of the range of values available to the respective color device's digital-to-analog conversion unit. Most devices have 8 bit DACs and thus have a range from 0 to 255. Some special devices may have more than 8 bits available.
    RedGamma
    Parameters of the gamma function for the red channel. The text for the meaning of the parameters.
    GreenGamma
    Parameters of the gamma function for the green channel. The text for the meaning of the parameters.
    BlueGamma
    Parameters of the gamma function for the blue channel. The text for the meaning of the parameters.
    RedPrimary
    Yxy-coordinates of the red color device primary at maximum luminance.
    GreenPrimary
    Yxy-coordinates of the green color device primary at maximum luminance.
    BluePrimary
    Yxy-coordinates of the blue color device primary at maximum luminance.
    DeviceColorTableFile
    Contains the full access path of a device color table. This will be explained later in this chapter.

    Screen calibration should not be done in a design file directly but should be done in a startup file. If the screen calibration data are contained in the design file then the design file is not portable. And screen calibration should be done such that every experiment which is run on a certain display device should use the same screen calibration data. This can be achieved by using the PXLab startup file mechanism. A file named screen.pxd should be put into the PXLab installation design file directory and this file should contain the calibration parameter setting. Here is an example of such a file:

    	// Date: Tue Nov 30 14:21:16 CET 2004
    	PrimaryScreen.ColorDeviceDACRange = 255;
    	PrimaryScreen.RedGamma = [2.2927118766973535, 1.0941907385275362];
    	PrimaryScreen.GreenGamma = [2.136562108023064, 1.2236609745347005];
    	PrimaryScreen.BlueGamma = [2.12673917562787, 1.1853268525118388];
    	PrimaryScreen.RedPrimary = [21.11, 0.634, 0.34];
    	PrimaryScreen.GreenPrimary = [53.12, 0.297, 0.598];
    	PrimaryScreen.BluePrimary = [9.337, 0.148, 0.064];
    
    This file has been created by ColorCalibrationTool and contains the settings for a primary screen. PXLab will automatically load this file at startup and set the calibration parameters accordingly. See section 'Initialization' in chapter 'Running an Experiment' for more details on startup files.

    Color Tables

    In some cases the calibration precision achievable with gamma function estimates will not be enough. PXLab can use a color table in those cases. A color table is a text file which contains a single color description on every line. The color description contains at least 6 entries: the first three of these 6 entries are the CIE XYZ-coordinates of a target color and the last three are the device intensity levels for each output channel. Here is an example for such a table which also contains the CIE Yxy-coordinates of the target colors in the first three columns:

     21.26 0.511 0.299 36.334 21.26 13.5097 189 53 82
     92.78 0.364 0.423 79.8391 92.78 46.719 209 207 129
     7.22 0.198 0.188 7.604 7.22 23.5802 43 68 112
     71.52 0.318 0.502 45.3055 71.52 25.6446 133 202 87
     53.564 0.309 0.329 50.3078 53.564 58.9367 169 164 163
    
    The first three entries in each line are the CIE Yxy-coordinates of the respective target color. The next three entries are the CIE XYZ-coordinates and the last three entries are the device RGB-values. PXLab uses only the last 6 entries of each line. These must be the CIE XYZ-coordinates and the device RGB-values.

    If a color table is loaded into the system then PXLab will always use it for those target colors defined in the table. If a color table is loaded and a target color is needed which is not contained in the color table then the previously described conversion method via the current gamma table is used. In order to identify the target color in the color table only the first 3 decimal digits after the decimal point are used.

    A color table may be loaded into the system by defining its file name in the monitor description file. The color table file must always be located in the same directory as the design file. This may be done by adding the following line to the previous example for a monitor desription file:

    	PrimaryScreen.DeviceColorTableFile = "coltab.dat";
    
    This adds a color table to the device color conversion package for the primary screen. The color table file named 'coltab.dat' must be contained in the same directory as the design file.

    The PXlab application ColorCalibrationTool can be used to create a color table file. This is described in a later section.

    Here is a short review on PXLab's initialization mechanism as described in more detail in the chapter named 'Running an Experiment': On startup the program searches for the file named 'pxlab.properties' which should be contained in the user's home directory or in the classpath. It contains the definitions of 'pxlab.home' and 'pxlab.local'. These are directory path names where PXLab might search for further initialization files. One of these directories should contain the file 'system.pxd' which can be used to initialize global experimental parameters. One of these parameters may be ScreenParameterFile which should evaluate to a string containing the screen parameter file name or a string array if there are two screens to handle. The screen parameter file name might define parameter PrimaryScreen.DeviceColorTableFile and SecondaryScreen.DeviceColorTableFile which give the file names of color table files. Screen device transforms are installed only immediately before the experiment is started. Thus the color table files will be read only after the screens have been initialized since only then will PXLab know which type of screen is to be used. In order to watch the color table initialization the debugging options 'files' and 'coldev' might be useful. Use the following command line to set these options:

      java de.pxlab.pxl.run.ExRun -D files -D coldev 
    

    Color Calibration Devices

    The GretagMacbeth Eye-One Color Pro Spectral Photometer

    Using the Eye-One requires that its drivers are installed. The most simple way to make sure that this is the case is to make sure that the software coming with the Eye-One has been installed and is working properly.

    GretagMacbeth provides a software development kit (SDK) for communicating with the Eye-One via software. You should get the Eye-One SDK for the C programming language. This package includes Java bindings for using Java to communicate with the Eye-One. The package also includes a driver which may be installed without installing the Eye-One application programs. The SDK documentation describes how to do that.

    [Driver installation can simply be done by plugging the Eye-One into the USB port and then tell the Windows Device Manager where the drivers are. In the SDK tree the drivers are contained in directory 'driver'. Windows does the rest.]

    The major part of the communication software is contained in the dynamic link library EyeOne.dll. The Java bindings are contained in the library JEyeOne.dll whose source code is part of the SDK. Both dynamic link libraries should be copied to the System32 subdirectory of the Windows system directory or any other path contained in the Windows search path..

    This is all that is to do. The PXlab application ColorCalibrationTool is now able to communicate with the Eye-One device. Here is a screenshot where the Eye-One has measured gamma functions.

    The Tektronix Lumacolor II with J1810 Colorimeter Head

    In order to use the Tektronix Lumacolor II with J1810 Colorimeter Head the only thing which has to be done is to install the Java Communications API software. This is described in the chapter 'Connecting External Devices'. The PXlab application ColorCalibrationTool is then able to communicate with the Tektronix device via its serial line interface. This device, however, is not able to do spectral measurements but only measure color coordinates. To work properly the device has to be set up such that it shows CIE 1931 Yxy-chromaticity coordinates.

    The Tektronix Lumacolor II may also be used as an input device in ordinary design files. This makes it possible to write a design file which runs luminance measurement from the Tektronix Lumacolor. Here is an example for such a design file:

    Experiment() {
      Context() {
        AssignmentGroup() {
          SubjectCode = "pxlab";
          RandomizeTrials = 0;
        }
        Session() {
          SessionStartMessage() {
            Text = "Press any key to start!";
    	FontSize = 60;
            Timer = de.pxlab.pxl.TimerCodes.RELEASE_RESPONSE_TIMER;
          }
          DeviceControl() {
    	DeviceType = de.pxlab.pxl.DeviceCodes.SERIAL_LINE;
    	Port = "COM1";
    	CommandCode = de.pxlab.pxl.DeviceControlCodes.OPEN_DEVICE;
    	Timer = de.pxlab.pxl.TimerCodes.NO_TIMER;
          }
        }  
        SessionEnd() {
          DeviceControl() {
    	DeviceType = de.pxlab.pxl.DeviceCodes.SERIAL_LINE;
    	CommandCode = de.pxlab.pxl.DeviceControlCodes.CLOSE_DEVICE;
    	Timer = de.pxlab.pxl.TimerCodes.NO_TIMER;
    	Overlay = 0;
          }
          SessionEndMessage() {
            Text = "E N D";
            Duration = 2000;
          }
        }
        Trial(TrialCounter, 
    	DeviceColorBar:A.RedValue, 
    	DeviceColorBar:A.GreenValue, 
    	DeviceColorBar:A.BlueValue, 
    	DeviceControl.ResponseCode) {
          DeviceColorBar:A() {
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
            Duration = 500;
          }
          DeviceColorBar:B() {
            Timer = de.pxlab.pxl.TimerCodes.NO_TIMER;
    	RedValue = Trial.DeviceColorBar:A.RedValue;
    	GreenValue = Trial.DeviceColorBar:A.GreenValue;
    	BlueValue = Trial.DeviceColorBar:A.BlueValue;
          }
          DeviceControl() {
    	DeviceType = de.pxlab.pxl.DeviceCodes.SERIAL_LINE;
    	CommandCode = de.pxlab.pxl.DeviceControlCodes.SEND_DATA;
    	Command = "!NEW\n";
            Timer = de.pxlab.pxl.TimerCodes.SERIAL_LINE_INPUT_TIMER;
    	Overlay = de.pxlab.pxl.OverlayCodes.JOIN;
          }
          Feedback() {
    	ResponseParameter = "Trial.DeviceControl.ResponseCode";
    	Evaluation = de.pxlab.pxl.EvaluationCodes.STRING_TO_COLOR;
    	Text = "%Trial.Feedback.Response@b%";
    	// Text = "%Trial.DeviceControl.ResponseCode%";
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
            Duration = 3000;
    	LocationY = 200;
          }
        }
      }
      Procedure() {
        Session() {
          Block() {
            Trial(?, <0, 255>, <0, 255>, <0, 255>, ?);
          }
        }
      }
    }
    

    This design file uses the following special features:

    • At session start we open the connection to the serial device using the DeviceControl object with a proper port name and the OPEN command.
            DeviceControl() {
      	DeviceType = de.pxlab.pxl.DeviceCodes.SERIAL_LINE;
      	Port = "COM1";
      	CommandCode = de.pxlab.pxl.DeviceControlCodes.OPEN_DEVICE;
      	Timer = de.pxlab.pxl.TimerCodes.NO_TIMER;
            }
      
      The connection to the device is closed at session end.
    • At each trial we first show a DeviceColorBar for 500 ms such that the colorimeter can 'adapt' to the target.
    • We then again show the same target but also send the measurement trigger signal to the Tektronix Lumacolor. The trigger is the string '!NEW\n' which makes the Tektronix begin a measurement cycle. The same DeviceControl object which sends the trigger also waits until a line of text has been found on the serial input line. This is achived by using the timer SERIAL_LINE_INPUT_TIMER which waits for a complete line of text input on the serial line:
            DeviceControl() {
      	DeviceType = de.pxlab.pxl.DeviceCodes.SERIAL_LINE;
      	CommandCode = de.pxlab.pxl.DeviceControlCodes.SEND_DATA;
      	Command = "!NEW\n";
              Timer = de.pxlab.pxl.TimerCodes.SERIAL_LINE_INPUT_TIMER;
      	Overlay = de.pxlab.pxl.OverlayCodes.JOIN;
            }
      
    • Finally we evaluate the response using the Feedback object. The evaluation code STRING_TO_COLOR converts the Tektronix data string into PXLab color coordinates. The Feedback object shows these coordinates on the screen.

    How to Create a Color Table File

    The application ColorCalibrationTool can be used to create a color table file. In order to create a color table file we first have to create a color target file. A color target file is a text file which contains a single line for each color target. The last three entries on each line must be the CIE Yxy-coordinates of the target color. All other entries on the line are ignored by the ColorCalibrationTool. Here is an example for such a color target file:

    [21.26,0.511,0.299]
    [92.78,0.364,0.423]
    [7.22,0.198,0.188]
    [71.52,0.318,0.502]
    [53.564,0.309,0.329]
    

    Note that square brackets and semicolons are allowed but are not needed. Here are the steps for creating the color table:

    1. The color target file has to be loaded into the ColorCalibrationTool using Menu 'File' and selecting 'Open Color Table'.
    2. The ColorCalibrationTool has to be set to create a color table by using menu 'Task' and select entry 'Create Color Table'.
    3. Make sure that the proper measurement device has been selected from menu 'Measurement Device'.
    4. Now the calibration process can be started. ColorCalibrationTool will search for the best RGB device values for each entry in the table. This will take some time, depending on how fast the measurement device is.
    5. After calibration is finished the data should be saved to a file using menu 'File' and selecting entry 'Save Color Table Data'.
    The final step is to enter the color table file path into the respective screen parameter file as described previously.

    References

    Berns, R. S., Motta, R. J., & Gorzynski, M. E. (1993). CRT Colorimetry. Part I: Theory and Practice. Colour Research and Application, 18, 299--314.

    Brainard, D. H., Pelli, D.G., and Robson, T. (2002). Display characterization. In the Encyclopedia of Imaging Science and Technology. J. Hornak (ed.), Wiley. 172-188. (PDF)

     

  19. Factorial Design
  20. The abstract structure of an experiment is defined by its factors and conditions. An experimental design by PXLab tries to make the abstract structure as clearly visible as possible by explicitly defining the experimental conditions with explicitly defined factors and their operationalizations.

    Factor Definitions

    Experimental factors are defined in the Factors() section of a design file. It starts with the Factors() statement and contains a block of factor definitions. There are three different types of factors: independent factors, dependent factors and covariate factors. Each of these has its own type of factor statement:

    The notions of independent and dependent factors are used here in their usual meaning. A covariate factor is a variable which describes some subject property which is collected at run time but only once and usually before the experiment starts. A typical case is age and sex of the subject. Covariate factors have only a single value per subject and session and are collected by directly asking the subject or the experimenter.

    A factor statement has three aspects:

    1. it assigns a name to the factor and creates an experimental parameter of that name if the factor is not an experimental parameter by itself;
    2. it associates a factor with one or more experimental parameters;
    3. for independent and covariate factors it defines all possible factor levels.

    Independent Factors and Factor Levels

    Assigning a name to a factor and associating the factor with experimental parameters is done by the argument list of a factor statement. The first argument defines the factor's name and the following arguments are the name of experimental parameters which are associated with that factor. These parameters may be considered to be the factor's operationalization. Here is a simple example which also defines the factor levels:

    There is one independent factor defined. Its name is "Luminance" and its experimental operationalization is the experimental parameter SimpleDisk.Color which is an experimental parameter which defines the color of a simple disk stimulus display. The block of a factor definitions contains the list of factor levels. Each factor level definition also contains the corresponding values of the experimental parameters which have been associated with the factor. Thus the independent factor "Luminance" of the above example has two levels which are called "8" and "90". The factor level "8" has an associated experimental parameter value [8.0, 0.330, 0.313] which defines the color coordinates of a dim gray light. The factor level "90" has an associated color parameter value of [90.0, 0.330, 0.313] which is a bright white light.

    Thus the factor definition argument list contains the factor name and the names of the associated experimental parameters and the factor level definitions contain the corresponding factor level values and their associated parameter values. Internally factors are treated like experimental parameters. If a factor name is not the name of an experimental parameter then the respective parameter is created at runtime.

    The number of experimental parameters associated with a factor is not limited. Here is a more complicated example from a Stroop experiment:

    There are two experimental parameters necessary to define a factor level here: "Congruency" in a Stroop experiment means that the color word names the color of its letters. For incongruent conditions the color word must not name the color of its letters and in a neutral condition no words are used at all. Thus not a single experimental parameter defines the congruency factor but the combined value of two stimulus aspects constitute the factor level.

    There may, however, exist also very simple cases where a factor already exists as a stimulus parameter. In this case the respective experimental parameter can be used as the only argument of the factor definition. The independent variable in a choice reaction time experiment for example might be the orientation of an arrow stimulus. The Display object Arrow which one would use in such an experiment already contains a parameter called 'Orientation'. Thus in this simple case the independent factor definition would be

    Dependent and Covariate Factors

    A similar syntax which has been explained for independent factor definitions is used for dependent factors and covariate factors. The main difference is that dependent factor definitions can only have a single argument and that it does not make sense to define factor levels for a dependent factor. Furthermore dependent factor names always have to be names of experimental parameters while covariate factor names may either be names of experimental parameters or may be previously undefined names.

    Covariate factors have an additional aspect. Usually their values are not determined while the experiment is running. In general the values of covariate factors will be determined outside of the computer controlled experiment. Thus the values of covariate factors are not set from within the experiment like experimental parameters are but these are gathered by a dialog window which pops up before the experiment starts and asks for the values of all covariate factors. Covariate factors which have their factor levels defined in the design file offer the user a choice to select one of these admissible factor levels as a current value for the covariate. Here is an example for the use of covariate factors.

    It defines two covariate factors "Sex" and "Age" with a restricted set of factor levels. When this experiment starts the user will see a dialog appear which asks for the covariate factor levels for the given subject. Every factor will have a choice field where the user can select one of the possible factor levels.

    Covariate factors like SubjectGroup also may have operationalizing parameters. These may be used to select sessions for groups of subjects. This is described in the 'Experimental Procedure' chapter.

    Experimental Conditions

    The factor definitions in a design file are used to build an internal condition table. The conditon table is a list of factor level combinations and their associated experimental parameter values. The table contains an entry for every possible combination of factor levels of all independent factors. Every entry also contains the experimental parameter values associated with the respective factor levels. The association between factor levels and experimental parameters may in some cases be rather complicated such that it is not possible to build the condition table simply by combinatorial methods. For these cases the condition table may explicitly be defined in the design file.

    Lets look at an example. Suppose we have a Stroop experiment with two factors: The first factor is "Congruency" as in our previous example. The second factor is "Color Associativity" of the distractor word. The highest degree of color associativity have words which themselves are color names, like "red". A medium level of color associativity is represented by words which are not color words but are associated with colors, like "blood" with red, or "grass" with green. We may thus have the following factor definitions:

    There is, however, no way to associate experimental display parameters with any of the factors such that a combinatorial method may be used to create all combinations of factors since both of these factors have to be associated with the same experimental parameters and the difference is with respect to the content of the distractor word only. In order to create the experimental conditions we thus have to explicitly define the condition table.

    The factor definitions and the condition table together completely define the operationalization of every independent experimental factor and its factor levels. For some experiments there may be a unique association between factors and experimental parameters. In this case automatic condition table creation can be used even if more than one experimental parameters are needed to operationalize a factor. If the condition table needs to be explicitly defined then the operationalization should only be done by condition statements. A condition table can be interpreted meaningfully only if all factors used in the table have factor definition statements. Otherwise it is impossible to decide which parameter names are factor names and which are operationalization parameters since experimental parameters may also be used as factors.

    Where is the condition table used? The condition table is used for defining trials. Every trial is a realization of a single experimental condition. When a trial is defined one can simply use the factor levels to define the experimental condition which should be realized in this trial:

          Trial("Congruent", "medium", 2);
    

    is enough to tell the system that the experimental condition

          Condition("Congruent", "medium", 2, Green, "GRASS");
    

    should be presented in that trial. The experimental parameter values need not be specified. These parameters are implicitly localized for a trial as any other experimental parameters of the trial.

    The condition table contains all possible experimental conditions. An automatically created condition table will be factorial complete. An explicitly defined condition table need only contain those conditions which are actually needed. In a certain sense the list of trials and the condition table contain the same information. The list of trials, however, implicitly contains more information: It groups conditions into procedural blocks and specifies the sequential properties within every block, and it defines how subjects are assigned to experimental conditions. Thus the list of trials defines whether a factor becomes a between factor or whether repeated measures are used. Every combination of these is possible.

     

  21. Procedure Definition
  22. The Procedure() section of an experimental design file controls the sequence of sessions, blocks, and trials and also controls the assignment of experimental conditions to subject groups. It starts with a declaration possibly containing a list of argument values and has one or more session blocks following it. Here is an example from the PXLab lab course:

      Procedure()
      {
        Session()
        {
          Block( 1, 0)
          {
            Trial( ?, 2, ?);
            Trial( ?, 90, ?);
          }
          Block( 2, 1)
          {
            Trial( ?, 2, ?);
            Trial( ?, 90, ?);
            Trial( ?, 2, ?);
            Trial( ?, 90, ?);
          }
        }
      }
    

    We have one Session() statement and this contains two Block() statements. The first block contains two Trial() statements and the second block contains four Trial() statements.

    Arguments of Procedure Units

    The arguments of the Procedure()Session(), Block(), and Trial() statements are defined by the respective display list definitions in the Context() section. In our example the arguments for these display list objects are:

        Session() {
          ...
        }
    
        Block( BlockCounter, StoreData) {
          ...
        }
    
        Trial( TrialCounter, Luminance, ResponseTime) {
          ...
        }
    

    The arguments of a procedure unit define those experimental parameters which should be localized to the respective procedure unit. Parameter names given in these definitions must be known parameters, either by being known to the system or by having been created by a previous new-statement or a previous factor definition. In our example the parameters BlockCounter and StoreData are local to each block. Every parameter has a global value. Local parameters are by default initialized to their global values. However, after changing the value of a local parameter, their global value is no longer accessible from within that block which defined the parameter as local. In our example the parameter StoreData may have a global value of 0. If the later Block() call sets the value of StoreData to something different, then the original global value is no longer accessible within this Block(). After leaving the Block() the global value is accessible again. This same mechanism is used for all data collection units.

    One may also think of local parameters as those parameters which can change their value for every session, block, or trial respectively while any other parameter remains constant. This also means that the local parameters within a procedure unit do and can not propagate their values to the next unit of the same type. Thus in our example we have a local Luminance parameter in every trial. The Luminance value in any trial has no influence on the luminance value in the next trial. There is, however, a mechanism to explicitly propagate parameter values to consecutive trials or blocks which we will discuss later.

    The arguments of each data collection unit are parameter values or parameter names. The parameter values become the parameters' values used in the scope of the respective data collection unit. The scope of a data collection unit is the set of sub-units which are contained between the unit's curly braces region. If an argument list contains a parameter name instead of a parameter value, then the value of this parameter is the value of the same parameter in the next higher level scope.

    Thus in our example the parameter Luminance has the value 2 in the first trial and the value 90 in the second trial. The parameter StoreData has the value 0 in both trials of the first block and has the value 1 in all trials of the second block. The question mark as a parameter value signals the system to use the parameter's value from the next outer scope. Usually this will be the global value. The question mark is also used for those parameters which are dependent on what happens within a trial. The parameter ResponseTime is an example for this. The response time will be created during the trial and the parameter ResponseTime will get the value of the time measured. Thus the question mark indicates that we do not know the value of this parameter before the trial has been run. The same holds for TrialCounter which is a counter which is incremented at each trial. After a trial has been run all parameters have a value defined and these are used later for data file output.

    Multiple Sessions

    A single design file may define multiple sessions. Remember that a session is a sequence of blocks which itself is a sequence of trials. The sessions defined in a design file may actually contain different blocks and trials since it is possible to define different sessions, blocks, and trials in a single design file. Furthermore the procedure section of a design file may contain multiple copies of a session or may contain sequences of different sessions which may be assigned to different subject groups.

    Here is a list of experimental scenarios with respect to the assignment of sessions to subjects.

    Single subject, single session.
    The most simple case of a single subject running a single session. This usually will be a demonstrations experiment.
    Multiple subjects, single session.
    The most simple serious case where a sample of subjects each runs a single session.
    Multiple subjects, multiple sessions.
    The case where each subject runs more than a single session and there are many subjects.

    How Sessions are Selected

    The sessions defined in the Procedure() node of a design file are implicitly numbered, starting with '1' for the first session defined. The experimental parameter SessionGroup is used to select a subset of the sessions defined for a given subject. Suppose we have 5 sessions defined in the Procedure() section of a design file. Setting

       SessionGroup = [1, 2, 3, 4, 5];
    

    will run every session for every given subject at each single run of the design. Now suppose we only want to run sessions 1, 3, and 5. We then define

       SessionGroup = [1, 3, 5];
    

    and every subject will only run this subset of sessions. We may also define something like

       SessionGroup = [1, 1, 3];
    

    in order to run session 1 twice and then run session 3.

    Session selection will usually be specific to groups of subjects. Every subject belongs to a subject group. If there are more than a single subject group then the parameter SubjectGroup can be used to assign a subject to a subject group. In this case the parameter SubjectGroup must be defined as a covariate factor. Suppose we have two subject groups. We define SubjectGroup as a covariate factor with two factor levels:

       CovariateFactor(SubjectGroup) {
          FactorLevel("treatment");
          FactorLevel("notreatment");
       }
    

    Now suppose furthermore that the factor levels 'treatment' and 'notreatment' are associated with different sessions. We may then define the parameter SessionGroup as an 'operationalization parameter' of SubjectGroup:

       CovariateFactor(SubjectGroup, SessionGroup) {
          FactorLevel("treatment", [1, 2, 3]);
          FactorLevel("notreatment", [1, 4, 5]);
       }
    

    This definition associates the sessions 1, 2, and 3 with the factor level 'treatment' and the sessions '1, 4, and 5 with the factor level 'notreatment'. Thus any subject who belongs to the subject group 'treatment' will only run the sessions 1, 2, and 3 while any subject belonging to subject group 'notreatment' will run the sessions 1, 4, and 5. Remember that any covariate factor will be asked for at the start of an experiment if it had not been defined on the command line. This means that after the experiment has been started the program will ask for the subject group level and will then select the proper sessions.

    Subjects Running More than a Single Session

    The previous examples assume that any subject runs the design file exactly once. This may be too restrictive. In many cases it will be necessary that a subject executes more than a single run. This may be controlled by the parameter SessionRuns. Suppose that every subject in the previous example will have to execute two runs with the first two sessions in the first and the final third session being executed in the second run. We define

       SessionRuns = [2, 1]; 
    

    This tells PXLab to divide data collection into two runs. The first run executes the first 2 sessions defined by the current value of SessionGroup and the second run executes the final (3rd) session selected by SessionGroup.

    Using the experimental parameter SessionRuns requires that PXLab has access to previous data files of any subject. This is achieved by starting the program with exactly the same subject code twice. PXLab will always try to find previous data files for the given subject. If such a data file is found then it will extract the necessary information from this data file and procede with data collection using all information in the previous data file. No covariate factor values will be asked for in this case since the subject code is sufficient to identify all data of the previous run.

    In case of multiple runs for a single subject PXLab may be asked to join subsequent data files for a single subject. This is done by setting the experimental parameter JoinDataTrees to 1:

       JoinDataTrees = 1;
    

    will result in a data tree file which contains the current and all previous data for a subject.

    Note, however, that parameters from a previous data tree will only be extracted, if PXLab knows the SubjectCode value at startup. Thus the SubjectCode parameter value has to be given in the command line or must be contained in the design file for this to work properly. If SubjectCode is undefined at startup then the runtime session parameter dialog pops up and asks for the subject code and any other covariate factor levels. In this case covariate factor information cannot be read from previous data files.

    Experiment() Node Arguments

    The Experiment() node itself may have arguments. Suppose the Experiment() node is declared as

      Experiment(SubjectGroup) {
        ...
      }
    

    Then the parameter SubjectGroup must be set at runtime. This may either be done from an external calling program or it may be done via a special pop-up dialog before the experiment is started. The mechanism is the same as the one which collects covariate factor levels.

    Subject Grouping and Block Selection

    In many cases of subject grouping it may be necessary to present different blocks of trials to different subject groups. This will frequently hold for experiments which have only a single session and use a between groups design. In this case it will be useful to create two or more subject groups and assign blocks of trials to subject groups.

    A block of trials may be assigned to a subject group simply by defining the parameter ActiveSubjectGroups to be a block parameter and to enter the respective subject group in the parameter value:

      Context() {
        Block(BlockCounter, ActiveSubjectGroups) {
           ...
        }
        ...
      }
    

    defines ActiveSubjectGroups as a block parameter. A block of trials is then assigned to a certain subject group if the current value of the parameter ActiveSubjectGroups contains the respective subject group name in its array of values:

          Block(?, "treatment") {
            Trial(...);
            ...
          }
          Block(?, ["treatment", "placebo"]) {
            Trial(...);
            ...
          }
    

    The first of these two blocks is presented to the 'treatment'-group only while the second block is presented to both subject groups.

    This mechanism only works for block and session arguments. Here is how the presentation procedure handles the active subject groups:

    1. It sets the block or session argument parameters to their respective values.
    2. It checks whether the current value of ActiveSubjectGroups contains the current value of SubjectGroup. If this is the case then the block start display and all trials in the block are run. If the current value of ActiveSubjectGroups does not contain the current value of SubjectGroups then the block start and end display and all trials of the respective block are skipped.

    Note that this mechanism requires that the parameter ActiveSubjectGroups must explicitly be defined. If this is not done then all presentation blocks are executed and there is no restriction for the given subject group.

     

  23. Running an Experiment
  24. A special feature of PXLab is that experiments may be run as local applications or as applets within HTML pages. It is even possible to create experimental design files which can do both without any change. Laboratory applications will usually be run as local applications. Internet experiments will use applets to run experimental design files. Applets, however, have certain restrictions which limit their usefulness for running experiments. They are very useful for running demonstration experiments where some of the restrictions do not apply. In these cases it is rather convenient to have an applet which at least in principle can do the same as a full featured laboratory experiment but does not need any special preparation and can be run by any modern browser.

    Running an Application

    There are two ways to run an experiment as an application. One is the command line application ExRun and the other one is the design editor ExDesignEditor which is described in chapter 'The Experimental Design Editor'.

    The class ExRun is mainly a command line application. It is controlled by command line options and arguments. The following table describes all available command line options.

    -s code set SubjectCode to 'code'
    -g code set SubjectGroup code to 'code'
    -r framed window on primary screen device
    -x full screen on primary screen device
    -z full screen on secondary screen device
    -w n set window width to n pixels
    -h n set window height to n pixels
    -R n set display device refresh rate n
    -S n set display device type or mode n. Use '?' to get a list of possible modes.
    -E enc use encoding 'enc'. Possible values are defined by
    -f file use design file 'file'
    -d file write formatted data to file 'file'
    -t file write processed data to file 'file'
    -i file add file 'file' to list of initialization files
    -p file use protocol output data file 'file'
    -J convert design file to a Java source file
    -H convert design file to a HTML text file
    -D code set debugging option 'code'. Use '?' to get a list of available debug option
    -L lang set default language for multi-language design files
    -T print design file tree after parsing
    -M start a memory monitor to watch heap space consumption
    -Z run data processing nodes only. No experiment is run.
    -V show version number only
    -? show this help text

    Thus the command line

      java de.pxlab.pxl.run.ExRun -f demo.pxd
    
    immediately starts the experiment running the design file 'demo.pxd' which must be contained in the current working directory.

    It also is possible to enter global parameter assignments in the command line. Such an assignment must not contain spaces and must be delimited by a colon:

      java de.pxlab.pxl.run.ExRun -f demo.pxd SubjectCode=4;
    
    This assigns the code '4' to the parameter 'SubjectCode'. Assignments are assignments of values to experimental parameters. Assignments must be closed by a ';'-character. Quote characters in assignments must be escaped by the '\'-character. Depending on the operating system it may also be necessary to quote assignments in the command line. Thus under Windows we actually have to type
      java de.pxlab.pxl.run.ExRun -f demo.pxd "SubjectCode=4;"
    

    The display device option '-S' selects the display device type and mode which is used for the stimulus display. The type may be full screen or window. The device may be the primary, the secondary or both screens. Here are the respective display device type codes as defined in DisplayDeviceCodes:

    CodeVerbalType
    0 'framedA display window embedded into a decorated frame (the default).
    1 'unframedA display window embedded into an undecorated frame.
    2 'fullscreen'An ordinary full screen display window.
    3 'secondary'A full screen display window on the secondary screen device.
    4 'exclusive'A full screen exclusive mode window. This mode should guarantee the best possible performance on most graphics devices.
    5 'secexclusive'A full screen exclusive mode window on the secondary screen device. This mode should guarantee the best possible performance on most graphics devices which can control two independent dsiplays.
    6 'dualframedTwo framed windows on a single device.
    7 'dualunframed'Two unframed windows on a single device.
    8 'dualfull'Two full screen windows on the primary and secondary screen device respectively.
    9 'dualexclusive'Two full screen exclusive mode windows on the primary and secondary screen device respectively.

    The verbal display type codes may be shortened until it uniquely defines one of the options.

    In addition to setting the display device type the option '-S' may also be used to set the mode of full screen exclusive windows. Thus if the option '-S 4' is set then an additional option may set the resolution and refresh rate of full screen exclusive mode. Thus '-S 800x600x100' will set a resolution of 800x600 pixels at a refresh rate of 100 Hz. Setting the resolution and refresh rate, however, requires that the graphics hardware supports the respective mode. The resolution may also be set by using the options '-w' and '-h' and the refresh rate may be set by using option '-R'. The 'Graphics Environment' submenu of the 'Info' menu of application ExDesignEditor may be used to see a list of most of the available pixel resolutions and refresh rates for the current graphics hardware.

    The debug option '-D' accepts numeric option codes as defined by DebugCodes or the following string options codes:

    files Show file open/close protocol.
    parser Print parser protocol.
    assignment Assignment execution trace.
    expression Expression evaluation trace.
    colorgamut Print colors which cannot be shown on the current color device.
    node Print design node creation protocol.
    display Trace special display object properties.
    list Trace display list management.
    push Show push/pop operations for design node arguments.
    argval Show display list argument values after execution.
    states Show session control, error, and feedback states.
    multisession check active session list for multiple sessions.
    adaptive Adaptive procedure parameter protocol.
    timing Show display list timing protocol.
    hrtiming Show display list timing protocol using nanosecond resolution.
    voicekey Trace voive key levels.
    media Trace media events.
    textposition Show text frames and position reference points.
    edit Show design tree editing operations.
    data Check data analysis computations.
    factory Trace design tree factory methods.
    symbol Watch the symbol table generation.
    focus Trace focus changes.
    errors Print stack trace on errors.
    none don't add a debug option.

    Entering as many letters as is necessary to identify an option is sufficient.

    When Running out Memory

    In some cases it may be possible that the default options for the Java Virtual Machine do not provide enough heap space for a PXLab application. If this happens then an experiment may be terminated with an OutOfMemoryError from the Java Virtual Machine.

    This problem may be solved by using a command line option for the java command. The option is '-Xmx' followed by the amount of heap memory to be used:

      java -Xmx256m de.pxlab.pxl.run.ExRun
    
    will start the application ExRun with a maximum heap size of 256 MB. This should be enough for most PXLab applications.

    The demonstration program VisionDemonstrationsshould always be started with option '-Xmx64m' to ensure proper working of all demos.

    Working with Applets

    There are two ways to integrate a PXLab applet into a HTML page: using class ExRunApplet or using class ExRunStarterApplet. In both cases there is an important restriction to observe: Multiple runs of experiments in the same context are not possible. PXLab tries to prevent a second instance of an experimental run to start when one is already running. However, this may not succede in all possible cases of applet usage. Note that this does not affect the applet classes ExRunApplet and ExRunStarterApplet themselves but only the experimental runs. Thus it is possible to have multiple starter applets embedded into a single HTML page, but at any point in time only one of them can be running an experiment.

    Using a Start Button to Start an Applet

    The applet class ExRunStarterApplet shows only a single start button within the HTML page and the experiment starts when this button is pressed. This opens a new window within which the experiment is run. Here again is the example for using the ExRunStarterApplet applet class as shown in chapter 'The Design of an Experiment':

    The HTML-code for embedding this starter button into a HTML page is:

    <applet 
            codebase="." 
            archive="exrunbutton.jar, pxlabrt.jar" 
            code="de.pxlab.pxl.run.ExRunStarterApplet.class"
    	width=500 height=60 mayscript>
      <param name="CommandLine" value="-S0">
      <param name="DesignFile" value="design_file_demo_01.pxd">
      <param name="StartButtonLabel" value="Start Demo Experiment"> 
    </applet>
    
    This starts the applet ExRunStarterApplet whose code is contained in the jar file pxlab.jar which itself is contained in the same directory as the HTML file containing this applet tag. The tag also defines three parameters of the applet:
    DesignFile
    is the name of the experimental design file. The name of this file is relative to HTML file's directory.
    StartButtonLabel
    is the label of the start button. Pressing this button starts the experiment.
    CommandLine
    is a string which may contain additional command line arguments. See the previous section for additional command line options.

    Note that the experimental design file must be defined by using the parameter DesignFile otherwise the applet will not be able to start an experiment. It is not possible here to use a command line option to define the design file name.

    Using an Embedded Applet Window

    The applet class ExRunApplet creates a window within the HTML page and can run the experiment within that window. The window contains a button bar which makes it possible to start or stop the experiment. Here we use the class ExRunApplet to embed the display window into the HTML-page:

    The HTML-code for embedding this applet is

    <applet
       archive="pxlabrt.jar" 
       code="de.pxlab.pxl.run.ExRunApplet.class" 
       width=640 height=480 mayscript>
       <param name="CommandLine" value="-S0">
       <param name="DesignFile" value="design_file_demo_01.pxd">
    </applet>
    
    Note that the class ExRunApplet accepts only two parameters:
    CommandLine
    The content of this parameter is evaluated in exactly the same way as the command line of an application.
    DesignFile
    The name of the design file to run.

    Applet Data

    Java applets are not allowed to write data to the client's file system. And usually this actually is not what is wanted when running an experiment on the net. In most cases the data should be sent back to the server. Currently PXLab uses the GET- or the POST-method of the HTTP protocol to send data to the server. The data file destination is defined within the experimental design file. Here are the rules for experiments which run as an applet:
    1. If the experimental parameter DataFileDestination is undefined then the applet opens another browser window and shows the data in that window. This requires that the applet tag has the mayscript property enabled and the JavaScript method showData() is defined:
      <SCRIPT>
        function showData(s) {
           ergWin = window.open()
           ergWin.document.write(s)
        }
      </SCRIPT>
      

      This piece of code must be contained in the HTML file containing the applet in order to satisfy the request for opening a new window for showing the data. Note, however, that this only works if the client's browser has JavaScript enabled and the browser has a correct implementation of JavaScript.

    2. If the experimental parameter DataFileDestination is defined and contains a valid URL then the data file is sent to that URL using the GET method as defined in the HTTP protocol. Thus the URL should contain a valid script program for accepting the data. Here is a simple example for a PHP script to accept the data of an experiment and show them in a browser window:
         echo $DataFileName;
         echo $Data;
      
      The PHP-variable DataFileName contains the name of the data file and the variable Data contains the complete set of data.
    3. A disadvantage of the GET-method is its limited capacity. Using the GET method transfers the data within the URI. If the parameter HTTPRequestMethod is set to 'POST' then the POST-method is used to transfer the data to the server. The receiving script may use the same variables as described for the GET-method to receive the data and store them in a file. Here is a PHP example which receives the data and then sends it via E-mail to some destination:

        mail($email, "PXLab Data File ".$DataFileName, $Data, "From: PXLab" ); 
      

      here the PHP-variable '$email' must contain a valid E-mail address.

    Parameter Initialization at Startup

    When PXLab starts an experiment it follows a special sequence of parameter initialization steps. This includes initialization from built in defaults but also includes initialization from parameter files which may be global system files or may be files specific for a single experiment, a single user or a special display device.
    1. Initialize to Built In Defaults
    2. The first step of initialization at startup is to set all experimental parameters to their built in defaults. Every experimental parameter has a default value which is defined in the source code. At startup every parameter is reinitialized to this value.

      After all parameters have their built in default values several assignment files are executed. These files may be used to adapt the default parameter values to the computing environment where PXLab is running.

    3. Global 'system.pxd' in Directory Named by 'pxlab.home'
    4. The first initialization file read is the file system.pxd which must be located in the global PXLab home directory. The location of the global PXLab home directory is defined in the PXLab properties file named pxlab.properties by the property pxlab.home. The pxlab.properties file is searched in the following locations: the current working directory, the user home directory, the Java class path, the Java library path, the Java library extensions directory, the Java installation jar files directory, and the Java installation executables directory.

    5. Local 'system.pxd' in Directory Named by 'pxlab.home'
    6. The next initialization file read is also called system.pxd but must be located in a local PXLab directory. This directory also is defined in the file pxlab.properties by the property pxlab.local. The separation between pxlab.home and pxlab.local may be especially useful for network installations of PXLab where the global PXLab home is located on a server and the local PXLab home is located on a client machine.

    7. Initialization of Local Screen Properties
    8. After the files system.pxd have been read PXLab tries to read local initialization files which define the display screen properties. The default name for such a file is screen.pxd, however, this name may be changed in the system.pxd file by setting the parameter ScreenParameterFile. This parameter may contain an array of file names for defining the screen parameters of multiple screen display systems. The location of these files must be the local PXLab home directory as defined by the property pxlab.local.

    9. Command Line Initialization Files
    10. Additional startup parameter value assignments may be contained in files which are given on the command line of a PXLab application by using the option -i. These files are executed after the system initialization files and before the experimental design file.

    11. Context Node Assignments
    12. The next step is that all assignments which are contained in the Context node of the design file are executed. This includes assignments in an AssignmentGroup node and assignments which are contained in DisplayList and Display definitions. This also creates all new experimental parameters defined in any AssignmentGroup.

    13. Command Line Assignments
    14. Finally all those assignments which are given in the command line are executed.

    15. Implicit Factor Parameters
    16. The last step then is to create all experimental parameters which are implicitly defined by the Factors node as independent factors and which do not yet exist.

    Initialization Files Overview

    FileLocationPurpose
    pxlab.properties the current working directory,
    the user home directory,
    the Java class path,
    the Java library path,
    the Java library extensions directory,
    the Java installation jar files directory,
    the Java installation executables directory
    Define the global and the local PXLab home directory. Example:
    pxlab.home=X:/pxlab
    pxlab.local=C:/pxlab
    system.pxddefined by the property pxlab.homedefine global (server related) system parameters.
    system.pxddefined by the property pxlab.localdefine local (client related) system parameters. Example:
    ScreenParameterFile = ["gamma1.pxd", "gamma2.pxd"];
    This defines screen initialization files for a dual screen display system. These files must be located in the directory defined by pxlab.local.
    screen.pxddefined by the property pxlab.localdefine display system parameters.
    option -icontained in the argumentrun time specific initializations.

     

  25. Data Files
  26. PXLab experiments can generate the following types of data files:

    • Formatted experimental parameter data. Contains a single line of data for every single trial. The data are values of arbitrary experimental parameters.
    • Data tree files. This is a file which is syntactically eqivalent to the respective design file but has all experimental parameter values set.
    • Factor level data. This file contains the factor levels of independent factors and the values of dependent factors for every single trial.

    Data file names are by default derived from the SubjectCode parameter value. If no data file exists for the given SubjectCode then the SubjectCode becomes the data file name root. If a data file for the current SubjectCode already exists, then a 3-digit number is added to the SubjectCode and the number is incremented until a data file name is found which does not yet exist in the target directory.

    Data File Destination

    The data file destination is defined by the experimental parameter DataFileDestination. Its meaning depends on whether the experiment is running as an applet or an application.

    Experiments Running as Applications

    For applications the parameter DataFileDestination gives the full path name of the root directory for storing data files:
     DataFileDestination = "C:/Data";
    

    All data files are stored in this directory or in subdirectories contained in this directory.

    By default the following experimental parameters define subdirectories of the data root for storing different types of data files. If a subdirectory name is defined to be an empty string then the respective data files are stored in DataFileDestination directly. The default value for DataFileDestination is the current working directory.

    TrialDataDirectory
    Trial data subdirectory of DataFileDestination. If this parameter is nonempty the it specifies a subdirectory of the DataFileDestination where to put raw trial data files (default: "dat").
    DataTreeDirectory
    Data tree subdirectory. If this parameter is nonempty then it specifies a subdirectory of the DataFileDestination where to put data tree files (default: "dtr").
    ProcessedDataDirectory
    Processed data subdirectory. If this parameter is nonempty the it specifies a subdirectory of the DataFileDestination where to put data processing results (default: "pdt"). Processed data are generated by DataDisplay objects only.
    PlotDataDirectory
    Plot data subdirectory. If this parameter is nonempty the it specifies a subdirectory of the DataFileDestination where to put plot data files (default: "pld"). Plot data are generated by DataDisplay objects only.

    Experiments Running as Applets

    In case of the experiment is running as an applet then DataFileDestination should contain the full URL where to send the data file to:

      DataFileDestination = "/pxlab/demos/mailer.php";
    

    The target should be a script in this case which accepts the data file and sends it to a save place. The method for sending data to this script is defined by the parameter HTTPRequestMethod. This parameter's value may be 'GET' or 'POST' for using the respective method of transfer to a server. In general it is better to use the POST method since the GET method accepts only a limited data file size. The POST method does not create a response page since it does not use the browser's connection to the server but creates its own connection to the server. One can see the server's echo on a POST request on the browser's Java console. The GET method uses the browser's connection to the server and sends the data encoded into the URL. The result is that the server's response is shown as a WWW page.

    There is another parameter for HTTP-Data transfers which may be used by the receiving script. This is the parameter DataFileDestinationAddress. This parameter may be set to an E-Mail address where the receiving script should send the data to. The parameter value is contained in the PXLab message header sent to the DataFileDestination. This message actually contains three variables:

    DataFileName
    The name of the data file as derived from SubjectCode and also contained in the experimental parameter DataFileName.
    Email
    The value of the parameter DataFileDestinationAddress which may be an E-Mail address used by the receiving script.
    Data
    This variable has the full data file as its value.

    Here is a PHP single-line-example which receives the data and then sends it via E-mail to some target destination:

      mail($_REQUEST['Email'], "PXLab Data File ".$_REQUEST['DataFileName'], $_REQUEST['Data'], "From: PXLab" );
    There elso exist some examples on how to use PXLab to send data to some arbitrary email address.

    See section 'Applet Data' of chapter 'Running an Experiment' for some more information on applet data storing in the case when DataFileDestination is undefined.

    The Data Tree

    The design file of a PXLab experiment contains undefined experimental parameter values in the Procedure() section, which are written as a question mark:
    Procedure() {
      Session() {
        Block() {
          Trial("House", ?, ?);
          Trial("Garden", ?, ?);
          Trial("Chair", ?, ?);
          Trial("Mend", ?, ?);
          Trial("Kornte", ?, ?);
          Trial("Begren", ?, ?);
          ...
        }
      }
    }
    
    After an experimental session these parameter values have been set by the results of the respective trials:
     Procedure()
      {
        Session()
        {
          Block()
          {
            Trial( "Chair", 1, 991);
            Trial( "House", 0, 278);
            Trial( "Begren", 1, 1275);
            Trial( "Kornte", 1, 782);
            Trial( "Garden", 0, 778);
            Trial( "Mend", 1, 613);
          }
        }
      }
    

    Here the first question mark was the unknown ResponseCode parameter value and the second question mark was the unknown value of ResponseTime. PXLab writes the data tree file for every experimental run. This file is almost identical to the design file, but all unknown parameter values of the design file are replaced by the values of the respective parameters at the respective trial. The file name extension of the data tree file is defined by parameter DataTreeFileExtension its default is '.dtr'. The data tree file is created if the experimental parameter StoreDataTree is non-null. Its default is 1.

    Note that the data tree file is only written at the end of a run. It is also used by experiments which have multiple runs for a single subject. When such an experimental run is started then PXLab checks for recent data tree files and extracts the subject dependent parameters from this file. The effect is that it is possible to write a design file such that every run of a single subject may be started with exactly the same command line. The data trees for multiple runs are accumulated if the experimental parameter JoinDataTrees is non-null. Its default is 0. If data trees are accumulated, then the last data tree file contains the complete set of data for a single subject. Section 'Multiple Runs for a Single Subject' of chapter 'Running an Experiment' contains more information on this topic.

    Parameter Values for Single Trials

    For every single trial PXLab writes the value of the experimental parameter DataFileTrialFormat to a data file. This data file's extension is defined by the parameter DataFileExtension and by default is '.dat'. To define the content of the data file we have to define DataFileTrialFormat. Here is an example:
      DataFileTrialFormat = "%SubjectCode% %Interference% %Trial.Feedback.Response% %Trial.TwoStrings.ResponseTime%";
    

    This enters the values of the experimental parameters SubjectCode, Interference, Trial.Feedback.Response, and Trial.TwoStrings.ResponseTime into the trial data string. These values are written to the data file for every trial and this is done immediately after the trial has been run. If the parameter DataFileBlockFormat is defined then its value is also written to the data file after every block which has been run.

    Factor Level Data

    A special version of the previously described parameter value data file is derived from the experiment's factorial structure. If the parameter DataFileTrialFormat is not defined then the data file contains the following columns of data for each trial:
    1. SubjectCode;
    2. TrialCounter;
    3. the sequence of factor levels of the independent factors;
    4. the sequence of factor levels of the covariate factors;
    5. the sequence of data values for the dependent factors.

    This factor level data file is only written if the factorial sctructure is defined. If no factorial sctructure is defined and DataFileTrialFormat also is undefined then the data file simply contains the values of all Trial arguments.

    Global Parameters for Output Files

    File Generation Flags

    RuntimeProtocol
    Flag to switch on a runtime protocol (default: 0).
    StoreData
    Flag to indicate that formatted data of blocks and trials should be stored in a formatted data file (default: 1).
    StoreDataTree
    Flag to indicate that data should be stored in a data tree file (default: 1).
    DataProcessingEnabled
    Flag to indicate that data processing is enabled (default: 0) such that processed data files will be generated.

    Runtime Protocol File Formats

    RuntimeProtocol
    Flag to switch on a runtime protocol (default: 0).
    ProtocolFileName
    Name of the runtime protocol file (default: empty).
    SessionDescriptorFormat
    Session descriptor file data format. This determines the format of an experiment's entry into the experiment log file. This file collects log data on experimental runs (default: "%ExperimentName% - Date: %Date% - File: %DataFileName%").

    Unprocessed Data Files

    StoreData
    Flag to indicate that formatted data of blocks and trials should be stored in a formatted data file (default: 1).
    StoreDataTree
    Flag to indicate that data should be stored in a data tree file (default: 1).
    DataFileDestination
    Data files destination root directory name or data destination URL (default: ".").
    TrialDataDirectory
    Formatted trial data subdirectory of DataFileDestination. If this parameter is nonempty the it specifies a subdirectory of the DataFileDestination directory where to put formatted trial data files (default: empty).
    DataTreeDirectory
    Data tree subdirectory of DataFileDestination. If this parameter is nonempty then it specifies a subdirectory of the DataFileDestination directory where to put data tree files (default: empty).
    DataFileName
    Formatted data file name (default: empty). If this parameter is empty then its value will be created from the SubjectCode parameter value.
    DataFileExtension
    Formatted data file name extension (default: ".dat").
    DataTreeFileExtension
    Data tree file name extension (default: ".dtr").
    DataFileHeader
    Format of the first line in the formatted data file (default: empty).
    DataFileTrialFormat
    Format of a single line of trial data (default: empty).
    DataFileBlockFormat
    Format of a single line of block data (default: empty).
    FactorialDataFormat
    Generated name array of factorial data. This array is used to create the single trial output format in case the parameter DataFileTrialFormat is not defined. If DataFileTrialFormat is defined then this array is not used. The array is created automatically from the information contained in the Factors() node of the design file (default: empty).

    Data File Transfer via HTTP

    HTTPRequestMethod
    HTTP Request method for data transfer to the data file destination directory via HTTP. Possible methods are 'GET' or 'POST' (default: "POST").
    DataFileDestinationAddress
    Data file destination E-mail address for data file transfers via HTTP. The receiver of a data file via HTTP should send the data file to this E-mail address. This parameter is currently ony used if the DataFileDestination is an URL (default: empty).

    Processed Data

    DataProcessingEnabled
    Flag to indicate that data processing is enabled (default: 0) such that processed data files will be generated.
    ProcessedDataDirectory
    Processed data subdirectory of directory DataFileDestination. If this parameter is nonempty the it specifies a subdirectory of the directory DataFileDestination where to put data processing results (default: empty).
    ProcessedDataFileExtension
    Data processing results file name extension (default: ".html"). The data processing results file either is defined by parameter FileName of the data processing object or is built automatically from the parameter DataFileName using extension ProcessedDataFileExtension.
    PlotDataDirectory
    Plot data subdirectory. If this parameter is nonempty the it specifies a subdirectory of the DataFileDestination where to put plot data files (default: "pld"). Plot data are generated by DataDisplay objects only.
    PlotDataFileExtension
    Plot data file extension (default: "pld").

     

  27. Data Processing
  28. Starting from version 2.1 there are some basic data processing tools implemented in PXLab. The major work in data processing will still be done outside of PXLab using some standard software package. A major task of the data processing tools will be to prepare data files for standard software packages.

    Data Processing Objects

    In addition to the display list objects which present stimuli the context section of a design file may also contain data processing objects. The following data processing list objects are available:
    • TrialData()
    • BlockData()
    • SessionData()
    • ProcedureData()
    • ExperimentData()
    Each of these will be executed immediately after the respective presentation list has been run. Thus as an example the SessionData() list will be executed after its Session() and SessionEnd() procedure has been run, including all blocks and trials contained in the Session(). The SessionData() list gets all data from its preceding Session() to work on. The ProcedureData() list is executed after the Procedure() and ProcedureEnd() list including all Session() nodes have been run.

    The ExperimentNode() is somewhat special since it is never executed in a data collection session but can only be run separately by the application ExStat. It can use multiple data files contained in a directory and combine them to a single data table.

    A data processing list like SessionData() itself does not do any processing. Computations are done by data processing objects contained in the list. Here is an example for a SessionData() list:

        SessionData(SubjectCode, TrialFactor, Direction, Trial.Arrow.ResponseTime) {
          Statistics(Trial.Arrow.ResponseTime) {
            Include = 1;
            Exclude = (Trial.Arrow.ResponseTime < 100) || (Trial.Arrow.ResponseTime > 800);
    	Stats = de.pxlab.pxl.StatCodes.N
    	        | de.pxlab.pxl.StatCodes.MEAN 
                    | de.pxlab.pxl.StatCodes.STDDEV 
                    | de.pxlab.pxl.StatCodes.STDERR;
          }  
        }
    
    The list contains a single data analysis object: Statistics. This object computes descriptive statistics of a single dependent variable. In this example the dependent variable is Trial.Arrow.ResponseTime. It is sepcified as a display object argument.

    Note that the SessionData() declaration also contains a list of arguments. These are the parameters whose values make up the data table which is used as an input for the data processing objects. The arguments of a data processing list like SessionData() define the grand data table which can be used by the data processing objects in its list.

    A data processing list can contain an arbitrary number of data processing objects. Multiple instances of the same class must be identified by an instance postfix in the same way as it is done with multiple display objects in a display presentation list.

    The input data table is created new for each data processing object. Thus the Include and Excludeparameter can be applied independently for every data processing object. However, the columns of the input data table are the same for every data processing object since these are defined by the data processing list arguments.

    Data processing object declarations have the same syntax as display object declarations. They also may have parameters and assignments are used to set their values.

    Here is how the data processing mechanism works:

    • After a Session() has been completed by its SessionEnd() object the respective SessionData() list is executed. This is done by successively executing every data processing object in the list.
    • Every data processing object first creates its own data table. The columns of the data table are defined by the argument list of the data processing list object which contains the data processing object. If the argument list is empty, then all those parameters are entered into the table which are declared as factors in the Factors() section of the design file.
    • The data table contains a single row for every trial contained in the respective display list tree. However, only trials which satisfy the Include and do not satisfy the Exclude condition defined by the data processing object are actually entered into the data table.
    • The data processing object uses the data table and runs some computations on it. In many cases only selected columns of the data table will be used. These are usually listed in the argument list of the data processing object. The exact usage of the data table is described in the documentation page of the respective data processing class. Note, however, that every data processing object argument must also be an argument of the data processing list.
    It is important to remember, that the columns of each data table are defined by the data processing list or the factors declaration of an experiment while the row entries are defined by the Include and the Exclude condition defined by the data processing object.

    Data Processing Applications

    Data processing can be run with ExRun during data collection or after data collection using option '-Z' of option ExRun. There also is a special application which does not run experiments but only runs data processing objects in a design file. This is application ExStat. It reads multiple data files and processes the data processing objects in the data files and also can process the data according to a special data processing design file given as an argument to ExStat. The data files read by ExStat may be data tree files generated by ExRun but may also be raw data files of arbitrary source. The option '-r' tells ExStat to expect raw data files.

    Where Processed Data are Stored

    There exist several ways to control where processed data are stored. The name of the processed data file may be given explicitely in the command line of the applications ExRun and ExStat using option '-t'. If this option is not present then the processed data file name is determined automatically. The way how the file name is derived differs for ExRun and ExStat.

    The Processed Data File Name in ExRun

    Since every run of ExRun uses a special subject identification code the processed data file name also is derived from parameter SubjectCode. It has the same root as the standard data file and its extension is defined by parameter ProcessedDataFileExtension which by default is 'html'. The standard data file destination is defined by parameter DataFileDestination and the subdirectory name TrialDataDirectory. The subdirectory for processed data files is defined by parameter ProcessedDataDirectory. Thus if all parameters are set then the processed data file is contained in subdirectory ProcessedDataDirectory of directory DataFileDestination and the file name is equal to DataFileName with extension ProcessedDataFileExtension.

    If ExRun is used to only process an already existing data tree file using option '-Z' then no data are collected and only the data processing nodes of the design tree are executed. Destination files are determined in exactly the same way as described in the previous paragraph.

    The Processed Data File Name in ExStat

    The behavior of ExStat depends on whether an explicit data processing design file which contains an ExperimentData() node is given in the command line (option '-f') or not. If no explicit design file is given, or if the design file does not contain an ExperimentData() node then ExStat behaves exactly like ExRun with option '-Z' for every data tree file which is processed.

    Processed data file names are determined differently by ExStat if an ExperimentData() node has to be processed. If a processed data file name has been given on the command line, then it is used. If no data output file name has been given on the command line then the file name is derived from the design file which contains the ExperimentData() node with the extension defined by ProcessedDataFileExtension. No destination directory mechanism is used in this case.

    An exception to this rule is the case, where an explicit data output file name is defined by the data processing object. This can be done with parameter FileName for every data processing object.

    General Properties of Data Processing Objects

    Clobal Parameters

    The following global parameter refer to data processing objects:

    DataProcessingEnabled
    This flag controls whether data processing objects are run during data collection or not. If true then the data processing objects defined in the design file are run otherwise no data processing is done. By default this parameter is 0.
    DataProcessingDirectory
    a subdirectory of DataFileDestination for data processing output files.
    DataProcessingFileExtension
    the extension for data processing output files. By default this is '.html'.

    Result Files

    The data generated by data processing objects are sent to their own data destination. This destination depends on whether data processing is run during a data collection session by ExRun or as a separate data processing session by ExStat. In the first case the data processing results destination directory is determined by the value of parameter DataProcessingDirectory. The file name root is identical to the raw data file name root and the file name extension is defined by parameter DataProcessingFileExtension which by default is '.html', since most data processing objects by default generate HTML output.

    If data processing objects are run by ExStat after data collection has been finished then the default destination file should be given using command line option '-t'.

    All data processing objects have a parameter named FileName. This may be used to define individual data file names for every data processing object.

    Common Parameters of Data Processing Objects

    Every data processing object has the following parameters:

    Include
    This flag controls the inclusion of trials into the data table. A trial is only then included into the data table if the Include parameter evaluates to true for this trial's parameter values.
    Exclude
    After Include has been evaluated this flag controls the exclusion of trials. Trials are excluded if the Exclude parameter evaluates to true for this trial's parameter values.
    ResultFormat
    This string defines the resulting output format. If it is undefined then a proper HTML output format is used. If ResultFormat is defined then it is used as an output string.
    HTMLFormat
    Some data processing objects can switch between unformatted text output and HTML formatted text output. If this parameter is 1 then HTML format is used.
    FileName
    If this parameter is defined then the output data are written to the respective file. If the parameter is not defined then the default file name mechanism described earlier is used.

    Available Data Processing Objects

    Export
    export a data table. Can either export raw data or factorial data where multiple observations of the same factor level are replaced by their mean values. Factor level tables will also have replaced missing values by the means of the same non-random factor level across the random factor. Here is an example for exporting a factorial data file. It exports the number of cases found for each factor level combination of the factors SubjectCode, Trial.SyntheticSound:A.WavePars, Trial.SyntheticSound:B.WavePars, and Trial.Message:C.ResponseCode. It thus generates the number of cases of each factor level combiation.
    Experiment() {
      Context() {
        ExperimentData(
    	SubjectCode, 
    	Trial.SyntheticSound:A.WavePars, 
    	Trial.SyntheticSound:B.WavePars, 
    	Trial.Message:C.ResponseCode) {
          Export() {
    	Include = StoreData;
    	Exclude = 0;
            DataType = de.pxlab.pxl.ExportCodes.FACTORIAL_FREQUENCY_DATA;
            DataFile = "pmfk_table.dat";
          }
        }
      }
    }
    
    The Export object can export these types of data files:
    RAW_DATA
    a data file with a single row for every trial, similar to the 'dat'-file.
    FACTORIAL_DATA
    a data file with a single row for every factor level combination of all factors, including random, independent, and dependent factor. If the original data table contains repetitions for a factor level combination then the factorial data file contains the arithmetic mean of these as its dependent factor value. If the original data table contains dependent values which are not convertible to a number then the respective value is replaced by the arithmetic mean of all other levels of the random factor for this factor level combination of independent factors.
    FACTORIAL_FREQUENCY_DATA
    this data format is derived from the factorial data format. It treats the dependent factor as an independent factor and has an additional column which contains the number of cases of each factor level combination in the factorial data table. Thus if the dependent factor lavel is the value of a response category then the factorial frequency table will contain how often each category has been observed at each factor level combination.
    REPEATED_MEASURES_DATA
    this table may be used as input to commercial statistics packages which treat repeated measures as multiple dependent variables, like SYSTAT or SPSS do. This table contains a column for every random and independent factor which is a between groups factor. If the design contains within or repeated measurement factors then the values of the dependent factor for all combinations of within factors are added as additional columns to the table. See Export for a more detailed description of the output.

    Export may also be used to transform data using the built in functions of PXLab. Here is an example of a design file which transforms a raw data file of CIE Yxy-chromaticity coordinates into RGB-coordinates.

    Experiment() {
      Context() {
        AssignmentGroup() {
          new a = 0;
          new b = 0;
          new c = 0;
          new c1 = arrayOf3(a, b, c);
          new c2 = toDevRGB(c1);
        }
        ExperimentData(a, b, c, c1, c2) {
          Export(a, b, c, c2) {
              FileName = "color_transformed.tmp";
    	  DataType = de.pxlab.pxl.ExportCodes.RAW_DATA;
          }
        }
        Trial(a, b, c) { 
        }
      }
    }
    

    Note that the Trial() declaration is used here to define names for the input data columns, since we expect a raw data table which does not contain parameter name declarations. This also implies that all parameter names are declared new in the design file. Here is an example for an input file for this design:

       10.089509  0.454104  0.458863
       11.6268    0.5049    0.431933
       ...
       7.107370   0.393992  0.273389
    

    And here is the corresponding output:

       10.089509  0.454104  0.458863  [112, 86, 21]
       11.6268    0.5049    0.431933  [138, 84, 10]
       ...
       7.107370   0.393992  0.273389  [117, 57, 82]
    

    If no Trial() declaration is contained in the design file and a raw data file is being processed then the columns are defined by the arguments of the data processing list node. In this case the number of columns and the number of arguments in the data processing list node have to be identical.

    Statistics
    compute descriptive statistics for a single variable. This example computes descriptive statistics for the two levels of 'Luminance' separately. Invalid responses are excluded.
    Experiment() {
      Context() {
        AssignmentGroup() {
          new invalid = (ResponseTime < 100) || (ResponseTime > 500);
        }
        ExperimentData(SubjectCode, TrialCounter, Luminance, ResponseTime) {
          Statistics:A(ResponseTime) {
    	Include = StoreData && (Luminance == 8);
    	Exclude = invalid;
    	Stats = de.pxlab.pxl.StatCodes.ALL;
          }
          Statistics:B(ResponseTime) {
    	Include = StoreData && (Luminance == 90);
    	Exclude = invalid;
    	Stats = de.pxlab.pxl.StatCodes.ALL;
          }
        }
      }
    }
    
    Anova
    compute factor level statistics and an analysis of variance for an arbitrary number of factors. Factor types are determined automatically.

    Here is a example for using Anova to compute an analysis of variance for the data of an experiment on the horizontal-vertical illusion. 'SubjectCode' is the random factor, the parameters Trial.HorizontalVerticalIllusion.Orientation, Trial.HorizontalVerticalIllusion.CutRatio, and FromWhere are independent variables and Trial.HorizontalVerticalIllusion.CutLine is the dependent variable. Note that the Anova object does not have an argument list which means that its parameters are the same as that of the ExperimentData node. No inclusion and exclusion conditions are defined and the default format is used. This design file can be used by the application ExStat to process multiple data files collected in a single directory. The data files must have been generated by the same design file.

    Experiment() {
      Context() {
        ExperimentData(SubjectCode, 
            Trial.HorizontalVerticalIllusion.Orientation, 
    	Trial.HorizontalVerticalIllusion.CutRatio,
            FromWhere, 
    	Trial.HorizontalVerticalIllusion.CutLine) {
          Anova() {
    	Include = 1;
    	Exclude = 0;
          }
        }
      }
    }
    

    Here is another example which also uses a new parameter defined in a separate AssignmentGroup node. Only those trials are included in the analysis which have the StoreData parameter set.

    Experiment() {
      Context() {
        AssignmentGroup() {
          new invalid = (ResponseTime < 100) || (ResponseTime > 500);
        }
        ExperimentData(SubjectCode, TrialCounter, Luminance, ResponseTime) {
          Anova(SubjectCode, Luminance, ResponseTime) {
    	Include = StoreData;
    	Exclude = invalid;
          }
        }
      }
    }
    
    Regression
    compute correlations and multiple linear regression. Here is an example which computes regression lines for the positive and negative items in a Sternberg paradigm. Note that 'NegPos' is a parameter which identifies an item as a negative or positive item. False responses are excluded.
    Experiment()
    {
      Context()
      {
        ExperimentData(
    	TrialCounter, 
    	NegPos, 
    	Trial.SerialLearningList.MemorySetSize,
    	Trial.Feedback.Response,
            Trial.Message.ResponseTime) {
          Regression:Neg(Trial.Message.ResponseTime, Trial.SerialLearningList.MemorySetSize) {
    	Include = NegPos;
    	Exclude = Trial.Feedback.Response;
          }
          Regression:Pos(Trial.Message.ResponseTime, Trial.SerialLearningList.MemorySetSize) {
    	Include = !NegPos;
    	Exclude = Trial.Feedback.Response;
          }
        }
      }
    }
    
    EllipseEstimation
    estimate parameters of a rotated ellipse. May be used to estimate confusion ellipses in color discrimination.
    VisualGammaEstimation
    estimate parameters of a gamma function from visual matching data.

     

  29. Connecting External Devices
  30. The default response collection devices for PXLab are the keyboard and the mouse. PXLab also supports the following external devices:
    • Switches, LED's or other electronic devices connected to the control lines of a serial port. No serial data lines are used. Input signals connected to a serial port control lines use the standard Java event handling and are highly reliable and very fast. These devices should be used if the most reliable response time measurement is needed.
    • A serial communication line. This may be used to communicate with devices connected to the serial communication line. Examples are color measurement devices with a serial communication interface.
    • Gaming devices connected to an USB port and supported by the DirectInput library of the Windows operating system. These are supported only under Windows and are not well integrated into the standard Java event handling mechanism. These devices have to be polled and should not be used for response time measurement since they introduce an unpredictable delay into the response event handling methods. Using these devices needs a DLL from the open source library JXInput being installed.
    • 3D SpaceMouse devices manufactured by 3DConnexion. These may be used to control up to 6 axes for manipulations in 3D space. PXLab abblications may use these devices for the adjustment of colors in 3D color space. These devices also have buttons which can be used for binary responses. Event handling for these devices is slow and my even slow down the event handling for other devices. So these devices should only be activated when really needed. The DLL needed for SpaceMouse access is contained in the extended PXLab runtime distribution archive. The device's driver has to be installed before the device can be used.

    Devices Connected to the Serial Port

    PXLab applications can access an external control box connected to one of the computer's serial communication ports. This makes it possible to
    • use external switches to collect responses,
    • control external signals, and
    • send synchronized trigger pulses to external devices.

    Although the external devices are connected to the serial communication port PXLab does not use serial data transfer in this case. PXLab does only use the control lines of the serial port and does not use the data transfer lines. This ensures fast response timing and proper signal synchronization.

    In addition to using the control lines of the serial port for response switches and signals, PXLab can also communicate with serial devives across the serial communication interface.

    External Response Buttons

    Figure 1 shows how to connect 4 response switches to the RS 232 serial communication port input control lines and Figure 2 shows how to connect two LEDs to the serial communication port output control lines. A trigger signal may be connected like one of the LED devices.

    Figure 1. Schematics how to connect 4 external response keys/switches to the serial RS232 port. The pin numbers are given for both the 9- and the 25-pin connector.

    External control box responses are activated by selecting a proper display timer. The interface TimerBitCodes has two predefined codes which activate the external control box buttons: XBUTTON_TIMER_BIT is the single bit code which activates external buttons and RESPONSE_TIMER is the general code which activates any subject response driven timer.

    When an external response button is used then the parameter ResponseCode of the respective Display object will identify the number of the external button which activated the response. These numbers are defined in class ResponseCodes for the 4 possible buttons of a serial communication port box:

       de.pxlab.pxl.ResponseCodes.SERIAL_PORT_BUTTON1
       de.pxlab.pxl.ResponseCodes.SERIAL_PORT_BUTTON2
       de.pxlab.pxl.ResponseCodes.SERIAL_PORT_BUTTON3
       de.pxlab.pxl.ResponseCodes.SERIAL_PORT_BUTTON4
    

    The PXLab external control box must be opened before it can be used. This is done by the special display object DeviceControl. This display object tells the system the name of the communication port which is used by the external response box. This display should be included in the Session start display list. Here is an example for opening and closing the external control box:

        Session() {
          DeviceControl() {
    	DeviceType = de.pxlab.pxl.DeviceCodes.SERIAL_PORT;
    	CommandCode = de.pxlab.pxl.DeviceControlCodes.OPEN_DEVICE;
            Port = "COM1";
          }
          ...
        }
        SessionEnd() {
          DeviceControl() {
    	DeviceType = de.pxlab.pxl.DeviceCodes.SERIAL_PORT;
    	CommandCode = de.pxlab.pxl.DeviceControlCodes.CLOSE_DEVICE;
            Port = "COM1";
          }
        }
    
    The parameter Port of the class DeviceControl defines the name of the serial port. Possible names are those which are supported by the Java communications API: "COM1" and "COM2" on PCs, and "Serial A" and "Serial B" on Sun Ultra workstations.

    Figure 2. Schematics how to connect 2 LEDs to the serial RS232 port. The pin numbers are given for both the 9- and the 25-pin connector.

    Steady State Signals

    External control box signals are controlled by special Display objects. An external control box signal line may be switched on by ExternalSignalOn(). Since external signals are not automatically cleared by a subsequent Display object there is an explicit clearing object: ExternalSignalOff(). Note that ExternalSignalOff() has NO_TIMER as its default timer such that switching off an external signal does not have a duration. IN some cases it might be useful to set the Overlay flag for one or both of these signal.

    Here is an example for switching an LED connected to the external control box. The stimulus signal is the LED and the response is collected by any of the usual response devices:

          ExternalSignalOn() {
    	Duration = 2000;
            Timer = de.pxlab.pxl.TimerCodes.LIMITED_RESPONSE_TIMER;
          }
          ExternalSignalOff();
    

    Note that the first display object ExternalSignalOn  switches the LED on and the object ExternalSignalOff  switches the LED off.

    Since the external control box can have multiple signal lines these objects have the experimental parameter Code which defines which of the signal lines to use. Note that although these parameters of ExternalSignalOn() and ExternalSignalOff() are independent, they have to have identical values in order to switch off a signal which has earlier been switched on. Here is a complete example for a small demo program which uses external LED signals and activates external response buttons:

    Experiment()
    {
      Context()
      {
        AssignmentGroup()
        {
          DataFileTrialFormat = "%SubjectCode% %TrialCounter% %Trial.SimpleDisk.ResponseTime%";
          SubjectCode = "pxlab";
        }
        Session()
        {
          /* This opens the external control box connected to the serial
             communication port and enables external On,
             Off, and Trigger signals. It also enables externl control box response
             events. */
          DeviceControl() {
    	/* The serial port name. */
    	Port = "COM1";
    	/* The command code for opening the external control box connected to the serial port. */
            CommandCode = de.pxlab.pxl.SerialCommunicationDeviceControlCodes.XOPEN;
          }
          Message()
          {
            Text = "Start!";
            Timer = de.pxlab.pxl.TimerCodes.RELEASE_RESPONSE_TIMER;
          }
        }
        SessionEnd()
        {
          /* Close the serial communication device. */
          DeviceControl() {
    	Port = "COM1";
            CommandCode = de.pxlab.pxl.SerialCommunicationDeviceControlCodes.XCLOSE;
          }
          Message()
          {
            Text = "E N D E";
            Duration = 500;
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
          }
        }
        Trial( TrialCounter, SimpleDisk.ResponseTime)
        {
          ClearScreen:Pause()
          {
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
            Duration = 500;
          }
    
          /* Switch ON the external signal and procede to the next Display without delay. */
          ExternalSignalOn() {
            Timer = de.pxlab.pxl.TimerCodes.NO_TIMER;
            Code = 1;
          }
          /* Also show a screen signal. */
          SimpleDisk()
          {
            Size = 300;
            Timer = de.pxlab.pxl.TimerCodes.RESPONSE_TIMER;
            Color = [40.0, 0.313, 0.329];
          }
          ExternalSignalOff() {
            Timer = de.pxlab.pxl.TimerCodes.NO_TIMER;
            Code = 1;
          }
          Feedback()
          {
            Text = "%Trial.SimpleDisk.ResponseTime% ms";
            Timer = de.pxlab.pxl.TimerCodes.CLOCK_TIMER;
            Duration = 500;
          }
        }
      }
    
      Procedure()
      {
        Session()
        {
          Block()
          {
            Trial( ?, ?);
            Trial( ?, ?);
            Trial( ?, ?);
          }
        }
      }
    }
    

    Synchronized Trigger Pulses

    The Trigger() object is similar to the ExternalSignalOn() display object. It does not change the screen and does not have a duration. Its only purpose is to send a trigger pulse to the first external signal line. The trigger pulse is very short and is strictly synchronous to the stimulus presentation.

    Serial Line Communication

    Devices connected to a PCs serial communication interface can talk to PXLab and vice versa. The major control tool for this is the DeviceControl object. This object can open and close the connection to a serial communication device and can send text across these lines. The PXLab ResponseManager is able to receive text from such a device in the same way as receiving responses from any other response device. The section on the Tektronix Lumacolor colorimeter in the color calibration chapter contains an example on how to use the DeviceControl object.

    Software Requirements for External Signals

    Using the serial communication port requires the installation of the Java Communications API for the respective machine. The download package contains instructions how to install the package for various operating systems and Java development kit versions.

    For installation under Windows you have to do the following steps:

    • Download the zip-archive javacomm20-win32.zip (274.432 Bytes) for Windows.
    • Unpacking the archive gives you three files:
      • move win32com.dll into JRE\bin
      • move comm.jar into JRE\lib\ext
      • move javax.comm.properties into JRE\lib
      where JRE stands for the installation directory of your Java runtime environment which might for example be '\Program Files\Java\jre1.5.0'. All other files of the archive are documentation.

    Note that this software is only required if external response buttons, external synchronization signals or other serial devices will to be used. Applets cannot use it.

    Gaming Input Devices

    The major difference between keyboard, mouse and serial port devices on one side and gaming devices like gamepads or joysticks is that the latter group has to be polled for reading their current states while the first group of input devices is interrupt driven and automatically generates an event when their state changes. Polling devices are not supported by Sun's Java runtime environment. The PXLab implementation uses the open source library JXInput to access these devices. This library is based on the Java Native Interface (JNI) and Windows DirectInput methods. Thus it will only run on Windows.

    DirectInput Device Activation

    PXLab by default does not access game devices. If such a device should be used it has to be activated before it can be used and it should be deactivated after it will no longer being used. This is done by executing the object with the proper device name, id number, and command code. Here is an example:
        Session() {
          DeviceControl() {
    	DeviceType = de.pxlab.pxl.DeviceCodes.DI_DEVICE;
    	DeviceID = 0;
    	CommandCode = de.pxlab.pxl.DeviceControlCodes.OPEN_DEVICE;
          }
        }
    
        SessionEnd() {
          DeviceControl() {
    	DeviceType = de.pxlab.pxl.DeviceCodes.DI_DEVICE;
    	DeviceID = 0;
    	CommandCode = de.pxlab.pxl.ResponseDeviceControlCodes.CLOSE_DEVICE;
          }
        }
    
    The device is opened at the start of the session and it is closed after the session has been finished. This is all which has to be done in order to use DirectInput device buttons for response collection. Any timer which has the attribute RESPONSE_TIMER set will accept these button responses. In order to restrict responses to certain buttons only the parameter ResponseSet should contain the possible button response code values. Here is a list of constant declarations which name DirectInput button codes:
    de.pxlab.pxl.ResponseCodes.DI_BUTTON1
    de.pxlab.pxl.ResponseCodes.DI_BUTTON2
    de.pxlab.pxl.ResponseCodes.DI_BUTTON3
    de.pxlab.pxl.ResponseCodes.DI_BUTTON4
    de.pxlab.pxl.ResponseCodes.DI_BUTTON5
    de.pxlab.pxl.ResponseCodes.DI_BUTTON6
    de.pxlab.pxl.ResponseCodes.DI_BUTTON7
    de.pxlab.pxl.ResponseCodes.DI_BUTTON8 
    

    Thus the definition

       ResponseSet = [de.pxlab.pxl.ResponseCodes.DI_BUTTON1,
                      de.pxlab.pxl.ResponseCodes.DI_BUTTON3]; 
    
    restricts possible responses to button 1 and 3 of the DirectInput device. There are two simple response time examples which use DirectInput buttons for response detection. These are contained in file srt_di.pxd and in file srt_di2.pxd in the demos directory of the PXLab documentation. These experiments will not run as applets since they need a special DirectInput interface DLL.
    Using DirectInput Axes

    Here is another example from the file color_adjust_di.pxd in the demos directory which uses two analog sticks on a gamepad to adjust a color. The gamepad has to be opened and closed in the same way as described in the preceding paragraph for any DirectInput device. The gamepad analog stick adjustment is activated by the timer property AXIS_TRACKING which has to be added to a display object's timer:

          SimpleBar:Adjusted() {
    	AdjustableColorPar = "Trial.SimpleBar:Adjusted.Color";
            Timer = de.pxlab.pxl.TimerCodes.RESPONSE_TIMER | de.pxlab.pxl.TimerCodes.AXIS_TRACKING;
    	HueStep = 0.01;
    	ChromaStep = -0.01;
    	LightnessStep = -0.01;
    	AdjustableCoordinates = de.pxlab.pxl.ColorAdjustBitCodes.LIGHTNESS 
                                    | de.pxlab.pxl.ColorAdjustBitCodes.HUE 
                                    | de.pxlab.pxl.ColorAdjustBitCodes.CHROMA;
    	AxesUsed = [0, 1, 3];
    	InitialRandomSteps = 1;
    	Width = 100;
    	Height = 200;
            LocationX = 50;
    	Overlay = de.pxlab.pxl.OverlayCodes.TRANSPARENT;
    	JustInTime = 1;
          }
    

    This shows a SimpleBar object whose color can be adjusted. Adjustment is stopped by pressing any response key or button. Note that the color adjustment parameters are described on the page of the Display object ColorAdjustableHSB

    Software Requirements

    Using game devices requires the JXInput runtime package written by Joerg Plewe to be installed. You may download this package from http://www.hardcode.de/jxinput/. The 'Download' page contains installation instructions. The JXInput runtime archive contains the following 2 files:
    jxinput.dll
    should be moved into a valid DLL directory like /Windows/System32;
    jxinput.jar
    must be contained in the CLASSPATH or can be copied into the Java extensions directory. This is the subdirectory 'lib/ext' of your Java runtime environment installation directory.
    That's all which is to be done.

    SpaceMouse Devices

    These are special devices for navigating in 3D space manufactured by 3DConnexion. They are connected to the USB port and contain a knob which allows translations along and rotations around 3 axes in space. The knob manipulations may be picked up by PXLab in order to adjust colors in 3-dimensional color space.

    SpaceMouse Activation

    The SpaceMouse driver installs the device as a DirectInput device. This means that it may be accessed in exactly the same way as any other DirectInput device. This is done by setting the parameter DeviceControl.DeviceType of the control object to the value DI_SPACE_MOUSE. Note that the parameter DeviceControl.AxisLimit also has to be adapted since the SpaceMouse does not restrict its axis values to the same range as analog sticks of gamepads do.

    The SpaceMouse driver may also be accessed directly. This delivers smoother axis motion but requires a special DLL to be installed. Direct access to the SpaceMouse driver is initialized by setting the parameter DeviceControl.DeviceType of the control object DeviceControl to the value SPACE_MOUSE. The example from the file color_adjust_sm.pxd in the demos directory uses direct access to the SpaceMouse driver. It is almost identical to the analog sticks example for gamepads. The only differences are the DeviceControl.DeviceType value used and the parameter ColorAdjustableHSB.AxesUsed which tells PXLab which of the available axes should be used for color adjustment. This parameter has to be changed since the SpaceMouse has 6 axes while a gamepad has only 4 axes. The SpaceMouse buttons are used exactly as DirectInput buttons. Their numbers are defined in File ResponseCodes.

    Software Requirements for a SpaceMouse

    The SpaceMouse devices need a special native library to connect to the device's driver. Thus before using the SpaceMouse device with PXLab the driver which comes with the device has to be installed and should work properly. The PXLab driver interface is contained in a library named JNIsiapp.dll which is contained in the PXLab extensions archive. It should be moved into a valid DLL directory like /Windows/System32. The Java part of the SpaceMouse driver is contained in the PXLab archive and does not need special installation.

     

  31. The Experimental Design Editor

  32. The PXLab design editor has not yet been updated to version 2. It is still based on PXLab version 1 and will create design files which are not compatible with PXLab 2. We thus recommend not to use it for the creation of PXLab 2 design files.


    In this chapter we define a simple experiment using the PXLab Experimental Design Editor. The experiment we create is a choice response time experiment. The stimulus is a left or right pointing arrow and the subject's task is to press a corresponding response key as fast as possible. The experimental question asked is whether left and right responses are equally fast. The independent variable is the arrow direction and the dependent variable is the response time.

    To define a new experiment we use the PXLab Design Editor. After starting the program it opens a new design tree window and shows a default design tree. The default tree has a root node called Experiment and has the tree top level nodes Context, Factors, and Procedure.

    The Context node defines the experimental stimuli. We open the Context node and see that there already are several subnodes:

    The AssignmentGroup node contains global parameter assignments which we will treat later. The Session node defines the stimulus display shown at the start of a session. The SessionEnd node defines the closing stimulus of a session. Block and BlockEnd do the same for a block of stimuli, however, these are only shown between successive stimulus blocks. The Trial node finally defines the stimulus for a trial. We begin with defining the start of a session.

    The Start of a Session

    We want our experimental session to start with a message telling the subject that he or she may start the session by simply pressing the space bar of the keyboard. When the space bar is pressed then there should be a delay of 300 ms and then the first trial should start.

    We open the Session node and see that by default the session starts with a Message display:

    The Message display object is a text paragraph centered on the screen. Its duration is controlled by the experimental parameters Timer and Duration. Opening the Message node shows us a list of parameters of this display object:

    Most of the parameters are set such that we can use the default values. We only want to change the message text here. This can be done by two ways: We can edit the design tree directly by triple-clicking on the assignment node or we can open the node's popup menu and select the entry named "Edit value". We chose the first method and enter the new text:

    Note that we use the character sequence "\n" to indicate a new line character in the text. Thus the text output on the screen will look like this:

    Press the
    SPACE bar
    to start the experiment!

    The parameter definition

         Timer = de.pxlab.pxl.TimerCodes.RELEASE_RESPONSE_TIMER

    defines the display object's timer to be a timer which waits until the subject releases the response key. Thus the message text is shown and then the program waits until the subject has pressed and released a response key. In order to make sure that only the space bar may be used to start the experiment we can define the parameter ResponseSet to contain only the space bar:

          ResponseSet = de.pxlab.pxl.KeyCodes.SPACE_KEY

    Note that design files may contain references to public integer constants defined within PXLab classes. These constants must be addressed by their name prefixed with the PXLab package and class name.

    Now we have defined the message which starts the session. Since we want to have a 300 ms delay after the message disappears we have to add another display object to the Session node. This must be a ClearScreen object since nothing is to be shown. To add the ClearScreen object we point at the Session node and press the right mouse button in order to get this node's popup menu. From "Add Display from Menu" we select the "Clear Screen" submenu and from this we select the "Clear Screen Display" object:

    Selecting the ClearScreen object opens a dialog which asks for a name for this ClearScreen object:

    This name is necessary since there may be several ClearScreen objects being used within the experiment. We enter "Session" as the instance name of the ClearScreen object and press the dialog's "OK"-Button.

    This adds a new instance of the class ClearScreen named "Session" to the display list of the Session node:

    Note that the name of the new instance of the class ClearScreen is "ClearScreen:Session" in order to indicate that this instance of the ClearScreen class has the instance name "Session".

    Since the new ClearScreen object's parameters still have its default values we have to open the new node and modify its timing parameters. We assign to it a clock controlled timer and set the duration to 300 ms:

    This finishes the session start display. We start with a message on the screen which is visible until the subject presses and releases the space bar. Then there is a 300 ms delay and then the first trial starts.

    We now can modify the SessionEnd node in a way such that it simply shows the string "E N D" for a fixed duration of 2 seconds and then stops the program:

    We do not modify the Block() and BlockEnd() nodes since these are only shown if an experiment contains more than a single block. This will not be the case in this little demo experiment and thus we do not need to change the defaults.

    Excursion: Using the Display Editor to Edit Display Objects

    Up to now we always have used the keyboard to enter parameter values of display objects. The Design Editor also has access to PXLab's Display Editor. We show its use for the Message object of the SessionEnd node. Suppose we have not yet set the properties of the SessionEnd Message object. We then point at the Message object and open the pop up menu by pressing the right mouse button:

    Activation of the "Edit" entry opens the Display Editor for the Message object:

    The Display Editor shows the color view of the display object. We change to the geometry view by pressing the "Geometry" button and chage the text to "E N D" by entering the string into the respective text field of the parameter "SessionEnd.Message.Text". Note that the text is shown immediately.

    We now switch to the timing view by pressing the "Timing" button and select the "CLOCK_TIMER" and set the "SessionEnd.Message.Duration" parameter's value to 2000 ms:

    This finishes the Message object for our SessionEnd node. We press the "OK" button of the Display Editor and switch back to the Design Editor. The Message object parameters show those values which have been entered into the Display Editor. This finishes our short excursion into the Display Editor and we move back to our experiment.

    The Trial Display

    A single trial of our simple experiment should look like this: First the signal is shown. This is an arrow which either points to the left or to the right. The subject then has to respond with pressing the corresponding arrow key on the keyboard. When the subject presses the key the arrow disappears and is replaced by a feedback message. If the response direction was correct then the response time is shown as a feedback. If the response was to the wrong side then a warning is shown. The feedback message is visible for 500 ms and followed by a delay of 2 seconds until the next signal appears.

    First we open the Trial node and remove the default Message display object by pointing at the Message node, pressing the right mouse button, and and selecting the "Remove" item from the popup menu.

    We then add the arrow display by pointing to the Trial node, pressing the right button and selecting the Arrow object from the "Simple Geometry" submenu of the "Add Display from Menu" popup menu entry:

    This, again opens a dialog for naming this instance of the display class Arrow:

    We can leave the text field empty in this case since there will be only a single instance of class Arrow in our experiment. Thus the instance name simply becomes "Arrow".

    We now have to modify some of the parameters of the Arrow object:

    1. The Timer parameter must be set to de.pxlab.pxl.TimerCodes.LIMITED_RESPONSE_TIMER. This assigns a timer to the Arrow object which is stopped by the subject's response or a fixed duration clock whichever comes first.
    2. The Duration parameter is set to 1200. This defines the time out limit for responses.
    3. The Color parameter of the arrow is set to [80.0, 0.313, 0.329] which corresponds to white at 80 candela per square meter.
    4. The parameter ResponseSet is set to an array which contains only the admissible response key codes. These are the left and right arrow keys which have the codes 37 and 39 respectively.

      Note that setting the parameter ResponseSet not only restricts the set of possible response keys but also enables a translation mechanism. This translates the response key codes to response code values which correspond to the position within the array ResponseSet. In our case this means that if the response set is defined to be [37, 39] and the actual response key has the code 39 then the parameter ResponseCode is set to 1 which is the index of the code 39 in the array ResponseSet. We thus have two possible ResponseCode values: 0 and 1.

      Also note that the current version of PXLab does not yet allow symbolic constants within array values.

    Thus the parameters of the Arrow stimulus object look like this:

    Note that we did not set the Orientation parameter which will be the independent variable in our experiment. This parameter will be set later when we define the parameters for every single trial of the experiment.

    The next display object in our trial display list is a Feedback object. We add it by pointing to the Trial node, selecting "Add from Menu" from the popup menu and choosing the "Feedback Message" from the "Feedback" submenu. Here too we do not use an instance modifier such that the design editor enters the instance named "Feedback" to the display list:

    The Feedback display class essentially is a text paragraph display object where the text to be shown is defined by conditions which are related to parameter values of other displays. The parameter Evaluation of a feedback object defines how the evaluation is done and the parameters CorrectText and FalseText contain the respective text strings for correct and false responses.

    The response in our experiment is stored in the parameter ResponseCode of the object named Trial.Arrow. Note that the full instance name of a display object has its display list name as a prefix. The response code may only be the code corresponding to the left or the right cursor key as translated by the ResponseSet mechanism. For every trial we will have to tell the Feedback object which of these codes is the correct response. This is done by setting the parameter CorrectCode of the Feedback display object. This, however, is not done now, but only later when the single trials are defined. For now we have to tell the Feedback object which type of evaluation to use by setting the parameter Evaluation to the value de.pxlab.pxl.EvaluationCodes.COMPARE_CODE and the parameter ResponseParameter to "Trial.Arrow.ResponseCode". This results in the following behavior: The feedback object compares the value of the parameters CorrectCode and Trial.Arrow.ResponseCode. If these are equal then the string contained in CorrectText is shown as a feedback. If the two parameter values are not equal then the string contained in parameter FalseText is shown.

    Thus we make the following modifications of the parameters of the feedback object:

    1. Set Timer to a fixed clock timer de.pxlab.pxl.TimerCodes.CLOCK_TIMER.
    2. Set Duration to 500 ms.
    3. Set Evaluation to the value de.pxlab.pxl.EvaluationCodes.COMPARE_CODE
    4. Set ResponseParameter to "Arrow.Trial.ResponseCode".
    5. set CorrectText to "%Trial.Arrow.ResponseTime% ms". This results in showing the response time since the parameter Trial.Arrow.ResponseTime contains the subject's response time and any parameter name enclosed between percent signs is replaced by its current value.
    6. We do not need to set FalseText since we can keep the default value "False!".

    The Feedback object then has these parameter values (we show only those which have been changed):

    Many experimental parameters have a strongly restricted set of possible values. The Design Editor knows about the set of possible values for most of the experimental parameters. Thus in order to find out what the possible values of a parameter are we have to point at the respective assignment, open the pop up menu by pressing the right mouse button and chose the "Edit" entry. Doing that for "Evaluation" opens the editing dialog for "Evaluation":

    We activate "COMPARE_CODE" and press the "OK" button.

    Finally we add a ClearScreen object to the trial display list, give it an instance name "Trial", assign it a clock timer and set its duration to 2000 ms:

    Note that we can remove those parameters from the design tree node which have their default values.

    We now have defined the stimuli for our simple choice response time experiment. The next step is to look at the factorial structure of the experiment.

    The Experimental Factors

    The Factors node defines the independent and dependent factors of an experiment. Our experiment is a very simple design with only a single factor, the direction of the stimulus arrow. The question posed by the experiment is whether left and right responses are equally fast. The independent factor is the direction of the stimulus and the dependent factor is the response time. The independent factor has 2 levels: left and right.

    The Factors node of the default design has already an independent factor defined. However, we want to change its name and we also have to define the operationalizing parameters of the factor. When defining an experimental factor we usually will use a symbolic name for the factor. In our example we use "Direction" as the name of the independent variable factor. Every factor must have one or more operationalizing parameters. These describe properties of a display object which actually operationalize the experimental factor.

    Which parameters of our stimuli operationalize the experimental factor? There are two parameters: The parameter Orientation of the arrow object and the parameter CorrectCodeof the Feedback object. To edit the IndependentFactor node we point at the node, open its popup menu, and select the "Edit Factor" item:

    This opens the dialog for editing Factor nodes. We enter the factor name "Direction" and aczivate its operationalizing parameters: Trial.Trial.Orientation and Trial.Feedback.CorrectCode. We also define the factor as a within-factor since every subject will run all conditions:

    Our independent factor has two factor levels: left and right direction of the stimulus array. In order to define the factor levels we press the "Define Factor Levels" button of the factor editing dialog and enter the factor levels:

    We use the value 1 for the left direction and 2 for the right direction. The arrow orientation has to be a rotation of 180 degree for left and of 0 degree for the right direction. The correct response code for the left arrow is 0 and for the right arrow it is 1. These values correspond to the index positions of the actual key codes de.pxlab.pxl.KeyCodes.LEFT_KEY (37) and de.pxlab.pxl.KeyCodes.RIGHT_KEY (39) in the array ResponseSet of the Arrow stimulus object. After closing the dialogs we see the independent factor definition in our design tree:

    We then point to the default dependent factor node, open its popu menu and edit this factor. We select the experimental parameter Trial.Arrow.ResponseTime as the dependent factor:

    This concludes the definition of the Factors node. We have defined an independent factor, the stimulus arrow direction and a dependent factor, the response time. The independent factor has two factor levels, left and right direction of the stimulus arrow:

    So we are now ready to define the trials of the experiment.

    Sessions, Blocks, and Trials

    Sessions, blocks, and trials are defined in the Procedure node of the design file. By default we have a single session containing a single block with a single trial:

    We will neither add sessions nor blocks but will define additional trials. However, before adding trials we have to look at PXLab's mechanism for defining experimental parameters for trials. We have already defined what happens during a single trial in the Trial node of the Context section. We have an Arrow object, a Feedback object and a ClearScreen object. All of these have experimental parameters which define their properties. However, these properties have to be modified by the experimental conditions which are realized in a single trial. Thus we have to tell every Trial node in the Procedure section which experimental condition is realized by that Trial. This is done by adding the respective factor level code into the Trail node's argument list.

    Since our independent factor Direction has two factor levels which have the codes 1 for left and 2 right, we need at least two different types of trials. The first type must realize factor level 1 for left directed arrows and the second type must realize factor level 2 for right directed arrows. We thus might expect the trial defintions to look like

         Trial(1)
         Trial(2) 
    

    for the two factor levels. However, this is not enough. Trial nodes do not only differ by the independent factor level they will also differ by the dependent factor value. This value, however, is not yet known when defining the trial. Unknown parameter values are described by a question mark in PXLab. Thus our minimal trial definition might look like

         Trial(1, ?)
         Trial(2, ?) 
    

    where the first argument now denotes the factor level and the second argument denotes the yet unknown dependent factor value. How can we tell PXLab what the Trial node argument values actually mean? This is done by listing the parameter names which correspond to the Procedure Trial argument values as arguments in the Trial node of the Context section. So in order to tell PXLab that trials have two arguments where the first argument is the independent factor Direction and the second argument is the dependent factor Trial.Arrow.ResponseTime we list these names as arguments of the Trial node in the Context section.

    We go back to the Context node, point at the Trial node to open its popup menu and select "Edit Arguments". This opens a dialog to edit the Trial node's argument list:

    The bottom list contains all experimental parameters which are available. Ordering is such that display parameters are listed first and system parameters are listed at the end. Parameters are sorted alphabetically within their respective group. Note that entering "Direction" as a factor name has implicitly created a parameter named "Direction" contained in the list. We select "Direction" and press the "Add" button. This moves the "Direction" parameter into the argument list at the top of the window. The same is done for the parameter "Trial.Arrow.ResponseTime":

    After closing the argument editing dialog we see the new arguments appear in the Trial node:

    Note that the "Trial" prefix of the parameter "Trial.Arrow.ResponseTime" has been removed within the context of the Trial node. Also note that the argument list of the Trial subnode in the Procedure section also has been changed in order to reflect the number of arguments of the Trial node. We see that the Trial argument list of the Trial node in the Procedure section has automatically been changed to have two arguments. We now edit the arguments of the first trial to have the value 1 for the direction parameter and the unknown question mark value for the response time parameter:

    Finally we open the popup menu for the Procedure Trial node, duplicate the node and edit the second node to have 2 as its first argument:

    Now we have defined two trials, one for each level of the independent factor. Of course we actually need more than two trials but all other trials we need are copies of the two different ones we have already defined and PXLab has a simple mechanism built in to replicate defined Procedure Trial nodes.

    There is a global experimental parameter named TrialFactor which defines how often each defined Trial is presented. The parameter TrialFactor is part of the Context section and may be included into the AssignmentGroup node which by default is defined as a subnode of Context. We go back to the Context node, open the AssignmentGroup node and open its popup menu:

    We choose the "Add Assignment" item and the editor opens a parameter assignment editing dialog:

    The dialog shows all global experimental parameters and we select "TrialFactor". This adds some parameter value editing tools to the dialog:

    We edit the value field such that it defines 2 replications for each trial, this is enough for testing. Closing the dialog adds the respective assignment to the AssignmentGroup node:

    This finishes the definition of our experiment. We choose "Save Design" or "Save Design as ..." from the "File" menu and save our definition to a file.

    Collecting Data

    Our experiment is now ready for a first run. We can open the design editor's file menu and select the "Run in Window" item or we simply push the tool bar button with the same label. This opens a framed window and starts the experiment. The session starts with the session start message. We press the space bar as requested and then we get four trials, two for every experimental condition. What remains to be done is to define where and how the data should be stored.

    Every session of an experiment creates a data file. By default the data file name is generated from the subject code. The subject code is a code which is stored in the global parameter SubjectCode and which may be entered at the command line when an experiment is started. We use PXLab's default subject code "pxlab" here since this code generates a special behavior: Repeated sessions with the same subject code overwrite the created data file. This is useful for testing. During data collection we use true subject codes and then data files are not overwritten by PXLab.
    To create true subject codes you have to add the parameter SubjectCode to the Procedure node arguments. (For further explication see chapter "Procedure Definition".) This results in a dialog popping up when the experiment is started requesting that the user enters a value - i. e. a subject code for the current subject - for the global parameter:

    The directory where the data file is stored is defined by the parameter DataFileDestination. We can set this to "." in order to store the data in the current directory.
    Note that you can store the data file on any existing directory on your computer. To store the data on any other directory simply set the parameter DataFileDestination to the adress of the desired directory - e. g.

    DataFileDestination = "D:/Experiment/Data"
    

    The data format is defined by the parameter DataFileTrialFormat. This parameter defines the format for the data stored for each trial. The format string is a string which may contain experimental parameter names enclosed in percent characters such that they are replaced by their current values during the experiment. We define DataFileTrialFormat such that the subject code, the factor level, and the response time value is stored for each trial.

    Since all of these parameters are global parameters we go back to the AssignmentGroup node and use its popup menu to add these definitions. We may also change the trial replication factor in order to have an acceptable number of repetitions per condition:

    That's it. We have defined our simple choice response time experiment and are now ready to collect data.

    Additional Features

    After running the experiment we may see that some desirable features are missing:
    1. The data file does not contain the information whether the response was correct or not. Solution: Add the parameter Response of the Feedback object to the trial arguments and to the DataFileTrialFormat string. This parameter contains the response as it is evaluated by the Feedback object.
    2. The data file should contain a counter which counts the trials. Solution: Add the global parameter TrialCounter to the trial arguments and to the DataFileTrialFormat string. This parameter is automatically incremented for each trial and thus simply counts the trials.
    3. There should be some trials for training and the data collected in the training trials should not be stored in the data file. Solution: Add a new block of a smaller number of trials whose data are not stored.

    Fixing the Data Format

    First we add the parameters TrialCounter and Trial.Feedback.Response to the Trial node arguments. We open the Trial subnode of the Context node and select the menu entry "Edit Arguments". We add "TrialCounter" and "Trial.Feedback.Response" to the list of arguments and use the "up"-button to move "TrialCounter" to the first position:

    After this is done we see that the editor has automatically changed the argument list of the Trial nodes in the Procedure tree also:

    Since both of the new parameters are dependent parameters which are controlled by the program we do not need to set their values in the Procedure tree. However, we have to insert them into the DataFileTrialFormat string in order to store the parameters' values into the formatted data file. We do this by editing the parameter assignment for DataFileTrialFormat in the AssignmentGroup using the "Edit Value" menu entry:

    It shows up in the tree:

    Adding Trials for Training

    Adding a block for training trials requires some more modifications:
    1. We need to add a new Block node in the Procedure tree. As a consequence we also have to define the stimuli for starting and ending a block since we then will have two blocks and these will be separated by block starting and ending stimuli. We did not define these until now since they are not shown if there is only a single block within a session.
    2. The Block node arguments have to be changed in order to tell the program whether the trials in the block have to be stored into the data file or not. The parameter TrialFactor also has to be modified in order to have different factors in the training and the data collection block.

    Let's first add another Block node to the Procedure tree. We open the popup menu of our Block node and select the "Duplicate" item. This adds a copy of the selected Block node to the Session node's list of blocks:

    Now let's change the Block node argument list. We have to add two parameters: StoreData in order to control whether the data are stored into the formatted data file, and TrialFactor in order to allow for different replication factors in each block.

    We open the Context node, triple click on the Block node and enter these two parameters into the argument list:

    Note that here the Procedure tree Block node arguments are modified automatically. We then can edit the Procedure Tree Block node argument values for the training block. The StoreData parameter is set to 0 and the TrialFactor parameter value is set to 5. This results in 10 trials for training which are not stored. The parameter StoreData is a boolean flag which is true if the parameter has a nonzero value and is false if the parameter is 0:

    A Final Remark on the Parameter Value Scope

    Looking at the parameter TrialFactor we see that this parameter now appears two times in our file: (1) it is set in the AssignmentGroup and (2) it is contained in the Block node argument list. This demonstrates the scope rules of PXLab parameter values. In general parameters have global values which are effective for the whole experiment. This rule is overridden if a parameter is contained in the argument list of a session, block, or trial node. In this case the respective parameter value becomes localized to the subtree of the respective node.

    Thus the assignment in the AssignmentGroup section assigns a global value to the parameter TrialFactor. Since TrialFactor is contained in the argument list of the Block node it gets local values for each Block of the Procedure section. This local value is effective for the respective Block node and all of its subnodes. Thus TrialFactor has the value 5 for all Trial nodes in the first block and has the value 20 for all Trial nodes in the second block.

    In our file we have explicitly defined the TrialFactor argument for the second block. However, we could also use the question mark '?' in the second block's argument list as a value for TrialFactor, since in this case the effective value for the TrialFactor would be the value effective in the surrounding scope which is controlled by the assignment in the AssignmentGroup which assigns the value 20 to the TrialFactor. Thus question marks as parameter values serve a double purpose: (1) they indicate that the respective parameter is a dependent parameter whose value need not be set as for example with Array.Trial.ResponseTime and (2) they implicitly assign the value of the surrounding scope to a parameter. This same mechanism is the reason why parameters like Direction or Feedback.Trial.CorrectCode which have different values for every trial must be Trial node parameters.

    There is a subtle difference between the parameter Direction and the parameter Feedback.Trial.CorrectCode: The latter is a parameter of the display object Feedback and is defined by this object's Java class code. 'Direction' is the name of an independent factor. The first name of an independent factor node's argument list always is the factor name. If this is not the name of an existing parameter then this parameter is created at runtime. It can, however, be used as any other parameter. This is the only case where parameters are implicitly created. In general new parameters must be created by explicit statements for creating a new parameter which could look line

         new Direction = 0
    
    and are allowed everywhere where an assignment statement is allowed.

    The Final Design File

    The complete experimental design is contained in the file crt_simple.pxd.

     

  33. The PXLab Software API
  34. PXLab Software Development System Installation

    If you want to develop your own Java programs using the PXLab APIs or if you want to extend PXLab classes by creating your own stimulus objects then you need to download the PXLab source archive PXLabSrc.zip from the PXLab download area. For Windows users we suggest that they install the zip archive into a directory named jpxl in the root of drive C: because that is how the build file is defined. Following this will make it easier to adapt the system to your needs. The source path will be c:/jpxl/src in this case. However, the source tree may of course be installed into any arbitrary directory. The build file has to be adapted accordingly.

    The PXLab source archive will install two directories:

    images
    contains various bitmap files.
    src>
    contains the Java source tree. The scr directory contains the build file named build.xml and two subdirectories: share and win32. The share subdirectory contains all shared sources and the win32 subdirectory contains the Windows native DLL sources for the library pxlab.dll.

    Software Needed

    PXLab needs some software packages for compilation. All of these are free or open source software packages.

    The Java Development Kit

    In order to compile the PXLab sources you have to download and install the latest Java Development Kit from Sun's download page. We do not follow the suggestion of the JDK installer to install the JDK into the 'Program files' directory but install the JDK into a subdirectory named jdk of a directory named javasoft on drive C:. This is for convenience and is not a necessity. Sun has the habit of creating a new name for every release of the JDK and so it would be neccessary to change the build file for every new release of the JDK. Always using jdk makes it easier to upgrade.

    A Build Tool: Apache Ant

    The PXLab source tree uses Apache Ant for compilation. Thus you should install Apache Ant to compile PXLab. We install Apache Ant into a subdirectory named ant of our javasoft directory. To get this we extract the Ant archive into directory javasoft and rename the resulting release dependent directory name to ant.

    A Parser Generator: JavaCC

    The PXLab design file grammar is defined by a JavaCC parser generator grammar file. Modification of the design file grammar or a recreation of the parser thus requires that the JavaCC parser generator is being installed. We download the archive and extract it into a subdirectory named javacc of the javasoft directory. This requires the same renaming procedure as we did with Ant, since JavaCC also carries its release number in the target directory.

    The Serial Communications API Package

    PXLab supports external devices connected to the serial port. The serial port software is not part of the JDK since the software support for the serial devices is hardware dependent. Using the serial communication port requires the installation of the Java Communications API for the respective machine and OS. The download package contains instructions how to install the package for various operating systems and Java development kit versions. For reasons unknown to me Sun does no longer support the Communications API for Windows. However, you may find the software using the link below.

    For installation under Windows you have to do the following steps:

    • Download the zip-archive javacomm20-win32.zip (274.432 Bytes) for Windows.
    • Unpacking the archive gives you three files:
      • move win32com.dll into JRE/bin
      • move comm.jar into JRE/lib/ext
      • move javax.comm.properties into JRE/lib
      where 'JRE' stands for the installation directory of your Java runtime environment which will in our default case be C:/javasoft/jdk/jre/. All other files of the archive are documentation.

    Note that at runtime this software is only required if external response buttons, external synchronization signals or other serial devices will to be used. Applets cannot use it.

    The Java Media Framework

    Playing movies with PXLab requires the Java Media Framework (JMF) package being installed. Be sure to follow the installation instructions of the JMF package carefully in order to make JMF work properly. It seems to be important that the order of installation is correct: First install the JDK, and then install the Java Media Framework. We install the JMF package into a subdirectory named jmf of the javasoft directory.

    The Java SVG Toolkit: Batik

    Images which are in the Scalable Vector Graphics (SVG) format can be shown with the Apache Java SVG toolkit Batik. The Batik toolkit has to be installed in order to compile the SVG support for PXLab. You may download the software from the Batik download page. PXLab uses only a subset of the Batik package. This subset is contained in a jar file named batik.jar which you will find in subdirectory src/share/ext of the PXLab source archive. Move this file into the same location (jdk/jre/lib/ext) as described earlier for the file comm.jar.

    Building PXLab

    Before starting the build under Windows you have to set some environment variables for the JDK, Ant, and JavaCC. There is a batch file named setup4pxlab.bat contained in directory jpxl/src/bin which can do this for you. You may run this batch in a Windows command window or you may use this batch file as a startup file for a Windows command window.

    The PXLab build file 'build.xml' is located in the PXLab top level source directory jpxl/src. If you did not follow our installation instructions you may have to modify the build file according to your installation locations. If you use our installation suggestions then chances are high that the build file works unmodified. Open a command window, run the startup batch file, change into directory jpxl/src and type command 'ant'. This should build the complete PXLab package.

    Implementation of new Display Objects

    All display objects of PXLab are subclasses of the abstract class Display. Any concrete subclass has to define at least the following three methods:

    1. a constructor for instantiating subclass objects;
    2. the method create() to set up the lists of display and timing elements;
    3. the method computeGeometry() for computing the geometric properties of the display elements contained in the display.

    These methods are abstract methods of class Display and must be defined concrete in any subclass.

    In addition to these methods every Display object has to define additional experimental parameters which describe its properties.

    Experimental Parameters

    Displays are described by experimental color, geometry, and timing parameters which are declared as public ExPar objects. This can be done during class initialization using public initializers. Here is an example for a simple bar display:
        public ExPar Color = new ExPar(COLOR, 
    	new ExParValue(PxlColor.gray), "Bar Color");
    
        public ExPar Width = new ExPar(HORSCREENSIZE, 
    	new ExParValue(100), "Bar Width");
    
        public ExPar Height = new ExPar(VERSCREENSIZE, 
    	new ExParValue(100), "Bar Height");
    
        public ExPar LocationX = new ExPar(HORSCREENPOS, 
    	new ExParValue(0), "Horizontal Center Position");
    
        public ExPar LocationY = new ExPar(VERSCREENPOS, 
            new ExParValue(0), "Vertical Center Position");
    
    This display uses five new experimental parameters. The first one is a color parameter and the other four are geometry parameters for width, height and location. The parameter types are given as arguments for the constructor. These type codes are only needed for interactive display editing purposes in order to provide most useful editing tools for the different types of parameters. The parameter type code is the first argument of the constructor ExPar(). The second argument is an initial value for each parameter and the third argument is a short label describing the parameter's purpose.

    Every display object inherits several parameters from class Display. These mainly refer to timing and response properties such that many Display objects need not define their own timing parameters.

    The Display Subclass Constructor

    The constructor of a display is a simple and small method which does nothing but enter the display's name and content topic into PXLab's list of displays and thus makes it appear in one of the display selection sub-menus. The submenu where the display appears for selection corresponds to its content topic ID and is given as the second argument to the method which defines the title and the topic. Here is an example for the constructor of the class SimpleBar:

        public SimpleBar() {
    	setTitleAndTopic("Simple Colored Bar", SIMPLE_GEOMETRY_DSP);
        }
    

    The constructor calls the method setTitleAndTopic() of class Display with the display's menu entry and the menu topic code. Note that no geometric, no color, and no timing properties are defined by the constructor.

    Instance Initialization

    A Display object does nothing as long as it is not shown on the screen. It only appears for selection in one of the menus. When a Display object is selected, however, it has to be created and initialized. This is done by the method
        protected int create()
    
    which is called by the Display manager after selection or by experimental runtime controllers when an experiment is being prepared. The method create() does the following initializations:

    • prepare the list of DisplayElement objects which make up the display's geometry and associate a color parameter with each display element;
    • defined the timing group of each display element;
    • define the timing properties of each timing group contained in the list of display elements;
    • return the index of the first active display element after startup.

    Defining the Geometric Properties of a Display

    The first step to create a display is to create the geometry of the display. A display's geometry is defined by a list of colored display elements which make up the display. The single elements are objects of type DisplayElement and are entered into the display's display element list by the method
        protected int enterDisplayElement(DisplayElement p, int[] timing_group)
    
    which also returns the object's index in the display element list. It is not necessary to completely define every element's geometry during initialization since this will be done later. What needs to be done is to associate a color with every display element. Thus the constructor of most DisplayElement objects gets at least one parameter: the color parameter associated with this object.

    The class DisplayElement is an abstract class which has several derived classes for different types of display elements. Here is a list of available display elements:

    All of these are subclasses of DisplayElement and have constructors which do not define the geometric properties but only set the element's color parameter. The following methods are available for setting the location and size of DisplayElement objects:

    • setLocation(int x, int y) for defining the position within the display,
    • setSize(int w, int h) for defining the width and height,
    • setRect(int x, int y, int w, int h) both sets the location and the size of a display element.

    Most of these display elements have further geometric properties which my be set during geometry recomputation. Look at the respective display element's description to find out about these methods.

    In our example we have only a single display element which gets the color parameter as an argument:

    	bar_idx = enterDisplayElement(new Bar(Color), group[0]);
    

    The return value of the method enterDisplayElement() is the index of the display element in a display's list of elements. This index will be used later so we have to store it in a local variable.

    Timing Groups of DisplayElement Objects

    Some displays may be divided up into subgroups of display elements such that all members of a group always appear simultanously on the screen whereas the single groups may be displayed sequentially. Many real experimental displays will have display element grouping in order to exactly control the sequential timing of groups of display elements.

    In order to tell the PXLab runtime system which display elements belong to the same timing group every DisplayElement object is assigned a timing group code when it is entered into the display element list of a Display object. There is a global constant integer array

       int[] group;
    

    which contains proper timing codes and we simply use this array's elements as timing group pattern. Thus the first timing group is assigned the group pattern value group[0], the second timing group gets group[1] and so on.

    In our example we have only a single display element and thus we have only a single timing group:

    	bar_idx = enterDisplayElement(new Bar(this.Color), group[0]);
    

    The timing properties of a timing group of display elements are defined by associating timing parameters with a display's list of timing groups. This is done by calling the method

       public int enterTiming(ExPar timing, ExPar duration, 
         ExPar responseSet, int refIndex, ExPar rtime, ExPar rcode);
    
    This method tells the real time control system which type of timer is to be used for measuring the timing group's duration, the duration value, the possible response set, the number of the timing group to refer to, and the experimental parameters which accept the actual response time or duration and the stopping code of the time interval. Note that response time and stopping code are data parameters which contain data after the display has been run. The other parameters define the timing properties.

    In our example we simply use the predefined experimental parameters of class Display in order to defined the timing properties and accept the response data:

       enterTiming(this.Timer, this.Duration, this.ResponseSet, 
          0, this.ResponseTime, this.ResponseCode)
    

    Return Value

    This is all that has to be done during initialization. The method create() returns the display element index which points to that display element which should be this display's active element when it first appears on a display editor. Here is the complete initialization method: of our simple example:

        private int bar_idx;
    
        protected int create() {
    	bar_idx = enterDisplayElement(new Bar(this.Color), group[0]);
    	defaultTiming(0);
    	return(bar_idx);
        }
    

    Note that the method enterTiming() has been replaced by defaultTiming(0) since we onyl use the default timing parameters.

    Computing the Display's Geometry

    After a display is initialized it can be shown on a display panel. This requires that the display's geometry is computed with respect to the display panel's size. This is done by the method
        protected void computeGeometry()
    
    This method is called by the realtime display controller. Before calling the computeGeometry() method the display controller sets the fields width and height of the active Display object. Here is a simple example for computeGeometry():
        protected void computeGeometry() {
    	DisplayElement bar = getDisplayElement(bar_idx);
    	int w = Width.getInt();
    	int h = Height.getInt();
    	bar.setRect(LocationX.getInt() - w/2, LocationY.getInt() - h/2, w, h);
        }
    
    The method getDisplayElement() retrieves a single DisplayElement object with the given index from a Display's list of display elements. In order to retrieve a certain element we use the expression
    	getDisplayElement(bar_idx)
    
    which gets us the DisplayElement object at index bar_idx in this display's list of display elements. The class DisplayElement uses the method setRect() to set a Bar's location and size. Thus
    	bar.setRect(LocationX.getInt() - w/2, LocationY.getInt() - h/2, w, h);
    
    sets this object's location and size from the parameter values LocationX, LocationY, Width, and Height.

    While the method create() is called only once when the display is being initialized, the method computeGeometry() is always called immediately before the display object appears on the screen. It is also called whenever the size of the display window has changed or whenever one of the geometry parameters of the display has been changed by a display editor.

    Computing Colors

    Some displays have colors which depend on other colors in the display. An example is a smooth transition from one color to another color like it is used in a Mach band display. In this case the dependent colors have to be recomputed whenever the independent colors are changed. Dependent colors should not be selected by the user for adjustment, they should only be selected for displaying their coordinates or for moving them to the color clipboard of a display editor.

    There are two ways for Display objects to handle dependent colors: The display may define experimental parameters for these colors and these parameters' values may be recomputed by the method computeColors() whenever any color of the display has been adjusted. In this case a dependent color may be selected as currently active color of a display editor. The second way how a Display object may handle dependent colors is to only create them when drawing is required. In this case it is not possible to select the dependent color in a display editor since there will be no geometry object in the display's display element list corresponding to this color. If the user clicks at the respective screen position she or he will get the underlying background object's color as a selection.

    In general dependent colors and the corresponding display elements should be entered into the respective tables whenever display editor selection should be possible. This will be the case when the respective object has a sufficiently large area to be accessible to the mouse pointer. If the respective object is so small that it hardly is accessible to the mouse pointer, then it does not make sense to enter it as a selectible object. The latter case usually holds for single screen lines as they are used for smooth color transitions.

    Here is an example for color computing from the display class AdditiveColorMixer. This class shows three overlapping squares for simulating additive color mixing. It defines three independent and four dependent color parameters.

        public ExPar Square1Color = new ExPar(COLOR, new ExParValue(PxlColor.red), 
                                    "Color of the First Square");
        public ExPar Square2Color = new ExPar(COLOR, new ExParValue(PxlColor.green), 
                                    "Color of the Second Square");
        public ExPar Square3Color = new ExPar(COLOR, new ExParValue(PxlColor.blue), 
                                    "Color of the Third Square");
        public ExPar Dep1Color = new ExPar(DEPCOLOR, new ExParValue (PxlColor.black),
                              "First Dependent Color");  
        public ExPar Dep2Color = new ExPar(DEPCOLOR, new ExParValue (PxlColor.black),
                              "Second Dependent Color");
        public ExPar Dep3Color = new ExPar(DEPCOLOR, new ExParValue (PxlColor.black),
                              "Third Dependent Color");
        public ExPar Dep4Color = new ExPar(DEPCOLOR, new ExParValue (PxlColor.black),
                              "Fourth Dependent Color");
    

    Note that the dependent colors get the type code DEPCOLOR to indicate that these are not adjustable. The method computeColors() computes the colors Dep1Color to Dep4Color as a mixture of the colors Square1Color to Square3Color:

        protected void computeColors() {
    	Dep1Color.set(Square1Color.getPxlColor().add(Square2Color.getPxlColor()));
            Dep2Color.set(Square2Color.getPxlColor().add(Square3Color.getPxlColor()));
            Dep3Color.set(Square3Color.getPxlColor().add(Square1Color.getPxlColor()));
            Dep4Color.set(Dep1Color.getPxlColor().add(Square3Color.getPxlColor()));
        }
    

    The class PxlColor has a method which creates the additive mixture of two colors:

       color1.add(color2)
    
    returns the additive mixture of color1 and color2 where both are PxlColor objects. So
       Square1Color.getPxlColor().add(Square2Color.getPxlColor())
    
    retrieves the colors coordinates from the experimental parameters Square1Color and Square2Color and computes their additive mixture.

    Note that while the methods create() and computeGeometry() are defined abstract in the class Display the method computeColors() is not. Thus concrete subclasses of Display need not implement computeColors() if they do not need to compute dependent colors.

    Standard Painting

    In many cases it will not be necessary to do any painting by the display itself. The superclass Display does that by walking through the list of display elements and drawing them using the information contained in each DisplayElement object. Thus most subclasses of Display will not have any paint methods. However, if the standard way of painting is not sufficient then a display may override the method

        public void showGroup(Graphics g)
    
    of class Display to do its own painting. Here is the code for the class Display's painting method which is simple enough:
        public void showGroup(Graphics g) {
    	setGraphicsContext(g);
    	showBackgroundElement();
    	showGroup();
        }
    

    This method first sets the drawing context and then paints the background. Display element painting actually is delegated to the method showGroup() which has this code:

        public void showGroup() {
    	if (ExPar.Stereographic.getFlag()) {
    	    showGroupStereo();
    	} else {
    	    DisplayElement de;
                long timingGroupPattern;
                int n = displayElementList.size();
                for (int i = 0; i < n; i++) {
    	        de = (DisplayElement)displayElementList.get(i);
    	        timingGroupPattern = de.getTimingGroupPattern();
    	        if ((timingGroupPattern & activeTimingGroup) != 0L) {
    		    de.show();
    		    updateBoundingBox(de.getBounds());
    	        }
    	    }
    	}
        }
    

    For non-stereo display objects this method walks through the list of display elements and calls their method show() if the respective display element is a member of the current timing group.

    The class Display also has methods to paint display objects which ignore the timing groups. These are used by display editors but not by experimental runtime controllers.

    Any display object which is not easily composed of objects from the DisplayElement class may override the above painting methods to do more complicated painting. Here is an example from class SearchPattern which overrides showGroup() in order to allow subclasses to override the methods for showing targets and distractors:

        protected void showGroup() {
    	show();
        }
        public void show() {
    	boolean showTarget = ShowTarget.getFlag();
    	graphics.setColor(DistractorColor.getDevColor());
    	for (int i = 0; i < (nItems-1); i++) showDistractorAt(positionOfItem(i));
    	if (showTarget) {
    	    graphics.setColor(TargetColor.getDevColor());
    	    showTargetAt(positionOfItem(nItems-1));
    	} else {
    	    showDistractorAt(positionOfItem(nItems-1));
    	}
        }
        protected void showDistractorAt(Point p) {
    	graphics.fillOval(p.x, p.y, itemSize, itemSize);
        }
        protected void showTargetAt(Point p) {
    	graphics.fillOval(p.x, p.y, itemSize, itemSize);
        }
    

    Note that both methods show() and showGroup() are overriden which is possible since this display has only a single timing group. The advantage is that both experimental runtime controllers and display editors are able to correctly show the display object.

    Display Element Properties

    The display elements available have been described earlier. Each display element has the following methods to set/get its location on the screen and its size:
        public void setLocation(Point l);
        public void setLocation(int x, int y);
        public Point getLocation();
        public void setSize(Dimension s);
        public void setSize(int width, int height);
        public Dimension getSize();
        public void setRect(Rectangle r);
        public void setRect(Point p, Dimension s);
        public void setRect(int x, int y, int width, int height);
        public void setCenterAndSize(int x, int y, int width, int height);
    

    Every display element has its own method to show itself:

        abstract public void show();
    
    This method must be defined by every subclass. A display element may also be shown in a color which differs from the one which has originally been assigned to it:
        public void show(ExPar i);
    
    Further display element properties depend on the type of display element and may be found by looking at ther respective API document.

    Simple Geometric Shapes

    The most commonly used type of geometric objects in PXLab's displays are filled geometric shapes. These are objects of type Bar(), Oval(), FilledPolygon()and Sector(). Outlined object types are Line(), Rect(), Ellipse(), PolyLine(), PolyLineClosed(), and Arc(). Bar, Oval, Rect, and Ellipse objects are completely defined by their location and size. FilledPolygon, PolyLine, and PolyLineClosed objects are subclasses of class DisplayElementPolygon and are defined by setting their Polygon:
         public void setPolygon(Polygon p);
    

    The location and size parameters are not used in this case.

    The classes Arc and Sector are subclasses of DisplayElementArc and their parameters for the starting angle and the angle size are set by calling setAngles():

        public void setAngles(int startAngle, int arcAngle) {
    

    Text Elements

    There are two major text elements: TextElement and TextParagraphElement. The TextElement is a single line of text while TextElement is a multiline paragraph of text which can do its own line breaking. These objects use the location parameter defined earlier but do not use the size parameter. Their size is defined by setting the font of the text object:

        public void setFont(String fn, int ft, int fs);
    

    Every text object has reference point which defines how the location parameter of the text object is interpreted:

        public static void setReferencePoint(int p)
    

    By default the location parameter specifies the left edge base line point of the text string. Other values may be set such that the location refers for example to the center base line point of a string. These values are available:

        public static final int BASE_LEFT    public static final int MIDDLE_LEFT    public static final int TOP_LEFT    public static final int BASE_CENTER    public static final int MIDDLE_CENTER    public static final int TOP_CENTER    public static final int BASE_RIGHT    public static final int MIDDLE_RIGHT    public static final int TOP_RIGHT

    Here the terms LEFT, CENTER, and RIGHT refer to the horizontal position while BASE, MIDDLE, and TOP refer to the vertical position. Note that the reference point of a text paragraph refers to the paragraph as a whole and not to a single line.

    When using a text object then the drawing color will usually be defined in a display's create() method:

    	text_idx = enterDisplayElement(new Text(Color));
    

    Since all size dependent parameters of a display should be defined in the method computeGeometry() this should be the place where to define the font and the location of the text object. The string content of the text should also be defined in the method computeGeometry() since only then can the string be reinitialized repeatedly from experimental parameters. The following example is from the class TextParagraph which creates a display containing a text paragraph:

        protected int textpar;
    
        protected int create() {
    	textpar = enterDisplayElement(new TextParagraphElement(this.Color), group[0]);
    	defaultTiming(0);
    	return(textpar);
        }
    
        protected void computeGeometry() {
    	double w = Width.getDouble();
    	int ww = (w < 1.0)? (int)(width * w): (int)w;
    	((TextParagraphElement)getDisplayElement(textpar)).setProperties(
                Text.getStringArray(),
                FontFamily.getString(), FontStyle.getInt(), FontSize.getInt(),
                LocationX.getInt(), LocationY.getInt(),
    	    ww,
                ReferencePoint.getInt(),
    	    Alignment.getInt(), 
                (Wrapping.getInt() != 0),
                LineSkipFactor.getDouble());
        }
    

    This creates a single paragraph of text. Note that the method setProperties() of class TextParagraphElement also accepts parameters to set the formatting style, line wrapping and line skip. Text paragraphs also need to know the intended width in order to do line breaking. The text of a text paragraph may be a single string or an array of strings. A single string may contain line break characters '\n' to force line breaks. The elements of a string array always force line breaks.

    Picture

    Picture objects are rectangular arrays of pixels and differ from all other objects in that they ignore the color parameter. They have their colors coded into the image pattern. Picture objects use the location parameter to define the top left position of the image but they do not use the size parameter since the picture's size is derived from the number of horizontal and vertical pixels. Pictures may be loaded from files or they may be created by drawing into an image buffer and connect this buffer to the picture. Here is an example from a sinusoidal grating which is created by drawing into an image buffer. During create() the display element is defined to be a Picture object:

        private int s1, s2;
    
        protected int create() {
    	Picture pict = new Picture();
    	pict.setColorPar(AColor);
    	s1 = enterDisplayElement(pict, group[0]);
    	defaultTiming(0); 
    	return(s1);
        }
    
        protected void computeColors() {
    	computeGeometry();
        }
    

    There also are two color parameters defined. These are used to compute the sinusoid which is a smooth transition from AColor to BColor. Here is the complete method for creating this picture object:

        Image imageBuffer;
        int x, y, w, h, w4;
    
        protected void computeGeometry() {
    	Rectangle r = largeSquare(width, height);
    	w4 = r.width/4;
    	w = w4 + w4 - 1;
    	h = r.height/2;
    	x = r.x + w4;
    	y = r.y + r.height/4;
    	setFramesPerCycle(w-1);
    	int fd = FrameDelay.getInt();
    	setFrameDelay(fd);
    	int fi = FrameIncrement.getInt();
    	setFrameIncrement(fi);
    	PxlColor[] ramp = AColor.getPxlColor().sinusoidalRampTo(
                              BColor.getPxlColor(), w4);
    	imageBuffer = displayPanel.createImage(4*w4-1, h);
    	Graphics g = imageBuffer.getGraphics();
    	for (int x = 0; x < w4; x++) {
    	    g.setColor(ramp[x].dev());
    	    g.drawLine(x, 0, x, h-1);
    	    if (x < (w4-1)) g.drawLine(w-x-1, 0, w-x-1, h-1);
    	    if (x > 0) g.drawLine(w+x-1, 0, w+x-1, h-1);
    	    if ((x > 0) && (x < (w4-1))) g.drawLine(4*w4-4-x, 0, 4*w4-4-x, h-1);
    	}
    	Picture p = (Picture)getDisplayElement(s1);
    	p.setImage(imageBuffer, displayPanel);
    	p.setLocation(x, y);
    	p.setClipRect(x, y, w, h);
        }
    

    It starts with computing the location and size of the picture. These are used to define the picture's clipping rectangle. The clipping rectangle defines that subsection of the image which actually is shown on the screen. This is used here since our image will be larger than the displayed picture. Then the number of frames per display cycle and delay time between successive frames for the animation are set. The color transition is computed by creating a sinusoidal ramp from color AColor to color BColor. Finally an image buffer of the appropriate size and a Graphics context for this buffer is created. Then the colors in the sinusoidal ramp array are used to draw the image lines into the buffer. Note that two periods are drawn since this makes it possible to shift the display window across the buffer during animation.

    The standard show() method of a picture object uses the location to position the picture and also sets the clipping rectangle if it is defined.

    Subgroups of Display Elements

    Every display contains a list of display elements. A display can define groups of display elements by adding a group pattern parameter to the display element when it is entered into the display element list. These groups are called timing groups because all members of a single group are always shown simultanously on the screen. A single display element may belong to more than a single timing group. A simple example is contained in the class TwoStrings which can show two lines of text with an SOA:

        private int signal, probe;
    
        protected int create() {
    	signal = enterDisplayElement(new TextElement(SignalColor), group[0]+group[1]);
    	int t = enterTiming(SOATimer, SOADuration, 0);
    	probe = enterDisplayElement(new TextElement(ProbeColor), group[1]);
    	t = enterTiming(Timer, Duration, ResponseSet, t, ResponseTime, ResponseCode);
    	return(signal);
        }
    

    The intention of this display is to first show a single line of text ("signal") and then after a certain SOA to add another line of text. This display contains two timing groups: the first group only contains the signal text and the second group both contains the signal and the probe text. Note that every timing group has its own timer and duration parameters. Only the second timing group explicitly sets its response parameter names since the response will be connected to the second timing group. No special drawing code needs to be written. The usual geometry parameters are set by the computeGeometry() method:

        protected void computeGeometry() {
    	String fnn = FontFamily.getString();
    	int fnt = FontStyle.getInt();
    	TextElement txt;
    	txt = (TextElement)getDisplayElement(probe);
    	txt.setFont(fnn, fnt, ProbeSize.getInt());
    	txt.setLocation(ProbeLocationX.getInt(), ProbeLocationY.getInt());
    	txt.setText(ProbeText.getString());
    	txt = (TextElement)getDisplayElement(signal);
    	txt.setFont(fnn, fnt, SignalSize.getInt());
    	txt.setLocation(SignalLocationX.getInt(), SignalLocationY.getInt());
    	txt.setText(SignalText.getString());
        }
    

    Animated Displays

    Display objects may be animated. Animations run in their own thread which is controlled by the realtime display controller. A display which may be animated has to tell this by overriding the method isAnimated():

        public boolean isAnimated() {return(true);}
    

    Animation is done frame by frame where a single frame corresponds to a single image of the animation movie. Animations usually are cyclic and the display tells the superclass how many frames constitute a single display cycle by using method

        public void setFramesPerCycle(int fpc)
    

    This is usually done from within a display's create() method. Animation speed may be controlled by timing parameters. Here is an example of the MovingDot() class:

        private int dot;
    
        protected int create() {
    	dot = enterDisplayElement(new Oval(Color), group[0]);
    	int t = enterTiming(OnOffTimer, OnDuration, 0);
    	enterDisplayElement(new Clear(), group[1]);
    	enterTiming(OnOffTimer, OffDuration, t);
    	setFramesPerCycle(2);
    	return(dot);
        }
    

    It contains two display elements: a dot and a clear screen object which belong to different timing groups and the timing group timer and duration parameters determine the duration of a single frame. Computing the geometric properties of this display is simple and does not differ from displays which are not animated:

        private int leftX, rightX, dotSize;
    
        protected void computeGeometry() {
    	int d = Distance.getInt();
    	dotSize = Size.getInt();
    	leftX = -d/2;
    	rightX = d/2;
    	getDisplayElement(dot).setCenterAndSize(leftX, 0, dotSize, dotSize);
        }
    

    The only additional method which must be provided by the animated display's own class is the method

        public void computeAnimationFrame(int frame);
    
    which has to recompute the geometric properties of the display such that it shows the given frame number. In our example the successive frames differ only by the horizontal position of the dot:
        public void computeAnimationFrame(int frame) {
    	getDisplayElement(dot).setCenterAndSize((frame == 0)? leftX: rightX, 0, dotSize, dotSize);
        }
    

     

  35. Design File Grammar
  36. This chapter describes the grammar of a PXLab design file. The grammar is defined by a language definition file for the JavaCC parser generator. This parser generator is used to automatically generate the parser for the PXLab design language. Design files are ASCII files and may be edited by any ASCII text editor. Design files may also be created by the PXLab Experimental Design Editor. An automatically generated BNF-version of the grammar is available too.

    Problems with the design file grammar may best be analyzed with the parser debugging option: '-D parser'. If this option is given at runtime then a complete parser protocol is generated.

    Keywords

    The grammar uses some key words which may not be used as names since these represent grammar tokens. These are: adjustable, AssignmentGroup, Block, BlockData, BlockEnd, Condition, ConditionTable, Context, CovariateFactor, DependentFactor, Experiment, ExperimentData, FactorLevel, Factors, false, IndependentFactor, new, Procedure, ProcedureData, ProcedureEnd, RandomFactor, Session, SessionData, SessionEnd, Trial, TrialData, true.

    Productions

    Start Symbol

    A design file is a declaration of the experiment:

       experimentDeclaration ::= 
          "Experiment" parameterIdentifiers "{"
             contextDeclaration 
             ( factorsDeclaration )?
             ( procedureDeclaration )?
          "}"
    

    The experiment declaration may have an argument list and contains context, factors, and procedure declarations in its body.

    Context Declaration: Display Lists and Global Assignments

    The runtime context defines and creates global parameter values and defines display lists for blocks, sessions, and trials.

       contextDeclaration ::= 
          "Context" parameterIdentifiers "{"
             ( 
    	    (   displayListDeclaration
                   | assignmentGroupDeclaration
                )
             )* 
          "}"
    

    The context declaration may also have arguments and contains a body with display list declarations and groups of parameter assignments.

    Display list declarations define what actually happens during a trial and during block start/end and session start/end events. Display objects are PXLab's primitive units of displays. A single trial contains a sequence of display objects which make up everything which is presented to the subject. Display object properties are controlled by parameters. The sequence of display objects is controlled by the display list declaration.

       displayListDeclaration ::= 
          (
             "ExperimentData" 
             "Procedure" 
    	 | "ProcedureEnd"
    	 | "ProcedureData"
             | "Session" 
    	 | "SessionEnd"
    	 | "SessionData"
    	 | "Block"
    	 | "BlockEnd"
    	 | "BlockData"
    	 | "Trial"
    	 | "TrialData"
          ) (":" Identifier )? displayListParameterIdentifiers "{" 
             ( displayDeclaration )*
          "}"
    

    There are 12 possible display list types: trial, trial data, block start/end/data, session start/end/data, procedure start/end/data and experiment data. Multiple display lists of a single type may be defined by adding an optional identifier postfix to the display list type name. Every list declaration may have arguments and contains a list of display declarations in its body.

       displayDeclaration ::= 
          displayIdentifier displayListParameterIdentifiers
             ( ";" 
             | ( 
                "{" ( parameterValueAssignment )*
    	    "}" 
    	   )
    	 )
    

    A display component has a name and an argument list. The name must be unique within the display list. It must be a Displayor a DataDisplay object subclass name possibly followed by an instance identifier.

       displayIdentifier ::= 
          Identifier ( ":" Identifier )?
    

    The display component may or may not have a body. The body, if it exists, contains a list of parameter assignments. These must be parameters of the display object. The display object name is the class name of the respective display. A parameter assignment usually assigns a value to an experimental parameter. It also may create a new parameter and may declare a parameter as an adjustable variable. Note that new parameter declarations are allowed in assignment groups only.

       globalParameterValueAssignment ::= 
          ( "new" )? parameterValueAssignment
       displayParameterValueAssignment ::= 
          ( "adjustable" )? parameterValueAssignment
       parameterValueAssignment ::= 
          Identifier ( "." Identifier )* "=" parameterValue ";"
    

    Global parameter assignments are grouped together in assignment groups.

       assignmentGroupDeclaration ::= 
          "AssignmentGroup" parameterIdentifiers "{" 
             ( globalParameterValueAssignment )* 
          "}"
    

    We have not yet defined parameter identifier lists. These are lists of experimental parameter names.

       parameterIdentifiers ::= 
          "(" ( parameterIdentifier ( "," parameterIdentifier )* )? ")" 
    
       parameterIdentifier ::= 
          ( displayListIdentifier "." displayIdentifier "." )? Identifier 
    

    An experimental parameter is named according to the field name in its class. Parameters of display objects used outside of the display declaration have to be fully qualified by the display list and the display names as a prefix. The display list prefix may be omitted when the display parameter is used as an argument of a display list declaration.

       displayListParameterIdentifiers ::= 
          "(" ( displayListParameterIdentifier ( "," displayListParameterIdentifier )* )? ")" 
    
       displayListParameterIdentifier ::= 
          ( ( displayListIdentifier "." )? displayIdentifier "." )? Identifier 
    

    Experimental Factors and Conditions

    The factors section is optional. It may be used to define experimental factors, factor levels and associate these with experimental display parameters. These associations are defined via a condition table. The factors declaration simply defines a list of factors and possibly a condition table. Note that there may be only a single condition table although the grammar does not control this.

       factorsDeclaration ::= 
          "Factors" parameterIdentifiers "{"
             ( ( factorDeclaration | conditionTableDeclaration ) )*
          "}"
    

    The declaration of a factor specifies its name and possibly its associated experimental parameters as an argument and its list of possible factor levels in a block. There are three different types of factors: independent, dependent, and covariate factors.

       factorDeclaration ::= 
          ( "RandomFactor"
             | "IndependentFactor" 
             | "DependentFactor"
             | "CovariateFactor"
          ) parameterIdentifiers
          ( ";"
    	| "{" ( factorLevelDeclaration )*
    	  "}" 
          )
    

    A factor level is a parameter value possibly followed by values of associated experimental parameters.

       factorLevelDeclaration ::= 
          "FactorLevel" parameterValues ";"
    

    The declaration of a condition table specifies the factor names and their associated experimental parameters as arguments and the list of possible conditions in a block.

       conditionTableDeclaration ::= 
          "ConditionTable" parameterIdentifiers
          ( ";"
    	| "{" ( conditionDeclaration )*
    	  "}" 
          )
    

    A condition declaration is a list of factor level values combined with the values of the associated experimental parameters.

       conditionDeclaration ::= 
          "Condition" parameterValues ";"
    

    Procedure, Sessions, Blocks, and Trials

    The procedural units sessions, blocks, and trials are collected together in the procedure declaration. These, however, are preceded by a section which defines the runtime arguments of sessions, blocks, and trials.

       procedureDeclaration ::= 
          "Procedure" parameterValues "{"
             ( sessionDeclaration )*
          "}"
    

    Session, blocks, and trials all have local parameter values. Blocks are nested into sessions and trials are nested into blocks.

       sessionDeclaration ::= 
          "Session" ( ":" Identifier )? parameterValues "{" ( blockDeclaration )* "}"
    
       blockDeclaration ::= 
          "Block" ( ":" Identifier )? parameterValues "{" ( trialDeclaration )* "}"
    
       trialDeclaration ::= 
          "Trial" ( ":" Identifier )? parameterValues ";"
    

    Procedure units which have an identifier postfix require a corresponding display list definition.

    The argument value lists of factor levels, conditions, sessions, blocks, and trials are values of experimental parameters.

       parameterValues ::= 
          "(" ( parameterValue ( "," parameterValue )* )? ")"
    

    Parameter Values

    The rest of the grammar description is concerned with methods to express parameter values.

       parameterValue ::= 
          ( defaultValue
            | array
            | replicator
            | expression
          )
    
       defaultValue ::=
          "?" 
    
       array ::= 
          "[" constantValueList "]"
    
       replicator ::=
         "<" constantValueList ">"
    
       constantValueList ::= 
          constantValue ( "," constantValue )* 
    
       constantValue ::= 
          valueIdentifier | signedLiteral
    
       valueIdentifier ::= 
          ( ( ( "de.pxlab." | "java." | "javax." ) 
              ( Identifier "." )* 
            ) | ( displayListIdentifier "." displayIdentifier "." )
          )? Identifier
    
       signedLiteral ::=
          ( "+" | "-" )? literal
    
    

    Experimental parameter values may in some cases be undefined. The question mark is used to indicate this. All experimental parameters actually are array valued, however, single valued expressions may also be used. Array valued parameters may only contain constant values as their components. These are class constants defined as static integers in a Java class or are possible signed literal constants.

    PXLab allows simple arithmetic expressions. This includes addition and multiplication and there also is a conditional expression for assigning conditional values to a parameter. The condition must be an experimental parameter too. Note that expression evaluation is done at runtime!

       expression ::= 
          conditionalOrExpression ( "?" expression ":" expression )?
    
       conditionalOrExpression ::= 
          conditionalAndExpression ( "||" conditionalAndExpression )*
    
       conditionalAndExpression ::= 
          inclusiveOrExpression ( "&&" inclusiveOrExpression )*
    
       inclusiveOrExpression ::=
          exclusiveOrExpression ( "|" exclusiveOrExpression )*
    
       exclusiveOrExpression ::= 
          andExpression ( "^" andExpression )*
    
       andExpression ::=
          equalityExpression ( "&" equalityExpression )*
    
       equalityExpression ::=
          relationalExpression ( ( "==" | "!=" ) relationalExpression )*
    
       relationalExpression ::=
          shiftExpression ( ( "<" | ">" | "<=" | ">=" ) shiftExpression )*
    
       shiftExpression ::= 
          additiveExpression ( ( "<<" | ">>" | ">>>" ) additiveExpression )*
    
       additiveExpression ::= 
          multiplicativeExpression ( ( "+" | "-" ) multiplicativeExpression )*
      
       multiplicativeExpression ::= 
          unaryExpression ( ( "*" | "/" | "%" ) unaryExpression )*
    
       unaryExpression ::= 
          ( ( "+" | "-" | "~" | "!" ) unaryExpression ) | primaryExpression
    
       primaryExpression ::= 
          literal 
          | Identifier parameterValues
          | valueIdentifier
          | "(" expression ")"
    
       literal ::=
          INTEGER_LITERAL 
          | FLOATING_POINT_LITERAL 
          | STRING_LITERAL 
          | "true" 
          | "false" 
    
    

    Integer, floating point, and string literals are defined as usual. An identifier followed by parameter values is a function call. Identifiers start with a letter or an underscore character and may have arbitrary length.