www.gusucode.com > signal 工具箱matlab源码程序 > signal/findsignal.m
function [istart_out,istop_out,dist_out] = findsignal(data,signal,varargin) %FINDSIGNAL Find signal location(s) via similarity search % [ISTART,ISTOP] = FINDSIGNAL(DATA,SIGNAL) finds the starting and % stopping indices of a segment of the data vector, DATA, that best % matches the search vector, SIGNAL, by minimizing the squared Euclidean % distance between the segment and SIGNAL. % % If DATA and SIGNAL are matrices, then they must contain the same number % of rows and ISTART and ISTOP correspond to the starting and stopping % columns of the region of DATA that best matches the search signal, % SIGNAL. % % [ISTART,ISTOP,DIST] = FINDSIGNAL(DATA,SIGNAL) additionally returns the % minimum squared Euclidean distance, DIST, between the data segment and % the signal. % % [ISTART,ISTOP,DIST] = FINDSIGNAL(...,'Normalization',NSTAT) will first % normalize the data and signal to constant parameter(s) before computing % the distance using the normalization statistic, NSTAT. The default is % 'none'. % 'center' subtract mean % 'zscore' subtract mean and divide by standard deviation % 'power' divide by mean % 'none' no normalization is performed % For matrices, 'center' and 'zscore' subtract the mean of each row % independently. The 'power' option is intended for signals with units in % power (e.g. Watts); it divides by the mean of all elements in the % matrix. 'zscore' divides by the standard deviation of all elements in % the matrix. % % [ISTART,ISTOP,DIST] = FINDSIGNAL(...,'NormalizationLength',NLENGTH) % specifies the length of a sliding window over which to normalize each % sample in both the signal and data. When the signal and data are % matrices, normalization is performed across NLENGTH columns. If % unspecified, the full length of the signal and data are used. % % [ISTART,ISTOP,DIST] = FINDSIGNAL(...,'MaxDistance',MAXDIST) returns the % start and stop indices of all segments whose distances from the signal % are both local minima and smaller than MAXDIST. The default value of % 'MaxDistance' is +Inf. % % [ISTART,ISTOP,DIST] = FINDSIGNAL(...,'MaxNumSegments',MAXSEG) locates % all segments whose distances from the signal are local minima and % returns up to MAXSEG segments with smallest distances. The default % value of 'MaxNumSegments' is +Inf when 'MaxDistance' is specified and 1 % when it is not. % % [ISTART,ISTOP,DIST] = FINDSIGNAL(...,'TimeAlignment',ALIGN) specifies % the time alignment technique used to compute the distance. The default % value is 'fixed' % 'fixed' do not stretch or repeat samples to minimize the distance % 'dtw' attempt to reduce distances by automatically repeating % consecutive samples in either the data or the signal. % 'edr' minimize the number of edits between the data segment and % signal that leaves all remaining samples within a tolerance % that must be specified by the 'EDRTolerance' parameter. % DIST returns the number of edits required. An edit consists % of inserting or removing a single sample in either the data % or the signal, or skipping a pair of samples in data and % signal. Use this option when the data or signal, or both, % have outliers. % % [ISTART,ISTOP,DIST] = FINDSIGNAL(...,'EDRTolerance',TOL) specifies the % tolerance, TOL, used to find the signal when using the 'edr' option of % the 'TimeAlignment' parameter. % % [ISTART,ISTOP,DIST] = FINDSIGNAL(...,'Metric',METRIC) finds the signal % by minimizing the specified distance metric defined by METRIC. The % default is 'squared'. % 'absolute' the sum of the absolute (manhattan) differences % 'euclidean' the root sum squared differences % 'squared' the squared Euclidean distance % 'symmkl' the symmetric Kullback-Leibler distance % When data and signal are matrices, the distances are taken between % corresponding column vectors; when data and signal are vectors, % distances are taken between the corresponding elements. Note that when % data and signal are vectors, 'absolute' and 'euclidean' are equivalent. % % FINDSIGNAL(...) without output arguments plots the unnormalized signal % and data, highlighting any identified signal(s) found in the data. % FINDSIGNAL displays the data as a line when it is a vector and as an % image when it is a matrix. If the matrix is complex, then the real and % imaginary portions appear in the top and bottom half of each image, % respectively. % % FINDSIGNAL(...,'Annotate',PLOTSTYLE) annotates a plot of the signal % with PLOTSTYLE. If PLOTSTYLE is 'data' the data is plotted where % matches to the signal are highlighted. If PLOTSTYLE is 'signal' the % signal is additionally plotted in a small plot. If PLOTSTYLE is 'all' % the signal, data, and normalized signal and normalized data are all % plotted. 'Annotate' is ignored if called with output arguments. The % default value of PLOTSTYLE is 'signal'. % % % Example 1: % % Find the segment of the data that has the closest squared euclidean % % distance to a signal consisting of one cycle of a sinusoid % data = exp(-((1:300)/100).^2).*cos(2*pi*(1:300)/100); % signal = sin(2*pi*(1:100)/100); % findsignal(data,signal) % % % Example 2: % % Locate the two best locations of the letter "A" in a % % handwriting sample % load blockletterex % letterA = MATLAB2(:,55:90); % findsignal(MATLAB1,letterA,'MaxNumSegments',2); % % % Example 3: % % Find the two best matches in a handwriting sample to the letter 'p' % % using dynamic time warping % load cursiveex % findsignal(data,signal,'TimeAlignment','dtw', ... % 'Normalization','center', ... % 'NormalizationLength',600, ... % 'MaxNumSegments',2) % % See also FINDDELAY, FINDPEAKS, DTW, EDR, ALIGNSIGNALS, FINDCHANGEPTS. % Copyright 2016 The MathWorks, Inc. narginchk(2,16); needsTranspose = iscolumn(data); if iscolumn(data) && isvector(signal) data = data.'; end if iscolumn(signal) && isvector(data) signal = signal.'; end validateattributes(data,{'single','double'},{'nonnan','2d','finite'},'findsignal','DATA',1); validateattributes(signal,{'single','double'},{'nonnan','2d','finite'},'findsignal','SIGNAL',2); [nstat,nlength,maxseg,maxdist,align,tol,metric,annotate] = parseInput(data,signal,varargin); if ~isempty(data) && ~isempty(signal) if size(data,1) ~= size(signal,1) error(message('signal:findsignal:RowMismatch')); end [ndata, nsignal] = normalize(data, signal, nstat, nlength); [istart, istop, dist] = getsegments(ndata,nsignal,align,tol,metric); else dist = NaN(1,0); istart = zeros(1,0); istop = zeros(1,0); nsignal = []; ndata = []; end % remove segments that do not fit the matching criteria [istart,istop,dist] = filtersegments(istart,istop,dist,maxseg,maxdist); if nargout == 0 findsignalplot(signal,data,nsignal,ndata,istart,istop,metric,maxseg,annotate); elseif needsTranspose % flip orientation to match input vector dist_out = dist.'; istart_out = istart.'; istop_out = istop.'; else dist_out = dist; istart_out = istart; istop_out = istop; end %------------------------------------------------------------------------- function [istart,istop,dist] = filtersegments(istart,istop,dist,maxseg,maxdist) % keep only the segments whose distances are within MAXDIST if isfinite(maxdist) idx = find(dist <= maxdist); dist = dist(idx); istart = istart(idx); istop = istop(idx); end % sort results by output distance metric [dist, idx] = sort(dist,'ascend'); istart = istart(idx); istop = istop(idx); % truncate list if MAXSEGS is specified if numel(dist) > maxseg dist = dist(1:maxseg); istart = istart(1:maxseg); istop = istop(1:maxseg); end %------------------------------------------------------------------------- function [istart,istop,dist] = getsegments(data,signal,align,tol,metric) % split real/imag if needed if ~isreal(data) || ~isreal(signal) data = [real(data); imag(data)]; signal = [real(signal); imag(signal)]; end if strcmp(align,'dtw') % seek segments such that: % dist(k) == dtw(data(:,istart(k):istop(k)),sig,metric) % warping paths may not overlap [istart, istop, dist] = dtwfindmex(data,signal,metric); elseif strcmp(align,'edr') % seek segments such that: % dist(k) == edr(data(:,istart(k):istop(k)),sig,tol,metric) % warping paths may not overlap [istart, istop, dist] = edrfindmex(data,signal,tol,metric); else %'fixed' % seek segments such that: % dist(k) == dist([data(:,istart(k):istop(k)); sig],metric) % segments must be local minima [istart, istop, dist] = finddistminima(data,signal,metric); end %------------------------------------------------------------------------- function [ndata, nsignal] = normalize(data,signal,nstat,nlength) if strcmp(nstat,'center') % remove constant bias ndata = data - movmean(mean(data,1),nlength,2); nsignal = signal - movmean(mean(signal,1),nlength,2); elseif strcmp(nstat,'power') % divide input (power) by average. ndata = data ./ movmean(mean(data,1),nlength,2); nsignal = signal ./ movmean(mean(signal,1),nlength,2); elseif strcmp(nstat,'zscore') % standardize each row to zero mean and unit norm ndata = lzscore(data,nlength); nsignal = lzscore(signal,nlength); else % 'none' ndata = data; nsignal = signal; end %------------------------------------------------------------------------- function ndata = lzscore(data,n) data = data - movmean(data,n,2); rowvar = movvar(data,n,1,2); m = size(data,1); % use unbiased estimate allstd = sqrt(n * sum(rowvar,1) / max(1,m*n-1)); ndata = data ./ allstd; %------------------------------------------------------------------------- function [nstat,nlength,maxseg,maxdist,align,tol,metric,annotate] = parseInput(data,signal,optargs) p = inputParser; p.addParameter('Normalization','none'); p.addParameter('NormalizationLength',2*max(1,max(size(data,2),size(signal,2)))); p.addParameter('MaxNumSegments',Inf); p.addParameter('MaxDistance',Inf); p.addParameter('TimeAlignment','fixed'); p.addParameter('EDRTolerance',NaN); p.addParameter('Metric','squared'); p.addParameter('Annotate','signal'); p.parse(optargs{:}); r = p.Results; nstat = validatestring(r.Normalization,{'center','zscore','power','none'}, ... 'findsignal','Normalization'); align = validatestring(r.TimeAlignment,{'fixed','dtw','edr'}, ... 'findsignal','MaxDistance'); metric = validatestring(r.Metric,{'absolute','euclidean','squared','symmkl'}, ... 'findsignal','Metric'); annotate = validatestring(r.Annotate,{'data','signal','all'}); nlength = r.NormalizationLength; validateattributes(nlength,{'numeric'},{'>',1,'integer','scalar','finite'}, ... 'findsignal','NormalizationLength'); [maxseg,maxdist] = parseFilterSettings(p,r); tol = parseTolerance(p,r); % Ensure signal, data, and normalization are properly conditioned when % using symmetric Kullback-Leibler distance if strcmp(metric,'symmkl') validateSymmKL(data, signal, nstat); end %------------------------------------------------------------------------- function tol = parseTolerance(p,r) % Ensure EDRTolerance is called with 'edr' flag. Otherwise error out. tol = r.EDRTolerance; if strcmp(r.TimeAlignment,'edr') && ~ismember('EDRTolerance',p.UsingDefaults) validateattributes(tol,{'numeric'},{'nonnegative','real','scalar','finite'}, ... 'findsignal','EDRTolerance'); elseif strcmp(r.TimeAlignment,'edr') error(message('signal:findsignal:MissingEDRTolerance')); elseif ~ismember('EDRTolerance',p.UsingDefaults) error(message('signal:findsignal:InvalidEDRTolerance')); end %------------------------------------------------------------------------- function [maxseg,maxdist] = parseFilterSettings(p,r) % get segment filter settings maxdist = r.MaxDistance; validateattributes(maxdist,{'numeric'},{'nonnegative','real','scalar','nonnan'}, ... 'findsignal','MaxDistance'); maxseg = r.MaxNumSegments; if ~isscalar(maxseg) || isfinite(maxseg) || isnan(maxseg) validateattributes(maxseg,{'numeric'},{'positive','integer','scalar','nonnan'}, ... 'findsignal','MaxNumSegments'); end % if neither setting is specified, just use the best segment if all(ismember({'MaxNumSegments','MaxDistance'},p.UsingDefaults)) maxseg = 1; end %------------------------------------------------------------------------- function validateSymmKL(data, signal, nstat) % Ensure signal, data, and normalization are properly conditioned for % symmetric Kullback-Leibler distance if ~any(strcmp(nstat,{'none','power'})) error(message('signal:findsignal:NonUnipolarNorm',nstat)); end if ~isreal(data) || any(data(:)<0) error(message('signal:findsignal:DataCannotBeNegative')) end if ~isreal(signal) || any(signal(:)<0) error(message('signal:findsignal:SignalCannotBeNegative')) end