Mercurial > hg > ltpda
diff m-toolbox/classes/+utils/@math/music.m @ 0:f0afece42f48
Import.
author | Daniele Nicolodi <nicolodi@science.unitn.it> |
---|---|
date | Wed, 23 Nov 2011 19:22:13 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/m-toolbox/classes/+utils/@math/music.m Wed Nov 23 19:22:13 2011 +0100 @@ -0,0 +1,262 @@ +function [music_data,msg] = music(x,p,varargin) +%MUSIC Implements the heart of the MUSIC algorithm of line spectra estimation. +% MUSIC is called by both PMUSIC and ROOTMUSIC. +% +% Inputs: +% +% x - vector or matrix. If vector it is a signal, if matrix it may be either a data +% matrix such that x'*x=R, or a correlation matrix R. +% p - scalar or two element vector. If scalar, it indicates the dimension of the +% signal subspace. If vector, p(2) is a threshold used to determine the +% aforementioned dimension. +% nfft - (optional) to be used only with PMUSIC. A scalar indicating the number of +% points used in the evaluation of the pseudospectrum. +% Fs - (optional) a scalar specifying the sampling frequency. If omitted, we work +% in rad/sample; if empty it defaults to 1 Hz. +% nw - (optional) a scalar or vector indicating either the order of the correlation +% matrix or (when a vector) a window whose length is the order of the matrix +% and whose values are used to window each column of the data matrix. +% noverlap - (optional) a integer indicating the number of samples to overlap from +% column to column. +% strings - Optional input strings are: 'corr', 'EV' and range ('half' or 'whole'). +% +% Outputs: +% +% msg - a possible error message. +% +% music_data - a structure with the following fields: +% +% noise_eigenvects - a matrix whose columns are the noise subspace eigenvectors. +% signal_eigenvects - a matrix whose columns are the signal subspace eigenvectors. +% eigenvals - the eigenvalues of the correlation matrix. +% p_eff - the effective dimension of the signal subspace. +% nfft - number of points used to evaluate the pseudospectrum (only used in PMUSIC). +% Fs - sampling freq. +% range - string indicating whether 'half' or the 'whole' pseudospectrum should be +% computed. (Only used in PMUSIC.) +% EVFlag - flag, 0 = MUSIC method; 1 = EigenVector method. + +% Author(s): R. Losada +% Copyright 1988-2006 The MathWorks, Inc. +% $Revision: 1.1 $ $Date: 2010/02/18 11:16:20 $ + +% References: +% [1] Petre Stoica and Randolph Moses, Introduction To Spectral +% Analysis, Prentice-Hall, 1997, pg. 15 +% [2] S. J. Orfanidis, Optimum Signal Processing. An Introduction. +% 2nd Ed., Macmillan, 1988. + + +xIsReal = isreal(x); +msg = ''; +music_data = []; + +if isempty(p), + msg = 'The signal subspace dimension cannot be empty.'; + return +end + +[opts,msg] = music_options(xIsReal,p,varargin{:}); +if ~isempty(msg), + return +end + +% Compute the eigenvalues and eigenvectors of the correlation matrix +[eigenvals,eigenvects] = computeeig(x,opts.CorrFlag,opts.CorrMatrOrd,opts.nw,opts.noverlap,opts.window,opts.EVFlag); + +% Determine the effective dimension of the signal subspace +p_eff = determine_signal_space(p,eigenvals); + +% Separate the signal and noise eigenvectors +signal_eigenvects = eigenvects(:,1:p_eff); +noise_eigenvects = eigenvects(:,p_eff+1:end); + +% Generate the output structure +music_data.noise_eigenvects = noise_eigenvects; +music_data.signal_eigenvects = signal_eigenvects; +music_data.eigenvals = eigenvals; +music_data.p_eff = p_eff; +music_data.nfft = opts.nfft; +music_data.Fs = opts.Fs; +music_data.EVFlag = opts.EVFlag; +music_data.range = opts.range; + + +%-------------------------------------------------------------------------------------- +function [options,msg] = music_options(xIsReal,p,varargin) +%MUSIC_OPTIONS Parse the optional inputs to the MUSIC function. +% MUSIC_OPTIONS returns a structure, OPTIONS, with the following fields: +% +% options.nfft - number of freq. points at which the psd is estimated +% options.Fs - sampling freq. if any +% options.range - 'onesided' or 'twosided' pseudospectrum (they correspond to +% 'half' and 'whole' respectively, but are returned as is by +% psdoptions.m +% options.nw - number of columns in the data matrix +% options.noverlap - number of samples to overlap +% options.window - a vector with window coefficients +% options.CorrFlag - a flag indicating whether the input is a correlation matrix +% options.EVFlag - flag, 0 = MUSIC method ; 1 = EigenVector method +% options.CorrMatrOrd - order of the correlation matrix to be used in computations + + + +% Assign Defaults +msg = ''; +options.nw = []; +options.noverlap = []; +options.window = []; +options.nfft = 256; +options.Fs = []; +options.CorrFlag = 0; +options.EVFlag = 0; +% Determine if frequency vector specified +freqVecSpec = false; +if (length(varargin) > 0 && isnumeric(varargin{1}) && length(varargin{1}) > 1) + freqVecSpec = true; +end + +if xIsReal && ~freqVecSpec, + options.range = 'onesided'; +else + options.range = 'twosided'; +end + +[options,msg] = psdoptions(xIsReal,options,varargin{:}); + +if length(options.nfft) > 1, + if strcmpi(options.range,'onesided') + warning(generatemsgid('InconsistentRangeOption'),... + 'Ignoring the ''onesided'' option. When a frequency vector is specified, a ''twosided'' PSD is computed'); + end + options.range = 'twosided'; +end + +% psdoptions doesn't handle this field, assign it separetely +options.CorrMatrOrd = 2*p(1); + +%----------------------------------------------------------------------------------------- +function [eigenvals,eigenvects] = computeeig(x,CorrFlag,CorrMatrOrd,nw,noverlap,window,EVFlag) +%COMPUTEEIG Compute eigenvalues and eigenvectors of correlation matrix. +% +% Inputs: +% +% x - input vector or matrix +% CorrFlag - (flag) indicates whether x is a correlation matrix +% nw - (integer) length of the rows of the data matrix +% (only used if x is vector) +% noverlap - (integer) overlap between the rows of the data matrix +% (used in conjunction with nw) +% window - (vector) window to be applied to each column of data +% matrix (not used if x is a correlation matrix) +% EVFlag - True if eigenvector method, false if MUSIC. +% +% +% Outputs: +% +% eigenvals +% eigenvects +% +% If x is a matrix, +% If CorrFlag = 1, input x is a correlation matrix, we compute the +% eigendecomposition and order the eigenvalues and eigenvectors. +% +% If x is a vector, +% a data matrix is formed by calling corrmtx unless a custom nw +% and noverlap are specified. In that case, we use buffer to form +% the data matrix. +% +% If window is not empty, each row of the data matrix will be +% multiplied by the window. + +% Determine if the input is a matrix +xIsMatrix = ~any(size(x)==1); + +if xIsMatrix && CorrFlag, + % Input is Correlation matrix + + % Compute the eigenvectors and eigenvalues + [E,D] = eig((x+x')/2); % Ensure hermitian + [eigenvals,indx] = sort(diag(D),'descend'); + eigenvects = E(:,indx); + +else + if xIsMatrix + % Input is already a data matrix + [Mx,Nx] = size(x); % Determine size of data matrix + if EVFlag && (Nx > Mx), + errmsg = 'The number of columns in the data matrix cannot exceed the number of rows.'; + error(generatemsgid('invalidDataMatrix'),errmsg); + end + + else + % x is a vector + x = x(:); % Make it a column + if isempty(nw), + x = corrmtx(x,CorrMatrOrd-1,'cov'); + else + if EVFlag && nw > (ceil((length(x)-nw)/(nw-noverlap))+1), + errmsg = sprintf(['The segment length and overlap specified result in\n',... + 'a data matrix with more columns than rows.']); + error(generatemsgid('invalidDataMatrix'),errmsg); + end + Lx = length(x); + x = buffer(x,nw,noverlap,'nodelay'); + if Lx <= nw, + error(generatemsgid('invalidSegmentLength'),'The segment length, NW, must be smaller than the signal length.'); + end + x = x'./sqrt(Lx-nw); % Scale appropriately such that X'*X is a scaled estimate of R + end + end + if ~isempty(window), + % Apply window to each row of data matrix + if length(window) ~= size(x,2), + error(generatemsgid('InvalidDimensions'),'Window length must equal the number of columns in the data matrix.'); + end + window = repmat(window(:).',size(x,1),1); + x = x.*window; + end + + % Compute the eigenvectors and eigenvalues via the SVD + [U,S,eigenvects] = svd(x,0); + eigenvals = diag(S).^2; % We need to square the singular values here +end + + +%-------------------------------------------------------------------------------------------- +function p_eff = determine_signal_space(p,eigenvals) +%DETERMINE_SIGNAL_SPACE Determines the effective dimension of the signal subspace. +% +% Inputs: +% +% p - (scalar or vector) signal subspace dimension +% (but may contain a desired threshold). +% eigenvals - (vector) contains the eigenvalues (sorted in decreasing order) +% of the correlation matrix +% +% Outputs: +% +% p_eff - The effective dimension of the signal subspace. If a threshold +% is given as p(2), the signal subspace will be equal to the number +% of eigenvalues, NEIG, greater than the threshold times the smallest +% eigenvalue. However, the dimension of the signal subspace is at most +% p(1), so that if NEIG is greater than p(1), p_eff will be equal to +% p(1). If the threshold criteria results in an empty signal subspace, +% once again we make p_eff = p(1). + + +% Use the signal space dimension or the threshold to separate the noise subspace eigenvectors +if length(p) == 2, + % The threshold will be the input threshold times the smallest eigenvalue + thresh = p(2)*eigenvals(end); + indx = find(eigenvals > thresh); + if ~isempty(indx) + p_eff = min( p(1), length(indx) ); + else + p_eff = p(1); + end +else + p_eff = p; +end + +% [EOF] - music.m