Mercurial > hg > ltpda
view m-toolbox/classes/@ao/psdconf.m @ 44:409a22968d5e default
Add unit tests
author | Daniele Nicolodi <nicolodi@science.unitn.it> |
---|---|
date | Tue, 06 Dec 2011 18:42:11 +0100 |
parents | f0afece42f48 |
children |
line wrap: on
line source
% PSDCONF Calculates confidence levels and variance for psd %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % DESCRIPTION: PSDCONF Input a spectrum estimated with psd or lpsd (Welch's % Overlapped Segment Averaging Method) and calculates confidence levels and % variance for the spectrum. % % CALL: [lcl,ucl] = psdconf(a,pl) % [lcl,ucl,var] = psdconf(a,pl) % % INPUTS: % a - input analysis objects containing power spectral % densities calculated with psd or lpsd. % pl - input parameter list % % OUTPUTS: % lcl - lower confidence level % ucl - upper confidence level % var - expected spectrum variance % % % If the last input argument is a parameter list (plist). % The following parameters are recognised. % % % NOTE1: PSDCONF checks the navs field to distinguish between psd and lpsd % power spectra. If a.data.navs is NaN then it assumes to dealing with lpsd % power spectrum at input. % % NOTE2: Copied directly from MATLAB chi2conf function (Signal Processing % Toolbox) and extended to do degrees of fredom calculation, variance % calculation, to input AOs and to input plist. % Copyright 2007 The MathWorks, Inc. % Revision: 1.6.4.4 Date: 2008/05/31 23:27:28 % % % <a href="matlab:utils.helper.displayMethodInfo('ao', 'psdconf')">Parameters Description</a> % % VERSION: $Id: psdconf.m,v 1.14 2011/05/18 07:53:57 mauro Exp $ % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function varargout = psdconf(varargin) %%% Check if this is a call for parameters if utils.helper.isinfocall(varargin{:}) varargout{1} = getInfo(varargin{3}); return end import utils.const.* utils.helper.msg(msg.PROC3, 'running %s/%s', mfilename('class'), mfilename); %%% Collect input variable names in_names = cell(size(varargin)); for ii = 1:nargin,in_names{ii} = inputname(ii);end %%% Collect all AOs [as, ao_invars] = utils.helper.collect_objects(varargin(:), 'ao', in_names); pl = utils.helper.collect_objects(varargin(:), 'plist', in_names); %%% avoid multiple AO at input if numel(as)>1 error('!!! Too many input AOs, PSDCONF can process only one AO per time !!!') end %%% check that fsdata is input if ~isa(as.data, 'fsdata') error('!!! Non-fsdata input, PSDCONF can process only fsdata !!!') end %%% avoid input modification if nargout == 0 error('!!! PSDCONF cannot be used as a modifier. Please give an output variable !!!'); end %%% Parse plists pl = parse(pl, getDefaultPlist()); %%% Find parameters conf = find(pl, 'conf'); conf = conf/100; % go from percentage to fractional %%% getting metho if isnan(as.data.navs) mtd = 'lpsd'; else mtd = 'psd'; end %%% switching over methods switch mtd case 'psd' %%% confidence levels for spectra calculated with psd % get number of averages navs = as.data.navs; % get window object w = as.hist.plistUsed.find('WIN'); % percentage of overlap olap = as.hist.plistUsed.find('OLAP')./100; % number of bins in each fft nfft = as.hist.plistUsed.find('NFFT'); % Normalize window data in order to be square integrable to 1 win = w.win ./ sqrt(w.ws2); % Calculates total number of data in the original time-series Ntot = ceil(navs*(nfft-olap*nfft)+olap*nfft); % defining the shift factor n = floor((Ntot-nfft)./(navs-1)); % Calculating correction factor sm = 0; % Padding to zero the window % willing to work with columns [ii,kk] = size(win); if ii<kk win = win.'; end win = [win; zeros(navs*n,1)]; for mm = 1:navs-1 pc = 0; for tt = 1:nfft pc = pc + win(tt)*win(tt + mm*n); end sm = sm + (1 - mm/navs)*(abs(pc)^2); end % Calculating degrees of freedom dof = (2*navs)/(1+2*sm); dof = round(dof); % Calculating Confidence Levels factors alfa = 1 - conf; c = chi2inv([1-alfa/2 alfa/2],dof); c = dof./c; % calculating variance expvar = ((as.data.y).^2).*2./dof; % calculating confidence levels lwb = as.data.y.*c(1); upb = as.data.y.*c(2); case 'lpsd' %%% confidence levels for spectra calculated with lpsd % get window used uwin = as.hist.plistUsed.find('WIN'); % extract number of frequencies bins nf = length(as.x); % dft length for each bin if ~isempty(as.procinfo) L = as.procinfo.find('L'); else error('### The AO doesn''t have any procinfo with the key ''L'''); end % set original data length as the length of the first window nx = L(1); % windows overlap olap = as.hist.plistUsed.find('OLAP'); dofs = ones(nf,1); cl = ones(nf,2); for jj=1:nf l = L(jj); % compute window switch uwin.type case 'Kaiser' w = specwin(uwin.type, l, uwin.psll); otherwise w = specwin(uwin.type, l); end % Normalize window data in order to be square integrable to 1 owin = w.win ./ sqrt(w.ws2); % Compute the number of averages we want here segLen = l; nData = nx; ovfact = 1 / (1 - olap); davg = (((nData - segLen)) * ovfact) / segLen + 1; navg = round(davg); % Compute steps between segments if navg == 1 shift = 1; else shift = (nData - segLen) / (navg - 1); end if shift < 1 || isnan(shift) shift = 1; end n = floor(shift); % Calculating correction factor sm = 0; % Padding to zero the window % willing to work with columns [ii,kk] = size(owin); if ii<kk owin = owin.'; end win = [owin; zeros(navg*n,1)]; for mm = 1:navg-1 pc = 0; for tt = 1:segLen pc = pc + win(tt)*win(tt + mm*n); end sm = sm + (1 - mm/navg)*(abs(pc)^2); end % Calculating degrees of freedom dof = (2*navg)/(1+2*sm); dof = round(dof); % Calculating Confidence Levels factors alfa = 1 - conf; c = chi2inv([1-alfa/2 alfa/2],dof); c = dof./c; % storing c and dof dofs(jj) = dof; cl(jj,1) = c(1); cl(jj,2) = c(2); end % for jj=1:nf % willing to work with columns dy = as.data.y; [ii,kk] = size(dy); if ii<kk dy = dy.'; rsp = true; else rsp = false; end % calculating variance expvar = ((dy).^2).*2./dofs; % calculating confidence levels lwb = dy.*cl(:,1); upb = dy.*cl(:,2); % reshaping if necessary if rsp expvar = expvar.'; lwb = lwb.'; upb = upb.'; end end %switch mtd % Output data % defining units inputunit = get(as.data,'yunits'); varunit = unit(inputunit.^2); varunit.simplify; levunit = inputunit; levunit.simplify; % variance dvar = fsdata(); dvar.setFs(as.data.fs); dvar.setT0(as.data.t0); dvar.setEnbw(as.data.enbw); dvar.setNavs(as.data.navs); dvar.setXunits(copy(as.data.xunits,1)); dvar.setYunits(varunit); dvar.setX(as.data.x); dvar.setY(expvar); ovar = ao(dvar); % Set output AO name ovar.name = sprintf('var(%s)', ao_invars{:}); % Add history ovar.addHistory(getInfo('None'), pl, [ao_invars(:)], [as.hist]); % lower confidence level dlwb = fsdata(); dlwb.setFs(as.data.fs); dlwb.setT0(as.data.t0); dlwb.setEnbw(as.data.enbw); dlwb.setNavs(as.data.navs); dlwb.setXunits(copy(as.data.xunits,1)); dlwb.setYunits(levunit); dlwb.setX(as.data.x); dlwb.setY(lwb); olwb = ao(dlwb); % Set output AO name clev = [num2str(conf*100) '%']; olwb.name = sprintf('%s_low_conf_level(%s)', clev, ao_invars{:}); % Add history olwb.addHistory(getInfo('None'), pl, [ao_invars(:)], [as.hist]); % upper confidence level dupb = fsdata(); dupb.setFs(as.data.fs); dupb.setT0(as.data.t0); dupb.setEnbw(as.data.enbw); dupb.setNavs(as.data.navs); dupb.setXunits(copy(as.data.xunits,1)); dupb.setYunits(levunit); dupb.setX(as.data.x); dupb.setY(upb); oupb = ao(dupb); % Set output AO name oupb.name = sprintf('%s_up_conf_level(%s)', clev, ao_invars{:}); % Add history oupb.addHistory(getInfo('None'), pl, [ao_invars(:)], [as.hist]); % output if nargout == 2 varargout{1} = olwb; % lower conf level varargout{2} = oupb; % upper conf level elseif nargout == 3 varargout{1} = olwb; % lower conf level varargout{2} = oupb; % upper conf level varargout{3} = ovar; % expected variance end end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Local Functions % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %----- Matlab Functions --------------------------------------------------- %-------------------------------------------------------------------------- function x = chi2inv(p,v) %CHI2INV Inverse of the chi-square cumulative distribution function (cdf). % X = CHI2INV(P,V) returns the inverse of the chi-square cdf with V % degrees of freedom at the values in P. The chi-square cdf with V % degrees of freedom, is the gamma cdf with parameters V/2 and 2. % % The size of X is the common size of P and V. A scalar input % functions as a constant matrix of the same size as the other input. % References: % [1] M. Abramowitz and I. A. Stegun, "Handbook of Mathematical % Functions", Government Printing Office, 1964, 26.4. % [2] E. Kreyszig, "Introductory Mathematical Statistics", % John Wiley, 1970, section 10.2 (page 144) if nargin < 2, error(generatemsgid('Nargchk'),'Requires two input arguments.'); end [errorcode p v] = distchck(2,p,v); if errorcode > 0 error(generatemsgid('InvalidDimensions'),'Requires non-scalar arguments to match in size.'); end % Call the gamma inverse function. x = gaminv(p,v/2,2); % Return NaN if the degrees of freedom is not a positive integer. k = find(v < 0 | round(v) ~= v); if any(k) tmp = NaN; x(k) = tmp(ones(size(k))); end end %-------------------------------------------------------------------------- function x = gaminv(p,a,b) %GAMINV Inverse of the gamma cumulative distribution function (cdf). % X = GAMINV(P,A,B) returns the inverse of the gamma cdf with % parameters A and B, at the probabilities in P. % % The size of X is the common size of the input arguments. A scalar input % functions as a constant matrix of the same size as the other inputs. % % GAMINV uses Newton's method to converge to the solution. % References: % [1] M. Abramowitz and I. A. Stegun, "Handbook of Mathematical % Functions", Government Printing Office, 1964, 6.5. % B.A. Jones 1-12-93 % Was: Revision: 1.2, Date: 1996/07/25 16:23:36 if nargin<3, b=1; end [errorcode p a b] = distchck(3,p,a,b); if errorcode > 0 error(generatemsgid('InvalidDimensions'),'The arguments must be the same size or be scalars.'); end % Initialize X to zero. x = zeros(size(p)); k = find(p<0 | p>1 | a <= 0 | b <= 0); if any(k), tmp = NaN; x(k) = tmp(ones(size(k))); end % The inverse cdf of 0 is 0, and the inverse cdf of 1 is 1. k0 = find(p == 0 & a > 0 & b > 0); if any(k0), x(k0) = zeros(size(k0)); end k1 = find(p == 1 & a > 0 & b > 0); if any(k1), tmp = Inf; x(k1) = tmp(ones(size(k1))); end % Newton's Method % Permit no more than count_limit iterations. count_limit = 100; count = 0; k = find(p > 0 & p < 1 & a > 0 & b > 0); pk = p(k); % Supply a starting guess for the iteration. % Use a method of moments fit to the lognormal distribution. mn = a(k) .* b(k); v = mn .* b(k); temp = log(v + mn .^ 2); mu = 2 * log(mn) - 0.5 * temp; sigma = -2 * log(mn) + temp; xk = exp(norminv(pk,mu,sigma)); h = ones(size(pk)); % Break out of the iteration loop for three reasons: % 1) the last update is very small (compared to x) % 2) the last update is very small (compared to sqrt(eps)) % 3) There are more than 100 iterations. This should NEVER happen. while(any(abs(h) > sqrt(eps)*abs(xk)) & max(abs(h)) > sqrt(eps) ... & count < count_limit), count = count + 1; h = (gamcdf(xk,a(k),b(k)) - pk) ./ gampdf(xk,a(k),b(k)); xnew = xk - h; % Make sure that the current guess stays greater than zero. % When Newton's Method suggests steps that lead to negative guesses % take a step 9/10ths of the way to zero: ksmall = find(xnew < 0); if any(ksmall), xnew(ksmall) = xk(ksmall) / 10; h = xk-xnew; end xk = xnew; end % Store the converged value in the correct place x(k) = xk; if count == count_limit, fprintf('\nWarning: GAMINV did not converge.\n'); str = 'The last step was: '; outstr = sprintf([str,'%13.8f'],h); fprintf(outstr); end end %-------------------------------------------------------------------------- function x = norminv(p,mu,sigma) %NORMINV Inverse of the normal cumulative distribution function (cdf). % X = NORMINV(P,MU,SIGMA) finds the inverse of the normal cdf with % mean, MU, and standard deviation, SIGMA. % % The size of X is the common size of the input arguments. A scalar input % functions as a constant matrix of the same size as the other inputs. % % Default values for MU and SIGMA are 0 and 1 respectively. % References: % [1] M. Abramowitz and I. A. Stegun, "Handbook of Mathematical % Functions", Government Printing Office, 1964, 7.1.1 and 26.2.2 % Was: Revision: 1.2, Date: 1996/07/25 16:23:36 if nargin < 3, sigma = 1; end if nargin < 2; mu = 0; end [errorcode p mu sigma] = distchck(3,p,mu,sigma); if errorcode > 0 error(generatemsgid('InvalidDimensions'),'Requires non-scalar arguments to match in size.'); end % Allocate space for x. x = zeros(size(p)); % Return NaN if the arguments are outside their respective limits. k = find(sigma <= 0 | p < 0 | p > 1); if any(k) tmp = NaN; x(k) = tmp(ones(size(k))); end % Put in the correct values when P is either 0 or 1. k = find(p == 0); if any(k) tmp = Inf; x(k) = -tmp(ones(size(k))); end k = find(p == 1); if any(k) tmp = Inf; x(k) = tmp(ones(size(k))); end % Compute the inverse function for the intermediate values. k = find(p > 0 & p < 1 & sigma > 0); if any(k), x(k) = sqrt(2) * sigma(k) .* erfinv(2 * p(k) - 1) + mu(k); end end %-------------------------------------------------------------------------- function p = gamcdf(x,a,b) %GAMCDF Gamma cumulative distribution function. % P = GAMCDF(X,A,B) returns the gamma cumulative distribution % function with parameters A and B at the values in X. % % The size of P is the common size of the input arguments. A scalar input % functions as a constant matrix of the same size as the other inputs. % % Some references refer to the gamma distribution with a single % parameter. This corresponds to the default of B = 1. % % GAMMAINC does computational work. % References: % [1] L. Devroye, "Non-Uniform Random Variate Generation", % Springer-Verlag, 1986. p. 401. % [2] M. Abramowitz and I. A. Stegun, "Handbook of Mathematical % Functions", Government Printing Office, 1964, 26.1.32. % Was: Revision: 1.2, Date: 1996/07/25 16:23:36 if nargin < 3, b = 1; end if nargin < 2, error(generatemsgid('Nargchk'),'Requires at least two input arguments.'); end [errorcode x a b] = distchck(3,x,a,b); if errorcode > 0 error(generatemsgid('InvalidDimensions'),'Requires non-scalar arguments to match in size.'); end % Return NaN if the arguments are outside their respective limits. k1 = find(a <= 0 | b <= 0); if any(k1) tmp = NaN; p(k1) = tmp(ones(size(k1))); end % Initialize P to zero. p = zeros(size(x)); k = find(x > 0 & ~(a <= 0 | b <= 0)); if any(k), p(k) = gammainc(x(k) ./ b(k),a(k)); end % Make sure that round-off errors never make P greater than 1. k = find(p > 1); if any(k) p(k) = ones(size(k)); end end %-------------------------------------------------------------------------- function y = gampdf(x,a,b) %GAMPDF Gamma probability density function. % Y = GAMPDF(X,A,B) returns the gamma probability density function % with parameters A and B, at the values in X. % % The size of Y is the common size of the input arguments. A scalar input % functions as a constant matrix of the same size as the other inputs. % % Some references refer to the gamma distribution with a single % parameter. This corresponds to the default of B = 1. % References: % [1] L. Devroye, "Non-Uniform Random Variate Generation", % Springer-Verlag, 1986, pages 401-402. % Was: Revision: 1.2, Date: 1996/07/25 16:23:36 if nargin < 3, b = 1; end if nargin < 2, error(generatemsgid('Nargchk'),'Requires at least two input arguments'); end [errorcode x a b] = distchck(3,x,a,b); if errorcode > 0 error(generatemsgid('InvalidDimensions'),'Requires non-scalar arguments to match in size.'); end % Initialize Y to zero. y = zeros(size(x)); % Return NaN if the arguments are outside their respective limits. k1 = find(a <= 0 | b <= 0); if any(k1) tmp = NaN; y(k1) = tmp(ones(size(k1))); end k=find(x > 0 & ~(a <= 0 | b <= 0)); if any(k) y(k) = (a(k) - 1) .* log(x(k)) - (x(k) ./ b(k)) - gammaln(a(k)) - a(k) .* log(b(k)); y(k) = exp(y(k)); end k1 = find(x == 0 & a < 1); if any(k1) tmp = Inf; y(k1) = tmp(ones(size(k1))); end k2 = find(x == 0 & a == 1); if any(k2) y(k2) = (1./b(k2)); end end function [errorcode,out1,out2,out3,out4] = distchck(nparms,arg1,arg2,arg3,arg4) %DISTCHCK Checks the argument list for the probability functions. % B.A. Jones 1-22-93 % Was: Revision: 1.2, Date: 1996/07/25 16:23:36 errorcode = 0; if nparms == 1 out1 = arg1; return; end if nparms == 2 [r1 c1] = size(arg1); [r2 c2] = size(arg2); scalararg1 = (prod(size(arg1)) == 1); scalararg2 = (prod(size(arg2)) == 1); if ~scalararg1 & ~scalararg2 if r1 ~= r2 | c1 ~= c2 errorcode = 1; return; end end if scalararg1 out1 = arg1(ones(r2,1),ones(c2,1)); else out1 = arg1; end if scalararg2 out2 = arg2(ones(r1,1),ones(c1,1)); else out2 = arg2; end end if nparms == 3 [r1 c1] = size(arg1); [r2 c2] = size(arg2); [r3 c3] = size(arg3); scalararg1 = (prod(size(arg1)) == 1); scalararg2 = (prod(size(arg2)) == 1); scalararg3 = (prod(size(arg3)) == 1); if ~scalararg1 & ~scalararg2 if r1 ~= r2 | c1 ~= c2 errorcode = 1; return; end end if ~scalararg1 & ~scalararg3 if r1 ~= r3 | c1 ~= c3 errorcode = 1; return; end end if ~scalararg3 & ~scalararg2 if r3 ~= r2 | c3 ~= c2 errorcode = 1; return; end end if ~scalararg1 out1 = arg1; end if ~scalararg2 out2 = arg2; end if ~scalararg3 out3 = arg3; end rows = max([r1 r2 r3]); columns = max([c1 c2 c3]); if scalararg1 out1 = arg1(ones(rows,1),ones(columns,1)); end if scalararg2 out2 = arg2(ones(rows,1),ones(columns,1)); end if scalararg3 out3 = arg3(ones(rows,1),ones(columns,1)); end out4 =[]; end if nparms == 4 [r1 c1] = size(arg1); [r2 c2] = size(arg2); [r3 c3] = size(arg3); [r4 c4] = size(arg4); scalararg1 = (prod(size(arg1)) == 1); scalararg2 = (prod(size(arg2)) == 1); scalararg3 = (prod(size(arg3)) == 1); scalararg4 = (prod(size(arg4)) == 1); if ~scalararg1 & ~scalararg2 if r1 ~= r2 | c1 ~= c2 errorcode = 1; return; end end if ~scalararg1 & ~scalararg3 if r1 ~= r3 | c1 ~= c3 errorcode = 1; return; end end if ~scalararg1 & ~scalararg4 if r1 ~= r4 | c1 ~= c4 errorcode = 1; return; end end if ~scalararg3 & ~scalararg2 if r3 ~= r2 | c3 ~= c2 errorcode = 1; return; end end if ~scalararg4 & ~scalararg2 if r4 ~= r2 | c4 ~= c2 errorcode = 1; return; end end if ~scalararg3 & ~scalararg4 if r3 ~= r4 | c3 ~= c4 errorcode = 1; return; end end if ~scalararg1 out1 = arg1; end if ~scalararg2 out2 = arg2; end if ~scalararg3 out3 = arg3; end if ~scalararg4 out4 = arg4; end rows = max([r1 r2 r3 r4]); columns = max([c1 c2 c3 c4]); if scalararg1 out1 = arg1(ones(rows,1),ones(columns,1)); end if scalararg2 out2 = arg2(ones(rows,1),ones(columns,1)); end if scalararg3 out3 = arg3(ones(rows,1),ones(columns,1)); end if scalararg4 out4 = arg4(ones(rows,1),ones(columns,1)); end end end %----- LTPDA FUNCTIONS ---------------------------------------------------- %-------------------------------------------------------------------------- % Get Info Object %-------------------------------------------------------------------------- function ii = getInfo(varargin) if nargin == 1 && strcmpi(varargin{1}, 'None') sets = {}; pl = []; else sets = {'Default'}; pl = getDefaultPlist; end % Build info object ii = minfo(mfilename, 'ao', 'ltpda', utils.const.categories.sigproc, '$Id: psdconf.m,v 1.14 2011/05/18 07:53:57 mauro Exp $', sets, pl); ii.setModifier(false); ii.setOutmin(2); ii.setOutmax(3); end %-------------------------------------------------------------------------- % Get Default Plist %-------------------------------------------------------------------------- function plout = getDefaultPlist() persistent pl; if exist('pl', 'var')==0 || isempty(pl) pl = buildplist(); end plout = pl; end function pl = buildplist() pl = plist({'conf', 'Required percentage confidence level. [0-100]'}, paramValue.DOUBLE_VALUE(95)); end % END % PARAMETERS: % 'conf' - Required percentage confidence level. It is a % number between 0 and 100 [Default: 95, % corresponding to 95% confidence].