image thumbnail

Custom triggers with Data Acquisition Toolbox (TM)

by

 

07 May 2009 (Updated )

Example of a data acquisition GUI with custom triggers

sw_pretrigger
% SW_PRETRIGGER
% A simple analog data acquisition gui with multiple channels and custom
% software pre-triggering 
%
% Requires: the Data Acquisition Toolbox and the Winsound adapter. It does
% not depend on any hardware-specific capabilities. 
%
% This file demonstrates how to: 
%
% 1) Use nested functions to simplify data acquisition programs
%
% 2) Use a simple GUI (e.g., a couple of pushbuttons) to start and stop 
%    data acquisition. 
%
% 3) Wait for an event/trigger (like a button press) and log pre-event or
%    pre-trigger data from multiple synchronized channels. (See more
%    detailed explanation below).
%
% 4) Record pretrigger data based on custom criteria. In the example below,
%    the triggering event is a button click. However, the event could be
%    any custom criterion, such as: 
%    * the data amplitude is greater than a threshold *and* a toggle button
%      is enabled, or
%    * the short-term correlation between the two channels is greater than
%      some threshold
%
% ----------------------------
% RECORDING PRE-TRIGGER DATA WITH MULTIPLE SYNCHRONIZED CHANNELS 
% (without using any hardware-specific capabilities)
%
% The Data Acquisition Toolbox supports triggered acquisition and 
% "pre-triggering" (i.e., the acquisition includes samples that occurred
% before the trigger event). 
% 
% However pretriggering can be tricky if you have multiple *synchronized*
% channels, as the hardware may not support synchronized acquisition of the
% pretrigger data. In this situation, it is simpler to do the
% pre-triggering yourself. The key idea is the following: 
%
%  * The DAQ hardware is started, synchronized and triggered at the
%    beginning of the session
%  * The data is acquired continuously from all the channels and added to a
%    circular buffer (http://en.wikipedia.org/wiki/Circular_buffer).  
%  * When the triggering is initiated (by pressing a button in a GUI)
%    the pretrigger data is extracted from the circular buffer and logged.
%
% ----------------------------
% RELEVANT MATLAB DOCUMENTATION
%
% Data Acquisition Toolbox
% http://www.mathworks.com/access/helpdesk/help/toolbox/daq/index.html 
%
% Configuring analog input triggers 
% http://www.mathworks.com/access/helpdesk/help/toolbox/daq/f9-49610.html
%
% Configuring the trigger delay (negative delay records "pretrigger" data)
% This is effective for single channels, but it does not guarantee
% synchronized acquisition across multiple channels.
% http://www.mathworks.com/access/helpdesk/help/toolbox/daq/triggerdelay.html

function sw_pretrigger

   if ~(exist('analoginput','file') == 2)
       error('This example requires the Data Acquisition Toolbox');
   end
   
    % ---------- create the gui
    myFigure = figure('units', 'pixels', ...
                       'menubar', 'none', ...
                      'DeleteFcn', @figureDelete);
    pos = get(myFigure,'Position');
    set(myFigure, 'Position', [pos(1:2) 130 130]);
    
    uicontrol('style', 'pushbutton', ...
              'string', 'Start data logging', ...
              'position', [10 60 100 40], ...
              'callback', @startLogging, ...
              'parent', myFigure);
          
    uicontrol('style', 'pushbutton', ...
              'string', 'Stop data logging', ...
              'position', [10 10 100 40], ...
              'callback', @stopLogging, ...
              'parent', myFigure);

              
    % ----------- configure the analog input objects

    % Using winsound for demo purposes.
    % Winsound doesn't allow selection of only channel 2, so
    % myAIObj0 and myAIObj1 both use channel 1.
    
    myAIObj0 = analoginput('winsound');
    addchannel(myAIObj0, 1);
    myAIObj1 = analoginput('winsound');
    addchannel(myAIObj1, 1);

    % code to synchronize the channels (e.g., configuring an external
    % clock or using HWDigitialTrigger) would go here
    
    mySampleRate = 11025;     % sample rate in hertz
    set([myAIObj0 myAIObj1], 'SampleRate', mySampleRate, ...
                   'TriggerType', 'Manual', ...
                   'SamplesPerTrigger', inf);

    nativeDataType = daqhwinfo(myAIObj0, 'NativeDataType');

    % The samples are stored in a circular buffer, which is a
    % computationally efficient way of storing the samples over the past 4 
    % seconds (myBufferRefillPeriod * myNumBuffers = 4).
    
    myBufferRefillPeriod = 0.4; % in seconds
    myBufferSize = ceil(myBufferRefillPeriod * mySampleRate); 
    myNumBuffers = 10;
    myNumChannels = 2;
    myCurrentBuffer = 1; % index of current buffer
    
    % the set of buffers
    myCircularBuffers = zeros(myBufferSize, myNumChannels, ...
                              myNumBuffers, nativeDataType);

    % the acquisition time of the first sample of each buffer                         
    myCircularBufferTimes = -1 * ones(myNumBuffers, 2);
    
    % The channels are presumed to be time-locked, so we only need to
    % configure SamplesAcquiredFcn for one channel
    set(myAIObj0, 'SamplesAcquiredFcnCount', myBufferSize, ...
             'SamplesAcquiredFcn', @refillBuffers);

    myDataBeingLogged = false;
    start([myAIObj0 myAIObj1]);
    trigger([myAIObj0 myAIObj1]);

    %% --------------------------------------
    function figureDelete(obj,eventdata) %#ok<INUSD,INUSD>
        % figure got deleted, so clean up analoginput objects
        stop([myAIObj0 myAIObj1]);
        delete([myAIObj0 myAIObj1]);        
        fprintf('Cleaned up analoginput objects\n');
    end
    
    %% --------------------------------------
    function startLogging(obj,eventdata) %#ok<INUSD,INUSD>     
        if myDataBeingLogged
            return; 
        end
                        
        % reorder the buffers to get the right time sequence
        indices = [myCurrentBuffer:myNumBuffers 1:myCurrentBuffer-1];      
        
        % If you have additional trigger conditions, put the logic here. 
        % proceedWithLogging = customCriterion(myCircularBuffers, indices);
        % if ~proceedWithLogging
        %    return;
        % end
        
        logStartTime = get(myAIObj0,'SamplesAcquired') / get(myAIObj0, 'SampleRate');        
        fprintf('\nStarting data logging (trigger at %4.4f sec)\n', logStartTime);
        
        % log the pre-trigger data
        fprintf('Pre-trigger data\n');        
        for i = indices
            if myCircularBufferTimes(i) >= 0
                logData(myCircularBuffers(:,:,i), myCircularBufferTimes(i,:));
            end
        end
        
        % all the data logged from here on is post-trigger
        fprintf('Post-trigger data\n');        
        myDataBeingLogged = true;
    end
    
    %% --------------------------------------
    function stopLogging(obj,eventdata) %#ok<INUSD,INUSD>   
        if ~myDataBeingLogged
            return; 
        end        
        fprintf('Stopping data logging\n');
        myDataBeingLogged = false;
    end   

    %% --------------------------------------
    function refillBuffers(obj,eventdata) %#ok<INUSD,INUSD>        
        ai0NumSamples = get(myAIObj0, 'SamplesAvailable');
        ai1NumSamples = get(myAIObj1, 'SamplesAvailable');
        if ai0NumSamples < myBufferSize || ai1NumSamples < myBufferSize
            return; 
        end
        
        [ai0data,ai0time] = getdata(myAIObj0, myBufferSize, 'native');
        ai1data           = getdata(myAIObj1, myBufferSize, 'native');

        newData = [ai0data ai1data];
        newDataTime = [ai0time(1) ai0time(end)];
        
        if myDataBeingLogged
            logData(newData, newDataTime);
        end

        myCircularBuffers(:,:,myCurrentBuffer) = newData;
        myCircularBufferTimes(myCurrentBuffer,:) = newDataTime;        
        if myCurrentBuffer == myNumBuffers
            myCurrentBuffer = 1;
        else
            myCurrentBuffer = myCurrentBuffer + 1;
        end                
    end
    
    %% --------------------------------------
    function logData(data,times)        
        [nsamples, nchannels] = size(data);
        fprintf('  Logging %04d samples x %d channels (samples from %2.4f to %2.4f sec)\n', ...
            nsamples, nchannels, times(1), times(2));
        % custom code for logging the data (e.g., saving to a file or
        % plotting) would go here        
    end
    
end

Contact us