www.gusucode.com > sigtools 工具箱matlab源码程序 > sigtools/+filtresp/nlm.m
classdef (Abstract) nlm < sigresp.freqaxiswnfft & sigio.dyproputil & hgsetget %filtresp.nlm class % filtresp.nlm extends sigresp.freqaxiswnfft. % % filtresp.nlm properties: % Tag - Property is of type 'string' % Version - Property is of type 'double' (read only) % FastUpdate - Property is of type 'on/off' % Name - Property is of type 'string' % Legend - Property is of type 'on/off' % Grid - Property is of type 'on/off' % Title - Property is of type 'on/off' % FrequencyScale - Property is of type 'string' % NormalizedFrequency - Property is of type 'string' % FrequencyRange - Property is of type 'string' % NumberOfPoints - Property is of type 'double' % NumberOfTrials - Property is of type 'double' % % filtresp.nlm methods: % checkfilters - Returns the valid filter indices. % createnfftprm - Create an nfft parameter object. % enablemask - Returns true if the object supports masks. % freqmode_listener - Listener for the freqmode parameter % getlegendstrings - Returns the legend strings % getlineorder - Return the line order. % getlinestyle - Get the linestyle. % getnffttag - Return string/tag for the nfft object. % objspecificdraw - Draw the NOISEPOWERSPECTRUM % setmontecarlo - Set Function for the number of trials. properties (AbortSet, SetObservable, GetObservable) %NUMBEROFTRIALS Property is of type 'double' NumberOfTrials IsOverlayedOn = false; end properties (Access=protected, AbortSet, SetObservable, GetObservable) %FILTERUTILS Property is of type 'filtresp.filterutils' FilterUtils = []; %FILTERINDICES Property is of type 'mxArray' FilterIndices = [ ]; end methods function value = get.NumberOfTrials(obj) value = getmontecarlo(obj,obj.NumberOfTrials); end function set.NumberOfTrials(obj,value) % DataType = 'double' validateattributes(value,{'double'}, {'scalar'},'','NumberOfTrials') obj.NumberOfTrials = setmontecarlo(obj,value); end function set.FilterUtils(obj,value) % DataType = 'filtresp.filterutils' validateattributes(value,{'filtresp.filterutils'}, {'scalar'},'','FilterUtils') obj.FilterUtils = value; end function set.FilterIndices(obj,value) obj.FilterIndices = value; end end % set and get functions methods %% public methods function [indices, mssgObj] = checkfilters(this) %CHECKFILTERS Returns the valid filter indices. Hd = this.Filters; indices = []; unsupportedSysObjCounter = 0; multirateObjCounter = 0; % If we find 1 dfilt or 1 qfilt,or 1 supported System object, then the analysis can work for indx = 1:length(Hd) if isa(Hd(indx).Filter,'mfilt.abstractmultirate') || isa(Hd(indx).Filter,'mfilt.cascade') multirateObjCounter = multirateObjCounter+1; elseif isprop(Hd(indx).Filter,'FromSysObjFlag') && Hd(indx).Filter.FromSysObjFlag if ~Hd(indx).Filter.SupportsNLMethods && ~isa(Hd(indx).Filter,'mfilt.abstractmultirate') unsupportedSysObjCounter = unsupportedSysObjCounter+1; else indices = [indices indx]; end elseif (isa(Hd(indx).Filter, 'dfilt.singleton') ... || (isa(Hd(indx).Filter, 'dfilt.multistage') && ~isa(Hd(indx).Filter, 'mfilt.multistage'))... || isa(Hd(indx).Filter, 'qfilt') ... || isa(Hd(indx).Filter, 'dfilt.abstractfarrowfd')) indices = [indices indx]; %#ok<*AGROW> end end if isprop(Hd(indx).Filter,'FromFilterBuilderFlag') && Hd(indx).Filter.FromFilterBuilderFlag mssgObj = []; else if unsupportedSysObjCounter == length(Hd) mssgObj = message('signal:filtresp:nlm:checkfilters:NotSupportedForSysObj',this.Name); else if isempty(indices) && multirateObjCounter == length(Hd) mssgObj = message('signal:filtresp:nlm:checkfilters:OnlySingleRate',this.Name); else mssgObj = message('signal:filtresp:nlm:checkfilters:NotForSysobjOnlySingleRate',this.Name); end end end this.FilterIndices = indices; end function createnfftprm(hObj, allPrm) % CREATENFFTPRM Create an nfft parameter object. createparameter(hObj, allPrm, 'Number of Points', getnffttag(hObj), [1 1 inf], 512); end function b = enablemask(hObj) %ENABLEMASK Returns true if the object supports masks. % abstractresp does not support masks. Only magresp and groupdelay. b = false; end function freqmode_listener(this, eventData) %FREQMODE_LISTENER Listener for the freqmode parameter freqaxis_freqmode_listener(this, eventData); hPrm = getparameter(this, getfreqrangetag(this)); if isempty(hPrm), return; end units = getsettings(getparameter(this, 'freqmode'), eventData); if strcmpi(units, 'on'), opts = {'[0, pi)', '[0, 2pi)', '[-pi, pi)'}; else opts = {'[0, Fs/2)', '[0, Fs)', '[-Fs/2, Fs/2)'}; end setvalidvalues(hPrm, opts); end function strs = getlegendstrings(hObj, varargin) %GETLEGENDSTRINGS Returns the legend strings strs = getlegendstrings(hObj.FilterUtils, varargin{:}); % Remove legends of filters that do not support the analysis if isa(hObj.SOSViewOpts,'dspopts.sosview') && ... isprop(hObj.SOSViewOpts,'View') && ... ~strcmp(hObj.SOSViewOpts.View,'Complete') && ... (length(hObj.Filters) == 1) && ... isa(hObj.Filters.Filter,'dfilt.abstractsos') % Second order sections are only shown when we have a single % filter. If this is the case and View is not set to Complete, and % the filter is SOS then just use all the legend strings return end if ~isempty(hObj.FilterIndices) if strcmpi(hObj.ShowReference,'off') strs = strs(hObj.FilterIndices); else legendCounter = 1; for filterIndx = 1:length(hObj.Filters) isQuantizedAndReferenceStrsAvailable = false; hFilter = hObj.Filters(filterIndx).Filter; if isa(hFilter,'dfilt.parallel') || isa(hFilter,'dfilt.cascade') for idx = 1:nstages(hFilter); isQuantizedAndReferenceStrsAvailable = isQuantizedAndReferenceStrsAvailable |... (isprop(hFilter.Stage(idx),'Arithmetic') && ... ~strcmpi(hFilter.Stage(idx).Arithmetic,'double')); end else isQuantizedAndReferenceStrsAvailable = isprop(hObj.Filters(filterIndx).Filter,'Arithmetic') && ... ~strcmpi(hObj.Filters(filterIndx).Filter.Arithmetic,'double'); end if isQuantizedAndReferenceStrsAvailable legend{filterIndx} = strs([legendCounter legendCounter+1]); legendCounter = legendCounter+2; else legend{filterIndx} = strs(legendCounter); %#ok<*AGROW> legendCounter = legendCounter+1; end end newStrs = {}; for idx = 1:length(hObj.FilterIndices) xx = legend(hObj.FilterIndices(idx)); newStrs = [newStrs xx{:}]; end strs = newStrs; end end end function order = getlineorder(this, varargin) %GETLINEORDER Return the line order. order = getlineorder(this.FilterUtils, varargin{:}); end function s = getlinestyle(this, indx) %GETLINESTYLE Get the linestyle. s = getlinestyle(this.FilterUtils, indx); end function tag = getnffttag(hObj) % GETNFFTTAG Return string/tag for the nfft object. tag = 'nfftfornlm'; end function [m, xunits] = objspecificdraw(this) %OBJSPECIFICDRAW Draw the NOISEPOWERSPECTRUM h = this.Handles; opts = getoptions(this); h.axes = h.axes(end); Hd = this.Filters; endx = []; for indx = 1:length(Hd), if ~isa(Hd(indx).Filter, 'qfilt'), endx = [endx indx]; end end Hd(endx) = []; if isempty(Hd), warning(message('signal:filtresp:nlm:objspecificdraw:onlyQfilt', this.Name)); h.line = []; set(this, 'Handles', h); m = 1; xunits = ''; return; end % Calculate the data [H, W, P, Nf] = nlm(Hd, opts{1}, this.NumberofTrials, opts{2}); [W, m, xunits] = normalize_w(this, W); % Get the subclass to convert it for plotting [W, Y] = getplotdata(this, H, W, P, Nf); % Plot the data h.line = freqplotter(h.axes, W, Y); % Save the handles set(this, 'Handles', h); % Put up the ylabel from the subclass ylabel(h.axes, getylabel(this)); end function t = setmontecarlo(this, t) %SETMONTECARLO Set Function for the number of trials. hPrm = getparameter(hObj, 'montecarlo'); if ~isempty(hPrm), setvalue(hPrm, t); end end end %% public methods methods (Hidden) %% possibly private or hidden function attachlisteners(this) %ATTACHLISTENERS filtutils = this.FilterUtils; l(1) = event.proplistener(filtutils, filtutils.findprop('Filters'), 'PostSet', @(s,e)lclfilter_listener(this,e)); l(2) = event.proplistener(filtutils, filtutils.findprop('SOSViewOpts'), 'PostSet', @(s,e)sosview_listener(this,e)); l(3) = event.proplistener(filtutils, filtutils.findprop('PolyphaseView') ,'PostSet', @(s,e)lclshow_listener(this,e)); l(4) = event.proplistener(filtutils, filtutils.findprop('ShowReference') ,'PostSet', @(s,e)lclshow_listener(this,e)); set(this, 'WhenRenderedListeners', l); attachfilterlisteners(this); end function formataxislimits(this) %FORMATAXISLIMITS h = this.Handles; ydata = get(h.line, 'YData'); xdata = get(h.line, 'XData'); if isempty(ydata) return; end % Display of magnitude is always in dB % Compute global Y-axis limits over potentially % multiple filter magnitude responses % yMin = Inf; % min global y-limit yMax = -Inf; % max global y-limit xMin = Inf; xMax = -Inf; if ~iscell(ydata) ydata = {ydata}; xdata = {xdata}; end for indx = 1:length(ydata) % Loop over the filter responses. thisMag = ydata{indx}; % Estimate y-limits for dB plots thisYlim = freqzlim_dB(thisMag); yMin = min(yMin, thisYlim(1)); yMax = max(yMax, thisYlim(2)); xMin = min(xMin, min(xdata{indx})); xMax = max(xMax, max(xdata{indx})); end % Make sure that the yMin and yMax aren't exactly equal. This can happen % in the GRPDELAY case for linear phase filters or in magnitude for all % pass filters. if yMin == yMax yMin = yMin-.5; yMax = yMax+.5; end scale = get(h.axes, 'XScale'); if strcmpi(scale, 'log'), set(h.axes, 'YLim',[yMin yMax]); else set(h.axes, 'YLim',[yMin yMax], 'XLim', [xMin xMax]); end end function fs = getmaxfs(this) %GETMAXFS fs = this.FilterUtils.getmaxfs; end function hPrm = getxaxisparams(hObj) %GETXAXISPARAMS % Author(s): J. Schickler % Copyright 1988-2003 The MathWorks, Inc. hPrm = hObj.Parameters; hPrm = find(hPrm, 'tag', 'unitcirclewnofreqvec','-or', ... 'tag', 'nfftfornlm', '-or', ... 'tag', 'freqmode', '-or', ... 'tag', 'frequnits', '-or', ... 'tag', 'montecarlo', '-or', ... 'tag', 'freqscale'); end function nlm_construct(hObj, varargin) %NLM_CONSTRUCT allPrm = hObj.freqaxiswnfft_construct(varargin{:}); hObj.FilterUtils = filtresp.filterutils(varargin{:}); findclass(findpackage('dspopts'), 'sosview'); % g 227896 addprops(hObj, hObj.FilterUtils); createparameter(hObj, allPrm, 'Number of Trials', 'montecarlo', [1 1 inf], 12); % You cannot disable the nfft. make sure the frequencyresp superclass % did not do it. d = hObj.DisabledParameters; indx = strcmpi(d, 'nfft'); if ~isempty(indx), d(indx) = []; set(hObj, 'DisabledParameters', d); end end %---------------------------------------------------------------------- function filtrespShowUpdate(this,~) deletehandle(this, 'Legend'); captureanddraw(this, 'both'); % Make sure that we put up the legend after so that it is on top of the % plotting axes. updatelegend(this); end %---------------------------------------------------------------------- function filtrespFiltUpdate(this, ~, varargin) attachfilterlisteners(this); % When the filters change the legend may be invalid. Delete it to % force an update. h = this.Handles; if isfield(h, 'legend') && ishghandle(h.legend) % We are going to delete the legend and hence set the legend % property to 'Off' so keep the actual legend value and set it % again after deletion. legendState = this.Legend; delete(h.legend); this.Legend = legendState; end draw(this, varargin{:}); % Make sure that we put up the legend after so that it is on top of % the plotting axes. updatelegend(this); end % ---------------------------------------------------------- function attachfilterlisteners(this) l = this.WhenRenderedListeners; l = l(1:4); Hd = this.Filters; if ~isempty(Hd) l(end+1) = event.listener(Hd, 'NewFs', @(s,e)fs_listener(this,e)); l(end+1) = event.proplistener(Hd, Hd(1).findprop('Name'), 'PostSet', @(s,e)name_listener(this,e)); end set(this, 'WhenRenderedListeners', l); end end %% possibly private or hidden end % classdef function out = getmontecarlo(hObj, out) %#ok<INUSD> hPrm = getparameter(hObj, 'montecarlo'); if ~isempty(hPrm), out = get(hPrm, 'Value'); else out = ''; end end % getmontecarlo % ---------------------------------------------------------- function sosview_listener(this, eventData) Hd = this.Filters; if length(Hd) == 1 if isa(Hd.Filter, 'dfilt.abstractsos') lclshow_listener(this, eventData); end end end % ---------------------------------------------------------- function lclshow_listener(this, ~) if ~this.IsOverlayedOn filtrespShowUpdate(this); end end % --------------------------------------------------------------------- function lclfilter_listener(this, eventData, varargin) if ~this.IsOverlayedOn filtrespFiltUpdate(this,eventData,varargin{:}); end end % --------------------------------------------------------------------- function name_listener(this, ~) if ishandlefield(this, 'legend'), h = this.Handles; delete(h.legend); end updatelegend(this); end % --------------------------------------------------------------------- function fs_listener(this, ~, varargin) if ~this.IsOverlayedOn filtrespFsUpdate(this); end end % -------------------------------------------------------------------- function ylim = freqzlim_dB(mag) % Estimate Y-axis limits to view magnitude dB response % Algorithm: % - Estimate smoothed envelope of dB response curve, % to avoid "falling into nulls" in ripple regions % - Add a little "extra space" (margin) at top and bottom % % Note: we do NOT use the max and min of the response itself % - min is an overestimate often going to -300 dB or worse % - max is an underestimate causing the response to hit axis % % Returns: % ylim: vector of y-axis display limits, [ymin ymax] MarginTop = 0.03; % 3% margin of dyn range at top MarginBot = 0.10; % 10% margin at bottom % Determine default margins % % Remove non-finite values for dynamic range computation magf = mag; magf(~isfinite(magf))=[]; dr = max(magf)-min(magf); % "modified" dynamic range % Handle the null case. if isempty(dr) ylim = [0 1]; return; end % Length of sliding window to compute "localized maxima" values % We're looking for the MINIMUM of the ENVELOPE of the input curve (mag). % The true envelope is difficult to compute due as it is positive-only % The length of the sliding window is important: % - too long: envelope estimate is biased toward "global max" % and we lose accuracy of envelope minimum % - too short: we fall into "nulls" and we're no longer tracking envelope % % Set window to 5% of input length, minimum of 3 samples Nspan = max(3, ceil(0.1*numel(mag))); % Compute mag envelope, derive y-limit estimates env = MiniMax(mag, Nspan); ymin = min(env) - dr*MarginBot; % Lower by fraction of dynamic range ymax = max(mag) + dr*MarginTop; % Raise by fraction of dynamic range ylim = [ymin ymax]; end % -------------------------------------------------------------------- function spanMin = MiniMax(mag,Nspan) %MiniMax Find the minimum of all local maxima, with each % maxima computed over NSPAN-length segments of input. Nele=numel(mag); if Nele<Nspan spanMin = min(mag); else % General case spanMin = max(mag(1:Nspan)); % max computed over first span intMax = spanMin; % interval max computed over all spans for i = 1:Nele-Nspan % already did first span (above!) % Overall equivalent code for this section: % spanMin = min(spanMin,max(mag(i:i+Ns1))); % % Update intMax, the maximum found over the current interval % The "update" is to consider just (a) the next point to bring % into the interval, and (b) the last point dropped out of the % interval. This produces an efficient "slide by 1" max result. % % Equivalent code: % intMax = max(mag(i:i+Ns1)); pAdd = mag(i+Nspan); % add point if pAdd > intMax % just take pAdd as new max intMax = pAdd; elseif mag(i) < intMax % Note: pDrop = mag(i-1) % just add in effect of next point intMax = max(intMax, pAdd); else % pDrop == last_intMax: recompute max intMax = max(mag(i+1 : i+Nspan)); end % Equivalent code: % spanMin = min(spanMin,intMax); if spanMin > intMax spanMin = intMax; end end end end