Extensions

Available Extensions

Extensions provide additional functionality for Weber. Currently there are two extensions availble:

Weber.@CedrusMacro.

Extension Website

@Cedrus()

Creates an extension for Weber allowing experiments to respond to events from Cedrus response-pad hardware. You can use iskeydown and iskeyup to check for events. To find the keycodes of the buttons for your response pad, run the following code, and press each of the buttons on the response pad.

run_keycode_helper(extensions=[@Cedrus()])
Do not call inside a package

Do not call @Cedrus inside of a package or in tests. It should only be used in one-off scripts. If WeberCedrus is not currently installed it will be installed by this macro using Pkg.add which can lead to problems when called in packages or tests.

If you want to include an extension without this behavior you can call @Cedrus_safe which mimics @Cedrus except that it will never call Pkg.add.

source
Weber.@DAQmxMacro.

Extension Website

@DAQmx(port;eeg_sample_rate,[codes])

Create a Weber extension that writes record events to a digital out line via the DAQmx API. This can be used to send trigger codes during eeg recording.

Arguments

  • port: should be nothing, to disable the extension, or the port name for the digital output line.

  • eeg_sample_rate: should be set to the sampling rate for eeg recording. This calibrates the code length for triggers.

  • codes: a Dict that maps record event codes (a string) to a number. This should be an Integer less than 256. Any codes not specified here will be automatically set, based on the order in which codes are recieved.

Example

The following experiment sends the code 0x01 to port0 on TestDevice.

port = "/TestDevice/port0/line0:7"
experiment = Experiment(extensions=[
  @DAQmx(port;eeg_sample_rate=512,codes=Dict("test" => 0x01))])
setup(experiment) do
  addtrial(moment(record,"test"))
end
run(experiment)
Do not call inside a package

Do not call @DAQmx inside of a package or in tests. It should only be used in one-off scripts. If WeberDAQmx is not currently installed it will be installed by this macro using Pkg.add which can lead to problems when called in packages or tests.

If you want to include an extension without this behavior you can call @DAQmx_safe which mimics @DAQmx except that it will never call Pkg.add.

source

Creating Extensions

The following functions are used when extending experiments.

To register your extension within Weber, so users can import your extension with ease, you use can use the @extension macro.

Weber.@extensionMacro.
@extension [Symbol] begin
  [docstring...]
end

Registers a given Weber extension. This creates a macro called @[Symbol] which imports Weber[Symbol] and calls Weber[Symbol].InitExtension, with the given arguments. InitExtension should return either nothing or an extension object.

The doc string is used to document the usage of the extension, and should normally include a link to the website of a julia package for the extension.

source

Functions operating over extensions

These functions operate directly on an ExtendedExperiment.

Base.nextMethod.
 next(experiment::ExtendedExperiment)

Get the next extended version of this experiment.

source
DataStructures.topMethod.
top(experiment::Experiment)

Get the the top-most extended verison for this experiment, if any.

source
Weber.extensionMethod.
extension(experiment::ExtendedExperiment)

Get the extension object for this extended expeirment

source

Extendable Private Functions

Weber.poll_eventsFunction.
 Weber.poll_events(callback,experiment,time)

Call the function callback, possibility multiple times, passing it an event object each time. The time at which the events are polled is passed, allowing this time to be stored with the event.

Warning

This function should never be called directly by user code. A new method of this function can be implemented to extend Weber, allowing it to report new kinds events.

source

Private Moment Functions

New Weber.SimpleMoment subtypes can define methods for the following functions to extend the runtime behavior of Weber.

Weber.prepare!Function.
Weber.prepare!(m,[onset_s])

If there is anything the moment needs to do before it occurs, it is done during prepare!. Prepare can be used to set up precise timing even when hardware latency is high, if that latency can be predicted, and accounted for. A moment's prepare! method is called just before the first non-zero pause between moments that occurs before this moment: in the simplest case, when this moment has a non-zero value for delta_t, preapre! will occur delta_t seconds before this moment. However, if several moments with no pause occur, prepare! will occur before all of those moments as well.

Prepare accepts an optional second argument used to indicate the time, in seconds from the start of the experiemnt when this moment will begin (as a Float64). This argument may be Inf, indicating that it is not possible to predict when the moment will occur at this point, because the timing depends on some stateful information (e.g. a participant's response). It is accetable in this case to throw an error, explaining that this kind of moment must be able to know precisely when it occurs to be prepared.

Note

This method is part of the private interface for moments. It should not be called directly, but implemented as part of an extension. You need only extend the method taking a single arugment unless you intend to use this information during prepartion.

source
Weber.handleFunction.
handle(exp,queue,moment,to_handle)

Internal method to handle the given moment object in a manner specific to its type.

The function handle is only called when the appropriate time has been reached for the next moment to be presented (according to delta_t) or when an event occurs.

The to_handle object is either a Float64, indicating the current experiment time, or it is an ExpEvent indicating the event that just occured. As an example, a timed moment, will run when it recieves any Float64 value, but nothing occurs when passed an event.

The queue is a MomentQueue object, which has the same interface as the Dequeue object (from the DataStructures package) but it is also iterable. Upon calling handle, top(queue) == moment.

Handle should return a boolean indicating whether the event was "handled" or not. If unhandled, the moment should remain on top of the queue. If returning true, handle should normally remove the top moment from the queue. Exceptions exist (for instance, to allow for loops), but one does not normally need to implement custom moments that have such behavior.

Note

This method is part of the private interface for moments. It should not be called directly, but implemented as part of an extension. It is called during the course of running an experiment.

source
Weber.moment_traceFunction.
moment_trace(m)

Returns the stacktrace indicating where this moment was defined.

Note

This method is part of the private interface for moments. It should not be called directly, but implemented as part of an extension. You can get a stacktrace inside the function you define that constructs your custom moment using stacktrace()[2:end].

source
Weber.delta_tFunction.
delta_t(m::AbstractMoment)

Returns the time, since the start of the previous moment, at which this moment should begin. The default implementation returns zero.

Note

This method is part of the private interface for moments. It should not be called directly, but implemented as part of an extension.

source