Skip to content

extending CTAP

Ben Cowley edited this page Jun 8, 2016 · 8 revisions

How to extend CTAP functionality

This page describes how to write new CTAP_*() and ctapeeg_*() functions, or add functionality to existing functions. The user should of course refer to the provided templates and existing functions, but the most efficient way to proceed is probably to use the pipeline under varying settings to become familiar with the way it works. The templates for new CTAP_*() and ctapeeg_*() functions are in the /templates directory. Abridged versions are shown below, with only the most relevant lines of code for discussion.

New CTAP_*() functions

As we see the CTAP_template_function() structure is very simple. Conceptually, a core function is defined, which may require some arguments. These arguments are defined in the Cfg struct by the user, and extracted in the first few lines, overriding any locally-set defaults. The core function should return the full list of arguments used which then updates the Cfg struct for reference. The outcome should be logged, and an entry made to the EEG.CTAP.history struct.

function [EEG, Cfg] = CTAP_template_function(EEG, Cfg)

%% create Arg and assign any defaults to be chosen at the CTAP_ level
Arg = struct(); %set local defaults if required
Arg = joinstruct(Arg, Cfg.ctap.template_function); %override with user params

%% CORE Call the desired core function, deal with the output.
[EEG, Arg, result] = default_core_function(EEG, Arg);

%% ERROR/REPORT
Cfg.ctap.template_function = Arg;
%log outcome to console and to log file
msg = myReport(sprintf('Describe results. Refer to args %s, %s.',...
    Arg.arg1, Arg.arg2), Cfg.env.logFile);
%create an entry to the history struct, with 
%   1. informative message, 
%   2. function filename
%   3. %the complete parameter set from the function call, for reference
EEG.CTAP.history(end+1) = create_CTAP_history_entry(msg, mfilename, Arg);

%% MISC Miscellaneous additional actions following core function success

Extending CTAP_*() functions

Unlike some ctapeeg_*() functions, CTAP_*() functions are not particularly designed to be extended. Each one has a specific purpose which is fulfilled in a singular way. If there are competing methodological options for the required functionality, they are usually presented as a switch statement in the associated ctapeeg_*() function. Having said that, there are a few exceptions:

  • CTAP_extract_signal() - strips out channels which are not EEG, which may be some peripheral biosignal e.g. electrocardiography (ECG). Files with the extracted signal are saved as .edf, except for electrodermal activity, EDA, which is saved as a .mat file for import to Ledalab toolbox. Users may wish to extend the functionality to save to their preferred format.
  • CTAP_load_events() - users could define their own entry in the switch statement for event-loading methods; although there is already an option to pass a function handle, provided because event loading is usually very specific to each experiment.
  • CTAP_peek_data() - this function provides a large number of statistical and visual info about the state of the EEG data. However users may find that they have other visuals they wish to create.
  • CTAP_run_ica() - this contains a switch statement for the possible methods of ICA: users may wish to add a method, such as the binica method, which is currently not implemented because we prefer FastICA.

New ctapeeg_*() functions

The main idea of ctapeeg_*() functions is just to be independent of the Cfg and EEG.CTAP structures, hence why they appear in the directory /src/generic.

The ctapeeg_template_function() structure is as follows. Local parameters can be set as usual, and the fields which are defined here are canonical for the function - in other words, here is defined the set of parameters that the user can specify in their configuration script, and this set will be the one reported in EEG.CTAP.history when the function is called by its parent CTAP_*() function. However the input from varargin will overwrite local values. The core of many ctapeeg_*() functions is a switch statement which allows the function to act as a wrapper for multiple methods. On the other hand, ctapeeg_*() core can also just be a single functionality.

varargouts should be 1. the argument structure, 2. the results, if any. varargout is used as the interface because the calling functions may not use these outputs: the idea is that CTAP_pipeline_looper() is able to call a ctapeeg_*() function directly, although this may produce some slightly inferior results, such as for the EEG.CTAP.history entry for that call.

function [EEG, varargout] = ctapeeg_template_function(EEG, varargin)

%% Unpack and store varargin
% If desired, the default values can be changed here:
Arg.param1 = '';
Arg.param2 = [];
% Arg fields are canonical, vargs data is canonical: intersect-join
Arg = intersect_struct(Arg, varargin);

%% CORE
switch Arg.method
case X
    result = some_eeg_functionality(EEG, Arg);
case Y
    result = some_other_eeg_functionality(EEG, Arg);
end

%% OUTPUT
varargout{1} = Arg;
varargout{2} = result;

Extending ctapeeg_*() functions

Many ctapeeg_*() functions are structured around providing a set of method options as a switch statement. This is how all the ctapeeg_detect_bad_*() functions work, also ctapeeg_epoch_data(), ctapeeg_export() (this switches on data type rather than method), ctapeeg_load_data() (this switches on file extension). An exception is ctapeeg_reject_data(), which switches on the type of bad data provided, and and only the specified types are available. To specify a new type of bad data, the user would have to provide detection functions to create it, and rewrite both ctapeeg_reject_data() and CTAP_reject_data(). This is not advised!