0001 function [ripples,sd,bad] = FindRipples(filtered,varargin)
0002 
0003 
0004 
0005 
0006 
0007 
0008 
0009 
0010 
0011 
0012 
0013 
0014 
0015 
0016 
0017 
0018 
0019 
0020 
0021 
0022 
0023 
0024 
0025 
0026 
0027 
0028 
0029 
0030 
0031 
0032 
0033 
0034 
0035 
0036 
0037 
0038 
0039 
0040 
0041 
0042 
0043 
0044 
0045 
0046 
0047 
0048 
0049 
0050 
0051 
0052 
0053 
0054 
0055 
0056 frequency = 1250;
0057 show = 'off';
0058 restrict = [];
0059 sd = [];
0060 lowThresholdFactor = 2; 
0061 highThresholdFactor = 5; 
0062 minInterRippleInterval = 30; 
0063 minRippleDuration = 20; 
0064 maxRippleDuration = 100; 
0065 noise = [];
0066 
0067 
0068 if nargin < 1 | mod(length(varargin),2) ~= 0,
0069   error('Incorrect number of parameters (type ''help <a href="matlab:help FindRipples">FindRipples</a>'' for details).');
0070 end
0071 
0072 
0073 if ~isdmatrix(filtered) | size(filtered,2) ~= 2,
0074     error('Parameter ''filtered'' is not a Nx2 matrix (type ''help <a href="matlab:help FindRipples">FindRipples</a>'' for details).');
0075 end
0076 
0077 
0078 for i = 1:2:length(varargin),
0079     if ~ischar(varargin{i}),
0080         error(['Parameter ' num2str(i+2) ' is not a property (type ''help <a href="matlab:help FindRipples">FindRipples</a>'' for details).']);
0081     end
0082     switch(lower(varargin{i})),
0083         case 'thresholds',
0084             thresholds = varargin{i+1};
0085             if ~isdvector(thresholds,'#2','>0'),
0086                 error('Incorrect value for property ''thresholds'' (type ''help <a href="matlab:help FindRipples">FindRipples</a>'' for details).');
0087             end
0088             lowThresholdFactor = thresholds(1);
0089             highThresholdFactor = thresholds(2);
0090         case 'durations',
0091             durations = varargin{i+1};
0092             if ~isdvector(durations,'#2','>0') && ~isdvector(durations,'#3','>0'),
0093                 error('Incorrect value for property ''durations'' (type ''help <a href="matlab:help FindRipples">FindRipples</a>'' for details).');
0094             end
0095             if length(durations) == 2,
0096                 minInterRippleInterval = durations(1);
0097                 maxRippleDuration = durations(2);
0098             else
0099                 minInterRippleInterval = durations(1);
0100                 minRippleDuration = durations(2);
0101                 maxRippleDuration = durations(3);
0102             end
0103         case 'frequency',
0104             frequency = varargin{i+1};
0105             if ~isdscalar(frequency,'>0'),
0106                 error('Incorrect value for property ''frequency'' (type ''help <a href="matlab:help FindRipples">FindRipples</a>'' for details).');
0107             end
0108         case 'show',
0109             show = varargin{i+1};
0110             if ~isastring(show,'on','off'),
0111                 error('Incorrect value for property ''show'' (type ''help <a href="matlab:help FindRipples">FindRipples</a>'' for details).');
0112             end
0113         case {'baseline','restrict'},
0114             restrict = varargin{i+1};
0115             if ~isempty(restrict) & ~isdvector(restrict,'#2','<'),
0116                 error('Incorrect value for property ''restrict'' (type ''help <a href="matlab:help FindRipples">FindRipples</a>'' for details).');
0117             end
0118         case 'stdev',
0119             sd = varargin{i+1};
0120             if ~isdscalar(sd,'>0'),
0121                 error('Incorrect value for property ''stdev'' (type ''help <a href="matlab:help FindRipples">FindRipples</a>'' for details).');
0122             end
0123         case 'noise',
0124             noise = varargin{i+1};
0125             if ~isdmatrix(noise) | size(noise,1) ~= size(filtered,1) | size(noise,2) ~= 2,
0126                 error('Incorrect value for property ''noise'' (type ''help <a href="matlab:help FindRipples">FindRipples</a>'' for details).');
0127             end
0128         otherwise,
0129             error(['Unknown property ''' num2str(varargin{i}) ''' (type ''help <a href="matlab:help FindRipples">FindRipples</a>'' for details).']);
0130     end
0131 end
0132 
0133 
0134 windowLength = round(frequency/1250*11);
0135 
0136 
0137 time = filtered(:,1);
0138 signal = filtered(:,2);
0139 squaredSignal = signal.^2;
0140 window = ones(windowLength,1)/windowLength;
0141 keep = [];
0142 if ~isempty(restrict),
0143     keep = filtered(:,1)>=restrict(1)&filtered(:,1)<=restrict(2);
0144 end
0145 
0146 [normalizedSquaredSignal,sd] = unity(Filter0(window,sum(squaredSignal,2)),sd,keep);
0147 
0148 
0149 thresholded = normalizedSquaredSignal > lowThresholdFactor;
0150 start = find(diff(thresholded)>0);
0151 stop = find(diff(thresholded)<0);
0152 
0153 if length(stop) == length(start)-1,
0154     start = start(1:end-1);
0155 end
0156 
0157 if length(stop)-1 == length(start),
0158     stop = stop(2:end);
0159 end
0160 
0161 if start(1) > stop(1),
0162     stop(1) = [];
0163     start(end) = [];
0164 end
0165 firstPass = [start,stop];
0166 if isempty(firstPass),
0167     disp('Detection by thresholding failed');
0168     return
0169 else
0170     disp(['After detection by thresholding: ' num2str(length(firstPass)) ' events.']);
0171 end
0172 
0173 
0174 secondPass = firstPass;
0175 iri = time(secondPass(2:end,1)) - time(secondPass(1:end-1,2));
0176 duration = time(secondPass(2:end,2)) - time(secondPass(1:end-1,1));
0177 toMerge = iri<minInterRippleInterval/1000 & duration<maxRippleDuration/1000;
0178 while any(toMerge),
0179     
0180     rippleStart = strfind([0 toMerge'],[0 1])';
0181     
0182     rippleEnd = rippleStart+1;
0183     secondPass(rippleStart,2) = secondPass(rippleEnd,2);
0184     
0185     secondPass(rippleEnd,:) = [];
0186     iri = time(secondPass(2:end,1)) - time(secondPass(1:end-1,2));
0187     duration = time(secondPass(2:end,2)) - time(secondPass(1:end-1,1));
0188     toMerge = iri<minInterRippleInterval/1000 & duration<maxRippleDuration/1000;
0189 end
0190 
0191 if isempty(secondPass),
0192     disp('Ripple merge failed');
0193     return
0194 else
0195     disp(['After ripple merge: ' num2str(length(secondPass)) ' events.']);
0196 end
0197 
0198 
0199 thirdPass = [];
0200 peakNormalizedPower = [];
0201 for i = 1:size(secondPass,1)
0202     maxValue = max(normalizedSquaredSignal([secondPass(i,1):secondPass(i,2)]));
0203     if maxValue > highThresholdFactor,
0204         thirdPass = [thirdPass ; secondPass(i,:)];
0205         peakNormalizedPower = [peakNormalizedPower ; maxValue];
0206     end
0207 end
0208 if isempty(thirdPass),
0209     disp('Peak thresholding failed.');
0210     return
0211 else
0212     disp(['After peak thresholding: ' num2str(length(thirdPass)) ' events.']);
0213 end
0214 
0215 
0216 peakPosition = zeros(size(thirdPass,1),1);
0217 for i=1:size(thirdPass,1),
0218     [~,minIndex] = min(signal(thirdPass(i,1):thirdPass(i,2)));
0219     peakPosition(i) = minIndex + thirdPass(i,1) - 1;
0220 end
0221 
0222 
0223 ripples = [time(thirdPass(:,1)) time(peakPosition) time(thirdPass(:,2)) peakNormalizedPower];
0224 duration = ripples(:,3)-ripples(:,1);
0225 ripples(duration<minRippleDuration/1000,:) = [];
0226 disp(['After min duration test: ' num2str(size(ripples,1)) ' events.']);
0227 
0228 
0229 duration = ripples(:,3)-ripples(:,1);
0230 ripples(duration>maxRippleDuration/1000,:) = [];
0231 disp(['After max duration test: ' num2str(size(ripples,1)) ' events.']);
0232 
0233 
0234 bad = [];
0235 if ~isempty(noise),
0236     
0237     squaredNoise = noise(:,2).^2;
0238     window = ones(windowLength,1)/windowLength;
0239     normalizedSquaredNoise = unity(Filter0(window,sum(squaredNoise,2)),sd,[]);
0240     excluded = logical(zeros(size(ripples,1),1));
0241     
0242     previous = 1;
0243     for i = 1:size(ripples,1),
0244         j = FindInInterval(noise,[ripples(i,1),ripples(i,3)],previous);
0245         previous = j(2);
0246         if any(normalizedSquaredNoise(j(1):j(2))>highThresholdFactor),
0247             excluded(i) = 1;
0248         end
0249     end
0250     bad = ripples(excluded,:);
0251     ripples = ripples(~excluded,:);
0252     disp(['After noise removal: ' num2str(size(ripples,1)) ' events.']);
0253 end
0254 
0255 
0256 if strcmp(show,'on'),
0257     figure;
0258     if ~isempty(noise),
0259         MultiPlotXY([time signal],[time squaredSignal],[time normalizedSquaredSignal],[time noise(:,2)],[time squaredNoise],[time normalizedSquaredNoise]);
0260         nPlots = 6;
0261         subplot(nPlots,1,3);
0262          ylim([0 highThresholdFactor*1.1]);
0263         subplot(nPlots,1,6);
0264           ylim([0 highThresholdFactor*1.1]);
0265     else
0266         MultiPlotXY([time signal],[time squaredSignal],[time normalizedSquaredSignal]);
0267 
0268         nPlots = 3;
0269         subplot(nPlots,1,3);
0270           ylim([0 highThresholdFactor*1.1]);
0271     end
0272     for i = 1:nPlots,
0273         subplot(nPlots,1,i);
0274         hold on;
0275           yLim = ylim;
0276         for j=1:size(ripples,1),
0277             plot([ripples(j,1) ripples(j,1)],yLim,'g-');
0278             plot([ripples(j,2) ripples(j,2)],yLim,'k-');
0279             plot([ripples(j,3) ripples(j,3)],yLim,'r-');
0280             if i == 3,
0281                 plot([ripples(j,1) ripples(j,3)],[ripples(j,4) ripples(j,4)],'k-');
0282             end
0283         end
0284         for j=1:size(bad,1),
0285             plot([bad(j,1) bad(j,1)],yLim,'k-');
0286             plot([bad(j,2) bad(j,2)],yLim,'k-');
0287             plot([bad(j,3) bad(j,3)],yLim,'k-');
0288             if i == 3,
0289                 plot([bad(j,1) bad(j,3)],[bad(j,4) bad(j,4)],'k-');
0290             end
0291         end
0292         if mod(i,3) == 0,
0293             plot(xlim,[lowThresholdFactor lowThresholdFactor],'k','linestyle','--');
0294             plot(xlim,[highThresholdFactor highThresholdFactor],'k-');
0295         end
0296     end
0297 end
0298 
0299 function y = Filter0(b,x)
0300 
0301 if size(x,1) == 1,
0302     x = x(:);
0303 end
0304 
0305 if mod(length(b),2) ~= 1,
0306     error('filter order should be odd');
0307 end
0308 
0309 shift = (length(b)-1)/2;
0310 
0311 [y0 z] = filter(b,1,x);
0312 
0313 y = [y0(shift+1:end,:) ; z(1:shift,:)];
0314 
0315 function [U,stdA] = unity(A,sd,restrict)
0316 
0317 if ~isempty(restrict),
0318     meanA = mean(A(restrict));
0319     stdA = std(A(restrict));
0320 else
0321     meanA = mean(A);
0322     stdA = std(A);
0323 end
0324 if ~isempty(sd),
0325     stdA = sd;
0326 end
0327 
0328 U = (A - meanA)/stdA;
0329