Contents

Gain from axon binary file

back to top
%---------------------------------
function datagain = abfgain(abf_file)
% gain = abfgain(abf_filename)
%
% input
% abf_file = name of abf file on path
%
% output
% datagain = amplitude gain

%Search the file for the Gain information
myfileN = abf_file; %enter file name
fid = fopen(myfileN,'r','ieee-le'); %open the abf file
sz=1;  %# of data entries to read
numType = 'float'; %precision format
myGainfileoffset = 268; %find the correct offset where data entry for gain begins
fseek(fid,myGainfileoffset,'bof'); %move field position
datagain = fread(fid,sz,numType); %read formatted data
fclose(fid); %close file

%---------------------------------

Load data from axon binary file

back to top
%---------------------------------
function [d,si]=abfload(fn,varargin)
% ** function [d,si]=abfload(fn,varargin)
% loads and returns data in the Axon abf format PRIOR TO VERSION 2.0.
% Data may have been acquired in the following modes:
% (1) event-driven variable-length
% (2) event-driven fixed-length
% (3) gap-free
% Information about scaling, the time base and the number of channels and
% episodes is extracted from the header of the abf file (see also abfinfo.m).
% All optional input parameters listed below (= all except the file name)
% must be specified as parameter/value pairs, e.g. as in
%          d=abfload('d:\data01.abf','start',100,'stop','e');
%
%                    >>> INPUT VARIABLES >>>
%
% NAME        TYPE, DEFAULT      DESCRIPTION
% fn          char array         abf data file name
% start       scalar, 0          only gap-free-data: start of cutout to be read (unit: sec)
% stop        scalar or char,    only gap-free-data: end of cutout to be read (unit: sec).
%             'e'                 May be set to 'e' (end of file).
% sweeps      1d-array or char,  only episodic data: sweep numbers to be read. By default,
%             'a'                 all sweeps will be read ('a')
% channels    cell array         names of channels to be read, like {'IN 0','IN 8'};
%              or char, 'a'       ** make sure spelling is 100% correct (including blanks) **
%                                 if set to 'a', all channels will be read
% chunk       scalar, 0.05       only gap-free-data: the elementary chunk size (Megabytes)
%                                 to be used for the 'discontinuous' mode of reading data
%                                 (when less channels shall be read than exist in file)
% machineF    char array,        the 'machineformat' input parameter of the
%              'ieee-le'          matlab fopen function. 'ieee-le' is the correct
%                                 option for windows; depending on the
%                                 platform the data were recorded/shall be read
%                                 by abfload 'ieee-be' is the alternative.
%
%                    <<< OUTPUT VARIABLES <<<
%
% NAME        TYPE            DESCRIPTION
% d                           the data read, the format depending on the recording mode
%             1. GAP-FREE:
%             2d array        2d array of size
%                              '<data pts>' by '<number of chans>'
%                              Examples of access:
%                              d(:,2)       data from channel 2 at full length
%                              d(1:100,:)   first 100 data points from all channels
%             2. EPISODIC FIXED-LENGTH:
%             3d array        3d array of size
%                              '<data pts per sweep>' by '<number of chans>' by '<number of sweeps>'
%                              Examples of access:
%                              d(:,2,:)            is a matrix which contains all events (at full length)
%                                                  of channel 2 in its columns
%                              d(1:200,:,[1 11])   contains first 200 data points of events #1
%                                                  and #11 of all channels
%             3. EPISODIC VARIABLE-LENGTH:
%             cell array      cell array whose elements correspond to single sweeps. Each element is
%                              a (regular) array of size
%                              '<data pts per sweep>' by '<number of chans>'
%                              Examples of access:
%                              d{1}                 a 2d-array which contains sweep #1 (all of it, all channels)
%                              d{2}(1:100,2)        a 1d-array containing first 100 data points of of channel 2 in sweep #1
%
% si          scalar          the sampling interval in usec
%
% Date of this version: June 17, 2011

% future improvements:
% - handle expansion of header in transition to file version 1.65 better

% --- defaults
% gap-free
start=0.0;
stop='e';
% episodic
sweeps='a';
% general
channels='a';
% the size of data chunks (see above) in Mb. 0.05 Mb is an empirical value
% which works well for abf with 6-16 channels and recording durations of
% 5-30 min
chunk=0.05;
machineF='ieee-le';
verbose=1;

% assign values of optional input parameters, if any were given
pvpmod(varargin);

if verbose, disp(['** ' mfilename]); end
d=[];
si=[];
if ischar(stop)
  if ~strcmpi(stop,'e')
    error('input parameter ''stop'' must be specified as ''e'' (=end of recording) or as a scalar');
  end
end

% --- obtain vital header parameters and initialize them with -1
% temporary initializing var
tmp=repmat(-1,1,16);
% for the sake of typing economy set up a cell array (and convert it to a struct below)
% column order is
%        name, position in header in bytes, type, value)
headPar={
  'fFileVersionNumber',4,'float',-1;
  'nOperationMode',8,'int16',-1;
  'lActualAcqLength',10,'int32',-1;
  'nNumPointsIgnored',14,'int16',-1;
  'lActualEpisodes',16,'int32',-1;
  'lFileStartTime',24,'int32',-1;
  'lDataSectionPtr',40,'int32',-1;
  'lSynchArrayPtr',92,'int32',-1;
  'lSynchArraySize',96,'int32',-1;
  'nDataFormat',100,'int16',-1;
  'nADCNumChannels', 120, 'int16', -1;
  'fADCSampleInterval',122,'float', -1;
  'fSynchTimeUnit',130,'float',-1;
  'lNumSamplesPerEpisode',138,'int32',-1;
  'lPreTriggerSamples',142,'int32',-1;
  'lEpisodesPerRun',146,'int32',-1;
  'fADCRange', 244, 'float', -1;
  'lADCResolution', 252, 'int32', -1;
  'nFileStartMillisecs', 366, 'int16', -1;
  'nADCPtoLChannelMap', 378, 'int16', tmp;
  'nADCSamplingSeq', 410, 'int16',  tmp;
  'sADCChannelName',442, 'uchar', repmat(tmp,1,10);
  'fADCProgrammableGain', 730, 'float', tmp;
  'fInstrumentScaleFactor', 922, 'float', tmp;
  'fInstrumentOffset', 986, 'float', tmp;
  'fSignalGain', 1050, 'float', tmp;
  'fSignalOffset', 1114, 'float', tmp;
  'nTelegraphEnable',4512,'int16',tmp;
  'fTelegraphAdditGain',4576,'float',tmp
};

fields={'name','offs','numType','value'};
s=cell2struct(headPar,fields,2);
numOfParams=size(s,1);
clear tmp headPar;

if ~exist(fn,'file'), error(['could not find file ' fn]); end
if verbose, disp(['opening ' fn '..']); end
[fid,messg]=fopen(fn,'r',machineF);

if fid == -1,error(messg);end;
% determine absolute file size
fseek(fid,0,'eof');
fileSz=ftell(fid);
fseek(fid,0,'bof');

