MasterScan
Lab_Matlab_control Master Branch
|
Superclass for objects that read data.
The dotsReadable superclass provides a uniform way to read data, such as data from a gamepad or eyetracker. It imposes a format for data and provides utilities for previewing data and for defining, detecting and enqueing events of interest.
dotsReadable itself is not a usable class. Rather, it provides a uniform interface and core functionality for subclasses. Subclasses must redefine the following methods in order to read actual data:
These methods are invoked internally. They encapsulate the details of how to read from any particular device. See the documentation for each of these methods for more information about how subclasses should redefine them.
Users should expect to call public methods like initialize(), preview(), and read(), which are the same for all subclasses.
dotsReadable() assumes that each input source or device has one or more components. Each component must be assigned a small, positive, unique, integer ID. This ID is used in many methods and properties to identify the component. A component might be an individual button on a game pad, or a data channel from an eye tracker.
For most properties and methods, data are formatted as matrix rows. Each row represents one measurement. Each row has three columns: [ID, value, time]. The ID is the ID of a device component. The value is any observed value. The time is a timestamp associated with the value.
Several properties use component ID as indexes. For example, the state property has one row for each component. If for some reason component IDs are non-sequential, the state property may have gaps between useful rows. This is expected. Using IDs as indices takes advantage of Matlab's array facility to provide quick, concise assignment and lookup of data.
dotsReadable provides utilities for defining, detecting, and enqueueing events of interest. Each component may define one event of interest at a time. An event can be defined for one of four possibilities:
Once defined, events are detected automatically during read() and can be read out sequentially from getNextEvent(). Event dectection relies heavily on the dotsReadable data format and ID indexes.
Properties and Events | |
Property | isAvailable = false |
| |
Property | components |
| |
Property | state |
| |
Property | history |
| |
Property | isAutoRead = true |
| |
Property | eventDefinitions |
| |
Property | initialEventQueueSize = 100 |
| |
Property | clockFunction |
| |
Property | filename |
| |
Property | filepath |
| |
Property | recordDuringCalibration = true |
| |
Property | useGUI = false |
Flag to use GUI for feedback, etc. | |
Property | useExistingCalibration = false |
Flag to skip calibration routine. | |
Property | calibrationUI |
Possibly use a keyboard or other UI to help with calibration. | |
Property | deactivateEventsAtStartTrial =false |
Flag to deactivate all events at the beginning of each trial. | |
Protected Properties | |
Property | eventQueue |
| |
Property | queueNext |
| |
Property | queueLast =0 |
| |
Property | isRecording =false |
| |
Property | didCalibrate = true |
Keep track of whether calibration occurs. | |
Property | defaultEventPrefix = 'event' |
Default prefix for event names: <prefix>_<componentName> | |
Property | eventSets |
Strutures with different named event definitions, in case we want to quickly swap between them. | |
Methods | |
self | dotsReadable () |
| |
initialize (self) | |
| |
calibrate (self, varargin) | |
More... | |
record (self, onFlag, filename) | |
More... | |
reset (self, varargin) | |
reset device More... | |
close (self) | |
| |
delete (self) | |
| |
read (self) | |
More... | |
flushData (self, waitForNoEvents) | |
More... | |
logData (self) | |
More... | |
stateAtTime | getState (self, time) |
More... | |
value data | getValue (self, ID) |
More... | |
event | defineEvent (self, name, varargin) |
More... | |
defineEventsFromComponents (self, names, varargin) | |
defineEventsFromComponents More... | |
defineEventsFromStruct (self, eventStruct, setName, keepExisting, inactivate) | |
defineEventsFromStruct More... | |
showActiveEvents (self) | |
Mostly for debugging. | |
activateEventSet (self, name) | |
Swap in a set of events. | |
activateEvents (self) | |
Activate all events. More... | |
deactivateEvents (self) | |
Deactivate all events. More... | |
activeFlags | getActiveFlags (self) |
Get all active flags. | |
setActiveFlags (self, activeFlags) | |
Set all active flags from array. | |
setEventsActiveFlag (self, activateList, deactivateList) | |
Set/unset activeFlag. More... | |
name data | getNextEvent (self, isPeek, acceptedEvents) |
More... | |
lastName lastID names IDs | getHappeningEvent (self, time) |
More... | |
name waitTime data | waitForEvents (self, eventNames, maxWait) |
Wait for event(s) | |
nEvents | getNumberOfEvents (self) |
More... | |
IDs | getComponentIDs (self) |
More... | |
ID | getComponentID (self, nameOrID) |
| |
name | getComponentName (self, nameOrID) |
| |
indices | getComponentIndicesByID (self, IDs) |
| |
indices | getComponentIndicesByName (self, names) |
| |
time | getDeviceTime (self) |
| |
setDeviceTime (self, val) | |
| |
startTrial (self, varargin) | |
More... | |
finishTrial (self, varargin) | |
More... | |
plotData (self) | |
More... | |
startTrialDevice (self, varargin) | |
Overloaded methods. | |
Protected Methods | |
isOpen | openDevice (self) |
More... | |
closeDevice (self) | |
More... | |
resetDevice (self, varargin) | |
Reset the device. | |
status | calibrateDevice (self, varargin) |
More... | |
isRecording | startRecording (self) |
| |
isRecording | stopRecording (self) |
| |
components | openComponents (self) |
More... | |
closeComponents (self) | |
More... | |
newData | readNewData (self) |
More... | |
isEvent | detectEvents (self, data) |
More... | |
newSize | resizeEventQueue (self, minSize, doClear) |
More... | |
nEvents | enqueueEvents (self, eventValues) |
More... | |
eventValue nEvents | dequeueEvent (self, isPeek) |
More... | |
Static Methods | |
static isHappening data readable | isEventHappening (readables, eventName) |
More... | |
static didHappen waitTime data readable name | waitForEvent (readables, eventName, maxWait) |
More... | |
static data | loadDataFile (filename, varargin) |
Load data from file. More... | |
calibrate | ( | self | , |
varargin | |||
) |
Calibrate the device
calibrateDevice is device specific and should return 0 if successful, otherwise an error
record | ( | self | , |
onFlag | , | ||
filename | |||
) |
Open/close data file associated with the device Arguments: onFlag ...
true to turn on (default), false to turn off filename ... string name of the file
reset | ( | self | , |
varargin | |||
) |
reset device
subclass-specific methods
read | ( | self | ) |
Add incoming data to history and detect events of interest.
read() updates device resources and components as defined by subclasses in readNewData(). It uses any new data to detect events of interest and enqueues any events in eventQueue. It appends any new data to history and updates the component summary in state.
read() should be called in order to get the latest device data. It may make sense to call read() periodically, or to call read() immediately before accessing history or state, or invoking getState(), getValue(), or getNextEvent().
flushData | ( | self | , |
waitForNoEvents | |||
) |
Delete historical data and reset the current state.
flushData() flush data deletes any data previously read. This includes the object's state, history, and eventQueue.
logData | ( | self | ) |
Record current and historical data in topsDataLog.
logData() saves all properties of a dotsReadable object as a struct in topsDataLog, using the class's name as the data group name.
stateAtTime getState | ( | self | , |
time | |||
) |
Get the state of device components as of the given time.
time | a time in the past to consider instead of the current time |
getState() summarizes the latest data for each device component. getState() returns a nx3 matrix where each row has the form [ID, value, time]. Each ID identifies a device component. Rows of the matrix are indexed by ID values. Each value is the latest value that was read at each component. Each time is the time when the corresponding value was read.
By default, getState() returns the latest data. If time is provided, components are summarized as of the given time and data read after the given time are ignored.
If isAutoRead is set to true, invokes read() to update component data before summarizing.
value data getValue | ( | self | , |
ID | |||
) |
Get the latest value for the given component.
ID | one of the integer IDs in components |
Returns the latest value that was read for the component identified by ID. Also returns as a second argument a data row of the form [ID, value, time], where the time is the time associated with the latest value.
If isAutoRead is set to true, invokes read() to update component data before accessing values.
event defineEvent | ( | self | , |
name | , | ||
varargin | |||
) |
Define the event of interest for one of the input components.
Required input:
name ... string name for an event of interest
Optional property/value pairs:
'component' ... string name or one of the integer IDs in components 'isActive' ... whether event is current active 'isInverted' ... whether to invert event detection logic 'lowValue' ... the lower bound on the event of interest 'highValue' ... the upper bound on the event of interest 'isRelease' ... event on release of component
defineEvent() sets parameters for detecting events of interest as the value of a component changes. ID specifies which component. Each component may define only one event of interest at a time, so repeated calls to defineEvent() with the same ID will replace previous event definitions.
name is an arbitrary string identifying the event of interest. lowValue and highValue define the boundaries of the event of interest. When the value of the component falls between lowValue and highValue, an event of interest occurs. If isInverted is provided and equal to true, the definition of the event will be inverted. The event will occur when the value of the component moves outside of lowValue and highValue.
Events of interest are detected during read(), as new data arrive. When events of interest are detected they are added to eventQueue. Events can be read out one at a time later, with getNextEvent().
Event does not use any timing information, so techniques like edge detection are not possible. As a consequence, the number of events that occur may depend on the sampling frequency or noisiness of input sources. To avoid redundant event detection, readNewData() may be implemented so as to smooth data, or the report data only when the value of a component changes.
Can be overloaded in subclasses
defineEventsFromComponents | ( | self | , |
names | , | ||
varargin | |||
) |
defineEventsFromComponents
Automatically define default events associated with all of the components (or from the list of names) Arguments: names ... optional cell array of string names of components to use varargin ... optional property/value pairs sent to defineEvent
defineEventsFromStruct | ( | self | , |
eventStruct | , | ||
setName | , | ||
keepExisting | , | ||
inactivate | |||
) |
defineEventsFromStruct
Takes a structure with information about events and calls defineEvent for each event. Arguments: eventStruct ... array of structs, one per event, with fields corresponding to properties defined in defineEvents (which may be overridden in subclasses). The only required field is 'name' (first argument to defineEvent), all others are optional paired arguments. setName ... string name to use to save this set of event definitions keepExisting ... string to keep existing definitions (default false) startInactive ... flag for initial isActive flag (default false)
activateEvents | ( | self | ) |
Activate all events.
To do this separately for each event, call defineEvent and set isActive flag to true
deactivateEvents | ( | self | ) |
Deactivate all events.
To do this separately for each event, call defineEvent and set isActive flag to true
setEventsActiveFlag | ( | self | , |
activateList | , | ||
deactivateList | |||
) |
Set/unset activeFlag.
NOTE: if anything changes here, be careful to update activateEvents and deactivateEvents, above, as appropriate Input lists are either string name of event, or cell array of string names of events
name data getNextEvent | ( | self | , |
isPeek | , | ||
acceptedEvents | |||
) |
Get the next event that was detected in read().
isPeek | whether to leave the next event in the queue |
getNextEvent() returns the name of the the next queued event of interest. The name corresponds to one of the name values in eventDefinitions. Also returns as second output the data which caused the event of interest. The data corresponds to one of the rows of history. Thus, the data has the form [ID, value, time].
Additional information about the event can be found in eventDefinitions(ID). Additional information about the component which caused the event can be found in components.
By default, the next event is read out of the queue and removed. If isPeek is provided and true, the event is read out but left in the queue to be read again.
If isAutoRead is set to true, invokes read() to update component data before getting the next event. jig added cell array of strings acceptedEvents to list names of events that can be used
lastName lastID names IDs getHappeningEvent | ( | self | , |
time | |||
) |
Get events that are happening at the current time.
time | a time in the past to consider instead of the current time. |
getHappeningEvent() summarizes events that are still happening, at the given time. If time is omitted, defaults to the current time. This contrasts with getNextEvent(), which recalls events that happened in the past. getHappeningEvent() does not affect the behavior of getNextEvent() or the values in eventQueue.
If no events are happening at the given time, returns ''. If one or more events is happening, returns the name of the last event that happened. Also returns as a second output the component ID for the last event.
Also returns as a third output a cell array of names of all the events that are happening. Also returns as a fourth output an array of component IDs for all the events that are happening.
nEvents getNumberOfEvents | ( | self | ) |
Get the number of events in eventQueue.
Returns the number of events which are currently enqueued in eventQueue.
IDs getComponentIDs | ( | self | ) |
Get an array of unique integer component IDs.
Returns an array of component IDs, which are unique, small, positive integers which identify device components.
startTrial | ( | self | , |
varargin | |||
) |
In case you need to turn on/off recording at the beginning of each trial (e.g., dotsReadableEyeEOG, using the PMD1208FS device)
finishTrial | ( | self | , |
varargin | |||
) |
In case you need to turn on/off recording at the beginning of each trial (e.g., dotsReadableEyeEOG, using the PMD1208FS device)
plotData | ( | self | ) |
Open a figure with continuously read device data.
Opens a new figure and plots component and event data. Continuously invokes read() and updates the plot as long as the figure is open.
|
protected |
Locate and acquire input device resources (for subclasses).
Subclasses must redefine openDevice(). They should expect openDevice() to be called during initialize() and when an object is constructed. openDevice() should locate, acquire, configure, etc. major device resources required for reading data. Specific resources relating to device components, like individual buttons of a gamepad, should be handled in openComponents().
openDevice() should return true if resources were successfully acquired and individual components are ready to be opened. Otherwise, openDevice() should return false.
|
protected |
Release input device resources (for subclasses).
Subclasses must redefine closedevice(). Any resources that were acquired by openDevice() should be released. It should be safe to call closeDevice() multiple times in a row.
|
protected |
Calibrate the device (for subclasses).
Subclasses must redefine calibrateDevice(). It should be safe to call calibrateDevice() multiple times in a row.
|
protected |
Locate and acquire device components (for subclasses).
Subclasses must redefine openComponents(). They should expect openComponents() to be called immediately after a successful call to openDevice(). Assuming the device was opened successfully, openComponents() should identify, acquire, configure, etc. specific components of interest, such as individual buttons on a gamepad.
openComponents() must assign a name and a unique ID to each component. Each name should be a short, human-readable string. Each ID should be a unique, small, greater-than-0 integer.
openComponents() must return names and IDs as a struct array with fields ID and name. The struct array should have one element per component. Subclasses may add additional fields to the components struct array, but ID and name are mandatory.
|
protected |
Release device components (for subclasses).
Subclasses must redefine closeComponents(). Any resources that were acquired by openComponents() should be released. It should be safe to call closeComponentes() multiple times in a row.
|
protected |
Read and format incoming data (for subclasses).
Subclasses must redefine readNewData() to update input devices, read from device components, and put data in the expected format.
readNewData() must return an nx3 matrix of data with rows of the form [ID, value, time]. Each ID must match one of the values in components.ID. Each value should be a new value that was read from the component. Each time should be a timestamp asociated with that value. Only new data, which has not yet been read, should be returned from readNewData().
|
protected |
Use new data to look up events of interest (used internally).
data | nx3 matrix of component data |
Expects rows of data to have the form [ID, value, time]. Uses ID values to look up parameters in eventDefinitions, and performs logical comparisons to determine which rows of data qualify as events of interest.
Returns a logical array with one element per row of data. Where the array is true, the corresponding row of data qualifies as an event of interest. If data is empty, returns [].
|
protected |
Resize and optionally clear the event queue (used internally).
minSize | the new minimum size for eventQueue |
doClear | whether or not to delete previously queued events |
Changes the size of eventQueue to agree with the given minSize. If minSize is smaller than the number of events currently in eventQueue, the size of the queue remains unchanged.
If doClear is true, deletes any previously queued events. This is a way to initialize eventQueue. If doClear is false, packs any queued events into the beginning of eventQueue. Events are re-packed regardless of minSize.
Returns the new size of eventQueue, which is at least minSize.
|
protected |
Add events of interest to the event queue (used internally).
eventValues | array of values to add to eventQueue |
Adds one or more new eventValues to eventQueue and does queue accounting. Returns the new total number of events in eventQueue.
|
protected |
Remove the next queued event of interest (used internally).
isPeek | whether to leave the next event in the queue |
Gets the next event from eventQueue and does queue accounting. If isPeek is provided and equal to true, leaves the event in the queue to be read again. Otherwise, removes the event.
Returns the next value queued in eventQueue. If there are no queued values, returns []. Returns as a second argument the new total number og events in eventQueue.
|
static |
Is the named event happening now?
readables | array or cell array of dotsReadable objects |
eventName | name of an event defined by readables |
Checks whether any of the given readables currently has an event happening with the given eventName. Does not invoke read() for any readable.
Returns true if any of given readables has eventName happening. Returns as a second output the data associated with the event. The data has the form [ID, value, time]. Returns as a third output the readable which has eventName happening. If more than one of the given readables has eventName happening, only returns the first readable.
|
static |
Wait for the named event to happen.
readables | array or cell array of dotsReadable objects |
eventName | name of an event defined by readables |
maxWait | maximum time to wait for eventName |
Waits for one of of the given readables to report that the given eventName happened. maxWait specifies how long to wait before giving up. Uses getDeviceTime() of the first readable to keep track of time. Invokes read() and checks each readable for events at least once, even if maxWait is zero or negative.
Returns true if any of given readables reports that eventName happened before maxWait. Returns as a second output the amount of time waited. Returns as a third output the data associated with the event. The data has the form [ID, value, time]. Returns as a fourth output the readable which reported eventName. If more than one of the given readables reports eventName, only returns the first readable.
|
static |
Load data from file.
Arguments: filename ... string name varargin ... (optional) flag to check for synch/calibration data in dataLog