Timing scriptsGeneral Task Design General Task DesignTiming scripts are the lines of code which actually determine when and how things occur during a trial of the task (example). A timing script is run once for each trial and determines all of the events and contingencies possible during that trial. Timing scripts generally operate independently of the specific stimuli used in a particular trial; they refer to these stimuli only by the column in which they appear in the conditions file. This allows a single timing file to be used across many different conditions which share identical temporal structure, but differ in their specific stimuli. The particular condition being run is determined by the options selected in the main menu before a timing script is called. The conditions file determines which timing script is executed once a condition has been selected (different conditions can be associated with different timing scripts). The diagram above shows the basic sequence of a standard two-alternative, forced-choice "delayed-match-to-sample" task (see also Example Task), in which the subject is presented with one visual stimulus and must remember it over a short delay in order to select it from among one or more distractors later. Much more complex tasks are possible in MonkeyLogic; however this very simple one allows a depiction of the basic process of constructing a task's timing script. In this particular implementation, the subject's eye movements will be monitored, and he or she must fixate on a central dot throughout the entire trial. When the choices are presented near the end of the trial, one is selected simply by saccading to it (looking at it). This diagram shows the bare-bones steps needed to construct the example delayed-match-to-sample ("DMS") task. The functions used for each step are shown in italics. Toggleobject is the primary function for presenting visual stimuli, turning on TTLs or analog outputs, etc. The eyejoytrack function is used to determine if the eye position (or joystick position, see below for function details) is where it is expected to be. The output of this function therefore determines whether to continue through the task or abort the trial (by setting the "trialerror" to the desired value and issuing a "return" statement). In addition to these basic elements, event-markers should be used to time-stamp behaviorally-relevant events; these allow post-hoc alignment of neural data with the appropriate triggers. An event-marker is simply a number which corresponds to a descriptive string explaining the time-stamped event. A timing script should assign a trial error value (using the TrialError function) to reflect the subject's performance on that trial. At the end of each trial , the value of the variable "rt" is used to update the reaction time bar graph in the control screen. This can be returned from the eyejoytrack function or it can be assigned manually. Timing scripts can, for the most part, use any valid MATLAB command and should be written as any standard MATLAB script. There are a few important guidelines which must be followed, however: Do not declare the script as a function. Any return command should appear separately (on its own line).
Trial-Specific FunctionsA timing script can use almost any valid MATLAB expression, and in addition can use any of these trial-specific functions: Editable This function displays static visual stimuli, presents movies, plays sounds, and delivers analog and TTL stimulation. Syntax: [fliptime framenumber] = toggleobject(object_number(s), [parameter, value])
Example: To turn on two images, stored in TaskObjects 3 and 4, one could do the following: pic1 = 3; Here, both pic1 and pic2 will appear simultaneously (during the same screen flip) because they were specified in one toggleobject command, and the time of that screen flip will be time-stamped with a code 23. The 'status' option is only necessary if there is any uncertainty about the status of one of the objects (e.g., if it were possible that one of them were already on, in which case that object would be toggled off). This function is also used to turn movies on and off. For example, if a movie object is in TaskObject 2: mov = 2;
Note that this command initializes the movie and puts up the first frame. However, the movie will not start playing until either the idle command or the eyejoytrack command is called, as these functions are responsible for "passing time" during a task. So to play the movie for "duration" ms, one would need a sequence such as: toggleobject(mov); Here, the movie will play until either duration ms has elapsed, or the subject acquires fixation on targetobject (see eyejoytrack, below, for this function's syntax and options). More than one movie can run simultaneously. A warning message will indicate if there are any skipped frames. To play the movie backwards at half-speed: toggleobject(mov, 'MovieStep', -0.5); If a movie is toggled off, then later toggled back on, it will resume playing from the frame at which it left off, unless the MovieStartFrame has been explicitly specified. Note that manually-set MovieStep and MovieStartFrame parameters of any particular movie object will persist across subsequent presentations of that object in that trial (but not across trials). The optional output variables fliptime and framenumber can be used to double-check video-timing, if necessary. These outputs are used internally by eyejoytrack to properly determine movie-presentation timing.
This function time-stamps a number corresponding to a behavioral code, to identify the times of behaviorally-relevent events for post-hoc data analysis. This number and its associated time-stamp are saved on the local machine running MonkeyLogic in the BHV file. In addition, this number is sent via a digital output port to another machine (e.g., a neural data acquisition system) if the behavioral codes and codes strobe I/O settings are have been defined in the main menu. Six event markers are always present within each trial; three code 9s and three code 18s at the beginning and end of every trial, respectively. These are to allow fail-safe re-alignement of a behavioral file and a separately acquired physiology file in the event of intermittent communication failure between the behavioral and neural data acquisition computers. These two codes, therefore, are reserved and should not be used in the the timing script. Syntax: eventmarker(codenumber(s))
Example: eventmarker([22 48]); Reserved Eventmarkers:
This function is used to track eye- and joystick position with respect to specified targets (i.e., visual stimuli presented using the toggleobject function), and to track button presses or lever position. In the case of eye and joystick signals, a target can determine a point where the subject must hold fixation or the joystick cursor for a specified length of time, or a target can determine a point to be acquired within the specified amount of time. In the case of buttons or levers, the target is a threshold input level (i.e., an adjustable threshold voltage for analog inputs or a fixed threshold value of 0.5 for digital inputs). To use this function, one must specify whether the goal for the subject is to hold fixation, hold a joystick target, acquire fixation, acquire a joystick target, hold-touch, or acquire-touch (see table, below). In addition to specifying the goal (i.e., the eyejoytrack subfunction), one must also specify the target (i.e., the TaskObject that is the focus of the action), the threshold (a radius in degrees of visual angle for eye- or joystick signals, or an input level for buttons or levers), and the time required or allowed. Note that eyejoytrack will work regardless of the current visibility of visual target objects (i.e., a visual target does not need to have been turned on using toggleobject); this allows the creation of targets that are invisible, yet are the nevertheless the focus of the desired action (e.g., allowing for memory-guided rather than visually-guided movements).
In the case of holding fixation or holding a joystick target, this function will continuously check eye or joystick position throughout the specified interval. If this position falls outside the specified radius before the total specified time has elapsed, the output will return "0" reflecting failure to hold fixation or hold target. If the entire time elapses and the subject has stayed within the specified bounds, the output will be "1." A second output, rt, will reflect the elapsed time (i.e., the fix / target break time, or reaction time). In the case of acquiring fixation or acquiring a joystick target, this function will continuously check eye or joystick position until the target object has been acquired or until the specified time has elapsed. If the subject acquires fixation or the joystick target before the specified time has elapsed, the output will be non-zero (and a second output-argument, rt, will reflect the elapsed time, i.e., fix / target acquisition time). If, on the other hand, the target object is not acquired during that interval, the output will be zero. Note that for fixation or target acquisition, multiple TaskObjects can be specified as potential targets; here, the output will reflect which target (by ordinal number of its specification) was acquired. In other words, if targets [3 4 5 6 7] are specified, and the subject lands on target #5, the output of eyejoytrack will be "3" because #5 is the third object in the list. In the case of acquiring "touch" (e.g., button press), eyejoytrack will return a "1" when the button input voltage rises past the given threshold (specified in-place of the "radius" argument, below), and a "0" if this threshold is never reached before the maximum duration expires. Conversely, "holdtouch" will return early with a "0" if the button voltage falls past the specified threshold before the maximum time expires, or "1" at the end of that duration if the voltage stays above the threshold for the entire time. Note that when transitioning between an "acquire" and a "hold" (i.e., the subject moves into a target which must then be held for a some time), a small amount of idle time (e.g., 50ms) is useful to allow settling of the signal wholly within the target. In other words, given a noisy signal which just begins to cross a threshold, the noise may cause that signal to momentarily drop below the threshold even though there is a clear over-all trajectory beyond it. This transient dip may be regarded as a break-fixation or break-target unless adequate time is allowed for the signal to move further into the target region. Syntax: [ontarget rt] = eyejoytrack(fxn, object_number, threshold, duration)
Examples: To wait for the acquisition of a target by movement of a joystick to within R degrees within a maximum of T time: ontarget = eyejoytrack('acquiretarget', target, R, T); If no target is acquired, "ontarget" will be zero. Otherwise, it will be the index into target of the object that was acquired. For example, if target = [3 5 7] and ontarget returns equal to "2", this indicates the subject acquired the second item in the list, TaskObject #5, by moving to within R degrees of its center, in less than T milliseconds. Alternatively, to require fixation on a fixspot within radius R degrees for a duration of T time: ontarget = eyejoytrack('holdfix', fixspot, R, T); Here, "ontarget" will be zero if fixation is broken before time T has elapsed, or one otherwise. Lastly, to do both simultaneously: ontarget = eyejoytrack('holdfix', fixspot, R1, 'acquiretarget', target, R2, T); In this case, "ontarget" will be a two-element vector specifying the results of each tracking operation in the order they were specified.
This function declares variables as parameters that are available for modification on-line (i.e., during task execution). To modify variables that have been declared as editable, pause the task (by pressing the [escape] key), and then press [v] as prompted. A menu will appear listing all the variables available for editing and their current values. Click on a value box to edit it. Syntax: editable('varname1', 'varname2', 'varname3', ... ) or editable({'varname1' 'varname2' 'varname3' ...})
Examples: To declare two variables, here called var1 and var2 , as editable: editable('var1', 'var2'); Suppose one wanted to make a variable used by a separate user function, such as a block-selection function, editable. In the timing script, declare: editable('var1'); Then, in the block-selection function, write: var1 = initialvalue; As shown here, var1 must be given an intial value in the block-select function because that function can be called before the first execution of the timing script. In addition, because the timing script has not yet been run, the field containing the editable variable may not yet exist, so the conditional is necessary. Note that changes to var1 may not take effect in the block-select function (or any similar user functions) until after the subsequent trial.
This function assigns user-specified activities to keyboard events during task execution. In other words, the press of a key during a trial will execute the callback function provided by the user. hotkey(key, fxn)
Example: To assign a long reward delivery event to the letter "t": hotkey('t', 'goodmonkey(500);'); This will deliver one half-second worth of reward every time the "t" key is pressed. Note that any argument to the callback function - here "500" - must be explicit, as noted above. And remember that certain hotkeys are predefined and should not be overriden. (See Predefined Hotkeys). This function allows a specified amount of time to elapse without placing requirements on eye- or joystick-position. During this time, eye- and/or joystick position will be shown on the control-display (this feature is what differentiates this function from built-in MATLAB functions such as "pause"). In addition, a second argument can be provided to determine the subject screen's background color during this interval (useful for "time-out" cues, etc.). idle(duration, [screen_color])
Example: To allow 1 second to pass during which the screen's background color is red (e.g., for a "time-out"): idle(1000, [1 0 0]);
This function toggles the subject's joystick cursor on or off. showcursor(['on' | 'off'])
Example: To turn on the joystick cursor: showcursor('on');
This function sets a path through which a video stimulus (static image or movie) is to be translated. success = set_object_path(object_number, xpath, ypath)
Example: To move an object in a counter-clockwise 3-degree circle over 1 second with a video refresh rate of 100 Hz pic = 1; Two parameters of toggleobject can by used to modify the starting position, and the direction and number of steps to move along the path at each video frame: 'StartPosition' and 'PositionStep'. For example, to move this picture in a clockwise rather than a counter-clockwise path, and with twice the speed: toggleobject(pic, 'PositionStep', -2); Here, two rotations will occur because the image is moving twice as fast, but the "idle" time remained 1 second. Note that PositionStep may be fractional, but StartPosition must be a positive integer index into the path vectors. A warning message will indicate if any frames are missed. If a visual stimulus is toggled off, then later toggled back on, it will resume movement along the path from the point at which it left off, unless the StartPosition has been explicitly specified. In addition, manually-set StartPosition and PositionStep parameters will persist across subsequent presentations of the same object until they are manually changed.
This function sets the desired duration of the next inter-trial interval. set_iti(duration)
This function moves a TaskObject to a new screen position. The object can be visible at the time of the move (i.e., activated by toggleobject), or it can be invisible. success = reposition_object(object_number, new_xpos, new_ypos)
Example: To move TaskObject #3 from where-ever it currently resides to a point in the upper-left quadrant: success = reposition_object(3, -5, 5);
Returns the time (in milliseconds) since the beginning of the trial. [t, framenumber] = trialtime
Returns the scancode of the first key which is pressed during the specified interval. scancode = getkeypress(maxtime)
Delivers reward by sending analog or TTL pulses to the device specified in the I/O menu. A solenoid for juice delivery is assumed to be the final actuator, but anything that can be activated by 5V signal pulses is acceptable. goodmonkey(duration, ['Parameter', 'Value'...])
Examples: To give three pulses of 50 ms reward: goodmonkey(50, 'NumReward' 3); To increase the pause-time between these pulses to 100 ms (from the default of 40 ms): goodmonkey(50, 'NumReward', 3, 'PauseTime', 100); To set the output voltage to 2.5V (from the default of 5V): goodmonkey(50, 'TriggerVal', 2.5);
Returns the x & y position of the eye-signal. [x y] = eye_position([screen_update_flag])
Returns the x & y position of the joystick. [x y] = joystick_position([screen_update_flag])
Returns one or more data points from an analog-input device. Syntax: [analogdata frq] = get_analog_data(signal, duration)
The TrialError function assigns a "trial error" value for the current trial. A single trial error value ranging from 0 to 6 (or the corresponding string) should be assigned to every trial to describe the subject's performance (i.e., success or lack thereof) on that trial. Trial errors are used in two important ways. First, they can aid behavioral analysis by providing a straightforward way to select those trials meeting a specific behavioral criterion (e.g., to look only at "correct" and "incorrect" trials while ignoring other sorts of errors, such as "break fixation"). Second, they are often necessary to direct the selection of subsequent conditions. If, for example, the "on error" option within the main menu is set to "repeat immediately", any trial that is not correct (i.e., not assigned a zero) will be followed by another trial of the same condition. If no trial error is assigned by the timing script, a value of 9 is assigned by default. Syntax: trialerror(number)
A trialerror = 9 will be assigned if no trialerror is assigned by the timing script.
This function displays a line of text to the control screen. user_text(text)
The TrialRecord StructureTrialRecord is a data structure, updated every trial, containing information about a subject's behavior, the blocks and conditions played, and other information potentially relevant to a user's need to control the flow of the task, choose or create stimuli, or display information. Functions that take the TrialRecord structure as input include:
TrialRecord contains the following fields:
Changes can be made to the TrialRecord structure by the timing script (e.g., adding a new field and assigning it a value). These changes will be maintained across trials and will be included in the TrialRecord structure as it is passed to other functions (for example, to block-change or block-selection functions, etc). However, any changes to TrialRecord made by those functions will not persist outside those non-timing script functions or across trials within those functions. Adding fields to TrialRecord is a useful way to allow variables used in those other functions to be editable. For example, in the timing script, one might write: editable('var1'); Then, in the separate user function (e.g., block-selection, condition-selection, block-change, or user-generated image (gen) function): if ~isfield(TrialRecord, 'var1'), The conditional statement is necessary if the user function can be called prior to the first execution of a trial (i.e., the first call to the timing function). This way, var1 will appear in the "edit variables" menu brought up by pressing [v] when the task is paused, and changes to this variable will be forwarded to all other functions that take "TrialRecord" as an input.
|
This site last updated: May, 2014 | © 2008-2014 |