Revised:
Expo is extensible through the addition of routines and analysis functions.
Each routine in a running state is managed by four functions:
Startup function. This is called once when the state begins and is used to initialize any conditions that the routine needs to control. For example, the routine to generate a texture uses its startup function to allocate memory for the textue. The state is not actually set running until startup functions for all routines have been called. Even if nothing needs to be initialized at startup, this function is required.
Continuation function. This is called on each timebase tick while the state is in its active phase (after any initial delay), and is the action function for the routine. Even if nothing needs to be done on each tick, this function is required.
End function. This is called immediately before the state stops its active phase (before any pause), and is used to do any cleaning-up required by the routine. This function is optional.
Capture function. This is called just before Expo captures the state of the routine (values of parameters, etc.) when making a routine event. This enables the routine to do any special housekeeping it needs to set the correct values in the parameters that will be recorded. This function is optional. Few routines need it.
When Expo calls each of these functions, it passes a pointer to a Routine class instance that contains run-time information that the function will need. The Routine instance does not contain all the information the execution functions might need (e.g., information about the maximum and minimum values of that can be assigned to parameters). The additional information is contained in a counterpart RINFO structure that defines the routine and its parameters, and information about special hardware requirements, etc.
The best and simplest way to add a routine to Expo is via a plugin. You create a plugin as an Xcode project. Xcode compiles the code into an executable module that Expo loads at runtime. A compiled plugin is an instance of an EXPlugin class. This class instance provides Expo with these elements:
The Expo distribution contains a Xcode projects (derived from an initial design by Ben Singer) for two model plugins. One is very simple, the other complex. You can adapt either of these to produce a routine that has the capabilities you need. Notes accompanying the distribution explain how to customize the projects.
When Expo starts it looks in the following places for plugins to load:
As Expo loads (or fails to load) each plugin it logs it on the Console.
The following sections provide detailed descriptions of the various components of a routine.
A filled out RINFO structure contains the information Expo needs to manage a routine. RINFO is defined in routinedefs.h as:
typedef struct { /* structure for routine */ short rid; /* id # (unique; used in refs from program) */ char *rname; /* routine name */ unsigned long bufsize; /* size of workspace */ unsigned long iflags; /* miscellaneous flags */ void (*start) (Routine *, void *); /* start function */ void (*cont) (Routine *, void *); /* continuation function */ void (*capture) (Routine *, void *); /* accessory data capture function */ void (*end) (Routine *, void *); /* end function */ short npars; /* number of parameters */ PINFO pars[PARMAX]; /* array of parameter structures */ NSString * helpanchor; // help anchor }RINFO;
rid A unique id number that identifies the routine. Expo refers to routines internally by id number.
The id of a new routine must be above 1000. This ensures that there will be no conflict between your routines and any new ones added to the source distribution. Expo will not load a plugin contining a routine whose id is less than 1000.
An id assigned to a routine should never be changed. If you change an id, previously saved programs will not work, and the contents of data files derived from any use of it will be inaccessible. |
rname A pointer to a character string that names the routine. This name appears in sheets and menus.
bufsize The size (in bytes) of any private workspace needed by the routine. Private workspace whose size is fixed is best defined by a structure; bufsize can be set to sizeof(the structure). See the example in the model plugin. The workspace is allocated before a program is run and is automatically freed afterwards. If a routine needs private memory whose size can't be known before a program starts, you can use the Routine method -getroutinememory to allocate it in the startup function (making sure that the function allocates memory only once).
iflags This contains miscellaneous flags (defined in routinedefs.h) that determine some of the routine's capabilities and hardware requirements. Some of the flags are obsolete.
RI_RECORDENABLE The default state of the routine is to enable recording of its parameter values (the Record check box is checked when the routine entry sheet is displayed).
RI_STOPENABLE The routine can assert a STOP signal, and the Assert STOP check box is enabled (but not checked). Otherwise the check box is dimmed.
RI_DONEENABLE The routine can assert a DONE signal, and the Assert DONE check box is enabled (but not checked). Otherwise the check box is dimmed.
RI_TOKENENABLE The routine can generate a display token.
RI_PERSISTENABLE The action undertaken by the routine can persist beyond the end of the state, and the Persist check box is enabled (but not checked). Otherwise the check box is dimmed.
RI_ISDEPRECATED A routine that can be used in a program but has been superseded by another routine with greater capabilities
RI_NEEDSDIGITALINPUT The routine cannot function without a ditital I/O device.
RI_ISDEPRECATED The routine can be used but has been superseded; its name does not appear in the routine menu.
RI_NEEDSANALOGINPUT The routine needs analog sampling enabled.
RI_NEEDSVIDEO The routine needs a Macintosh color monitor.
RI_NEEDSFRAGMENTSHADER The routine requires video hardware that supports texture fragment shading.
RI_NEEDSSOUND The routine needs to use a Macintosh sound channel. Obsolete.
RI_NEEDSANALOGOUTPUT The routine needs a device that provides analog output.
RI_NEEDSSPIKEINPUT The routine needs an active spike template.
RI_NEEDSINF The routine needs an interferometer and associated hardware (obsolete).
RI_NEEDSTRAPPEDEVENTS The routine reads the keyboard/mouse/wheel.
RI_NEEDSMOUSE The routine uses the mouse
RI_ISRESOURCE The routine uses a resource
RI_ISOBSOLETE The routine is obsolete
RI_HASFREQUENCYSOURCE A parameter provides a frequency source
RI_HASTIMEDELAYSOURCE A parameter provides a time delay source
RI_NEEDSDIGITALOUTPUT The routine needs a device that provides digital input
(*start) (Routine *) A pointer to the startup function.
(*cont) (Routine *) A pointer to the continuation function.
(*capture) (Routine *) A pointer to an (optional) auxiliary data capture function. If you need no auxiliary function (most routines do not), set the pointer to NULL.
(*end) (Routine *) A pointer to an (optional) end function. If you need no end function (most routines do not), set the pointer to NULL.
npars The number of parameters the routine uses. Routines can have an unlimited number of parameters, each of which has associated with it a PINFO object that defines units, limits, default values etc. (see below).
pars[PARMAX] This is an array of PINFO objects, each of which contains the information that defines a single parameter. PARMAX (currently 12) is defined in etype.h. PARMAX is defined only for purposes of initializing RINFO structures, and for managing the interface for entering parameters in routines (in EditWindow.nib). It can be set to whatever value is needed to accommodate the parameters in the largest Routine in Expo. If you need to increase PARMAX, be sure to add corresponding interface elements and connections to the routine sheet in the EditWindow.nib file that manages the editing window.
helpanchor. A string that describes the Help system anchor for help on the routine.
typedef struct { /* structure for info about parameter */ char *parname; /* name of parameter */ unsigned long flags;/* misc flags (e.g. reading/writing on variable; asserting event) */ char * mitems; /* compound string of names for menu */ short ucapture; /* unit in which data captured */ short udefault; /* default unit of expression */ long ubase; /* type of base unit of measurement (also used as ResType) */ float min; /* lower bound in base unit */ float max; /* upper bound in base unit */ float ivalue; /* default initial value */ }PINFO;
This structure contains Expo's definition of a parameter, and is defined in routinedefs.h. An initialized array of these structures is required for each routine; this array is the pars member of the RINFO structure that contains the full description of a routine. For each PINFO in the RINFO structure, there is a corresponding Param class instance in the run-time Routine class instance.
parname A character string that contains the name of the parameter. This is used in sheets and menus.
flags This contains miscellaneous flags (defined in routinedefs.h):
P_RESOURCE The parameter uses a resource. Expo uses this flag to determine whether the name in the vname field of the corresponding PARAM structure defines a resource or a variable.
P_WW The parameter writes to a variable.
P_NEEDSVAR The parameter must be a variable (not a constant).
P_ITEMMENU The parameter must be chosen from a menu defined by the list of items in mitems (below).
P_ISTIMESOURCE This flag is used to identify a parameter that can provide a time signal for analyzing data (e.g., to determine the frequencies to examine in doing Fourier transforms).
P_NEEDSINT The parameter must have an integral value.
P_INTINBASE The parameter must have an integral value in its base unit. This is used in unit conversions. If this flag is set Expo will ensure that the parameter's value in its base unit is always an integer.
P_CHARITEM The parameter is a character string. The string itself is stored as the parameter's value, which is defined as a double, so a cast is required to get access to it as a string.
P_MAXMINWRAP The minimum allowable value of the parameter maps to the maximum value (e.g., 360 degrees) so an out of range value (which could be generated through a variable at run time) can be put in range by adding the range to a value that is too low, or subtracting the range from a value that is too high. Expo makes the conversion automatically if this flag is set; otherwise potentially out of range values are clipped to the min or max.
P_XSCREEN The parameter value has an upper limit equal to the width of the current screen (in pixels). The limit is set dynamically on selection of the active monitor.
P_HXSCREEN The parameter has limiting values equal to ± half the width of the current screen (in pixels). The limits are set dynamically on selection of the active monitor.
P_YSCREEN The parameter value has an upper limit equal to the height of the current screen (in pixels). The limit is set dynamically on selection of the active monitor.
P_HYSCREEN The parameter has limiting values equal to ± half the height of the current screen (in pixels). The limits are set dynamically on selection of the active monitor.
P_ISSTIMTRACE The parameter can provide a stimulus trace signal for analysis routines.
P_ISTRIGSTART The parameter can provide a time marker to which an analysis can be keyed.
mitems If the parameter is to be set by choosing from a menu (P_ITEMMENU is set in flags), the string defined here contains the list of item names as a compound string, terminated by two NULLS.
ucapture This defines the unit it which the parameter value is captured when it is recorded. The symbolic names of units are defined in unit.h. The capture unit would (should) normally be the same as the base unit, and is defined separately only because some early-written routines in Expo captured parameter values in units that did not always match the base unit.
udefault This defines the default unit it which the parameter is expressed for display to the user.
ubase This defines the base unit in which the parameter value is maintained. All arithmetic on parameters, and the tests for limiting values, is undertaken in the base unit. If the parameter is a resource (P_RESOURCE is set), this value defines the resource type.
min This defines the minimum value of the parameter in the base unit. This, and the following max value, are usually constants, but for some parameters (e.g., ones that specify spatial frequency in cycles/deg., and whose max and min values appropriately depend upon viewing distance), the max and min values are set at run-time, through the function unit_scaleforenvironment(), in unit.m.
max This defines the minimum value of the parameter in the base unit.
If a parameter's max and min values are the same, Expo will do no range checking on the parameter's value, or on the value of a variable defined by it. |
ivalue This defines the default value of the parameter in the base unit.
A Routine is a subclass of NSObject; all the methods that manage Routines are in Routine.m (with declarations in Routine.h). Its instance variables are:
@interface Routine : NSObject <NSCoding,NSCopying> { RINFO * rinfo; /* pointer to info structure */ short rid; /* identifier */ short rflags; /* flags (e.g. assert event) */ void * gptr; //special memory pointer void * wspptr; // workspace DISPLAYTOKEN token; // display token NSString * rlabel; // routine label NSMutableArray * pbase; /* array of params */ }
This Routine instance contains all the run-time information about a routine. Each state in a program contains an array of instances, one for each routine that will be run during the state. The instance is set up by Expo when a program is created or loaded from a file. Expo passes a pointer to the Routine instance when it calls each execution function. Most of the information required by the execution functions concerns parameter values, and these are accessible through the member rpars, an array of Param instances, described below.
rinfo contains a pointer to the RINFO object that defines all the basic attributes of the routine.
rid This is the unique id of the routine.
rflags This contains miscellaneous flags, defined in routinedefs.h:
RSELECT This is used during entry and editing of a routine, to mark whether or not it has been selected. It has no relevance at run-time.
R_TOKEN This is TRUE if the routine should create a display token. The state of this flag is determined by the state of the Make Token check box in the sheet used to edit routines.
R_RECORD This is TRUE if the capture function should record the values of parameters and put them in the event queue. The state of this flag is determined by the state of the Record check box in the sheet used to edit routines.
R_STOP This is TRUE if the continue function should assert a STOP event when the routine has done what it needs to do. The state of this flag is determined by the state of the Assert STOP check box in the sheet used to edit routines.
R_DONE This is TRUE if the continue function should assert a DONE event when the routine has done what it needs to do. The state of this flag is determined by the state of the Assert DONE check box in the sheet used to edit routines.
R_PERSIST This is TRUE if the conditions set by the routine should persist beyond the end of the state. If the flag is SET, Expo does not call the end function for the routine. The state of this flag is determined by the state of the Persist check box in the sheet used to edit routines.
gptr If the routine requires a block of dynamically-allocated private memory whose size can be determined only at runtime, you should use the Routine method -getroutinememory to allocate it in the startup function. gptr will contain the pointer to the block. A buffer allocated by -getroutinememory is automatically freed by Expo when the running program is finished. See startcolortexture() in globjects.m for an example of how to allocate memory in a startup function.
wspptr If the routine requires private workspace, wspptr will point to a buffer that is the size declared by the RINFO member bufsize. Each invocation of a routine in a program is given a unique workspace.
token For visual display routines that can display tokens, this contains the information needed to form the displayable object.
rlabel This contains the text of any label attached to the routine.
pbase This is an array of Param instances that contain the run-time information about each parameter in the routine.
A Param is a subclass of NSObject; all the methods that manage Param are in Param.m (with declarations in Param.h). Its instance variables are:
@interface Param : NSObject <NSCoding,NSCopying> { PINFO * pinfo; // pointer to info structure char vname[NAMELEN];/* name of variable */ char rpflags; /* misc flags */ char op; /* operation between variable and constant */ Variable * vptr; /* ptr to variable (use only when running) */ short uhold; /* unit in which value held */ double evalue; /* constant value, or value used in op on variable */ }
An instance of this class contains run-time information about a parameter. An array of Param instances is the pbase member of the Routine instance.
pinfo contains a pointer to the PINFO object that defines all the basic attributes of the parameter for the routine.
vname[NAMELEN] This character string contains the name of the variable that holds the value of the parameter, or, if the parameter is a resource, the name of the resource. vname is an empty string if the parameter value is a constant.
rpflags Two flags are at present defined:
RP_ISINRANGE The parameter value has been set as part of a range set on a matrix dimension.
RP_ISRATIORANGE (Meaningful only when RP_ISINRANGE is TRUE). The range of values is set to be spaced by equal ratios. Otherwise the range is spaced by equal intervals.
op If the parameter value is taken from a variable, some additional operation is performed between it and an auxiliary value. op defines the kind of operation performed. Execution functions generally need not pay attention to this if they use Expo's standard ways to set and load parameter values (see the following section on Writing Execution Functions).
vptr Points to the instance of the variable containing the value for the parameter. Appendix B provides a full description of the Variable class. When Expo loads or creates a program it uses the name in vname to find a pointer to the variable (if the variable is not already defined Expo will create it and provide the pointer). The pointer is installed in vptr. Execution functions never need to deal directly with this. Expo provides special functions for setting and retrieving the values of parameters (see the following section on Writing Execution Functions) and these deal with variables as necessary.
uhold Identifies the unit in which the parameter value is held.
evalue Holds the parameter's value (in units of type uhold), if the parameter is a constant, or the auxiliary valued to be added, subtracted, etc. (if the parameter value is obtained from a variable). Don't read this value directly to recover the value of a parameter. Use one of Expo's special functions that properly takes account of values held in variables (see the following section on Writing Execution Functions).
Your execution functions will almost always want to recover values from routine parameters, or set values in variables attached to routine parameters. Expo provides two Param methods "getrunvalue" and "setrunvalue" to get and set parameter values.
To recover a value from a parameter, use the instance method
- (double)getrunvalue:( int32_t )unit
where unit is the id of the unit in which you want the result expressed. You need never be concerned with the source of the value (a constant or a variable).
To set a value in a variable attached to a parameter, use the instance method
- (void)setrunvalue:(double)value
This function is called once for each routine every time a state is started by a slot in the schedule. You should do any one-time initializing here. Not all routines need to do something in the start function.
This is called on every timebase tick. Whatever it does should be completed within the time of a clock tick. Expo will warn you (and optionally abort a running program) if it cannot pass through all the cont functions for a state in a single tick. Not all routines need to do something in the cont function.
If you want your cont function to assert STOP or DONE signals, look at the function rkey_cont() in the module input.m.
This function is optional. For each routine in a running program Expo captures routine data by calling an instance method that forms an event record and puts it in the event queue. This event record contains the values of the parameters in the routine at the time the values were captured. For almost every routine the capture method does all that is needed, and you need do nothing specific to ensure that data are recorded when required.
A particular routine might need to calculate some result at the time its data are captured. Expo makes provision for this by calling an auxiliary capture function (if you have defined it) from within the standard capture function, before saving the current values of parameters. If you need no capture function, set its name to NULL in the RINFO entry for the routine.
Expo calls the standard capture method only if the Routine instance has R_RECORD set in its rflags, and the recording of routine events has been enabled by the Events settings for the current slot in the program schedule. In any case Expo calls the function at most once for each pass through the state. When the function is called depends on the Events setting of the slot that schedules the state.
This function is optional. If it is defined it is called at the end of a state only if the R_PERSIST is cleared in the Routine rflags (the end function is always called for the state that is running immediately before a program stops or is aborted). You should do any terminal housekeeping here. Not all routines need to do something in an end function. If you need no end function, set its name to NULL in the RINFO entry for the routine.
Expo provides two utility functions to monitor and display the time taken by particular operations. debugmarktime() saves the system time at the moment it is called. debuglogtime() displays in the console window the time elapsed since debugmarktime() was last called. Both functions are declared in util.m.
Data analysis is organized through a cell in the table displayed in the main analysis window. Each cell contains information that enables Expo to find in the event queue the events for analysis (e.g., the value of a parameter in a routine, or the set of spikes collected while the state/slot was active) and information about a function that will be called to undertake the analysis. When you use the main analysis window to Set an analysis in a cell, Expo fills in several of the elements in a Datacell class instance attached to the cell, then assembles the data required (information about the sets of spikes or analog signals collected when the state/slot was run, or the set of parameter values), passes the data to the analysis function specified in the cell, and saves in the Datacell sinstance the result returned.
Expo has three kinds of analysis functions, those designed to work with spike times, those designed to analyze continuously recorded analog signals, and those designed to work with the values of routine parameters. Expo calls these functions in the same way, but passes different kinds of events to them (in the case of spikes and analog signals, Expo passes arrays of events that define time bands for which data should be extracted from the spike train or analog signal queue; in the case of routine parameters, Expo passes arrays of events that contain parameter values).
Each analysis function in Expo must be defined through a standard structure. This AINFO structure (defined in analysis.h) identifies the function that Expo will call to undertake the analysis, and contains information about any auxiliary parameters required for the analysis. The AINFO structure also contains information about how Expo should build (in the main analysis window) a menu that displays the name of the function, and a menu that displays the units in which it can express results.
To install a new function you must:
1. Write an analysis function to handle an array of events passed by Expo, and calculate a single valued result, or an array of results.
2. If the function requires auxiliary information (like those that do Fourier transforms on spike trains), you must write a setup function that puts up a sheet and recovers the auxiliary parameter values from it.
3. Fill out an AINFO structure that defines pointers to the analysis function and any setup function, and provides miscellaneous other information. This completed structure becomes a member of the global array afarray (or, for spike analysis, sparray, or for analog signal analysis asarray), through which Expo manages the analysis. afarray, sparray and asarray are defined in analysis.c.
This contains Expo's internal description of an analysis function. The structure is defined in afunctions.h. The global arrays afarray, sparray and asarray (defined in analysis.h) contain an initialized AINFO for each analysis that Expo can undertake.
typedef struct { /* structure for analysis functions */ short aid; /* analysis function id */ char *name; /* analysis name */ unsigned short pcount; /* auxiliary param count */ double (*afunct)(Datacell *cptr); /* analysis function */ int selector; /* setup function selector for params */ short ubase; /* base unit of expression (overrides param unit if <> 0) */ short upref; /* preferred default unit for setting up menus */ short aflags; /* misc flags for analysis */ char * auxstring[APARNUM]; /* pointers to auxiliary par text strings */ float defpars[APARNUM]; /* default param values */ } AINFO;
aid. This is a unique id that identifies the type of analysis. Expo stores the id in saved analysis templates, and uses it to retrieve information about the analysis. The id need not match the index in the array afarray or sparray or asarray.
name. A pointer to a character string that names the analysis. The name appears in menus and sheets.
pcount. The number of auxiliary parameters the function requires. This may be up to APARNUM (currently 18), defined in analysis.h.
afunct. The pointer to the analysis function. When Expo calls this function it passes a pointer to the Datacell instance that contains other information required for the analysis, including the id of the routine or spike or signals channel, the index of the parameter for which analysis is required, and the count of matching events. The Datacell class is discussed below.
selector. A constant (defined in an enum in analysis.h) that identifies the sheet Expo should display in the analysis window if it needs to gather auxiliary parameters for the analysis routine. If your routine uses auxiliary parameters you will need to construct a sheet (using Interface Builder) in the file ExamWindow.nib, and link this to code in E_examwincontroller.m to recover values of auxiliary parameters. To see how this is done, search E_examwincontroller.m for occurrences of SHEET_HISTOGRAM.
ubase. The base unit in which the result of the analysis will be computed. This determines the options available in the Unit menu in the main analysis window. If this is 0, Expo takes as the base unit the default unit for the parameter being analyzed.
upref. The unit initially displayed in the Unit menu when the analysis is first chosen.
aflags. Miscellaneous flags. Three flags are defined (in analysis.h):
AS_ONLYBASE The analysis must be undertaken in the base unit (no other units will be offered in the Unit menu).
AS_NEEDS_BASE_EVENTS
AS_ERROR_BAR The routine results in an error value. It will be plotted as an error bar, unless the user unchecks to box Graph Errors as Such under graph options.
auxstring. An array of strings that provide brief names for any auxiliary parameters used in the analysis. Expo uses these strings when displaying information about auxiliary parameters in the main analysis window.
defpars. An array of default values for any auxiliary parameters. This is currently unused. Default values are built into the sheet through which you set parameter values; the sheet retains settings between calls.
When Expo calls an analysis function it passes a pointer to an instance of Datacell. This instance contains all the information needed to organize the analysis of data in a single cell of the table. It also holds the results of the analysis.
Each Datacell instance contains a pointer to an object (a bandmap) that provides access to all the events collected by Expo while the program was running. The analysis function uses Bandmap and its associated objects (Band and Passinfo) to find the events it needs.
This contains all the information about where the analysis routine can find data on which it should operate. A Bandmap is a subclass of NSObject; all the methods that manage Bandmap are in Bandmap.m (with declarations in Bandmap.h). Its instance variables are:
@interface Bandmap : NSObject { Program * program; // associated program UGROUP * ugrp; // associated unit group NSMutableArray * sequence; // sequenced array of passinfos NSMutableArray * bands; /* array of bands */ int baseeventindex; // index in queue of next event to be placed int spikeindex; // index in spike queue of next spike to be placed int analogindex; // index in analog event queue of event to be examined int startslotloop; // serial number of first slot in any loop in schedule int endslotloop; // serial number of last slot in any loop in schedule int lastslotserial; // serial number of prior slot in schedule Block * lastblock; // prior block run unsigned long passindex; // total number of passes scanned (all states) }
program. The Program instance whose data are being handled.
ugrp. A pointer to the array of unit conversion factors appropriate for the data at the time they were collected.
sequence. An array of Passinfo objects representing all passes through all states, in the order they were run by the program.
bands. An array of instances of the Band class. Each Band instance (described below) contains all the information neded to locate the events that occurred during all passes through a particular slot/state.
The other instance variables are used internally when the Bandmap is being built by Expo, and are not relevant to analysis functions.
This contains all the information about events collected during each pass through a particular slot/state. Each Bandmap contains an array of Band instances (one band for each row in the analysis table). A Band is a subclass of NSObject; all the methods that manage Band are in Band.m (with declarations in Band.h). Its instance variables are:
@interface Band : NSObject { int firstpass; // scope first pass int lastpass; // scope last pass int skipstep; // scope step int statecondition; // index of block for which a pass condition applies int matrixcondition; // # matrix dimensions for which a pass condition applies int conditiontype; // stipulates constion on prior or succeeding pass int conditionstart[DIMMAX]; // array of start indexes for dimensions int conditionstop[DIMMAX]; // array of stop indexes for dimensions int passindex; // for enumerating through list int passesdone; // number of passes accepted through enumeration NSMutableArray * passes; /* array of Passinfos */ };
firstpass. Holds the index of the pass through the slot/state at which analysis is to begin. This is taken from the Scope setting in the analysis window.
lastpass. Holds the index of the pass through the slot/state at which analysis is to end. This is taken from the Scope setting in the analysis window.
skipstep. Holds the step by which the pass index is increased during analysis of the array of passes. This is taken from the Scope setting in the analysis window.
passes. An array of instances of Passinfo, each of which contains pointers to the events and queue locations for the data collected during one pass. There is one Passinfo instance for each analyzable pass through the slot/state.
The other instance variables are used internally when the Band is managed by Expo, and are not relevant to analysis functions.
This contains all the information about events collected during a single pass through a particular slot/state. Each Band instance in a Bandmap contains an array of Passinfo, one for each pass the running program made through a particular slot/state. Passinfo is a subclass of NSObject; all the methods that manage Passinfo are in Passinfo.m (with declarations in Passinfo.h). Its instance variables are:
@interface Passinfo : NSObject { Block * block; // block run Slot * slot; // slot run long slotstarttime; // slot start time long passindex; /* index of this pass in whole sequence of passes */ Block * prevblock; /* prior block run */ Block * nextblock; /* subsequent block run */ long firstspike; /* index of first spike */ long starttime; /* start time */ long endtime; /* end time */ long analogeventindex; /* index of analog sample event */ long analogoffset; /* index into event of scan sample we want */ NSMutableArray * rebase; // array of ptrs to routine events }
block. Pointer to the Block that was active during this pass.
slot. Pointer to the Slot that was active during this pass.
slotstarttime. Time (in event ticks since the start of the program) at which the slot became active.
passindex. Zero-based absolute position (in all passes through all Slots in the running program) of this pass.
prevblock. Pointer to the Block that was run immediately before the one active in this pass.
nextblock. Pointer to the Block that was run immediately after the one active in this pass.
firstspike. Index in the spike time queue of the first spike that was recorded after this pass began.
starttime. Holds the time (in event ticks since the start of the program) at which the state became activethe time after any start delay in the slot.
endtime. Holds the time (in event ticks since the start of the program) at which the state ceased to be active activethe time before any pause in the slot.
analogeventindex. The index in the analog sample queue of analog event that was being recorded when the pass started.
analogoffset. Index in the analog event of the first sample recorded after the pass began.
rebase. Array of pointers to the routine events recorded during this pass.
When Expo calls an analysis function the Datacell instance passed to the function contains all the information needed to deal with the data. Much of that information is in the band instance variable in the Datacell.
If the analysis function succeeds it must do the following:
If the result is a scalar, the function must return the value. The returned value is automatically scaled and converted to the appropriate unit .
If the result is an array, the function must allocate the array (of doubles) using malloc() or calloc(), load it with the results and call the Datacell instance method
- (void)loadarray:(double *)aptr oflength:( int32_t )size;passing the array and the number of values it contains. If the array is one-dimensional, your function should return 1; if the array is multi-dimensional, your function should return the number of values in the first (outermost) dimension. Expo does not automatically scale and convert the unit of array results. If your function creates an array, you should apply any unit conversions and then scale the results before sending the loadarray message. Expo disposes of the array when it no longer needs it.
The function must call the Datacell instance method
- (void)setflags:( int32_t )flag;passing the flag D_HASRESULT.
The function must call the Datacell instance method
- (void)setecount:( int32_t )count;passing the number of passes on which the result is based.
For examples of analysis functions see the module stateanal.m
The conversion of results between units becomes an issue in analyzing spike trains, because Expo cannot always know in advance of doing the analysis what the conversion factor should befor example, to convert a total spike count to spikes/sec Expo needs to know the time over which spikes were accumulated.
The function s_scount(), in spikeanal.m, provides an example of how to extract information about spikes, and how to set up the unit conversions.
Almost all analog signal analysis will require the values from different channels to be separated (in the event queue these are interleaved in scans). Expo provides (in analoganal.m) a utility function buildasarray() that will build and return a two-dimensional array of doubles containing the signal values for a specified number of channels (the first dimension indexes the channel number, the second dimension indexes the sample time). The analysis function alg_trace() in analoganal.m provides an example of how to use buildasarray(). If you use buildasarray() you must ultimately free the returned array, or assign it as an array result with the Datacell method loadarray, in which case Expo will dispose of it when appropriate.