% read all vital information in header
% convert names in structure to variables and read value from header
for g=1:numOfParams,
  if fseek(fid, s(g).offs,'bof')~=0,
    fclose(fid);
    error(['something went wrong locating ' s(g).name]);
  end;
  sz=length(s(g).value);
  eval(['[' s(g).name ',n]=fread(fid,sz,''' s(g).numType ''');']);
  if n~=sz,
    fclose(fid);
    error(['something went wrong reading value(s) for ' s(g).name]);
  end;
end;

if lActualAcqLength<nADCNumChannels,
  fclose(fid);
  error('less data points than sampled channels in file');
end
% the numerical value of all recorded channels (numbers 0..15)
recChIdx=nADCSamplingSeq(1:nADCNumChannels);
% the corresponding indices into loaded data d
recChInd=1:length(recChIdx);
% the channel names, e.g. 'IN 8'
recChNames=(reshape(char(sADCChannelName),10,16))';
recChNames=recChNames(recChIdx+1,:);

chInd=[];
eflag=0;
if ischar(channels)
  if strcmp(channels,'a')
    chInd=recChInd;
  else
    fclose(fid);
    error('input parameter ''channels'' must either be a cell array holding channel names or the single character ''a'' (=all channels)');
  end
else
  for i=1:length(channels)
    tmpChInd=strmatch(channels{i},recChNames,'exact');
    if ~isempty(tmpChInd)
      chInd=[chInd tmpChInd];
    else
      % set error flag to 1
      eflag=1;
    end
  end;
end
if eflag
  fclose(fid);
  disp('**** available channels:');
  disp(recChNames);
  disp(' ');
  disp('**** requested channels:');
  disp(strvcat(channels));
  error('at least one of the requested channels does not exist in data file (see above)');
end

% fFileVersionNumber needs a fix - for whatever reason its value is sometimes
% a little less than what it should be (e.g. 1.6499999xxxx instead of 1.65)
fFileVersionNumber=.001*round(fFileVersionNumber*1000);

% gain of telegraphed instruments, if any
if fFileVersionNumber>=1.65
  addGain=nTelegraphEnable.*fTelegraphAdditGain;
  addGain(addGain==0)=1;
else
  addGain=ones(size(fTelegraphAdditGain));
end

% tell me where the data start
blockSz=512;
switch nDataFormat
  case 0
    dataSz=2;  % bytes/point
    precision='int16';
  case 1
    dataSz=4;  % bytes/point
    precision='float32';
  otherwise
    fclose(fid);
    error('invalid number format');
end;
headOffset=lDataSectionPtr*blockSz+nNumPointsIgnored*dataSz;
% fADCSampleInterval is the TOTAL sampling interval
si=fADCSampleInterval*nADCNumChannels;

if ischar(sweeps) && sweeps=='a'
  nSweeps=lActualEpisodes;
  sweeps=1:lActualEpisodes;
else
  nSweeps=length(sweeps);
end;

switch nOperationMode
  case 1
    if verbose,
      disp('data were acquired in event-driven variable-length mode');
    end
    warndlg('function abfload has not yet been thorougly tested for data in event-driven variable-length mode - please double-check that the data loaded is correct','Just a second, please');
    if (lSynchArrayPtr<=0 || lSynchArraySize<=0),
      fclose(fid);
      error('internal variables ''lSynchArraynnn'' are zero or negative');
    end
    switch fSynchTimeUnit
      case 0  % time information in synch array section is in terms of ticks
        synchArrTimeBase=1;
      otherwise % time information in synch array section is in terms of usec
        synchArrTimeBase=fSynchTimeUnit;
    end;
    % the byte offset at which the SynchArraySection starts
    lSynchArrayPtrByte=blockSz*lSynchArrayPtr;
    % before reading Synch Arr parameters check if file is big enough to hold them
    % 4 bytes/long, 2 values per episode (start and length)
    if lSynchArrayPtrByte+2*4*lSynchArraySize<fileSz,
      fclose(fid);
      error('file seems not to contain complete Synch Array Section');
    end
    if fseek(fid,lSynchArrayPtrByte,'bof')~=0,
      fclose(fid);
      error('something went wrong positioning file pointer to Synch Array Section');
    end
    [synchArr,n]=fread(fid,lSynchArraySize*2,'int32');
    if n~=lSynchArraySize*2,
      fclose(fid);
      error('something went wrong reading synch array section');
    end
    % make synchArr a lSynchArraySize x 2 matrix
    synchArr=permute(reshape(synchArr',2,lSynchArraySize),[2 1]);
    % the length of episodes in sample points
    segLengthInPts=synchArr(:,2)/synchArrTimeBase;
    % the starting ticks of episodes in sample points WITHIN THE DATA FILE
    segStartInPts=cumsum([0 (segLengthInPts(1:end-1))']*dataSz)+headOffset;
    % start time (synchArr(:,1)) has to be divided by nADCNumChannels to get true value
    % go to data portion
    if fseek(fid,headOffset,'bof')~=0,
      fclose(fid);
      error('something went wrong positioning file pointer (too few data points ?)');
    end
    for i=1:nSweeps,
      % if selected sweeps are to be read, seek correct position
      if ~isequal(nSweeps,lActualEpisodes),
        fseek(fid,segStartInPts(sweeps(i)),'bof');
      end;
      [tmpd,n]=fread(fid,segLengthInPts(sweeps(i)),precision);
      if n~=segLengthInPts(sweeps(i)),
        warning(['something went wrong reading episode ' int2str(sweeps(i)) ': ' segLengthInPts(sweeps(i)) ' points should have been read, ' int2str(n) ' points actually read']);
      end;
      dataPtsPerChan=n/nADCNumChannels;
      if rem(n,nADCNumChannels)>0,
        fclose(fid);
        error('number of data points in episode not OK');
      end
      % separate channels..
      tmpd=reshape(tmpd,nADCNumChannels,dataPtsPerChan);
      % retain only requested channels
      tmpd=tmpd(chInd,:);
      tmpd=tmpd';
      % if data format is integer, scale appropriately; if it's float, tmpd is fine
      if ~nDataFormat
        for j=1:length(chInd),
          ch=recChIdx(chInd(j))+1;
          tmpd(:,j)=tmpd(:,j)/(fInstrumentScaleFactor(ch)*fSignalGain(ch)*fADCProgrammableGain(ch)*addGain(ch))...
            *fADCRange/lADCResolution+fInstrumentOffset(ch)-fSignalOffset(ch);
        end;
      end
      % now place in cell array, an element consisting of one sweep with channels in columns
      d{i}=tmpd;
    end;
  case {2,5}
    if nOperationMode==2
      if verbose
        disp('data were acquired in event-driven fixed-length mode');
      end
    else
      if verbose
        disp('data were acquired in waveform fixed-length mode (clampex only)');
      end
    end
    % determine first point and number of points to be read
    startPt=0;
    dataPts=lActualAcqLength;
    dataPtsPerChan=dataPts/nADCNumChannels;
    if rem(dataPts,nADCNumChannels)>0,
      fclose(fid);
      error('number of data points not OK');
    end
    dataPtsPerChanPerSweep=dataPtsPerChan/lActualEpisodes;
    if rem(dataPtsPerChan,lActualEpisodes)>0
      fclose(fid);
      error('number of data points not OK');
    end
    dataPtsPerSweep=dataPtsPerChanPerSweep*nADCNumChannels;
    if fseek(fid,startPt*dataSz+headOffset,'bof')~=0
      fclose(fid);
      error('something went wrong positioning file pointer (too few data points ?)');
    end
    d=zeros(dataPtsPerChanPerSweep,length(chInd),nSweeps);
    % the starting ticks of episodes in sample points WITHIN THE DATA FILE
    selectedSegStartInPts=((sweeps-1)*dataPtsPerSweep)*dataSz+headOffset;
    for i=1:nSweeps,
      fseek(fid,selectedSegStartInPts(i),'bof');
      [tmpd,n]=fread(fid,dataPtsPerSweep,precision);
      if n~=dataPtsPerSweep,
        fclose(fid);
        error(['something went wrong reading episode ' int2str(sweeps(i)) ': ' dataPtsPerSweep ' points should have been read, ' int2str(n) ' points actually read']);
      end;
      dataPtsPerChan=n/nADCNumChannels;
      if rem(n,nADCNumChannels)>0
        fclose(fid);
        error('number of data points in episode not OK');
      end;
      % separate channels..
      tmpd=reshape(tmpd,nADCNumChannels,dataPtsPerChan);
      % retain only requested channels
      tmpd=tmpd(chInd,:);
      tmpd=tmpd';
      % if data format is integer, scale appropriately; if it's float, d is fine
      if ~nDataFormat
        for j=1:length(chInd),
          ch=recChIdx(chInd(j))+1;
          tmpd(:,j)=tmpd(:,j)/(fInstrumentScaleFactor(ch)*fSignalGain(ch)*fADCProgrammableGain(ch)*addGain(ch))...
            *fADCRange/lADCResolution+fInstrumentOffset(ch)-fSignalOffset(ch);
        end;
      end
      % now fill 3d array
      d(:,:,i)=tmpd;
    end;

  case 3
    if verbose, disp('data were acquired in gap-free mode'); end
    % from start, stop, headOffset and fADCSampleInterval calculate first point to be read
    %  and - unless stop is given as 'e' - number of points
    startPt=floor(1e6*start*(1/fADCSampleInterval));
    % this corrects undesired shifts in the reading frame due to rounding errors in the previous calculation
    startPt=floor(startPt/nADCNumChannels)*nADCNumChannels;
    % if stop is a char array, it can only be 'e' at this point (other values would have
    % been caught above)
    if ischar(stop),
      dataPtsPerChan=lActualAcqLength/nADCNumChannels-floor(1e6*start/si);
      dataPts=dataPtsPerChan*nADCNumChannels;
    else
      dataPtsPerChan=floor(1e6*(stop-start)*(1/si));
      dataPts=dataPtsPerChan*nADCNumChannels;
      if dataPts<=0
        fclose(fid);
        error('start is larger than or equal to stop');
      end
    end
    if rem(dataPts,nADCNumChannels)>0
      fclose(fid);
      error('number of data points not OK');
    end
    if fseek(fid,startPt*dataSz+headOffset,'bof')~=0,
      fclose(fid);
      error('something went wrong positioning file pointer (too few data points ?)');
    end
    % *** decide on the most efficient way to read data:
    % (i) all (of one or several) channels requested: read, done
    % (ii) one (of several) channels requested: use the 'skip' feature of
    % fread
    % (iii) more than one but not all (of several) channels requested:
    % 'discontinuous' mode of reading data. Read a reasonable chunk of data
    % (all channels), separate channels, discard non-requested ones (if
    % any), place data in preallocated array, repeat until done. This is
    % faster than reading the data in one big lump, separating channels and
    % discarding the ones not requested
    if length(chInd)==1 && nADCNumChannels>1
      % --- situation (ii)
      % jump to proper reading frame position in file
      if fseek(fid,(chInd-1)*dataSz,'cof')~=0
        fclose(fid);
        error('something went wrong positioning file pointer (too few data points ?)');
      end
      % read, skipping nADCNumChannels-1 data points after each read
      dataPtsPerChan=dataPts/nADCNumChannels;
      [d,n]=fread(fid,dataPtsPerChan,precision,dataSz*(nADCNumChannels-1));
      if n~=dataPtsPerChan,
        fclose(fid);
        error(['something went wrong reading file (' int2str(dataPtsPerChan) ' points should have been read, ' int2str(n) ' points actually read']);
      end
    elseif length(chInd)/nADCNumChannels<1
      % --- situation (iii)
      % prepare chunkwise upload:
      % preallocate d
      d=repmat(nan,dataPtsPerChan,length(chInd));
      % the number of data points corresponding to the maximal chunk size,
      % rounded off such that from each channel the same number of points is
      % read (do not forget that each data point will by default be made a
      % double of 8 bytes, no matter what the original data format is)
      chunkPtsPerChan=floor(chunk*2^20/8/nADCNumChannels);
      chunkPts=chunkPtsPerChan*nADCNumChannels;
      % the number of those chunks..
      nChunk=floor(dataPts/chunkPts);
      % ..and the remainder
      restPts=dataPts-nChunk*chunkPts;
      restPtsPerChan=restPts/nADCNumChannels;
      % chunkwise row indices into d
      dix=(1:chunkPtsPerChan:dataPtsPerChan)';
      dix(:,2)=dix(:,1)+chunkPtsPerChan-1;
      dix(end,2)=dataPtsPerChan;
      if verbose && nChunk
        disp(['reading file in ' int2str(nChunk) ' chunks of ~' num2str(chunk) ' Mb']);
      end
      % do it: if no remainder exists loop through all rows of dix,
      % otherwise spare last row for the lines below (starting with
      % 'if restPts')
      for ci=1:size(dix,1)-(restPts>0)
        [tmpd,n]=fread(fid,chunkPts,precision);
        if n~=chunkPts
          fclose(fid);
          error(['something went wrong reading chunk #' int2str(ci) ' (' ...
            int2str(chunkPts) ' points should have been read, ' int2str(n) ' points actually read']);
        end
        % separate channels..
        tmpd=reshape(tmpd,nADCNumChannels,chunkPtsPerChan);
        d(dix(ci,1):dix(ci,2),:)=tmpd(chInd,:)';
      end
      % collect the rest
      [tmpd,n]=fread(fid,restPts,precision);
      if n~=restPts
        fclose(fid);
        error(['something went wrong reading last chunk (' ...
          int2str(restPts) ' points should have been read, ' int2str(n) ' points actually read']);
      end
      % separate channels..
      tmpd=reshape(tmpd,nADCNumChannels,restPtsPerChan);
      d(dix(end,1):dix(end,2),:)=tmpd(chInd,:)';
    else
      % --- situation (i)
      [d,n]=fread(fid,dataPts,precision);
      if n~=dataPts,
        fclose(fid);
        error(['something went wrong reading file (' int2str(dataPts) ' points should have been read, ' int2str(n) ' points actually read']);
      end;
      % separate channels..
      d=reshape(d,nADCNumChannels,dataPtsPerChan);
      d=d';
    end
    % if data format is integer, scale appropriately; if it's float, d is fine
    if ~nDataFormat
      for j=1:length(chInd),
        ch=recChIdx(chInd(j))+1;
        d(:,j)=d(:,j)/(fInstrumentScaleFactor(ch)*fSignalGain(ch)*fADCProgrammableGain(ch)*addGain(ch))...
          *fADCRange/lADCResolution+fInstrumentOffset(ch)-fSignalOffset(ch);
      end;
    end
  otherwise
    disp('recording mode of data must be event-driven variable-length (1), event-driven fixed-length (2) or gap-free (3) -- returning empty matrix');
    d=[];
    si=[];
end;

fclose(fid);

%---------------------------------

Controller for abfload function

back to top
%---------------------------------
function pvpmod(x)
% PVPMOD             - evaluate parameter/value pairs
% pvpmod(x) assigns the value x(i+1) to the parameter defined by the
% string x(i) in the calling workspace. This is useful to evaluate
% <varargin> contents in an mfile, e.g. to change default settings
% of any variable initialized before pvpmod(x) is called.
%
% (c) U. Egert 1998
% this loop is assigns the parameter/value pairs in x to the calling
% workspace.

if ~isempty(x)
   for i = 1:2:size(x,2)
      assignin('caller', x{i}, x{i+1});
   end;
end;

%---------------------------------

Filter waveform data

back to top
%---------------------------------
function yfilt = myFilter(y)
%load data that need to be filtered
%a zerophase filter will be used to process data
%the zero phase filter parameters will filter out large noise data
%like drift.

%input
%   unfiltered signal "y"

%output
%   filtered signal "yfilt"

% Display start of filtering
disp('Filtering Signal...');
disp(' ');
% filter cell1 and assign to workspace
windowSize = round(0.025*length(y))*2 + 1; %get an odd value for window, approx 5% wavefrom length
tempfilt = filtfilt(ones(windowSize,1)/windowSize,1,y); %set zero phase filter parameters
y = y - tempfilt; %remove any large noise fluxuations, like drift
y = abs(-y); %make all values positive
setzero = min(y); %get min value
y = y-setzero; %%bring min value to zero

yfilt = y;

% Display end of filtering
disp('...Filtering Complete');
disp(' ');

%---------------------------------

Cropp waveform

back to top
%---------------------------------
function [croppedSignal, limitsCrop, hfigOriginal, hfigCrop] = signalCrop(Signal)
% % [croppedSignal, limitsCrop] = signalCrop(Signal);
% % Crop a signal by selecting points with mouse
% %
% % Input
% % (Signal) - input waveform that needs to be cropped
% %
% % Output
% % (croppedSignal) - cropped waveform
% % (limitsCrop) - original indices or selected waveform to be cropped
% % (hfigOriginal) - handle to original figure window
% % (hfigCrop) - handle to cropped figure window

mySignal = Signal;

% initialize titles on figure
hfigOriginal = figure('name','Original Signal');
plot(mySignal); %plot signal
title({'CLICK TO CROP THE SIGNAL';...
    'Use LEFT Click to add Vertical lmit';...
    'Use RIGHT CLICK to add LAST limit'});
disp('CROP LIMITS');
disp('Left mouse button picks points.');
disp('Right mouse button picks last point.');
disp(' ');

% use ginput to get user interaction. Continue until terminated with right
% click
xyCropp = []; %initialize clicked coordinates variable
n = 0; %counts number of times clicked
lastpoint = 1; %tracks left click, right click and middle click != 1
pointsY = axis; %assigns axis dimensions
pointsY = pointsY(3:4); %keeps only mySignal dimensions
while lastpoint == 1
    [xi,yi,lastpoint] = ginput(1); %ginput calls input routine
    line([xi,xi],pointsY,'color','r') %uses last input and draws vertical line in red
    n = n+1; %increment clicks
    xyCropp(:,n) = [xi;yi]; %save click coordinates, use later to reference new cropped graph
end

% select subregion of signal using user input
starty = round(min(xyCropp(1,:)));
endy = round(max(xyCropp(1,:)));
croppedSignal = mySignal(starty:endy);
limitsCrop = [starty,endy]; % save the cropped limits for future use

% plot the cropped signal
hfigCrop = figure('name','Cropped Signal');
plot(croppedSignal);
title('Cropped Signal');

%---------------------------------

Peak detection

back to top
%---------------------------------
function [peakSet peakThreshold hfigPeak] = getPeaks(Signal)
% Interactive Peak Detection by Threshold
% peakSet = getPeaks(Signal);
%
% Input
% Signal - give a waveform signal, should be continuous
%
% Output
% peakSet - gives the indices of all peaks above threshold level
% peakThreshold - peak threshold value
% hfigPeak - handle to peakSet figure window
%
% Interective Threshold method of peak detection. Left click allows to
% refresh peak detection. Any click other than left ends peak search
% and outputs peaks.
% Search relies on user input threshold. All values above threshold are
% isolated and clusted by checking for discontinuites with respect to
% indices. The max value (y(x)) of each peak set of values is determined.
% Noise, classified as signals that oscillate through the threshold on the
% rising or falling legs of a peak, generates false positive results.
% False positives are removed by comparing two lists, threshold vs
% 50%threshold. Values that are found in both lists are preserved.
%

% E. Maravilla, 2014/03/05

mySignal = Signal;
disp('PEAK THRESHOLD');
disp('Left mouse button sets threshold.')
disp('Right mouse button sets threshold and terminates interaction.')
disp(' ');

xyThresh = []; %initialize threshold variable
n = 0; %counts number of times clicked
lastpoint = 1; %tracks left click, =1. Right click and middle click =2 & =3

% Initialize figure titles
hfigPeak = figure('name','Cropped Signal for Peak Detection');
plot(mySignal); %plot signal
title({'Click to ADD threshold value';...
    'use LEFT CLICK to add a new threshold';...
    'use RIGHT CLICK to add final threshold'});
pointsX = axis; %assigns axis dimensions
pointsX = pointsX(1:2); %keeps only X dimensions

% use ginput function to get user input. iterate until terminated with
% right click
while lastpoint == 1
    [xi,yi,lastpoint] = ginput(1); %ginput calls input routine
    plot(mySignal); %redraw figure
    title({'Click to ADD threshold value';...
    'use LEFT CLICK to add a new threshold';...
    'use RIGHT CLICK to add final threshold'});
    xlim(pointsX); %maintain x-axis limits
    line(pointsX,[yi,yi],'color','r'); %uses last input and draws vertical line in red
    n = n+1; %increment clicks
    xyThresh = [xi;yi]; %save click coordinates
    peaksThresh = yi; %threshold
    %%Geat max point from set of values above a threshold
    xThresh = find(mySignal > peaksThresh); %get index (w.r.t mySignal) above threshold value
    xThreshdiff = diff(xThresh); %get the neighbor distance between successive values that satisfied threshold screen
    %length is 1 less than xThresh length
    xCluster = find(xThreshdiff > 1); %get index that marks end of peak
    xCluster = xCluster+1; %shift, now index marks start of peaks
    %xCutoff = xThresh(xCluster); %get the original inices w.r.t ycropped of the cutoffs
    %yValues = mySignal(xThresh); %get y values of peaks
    %iterate through each set of peak values and find local max
    Apeak = [];
    for k = 1:length(xCluster)+1 %need one more iteration to find last peak
        if k == 1 %case when finding first peak
            peakSet = xThresh(1:xCluster(k)-1);
            [tempY,tempX] = max(mySignal(peakSet));
            Apeak(1) = peakSet(tempX);
        elseif k == length(xCluster)+1 %case when finding last peak
            peakSet = xThresh(xCluster(k-1):end); %cannot access k when index = length+1
            [tempY,tempX] = max(mySignal(peakSet));
            Apeak(k) = peakSet(tempX);
        else %case when finding peaks between first and last
            peakSet = xThresh(xCluster(k-1):xCluster(k)-1); %never reached k becasue have separate case
            [tempY,tempX] = max(mySignal(peakSet));
            Apeak(k) = peakSet(tempX);
        end
    end
    hold on; plot(Apeak,mySignal(Apeak),'go');
    hold off;

% Eliminate noise on rising/falling legs of peaks that cross thresh
% level. This will eliminate false positive peaks
    if lastpoint ~= 1
        plot(mySignal); %redraw figure
        title({'Click to ADD threshold value';...
        'use LEFT CLICK to add a new threshold';...
        'use RIGHT CLICK to add final threshold'});
        xlim(pointsX); %maintina x-axis limits
        line(pointsX,[yi,yi],'color','r'); %uses last input and draws vertical line in red
        n = n+1; %increment clicks
        xyThresh = [xi;yi]; %save click coordinates
        peaksThresh = 0.25*yi; %--==Use lower thresh, Keep peaks that are present under both threshold conditions==--
        %%Geat max point from set of values above a threshold
        xThresh = find(mySignal > peaksThresh); %get index (w.r.t mySignal) above threshold value
        xThreshdiff = diff(xThresh); %get the neighbor distance between successive values that satisfied threshold screen
        %length is 1 less than xThresh length
        xCluster = find(xThreshdiff > 1); %get index that marks end of peak
        xCluster = xCluster+1; %shift, now index marks start of peaks
        %xCutoff = xThresh(xCluster); %get the original inices w.r.t ycropped of the cutoffs
        %yValues = mySignal(xThresh); %get y values of peaks
        %iterate through each set of peak values and find local max
        Apeak2 = [];
        for k = 1:length(xCluster)+1 %need one more iteration to find last peak
            if k == 1 %case when finding first peak
                peakSet = xThresh(1:xCluster(k)-1);
                [tempY,tempX] = max(mySignal(peakSet));
                Apeak2(1) = peakSet(tempX);
            elseif k == length(xCluster)+1 %case when finding last peak
                peakSet = xThresh(xCluster(k-1):end); %cannot access k when index = length+1
                [tempY,tempX] = max(mySignal(peakSet));
                Apeak2(k) = peakSet(tempX);
            else %case when finding peaks between first and last
                peakSet = xThresh(xCluster(k-1):xCluster(k)-1); %never reached k becasue have separate case
                [tempY,tempX] = max(mySignal(peakSet));
                Apeak2(k) = peakSet(tempX);
            end
        end
        %%compare both peak lists, comparing x-coordinate, if found twice then keep
        [comboApeak,choose1] = max([length(Apeak),length(Apeak2)]); %find longest list
        switch choose1
            case 1
                temp = zeros(1,length(Apeak));
                temp(1:length(Apeak2)) = Apeak2;
                Apeak2 = temp;
                matchApeak = [];
                countPeaks = 1;
                for k = 1:length(Apeak)
                    checkPeak = find(Apeak == Apeak2(k)); %will give index of match or empty matrix
                    if ~isempty(checkPeak)
                        matchApeak(countPeaks) = checkPeak;
                        countPeaks = countPeaks+1;
                    else
                        continue
                    end
                end
                Apeak = Apeak(matchApeak);
            case 2
                temp = zeros(1,length(Apeak2));
                temp(1:length(Apeak)) = Apeak;
                Apeak = temp;
                matchApeak = [];
                countPeaks = 1;
                for k = 1:length(Apeak2)
                    checkPeak = find(Apeak2 == Apeak(k));
                    if ~isempty(checkPeak)
                        matchApeak(countPeaks) = checkPeak;
                        countPeaks = countPeaks+1;
                    else
                        continue
                    end
                end
                Apeak = Apeak2(matchApeak);
        end

        hold on; plot(Apeak,mySignal(Apeak),'go');
        hold off;
     end

end
% close gcf
peakThreshold = yi;
peakSet = Apeak;

%---------------------------------

Event Detection

back to top
%---------------------------------
function [myEvents, myEventsFlag, hfigEvents] = getEvents(peakSet,Signal)
%Identify, Plot, and Flag Events as "Good" and "Bad"
%
% [myEvents, myEventsFlag] = getEvents(peakSet,Signal)
%
% -=INPUT=-
% peakSet - index of list of all peaks w.r.t. Signal
% Signal - data signal
%
% -=OUTPUT=-
% myEvents - [#x3] list of #events with [start,peak,end] of event
% myEventsFlag - [#x1] array of 1's and 0's indentifying "good" & "bad" events, 1=bad
% hfigEvents = handle to events figure window

y3 = Signal; %reassign variable
peakcount = 1; %initialize peakcount, will be used to keep count when peak from peakSet is skipped
for i=1:length(peakSet);
    % initial conditions for event
    % need to find a & c, the start and end index of event
    peaktemp = peakSet(i); %get index of peak w.r.t event
    % get index of peak
    b = peaktemp;

    % Left leg of peak
    if peaktemp < 101 % skip peak if the left of waveform from peak is less than 101 points
        continue
    end
    % compute a running average of slope values
    for j = 1:100 %iterate an arbitrary number of times and collect average slope values (span 5 points)
        ytemp = y3(peaktemp-5 +1 -(j-1):peaktemp -(j-1)); %-5+1 = span 5 to the left, -(j-1) = shift left by amount indicated counter
        slopetemp = diff(ytemp); %the 1st order discrete derivative using a curve that spans 5 points
        slopemean(j) = mean(slopetemp); %average of slope values
    end

    % find the point where the average slope falls below a given % of min slope value
    mycutoff = 0.0001; %arbitrary value for threshold cutoff
    slopethresh = []; %reset slpethresh
    while isempty(slopethresh)
        slopethresh = find(slopemean < mycutoff *min(slopemean));
        mycutoff = mycutoff + 0.001; %increment threshold until successful
        if mycutoff > 0.5
            break %stop loop if threshold value exceeds 50%
        end
    end
    slopethresh = slopethresh(1); %extract first instance along entire event that satisfied cutoff criteria
    % get start index of event
    a = peaktemp-2 -(slopethresh-1);

    % Right leg of peak
    if length(y3)-peaktemp < 101 % skip peak if the right of waveform from peak is less than 101 points
        continue
    end
    for j = 1:100
        ytemp = y3(peaktemp +(j-1):peaktemp+5 -1 +(j-1)); %+5-1 = span 5, +(j-1) = shift right with counter
        slopetemp = diff(ytemp);
        slopemean(j) = mean(slopetemp); %average of slope values
    end
    % find the point where the average slope falls below % of min slope value
    windowSize = 13;
    Aveslopemean = filter(ones(1,windowSize)/windowSize,1,slopemean); %compute average of slopemean
    mycutoff = 0.0001; %establish threshold criteria
    slopethresh = []; %reset slopethresh
    while isempty(slopethresh)
        slopethresh = find(Aveslopemean > mycutoff *min(Aveslopemean));
        mycutoff = mycutoff + 0.001; %increment threshold until successful
        if mycutoff > 0.5
            break %stop loop if threshold value exceeds 50%
        end
    end
    slopethresh = slopethresh(1); %extract first instance along entire event that satisfied cutoff criteria
    % get end index of event
    c = peaktemp+2 +(slopethresh-1);

    % create an array storing the start, peak, and end of event
    peakEvents(peakcount,1:3) = [a, b, c]; %store curves to array
    peakcount = peakcount+1; %increment counter for event
end
myEvents = peakEvents; %copy to variable used by function

% Plot the Signal with "bad" events highlighted in red
hfigEvents = figure;
plot(y3,'k'); hold on;
for k = 1:length(peakEvents)
    a = peakEvents(k,1);
    c = peakEvents(k,3);
    plot(a:c,y3(a:c),'r');
end

% Get flagged events
myEventsFlag = flagEvents(myEvents,y3); %call flagEvents function

%---------------------------------

Flag Bad Events

back to top
%---------------------------------
function myEventsFlag = flagEvents(myEvents,Signal)
%Flag Bad Events
% myEventsFilter = filterEvents(myEvents,Signal)
%
% Input
% myEvents - output from myEvents = getEvents
% Signal - waveform
%
% Output
% myEventsFilter - array of 1 or 0 indicating "bad" events
%

% Modify vector shape
% Combine the events into one variable
for k = 1:length(myEvents)
    E{k,1} = Signal(myEvents(k,1):myEvents(k,3)); %y-values
    E{k,2} = myEvents(k,1):myEvents(k,3); %x-indices
end

% Split the event(s) in half, at peak value
for k = 1:length(myEvents)
    Esplit{k,1} = Signal(myEvents(k,1):myEvents(k,2)); %left leg
    Esplit{k,2} = Signal(myEvents(k,2):myEvents(k,3)); %right leg
end
% First Filter Condition
% Get the min y-value of each half and the mean y-value of the two combined
for k = 1:length(Esplit)

    ypeakval = max(E{k,1}); %get peak value
    ytemp1 = Esplit{k,1}; %make temp waveform of rising leg
    [min1temp(k) min1idx(k)] = min(ytemp1); %get min y-value of risign leg and index

    ytemp2 = Esplit{k,2}; %make temp waveform of falling leg
    [min2temp(k) min2idx(k)] = min(ytemp2); %get min y-value of falling leg and index

    mean12temp(k) = mean([min1temp(k), min2temp(k)]); %get mean of both waveform mins

    yAmp = ypeakval - mean12temp(k); %get relative amplitude
    yshift = abs(min1temp(k) - min2temp(k)); %get difference between start and end conditions

%   Establish a flag that characterizes/identiies problematic event waveforms
    if yshift > 0.2 * yAmp %characteristic of "bad" waveforms
        myflag1(k) = 1; %bad waveform
    else
        myflag1(k) = 0; %good waveform
    end
    if length(ytemp2) < 10
        myflag1(k) = 1; %bad waveform
    end

end
% Second Filter Condition
% Establish 2nd criteria for identifying problematic events
mystd = std(mean12temp); %standard deviation
meanall = mean(mean12temp); %mean value of all events under consideration
% 2nd criteria identifies false positives, removes events that have a mean 1 standard deviation away from group mean
for k = 1:length(Esplit)
    if mean12temp(k) > meanall + 2*mystd %2 standard deviations from mean is general rule for exclusion
        myflag2(k) = 1; %bad waveform
    else
        myflag2(k) = 0; %good waveform
    end
end

% Combine both flags logically. If flag1==flag2, pass flag1 to flag. If
% flag1 ~= flag2, pass 1 to flag.
for k = 1:length(Esplit)
    if myflag1(k) == myflag2(k)
        myflag(k) = myflag1(k);
    elseif myflag1(k) ~= myflag2(k)
        myflag(k) = 1;
    end
end
% Third Filter Condition
% Isolate events that were not filtered
% use myflag to remove filtered events

myEventsFlagKeep = find(myflag == 0);
if length(myEventsFlagKeep) >= 1
    for k = 1:length(myEventsFlagKeep)
        myEventsFilt{k,1} = E{myEventsFlagKeep(k),1};
        myEventsFilt{k,2} = E{myEventsFlagKeep(k),2};
    end
    % Establish routine that captures bias selection of "bad" events by using
    % the amplitude and the length of the evnent to establish programmable
    % criteria that recreates bias user selection with 90% accuracy.
    for k = 1:length(myEventsFilt)
        a = myEventsFilt{k,1}(1);
        b = max(myEventsFilt{k,1});
        c = myEventsFilt{k,1}(end);
        ab = b-a;
        bc = b-c;
        EventAmp(k) = mean([ab bc]);
        EventLength(k) = myEventsFilt{k,2}(end) - myEventsFilt{k,2}(1);
    end
    EventAmpAve = mean(EventAmp);
    EventLengthAve = mean(EventLength);
    EventAmpComplex = EventAmp/EventAmpAve;
    EventLengthComplex = EventLength/EventLengthAve;
    EventComplex = EventAmpComplex.*EventLengthComplex;
    EventComplexAve = mean(EventComplex);

    % Loop through all events kept after filtering and filter again based on
    % the complex value determined from amplitude and length of event. Flag
    % events that fall below 20% of the EventComplexAve value
    for k = 1:length(myEventsFilt)
        if EventComplex(k) < 0.20*EventComplexAve
            myflag(myEventsFlagKeep(k)) = 1; %assign to index of myFlag
        end
    end
end


% % Plottting Events and identify the "bad" events
% for k = 1:length(Esplit)
%
%     ytemp1 = Esplit{k,1}; %make temp waveform of rising leg
%     ytemp2 = Esplit{k,2}; %make temp waveform of falling leg
% % plot rising and falling legs in same figure, but in different axis
%     figure;
%     subplot(1,2,1);
%         if myflag(k) == 1
%             plot(Esplit{k,1},'r','LineWidth',3); %paint red if "bad"
%         else
%             plot(Esplit{k,1},'g','LineWidth',3); %pain green if "good"
%         end
%         hold on; plot(min1idx(k),ytemp1(min1idx(k)),'ok','MarkerSize',8,'LineWidth',3.5); %mark mean of rising leg
%         plot(1:length(ytemp1),repmat(mean12temp(k),length(ytemp1),1),'--k','LineWidth',3.5); %mark mean of all event legs
%     subplot(1,2,2);
%         if myflag(k) == 1
%             plot(Esplit{k,2},'r','LineWidth',3); %paint red if "bad"
%         else
%             plot(Esplit{k,2},'g','LineWidth',3); %pain green if "good"
%         end
%         hold on; plot(min2idx(k),ytemp2(min2idx(k)),'ok','MarkerSize',8,'LineWidth',3.5); %mark mean of rising leg
%         plot(1:length(ytemp2),repmat(mean12temp(k),length(ytemp2),1),'k--','LineWidth',3.5); %mark mean of all event legs
%
% end

myEventsFlag = myflag';

%---------------------------------

Generate Descriptive Stats

back to top
%---------------------------------
function [EventDataStats hfigStats] = getEventStats(myEvents,Signal,myEventsFlag,si)
% EventDataStats = getEventStats(myEvents,Signal,myEventsFlag);
% get activaton and decay descriptives
%
% Input
% myEvents = row array of peak waveform limits (start,peak,end) or
% (start,end)
%
% Signal = waveform collecting stats from
%
% myEventsFlag = array of 1 & 0 indentifying "bad" events, 1 == bad
% si = sample interval in seconds
%
% Output
% EventDataStats -
%     EventDataStats(k,1) = Amptemp; %amplitude
%     EventDataStats(k,2) = 1/beta1temp; %activation time constant
%     EventDataStats(k,3) = 1/beta2temp; %decay time constant
%     EventDataStats(k,4) = AUCtemp; %area under curve
%     EventDataStats(k,5) = Rsquare1; %goodness of fit for activation
%     EventDataStats(k,6) = Rsquare2; %goodness of fit for decay
% hfigStats - array of figure handles to fit curve figure windows
%
% external functions called
% myfity1 = established to fit an exponential decay curve

%     used for transpose on activation, but should be modified
%

% Remove "bad" events from applying fit curves
myEventstemp = myEvents;
clear myEvents
count = 1; %setup counter for indexing into new array
for k = 1:length(myEventstemp)
    if myEventsFlag(k) == 1
        continue %do not increment index if skipping a "bad" event
    else
        myEvents(count,:) = myEventstemp(k,:);
        count = count+1; %increment counter if copying "good" event
    end
end
% Display information about "good" and "bad" events
disp('Analyzing...'); %show in command line
disp(' ');
disp(['Total Events Found = ', num2str(length(myEventstemp))]); %show total # of events
disp(['"Good" Events Found = ', num2str(length(myEvents))]); %show # of "good" events
disp(['"Bad" Events Found = ', num2str(length(myEventstemp) - length(myEvents))]); %show #of "Bad" events
disp(' ');

% Convert myEvents to a triple data array if is double set array
if size(myEvents,2) == 2 %convert to triple set (start,peak,end)
    myEventsTemp = myEvents;
    clear myEvents
    for k = 1:length(myEventsTemp)
        a = myEventsTemp(k,1);
        c = myEventsTemp(k,2);
        [unused,bindex] = max(Signal(a:c));
        b = a + bindex - 1; %get the peak with respect to start of event, subtract 1 b/c array indexing starts at 1, not 0
        myEvents(k,1:3) = [a,c,b];
    end
end

hfigs = []; %initialize array
% Loop through each event and apply myfity, external fit curve function
for k = 1:length(myEvents) %waveforms have 3 indices, column vector of successive events
    disp(['Fitting Event ', num2str(k)]);
    a = myEvents(k,1);
    b = myEvents(k,2);
    c = myEvents(k,3);
    ab = Signal(a:b);
    bc = Signal(b:c);
    ac = Signal(a:c);

    % compute the factor to make si = 0.0001
    resF = si/0.0001;

    % resample data using resample function
    [yRising yDecay yResampled siNew] = myResample_simple_extend(Signal,a,c,si,resF);

    ytemp = ab;%activation curve
    ytemp = yRising; %resampled activation curve
    mintemp = min(ytemp);
    ytemp = ytemp - mintemp; %translate curve to origin
    %xtemp = 0:(length(ytemp)-1);
    %xtemp = xtemp*si;
%   plot if fitting an even event
    makefig1 = 'n'; % variable for makefig decision
    if makefig1 == 'y'
        hfigtemp1 = figure('name',['Rising Edge_Event_', num2str(k)]); %enter title as figure name
        hfigs(k,1) = hfigtemp1;
    end
    %[mtemp, stemp] = myfity1(flipud(ytemp),makefig1,si); %curve fit model & stats for activation
    [mtemp, stemp] = myfity2(ytemp,makefig1,siNew,' '); %curve fit model & stats for resampled activation curve

    beta1temp = mtemp.beta; %activation 1/tau
    Rsquare1 = stemp.rsquare; %R square error of fit

%     eval(['EPC' num2str(k) 'beta1 = beta1temp;'])
%     eval(['EPC' num2str(k) 'y1 = ytemp;'])
%     eval(['EPC' num2str(k) 'x1 = xtemp;'])
%     eval(['EPC' num2str(k) 'y1curve = mtemp;'])%curve fit model for activation
%     eval(['EPC' num2str(k) 'y1stat = stemp;'])%curve fit stats for activation

    ytemp = bc; %decay curve
    ytemp = yDecay; %resampled decay curve
    mintemp = min(ytemp);
    ytemp = ytemp - mintemp; %translate curve to origin
    %xtemp = 0:(length(ytemp)-1);
    %xtemp = xtemp*si;
    makefig2 = 'n'; % variable for makefig decision
    if makefig2 == 'y'
        hfigtemp2 = figure('name',['Falling Edge_Event_', num2str(k)]); %enter title as figure name
        hfigs(k,2) = hfigtemp2;
    end
    %[mtemp, stemp] = myfity1(ytemp,makefig2,si); %curve fit model & stats for decay
    [mtemp, stemp] = myfity1(ytemp,makefig2,siNew,' '); %curve fit model & stats for resampled decay curve

    beta2temp = mtemp.beta; %decay 1/tau
    Rsquare2 = stemp.rsquare; %R square error of fit

%     eval(['EPC' num2str(k) 'beta2 = beta2temp;'])
%     eval(['EPC' num2str(k) 'y2 = ytemp;'])
%     eval(['EPC' num2str(k) 'x2 = xtemp;'])
%     eval(['EPC' num2str(k) 'y1curve = mtemp;']) %curve fit model for decay
%     eval(['EPC' num2str(k) 'y1stat = stemp;']) %curve fit stats for decay

    [yRising yDecay yResampled siNew] = myResample_simple(Signal,a,c,si,resF);

    ytemp = ac; %whole waveform
    ytemp = yResampled;
    mintemp = min(ytemp);
    ytemp = ytemp - mintemp; %translate to origin for AUC determination
    xtemp = 0:(length(ytemp)-1);
    %xtemp = xtemp*si;
    xtemp = xtemp*siNew;

%     eval(['EPC' num2str(k) 'y3 = ytemp;'])
%     eval(['EPC' num2str(k) 'x3 = xtemp;'])

    AUCtemp = trapz(xtemp,ytemp); %compute area under curve

%     eval(['EPC' num2str(k) 'AUC = AUCtemp;'])

%     base1temp =  mean (Signal(a-3:a));
%     base2temp =  mean (Signal(c:c+3));
%     basetemp = mean([base1temp,base2temp]);
%     peaktemp = Signal(b);
%     Amptemp = abs(peaktemp - basetemp); %abs amplitude

    Amptemp = max(ytemp);

%     eval(['EPC' num2str(k) 'Amp = Amptemp;'])

    EventDataStats(k,1) = Amptemp;
    EventDataStats(k,2) = 1/beta1temp;
    EventDataStats(k,3) = 1/beta2temp;
    EventDataStats(k,4) = AUCtemp;
    EventDataStats(k,5) = Rsquare1;
    EventDataStats(k,6) = Rsquare2;

    %eval(['EPC' num2str(k) 'y1 = c1( a( (1+(k-1)*3) ):a( (2+(k-1)*3) ) );'])
    %    eval( 'ytemp = eval([ ''min ( eval([''''EPC'''' num2str(k) ''''y1'''']) )'' ])' );

end

hfigStats = hfigs;

% Dispaly end of analysis
disp(' ');
disp('...Analysis Complete');

%---------------------------------

Exponential decay fit

back to top
%---------------------------------
function [Curvefit, goodness] = myfity1(y1,makeplot,si,unitsY)
% [Curvefit, goodness] = myfity1(y1,makeplot,si,yUnit)
% Curve Fit
%
% Input
% y1 - curve to be fitted must be exponential decay curve
% makeplot - enter plot option PLOT (y), DONT PLOT (n)
% si - sample interval in seconds (si)
% unitsY - units of y-axis
%
% Output
% Curvefit - curve fit to data
% goodness - goodness of fit least square error
%

Y = y1(:); % make column vector

% % Extend curve plateu value
% a = ones(1, 10+length(Y) ); %make array length Y + 10
% a = a*Y(end); %make array equal to platue value
% Y = [Y a]; %concatenate, extend platue
x = 0:(length(Y)-1); %start x-axis from zero
x = x*si; %convert to seconds
x = x(:); %make column vector
%x = x+(5*FrameTreat); %shift the curve to match time stamp
%myc = round(min(Y));
%myalpha = round(max(Y)-myc);
myalpha = max(Y);
%temp = 0.63*myalpha+myc; %find value 63%
temp = 0.63*myalpha; %find value 63%
temp = abs(Y-temp); %subtract value 66% of max from curve
[~, tempx] = min(temp); %get index of min difference = index of 63%myalpha+myc
mybeta = 1/(x(tempx));
%r = 5*FrameTreat; %create right shift known constant (seconds)

% % For condition where intercept is passed into fitoptions
% s = fitoptions('Mehtod','NonlinearLeastSquares','Robust','LAR',...
%                 'Lower',[myalpha-.01,0,myc-.01],...
%                 'Upper',[myalpha+.01,inf,myc+.01],...
%                 'Startpoint',[myalpha,mybeta,myc]); %set the fitting parameters
s = fitoptions('Mehtod','NonlinearLeastSquares','Robust','LAR',...
            'Lower',[myalpha-1,0],...
            'Upper',[myalpha+1,inf],...
            'Startpoint',[myalpha,mybeta]); %set the fitting parameters

% f = fittype( @(alpha,beta,c,x) alpha*(1 - exp(-beta *(x-r))) +c,...
%             'coefficients',{'alpha','beta','c'},'options',s ); %set the curve fit function
% f = fittype( @(alpha,beta,x) alpha*(1 - exp(-beta *(x))),...
%             'coefficients',{'alpha','beta'},'options',s ); %set the curve fit function
f = fittype( @(alpha,beta,x) alpha*(exp(-beta *(x))),...
        'coefficients',{'alpha','beta'},'options',s ); %set the curve fit function

[Curvefit, goodness] = fit(x,Y,f); %fit data to an eponential rise curve

% Plot the original waveform, fit, and fit stats in figure
% make plot of curve fit if requested
if strncmpi(makeplot,'y',1) %case insensitive comparison of first character
%figure;
plot(Curvefit,'k-'),hold on %create a plot of the curve fit and original data
plot(x, Y,'k.','markersize',12) %plot data points
legend('Fit Curve', 'Data','Location','SouthEast') %place legend
xlabel('Time(seconds)','FontSize',20) %x-axis label
ylabel(['Amplitude (', unitsY, ')'], 'FontSize',20) %y-axis label
Txticks = get(gca,'XTick'); %x-axis tick array
Pxticks = round(length(get(gca,'XTick'))/4); %1/4 length of x-axis tick array
xticks = Txticks(Pxticks); %1/4 length value of x-axis ticks
%tempytick = abs(x-xticks); %x-axis data - x-axis 1/4 length tick value
%[~, tempytick] = min(tempytick); %find position of min difference
tempytick = 1;
% make strings of equations using tex syntax
strn1 = '$y={\alpha}\exp\left({-\beta}{x}\right)$';
strn2 = sprintf('$y={%0.4f}\\exp\\left({-%0.g}{x}\\right)$',Curvefit.alpha, Curvefit.beta);
strn3 = sprintf('$R^2={%0.4f}$',abs(goodness.rsquare));
strn4 = sprintf('$\\tau=\\frac{1}{\\beta}={%0.4g}\\left( seconds \\right)$',(1/Curvefit.beta));

% % For plotting w.r.t. origin
text('Interpreter','latex','string',...
{strn1;strn2;strn3;strn4},...
'Position',[xticks,Y(tempytick)],...
'VerticalAlignment','Cap','FontSize',16)

end
end

%---------------------------------

Expoential rise fit

back to top
%---------------------------------
function [Curvefit, goodness] = myfity2(y1,makeplot,si,unitsY)
% [Curvefit, goodness] = myfity2(y1,makeplot,si,yUnit)
% Curve Fit
%
% Input
% y1 - curve to be fitted must be exponential rising curve
% makeplot - enter plot option PLOT (y), DONT PLOT (n)
% si - sample interval in seconds (si)
% unitsY - units of y-axis
%
% Output
% Curvefit - curve fit to data
% goodness - goodness of fit least square error
%

    Y = y1(:);%curve, make column vector
    %Y data index starts at 1

    % Translate Y to origin, eliminating constant terms
    minY = min(Y);
    Y = Y - minY;

    % Create time vector
    x = 0:(length(Y)-1); %start x-axis from zero
    x = x*si; %convert to seconds
    x = x(:); %make column vector
    %x = x+(5*FrameTreat); %shift the curve to match time stamp
    %myc = round(min(Y));
    %myalpha = round(max(Y)-myc);

    myalpha = max(Y);
    %temp = 0.63*myalpha+myc; %find value 63%
    temp = 0.63*myalpha; %find value 63%
    temp = abs(Y-temp); %subtract value 66% of max from curve
    [~, tempx] = min(temp); %get index of min difference = index of 63%myalpha+myc
    mybeta = 1/(x(tempx));
    %r = 5*FrameTreat; %create right shift known constant (seconds)

    % s = fitoptions('Mehtod','NonlinearLeastSquares','Robust','LAR',...
    %                 'Lower',[myalpha-.01,0,myc-.01],...
    %                 'Upper',[myalpha+.01,inf,myc+.01],...
    %                 'Startpoint',[myalpha,mybeta,myc]); %set the fitting parameters
    s = fitoptions('Mehtod','NonlinearLeastSquares','Robust','LAR',...
                    'Lower',[myalpha-1,0],...
                    'Upper',[myalpha+1,inf],...
                    'Startpoint',[myalpha,mybeta]); %set the fitting parameters


    % f = fittype( @(alpha,beta,c,x) alpha*(1 - exp(-beta *(x-r))) +c,...
    %             'coefficients',{'alpha','beta','c'},'options',s ); %set the curve fit function
    % f = fittype( @(alpha,beta,x) alpha*(exp(-beta *(x))),...
    %             'coefficients',{'alpha','beta'},'options',s ); %set the curve fit function

    f = fittype( @(alpha,beta,x) alpha*(1 - exp(-beta *(x))),...
                'coefficients',{'alpha','beta'},'options',s ); %set the curve fit function

    [Curvefit, goodness] = fit(x,Y,f); %fit data to an eponential rise curve

    % Plot the original waveform, fit, and fit stats in figure
    % make plot of curve fit if requested
    if strncmpi(makeplot,'y',1) %case insensitive comparison of first character
        %figure;

        figure, plot(Curvefit,'k-'),hold on %create a plot of the curve fit and original data
        plot(x, Y,'k.','markersize',12) %plot data points
        legend('Fit Curve', 'Data','Location','SouthEast') %place legend
        xlabel('Time(seconds)','FontSize',20) %x-axis label
        ylabel(['Amplitude (', unitsY, ')'], 'FontSize',20) %y-axis label
        Txticks = get(gca,'XTick'); %x-axis tick array
        Pxticks = round(length(get(gca,'XTick'))/4); %1/4 length of x-axis tick array
        xticks = Txticks(Pxticks); %1/4 length value of x-axis ticks
        tempytick = abs(x-xticks); %x-axis data - x-axis 1/4 length tick value
        [~, tempytick] = min(tempytick); %find position of min difference

        % make strings of equations using tex syntax
        strn1 = '$y={\alpha}\left(1-\exp({-\beta}{x})\right)$';
        strn2 = sprintf('$y={%0.4f}\\left(1-\\exp({-%0.g}{x})\\right)$',Curvefit.alpha, Curvefit.beta);
        strn3 = sprintf('$R^2={%0.4f}$',abs(goodness.rsquare));
        strn4 = sprintf('$\\tau=\\frac{1}{\\beta}={%0.4g}\\left( seconds \\right)$',(1/Curvefit.beta));

        % % For plotting w.r.t. origin
            text('Interpreter','latex','string',...
            {strn1;strn2;strn3;strn4},...
            'Position',[xticks,Y(tempytick)],...
            'VerticalAlignment','Cap','FontSize',16)

    end

end

%---------------------------------

Resample data version1

back to top
%---------------------------------
function [yRising yDecay yResampled siNew] = myResample_simple(Signal,idxStart,idxEnd,si,resF)
% [yRising yDecay yResampled siNew] = myResample(Signal,idxStart,idxEnd,si)
% implement resampling by interpolation
% resample data to increase sample rate by a factor of 10
% example: old sample rate = 500 samples per second
%           new sample rate = 500*10 = 5000 samples per second
%
% Output
%   yRising = new y-values of length [((n-1)*10)+1], where n = old # of samples
%   yDecay = ''
%   yResampled = [yRising yDecay]
%   siNew = sampling interval for resampled data
%
% Input
%   Signal = y-values of signal as vector
%   idxStart = start of event
%   idxEnd = end of event
%   si = sample interval
%   resF = resampling factor

% 19-Apr-2014 13:30:42
%
% START
%
% use signal start and end limits to extract event
y = Signal(idxStart:idxEnd);
mintemp = min(y);
y = y - mintemp; %translate curve to origin

% setup and perform interpolation
x = 1:length(y); %make x index vector of length y
xi = 1:(1/resF):length(x); %make interpolation vector
yi = interp1(x,y,xi,'spline'); %use spline interpolation method to resample data
% get new sample interval time
siNew = si/resF; %compute new sample interval based on resF resampling
%
% NEXT
%
% after resampling data find x-index for both rising and falling legs of
% curve that point to the two y-values that are closest to 75%max(y-values)
% divide curve into halfs and find index of y-value that has the minimum
% square error away from 75%max(y-value)
%
% split curve into rising and decaying parts
[yMaxval yMaxidx] = max(yi);
if yMaxidx > length(yi)-20 %redo if max value is found at end of curve
    [yMaxval yMaxidx] = max(yi(1:end-20));
end

PercMax = 0.75*yMaxval; %calculate 75% of amplitude

aStart = 1;
bMid = yMaxidx;
cEnd = length(yi);

% crop left leg from min y-value to end
yLeft = yi(aStart:bMid);
[notused yRisingtempidx] = min(yLeft);
yRisingtemp = yLeft(yRisingtempidx:end);
% % find left leg index whith y-value closest to 0.75*amp
% yDiff = (yRisingtemp-PercMax).^2;
% [notused yLeftidx] = min(yDiff);

% eliminate starting tail from rising curve, using max(d/dx) & max(d/dx^2)
[~,dx1] = max(diff(yRisingtemp));
[~,dx2] = max(diff(diff(yRisingtemp)));
if dx2 < dx1 %&& abs(dx1-dx2) < 0.5*length(yRisingtemp)
    dxpos = dx2;
else
    dxpos = dx1;
end

% Reset if selection failed by creeping too far up
if yRisingtemp(dxpos)-min(yRisingtemp) > 0.3*max( (yRisingtemp-min(yRisingtemp)) )
    dxpos = 1;
end

% build rising curve
yRising = yRisingtemp(dxpos:end);

% crop right leg from start to min y-value
yRight = yi(bMid:cEnd);
[notused yDecaytempidx] = min(yRight);
yDecaytemp = yRight(1:yDecaytempidx);
% find right leg index whith y-value closest to 0.75*amp
yDiff = (yDecaytemp-PercMax).^2;
[notused yRightidx] = min(yDiff);
% build decay curve
yDecay = yDecaytemp(yRightidx:end);

% build whole curve using the left and right legs of event
yResampled = yi((yRisingtempidx+dxpos-1):bMid+yDecaytempidx-1);

%---------------------------------

Resample data version1

back to top
%---------------------------------
function [yRising yDecay yResampled siNew] = myResample_simple(Signal,idxStart,idxEnd,si,resF)
% [yRising yDecay yResampled siNew] = myResample(Signal,idxStart,idxEnd,si)
% implement resampling by interpolation
% resample data to increase sample rate by a factor of 10
% example: old sample rate = 500 samples per second
%           new sample rate = 500*10 = 5000 samples per second
%
% Output
%   yRising = new y-values of length [((n-1)*10)+1], where n = old # of samples
%   yDecay = ''
%   yResampled = [yRising yDecay]
%   siNew = sampling interval for resampled data
%
% Input
%   Signal = y-values of signal as vector
%   idxStart = start of event
%   idxEnd = end of event
%   si = sample interval
%   resF = resampling factor

% 19-Apr-2014 13:30:42, 12-May-2014 10:06:54
%
% START
%
% use signal start and end limits to extract event
y = Signal(idxStart:idxEnd);
mintemp = min(y);
y = y - mintemp; %translate curve to origin

% setup and perform interpolation
x = 1:length(y); %make x index vector of length y
xi = 1:(1/resF):length(x); %make interpolation vector
yi = interp1(x,y,xi,'spline'); %use spline interpolation method to resample data
% get new sample interval time
siNew = si/resF; %compute new sample interval based on resF resampling
%
% NEXT
%
% after resampling data find x-index for both rising and falling legs of
% curve that point to the two y-values that are closest to 75%max(y-values)
% divide curve into halfs and find index of y-value that has the minimum
% square error away from 75%max(y-value)
%
% split curve into rising and decaying parts
[yMaxval yMaxidx] = max(yi);
if yMaxidx > length(yi)-20 %redo if max value is found at end of curve
    [yMaxval yMaxidx] = max(yi(1:end-20));
end

PercMax = 0.75*yMaxval; %calculate 75% of amplitude

aStart = 1;
bMid = yMaxidx;
cEnd = length(yi);

% crop left leg from min y-value to end
yLeft = yi(aStart:bMid);
[notused yRisingtempidx] = min(yLeft);
yRisingtemp = yLeft(yRisingtempidx:end);
% % find left leg index whith y-value closest to 0.75*amp
% yDiff = (yRisingtemp-PercMax).^2;
% [notused yLeftidx] = min(yDiff);

% eliminate starting tail from rising curve, using max(d/dx) & max(d/dx^2)
[~,dx1] = max(diff(yRisingtemp));
[~,dx2] = max(diff(diff(yRisingtemp)));
if dx2 < dx1 %&& abs(dx1-dx2) < 0.5*length(yRisingtemp)
    dxpos = dx2;
else
    dxpos = dx1;
end

% Reset if selection failed by creeping too far up
if yRisingtemp(dxpos)-min(yRisingtemp) > 0.3*max( (yRisingtemp-min(yRisingtemp)) )
    dxpos = 1;
end

% build rising curve
yRising = yRisingtemp(dxpos:end);
% extend platue by 5%
a = ones(1, round(0.05*length(yRising))+length(yRising) ); %make array length Y + 5%(length Y)
a = a*yRising(end); %make array equal to platue value
yRising = [yRising a]; %concatenate, extend platue

% crop right leg from start to min y-value
yRight = yi(bMid:cEnd);
[notused yDecaytempidx] = min(yRight);
yDecaytemp = yRight(1:yDecaytempidx);
% find right leg index whith y-value closest to 0.75*amp
yDiff = (yDecaytemp-PercMax).^2;
[notused yRightidx] = min(yDiff);
% build decay curve
yDecay = yDecaytemp(yRightidx:end);

% build whole curve using the left and right legs of event
yResampled = yi((yRisingtempidx+dxpos-1):bMid+yDecaytempidx-1);

%---------------------------------

Get relative data paths to data files

back to top
      %---------------------------------
      function rawDataPaths()
      % ADD SPECIFIC FOLDERS TO SEARCH PATH CONTAINING RAW DATA, ABF FILES
      % ...\EPCs\Group 1;
      % ...\EPCs\Group 2;
      % ...\MEPPs\Group 1;
      % ...\MEPPs\Group 2;
      % along with all subfolders
      % FINAL WORKING FORM
      % 07-May-2014 00:39:20
      % ADD SPECIFIC FOLDERS TO SEARCH PATH CONTAINING RAW DATA, ABF FILES
      mystring = pwd; %get full path of current directory
      myPath_2 = regexp(mystring, '.+(?=\\.+\\.+$)','match'); %get parsed path name, two folders up
      FolderSearch_2 = char(myPath_2); %get path to folder 2 level above where scripts is
      % Generate character list for all paths in FolderSearch2
      All_paths_2temp = genpath(FolderSearch_2); %
      % Using the original fullfile output, Searching for
      % ...\EPCs\Group 1;
      % ...\EPCs\Group 2;
      % ...\MEPPs\Group 1;
      % ...\MEPPs\Group 2;
      % Scan string with all full file paths
      matchStr_2 = regexp(All_paths_2temp, '[A-Z]:\\[^;]+\\(EPCs|MEPPs)\\Group \d;', 'match');
      % Replace the semicolon(;) with backslash(\)
      matchStr_22 = regexprep(matchStr_2, ';', '\\');
      % Add paths of folders
      for k = 1:length(matchStr_22)
      addpath(genpath(matchStr_22{k}));