from Nonparametric Power Spectrum Estimation With Thresholded Cepstrum by Steve Lutes
A function to nonparametrically estimate the power spectrum via thresholded cepstrum

ln(PowerSpectrum(i))', 'Power spectrum is in log to the base e (natural log) units.'); end % End code to make the output the PSD, DB, normalized, etc
function [SpectrumOut, Message, StatusFlag] = CepSpec(Input, BandInfo, OutOptions)

% Function to compute the power spectrum from the cepstrum via the method
% described in "Smoothed Nonparametric Spectral Estimation Via Cepstrum 
% Thresholding" by Perte Stocia and Niclas Sandgren, IEEE Signal Processing 
% Magazine, November 2006 vol 23, number 6
%
% This software copyright by Steve Lutes 11/11/2006. All rights reserved
% Commerical use requires a license. Non-commerical use requires acknowledging
% Steve Lutes as author of this software. Send e-mail to utahraptor@intergate.com for
% more info.
% 
% How to use: 
%
%	[SpectrumOut, Message, StatusFlag] = CepSpec(Input, BandInfo, OutOptions) 
%
%---Outputs------------------------------------------------------------------------------
%
%	SpectrumOut- an array length(Input) / 2 points long holding the
%				 power spectrum of Input.
%
%	Message - a text string telling of problems, if any. If no trouble, Message = 'Ok' plus
%			  whatever output option user has specified
%
%	StatusFlag- a numeric scalar telling of trouble. = 0 means all ok, = 1 means trouble
%
%---Inputs-------------------------------------------------------------------------------
%
%	Input- a numeric array (row or colum vector) of input data
%
%	BandInfo- a string of characters. Enter 'narrow' for a narrow-band signal
%			  or 'wide' for a wide-band signal. Mixed, all upper - or all lower-case
%			  will work. e.g. 'narrow', 'NARROW', 'Wide'.
%
%	
%	OutOptions- a string determining how the estimate of the power specta is 
%			    numerically altered. Mixed case, all lower case or all upper case are
%				valid. Options are:
%
%				[] or 'raw' - nothing happens. Spectral estimate is not altered
%
%				'norm' - All spectral values are normalized to one. This is done
%					 	 by dividing all values by the maximum. This
%						 PowerSpectrumNormalized(i) =  PowerSpectrum(i) / max(PowerSpectrum)
%			
%
%				'dB10' - PowerSpectrumdB10(i) =  10*log10[PowerSpectrum(i) / max(PowerSpectrum)]
%						 This is for a power signal
%
%				'dB20' - PowerSpectrumdB20(i) =  20*log10[PowerSpectrum(i) / max(PowerSpectrum)]
%						 This is for a voltage or current signal
%
%				'psd' - PowerSpectrum_psd(i) = PowerSpectrum(i)/ sum(PowerSpectrum(i))
%	                    This gives the power spectral density function. Dividing out all
%						values of PowerSpectrum(i) by sum(PowerSpectrum(i)) makes sure that
%						sum(PowerSpectrum_psd(i)) = 1. This is how a probability density function
%						behaves.
%
%				'log10' - PowerSpectrum_log10(i) = log10(PowerSpectrum(i)). Log10 is the 
%						  log to the base 10
%
%				'ln' - PowerSpectrum_ln(i) = ln(PowerSpectrum(i)) or log to the base e (natural log)
%				

% All numerics ok 11/11/2006

Message = 'Ok';  % Set output message to default value
StatusFlag = 0;  % Init trouble flag
OutOptionsList = strvcat('raw','norm','dB10','dB20','psd','log10','ln'); % List of valid output options

% Start code for error trap on Input data
if isempty(Input) == 1
	
	% Trap for empty value of input data. Ok 11/12/2006
	disp(['Empty value for input data entered. Please use a row or column vector for the input data.'])
	Message = ['Empty value for input data entered. Please use a row or column vector for the input data.'];
	StatusFlag = 1;
	return

elseif isnumeric(Input) == 0

	% Trap for non-numeric value(s) of input data. Ok 11/12/2006
	disp(['Non-numeric value(s) for input data entered. Please use a numeric row or column vector for the input data.'])
	Message = ['Non-numeric value(s) for input data entered. Please use a numeric row or column vector for the input data.'];
	StatusFlag = 1;
	return

elseif sum(size(Input)) <= 2

	% Trap for scalar value(s) of input data. Ok 11/12/2006
	disp(['Scalar for input data entered. Please use a numeric row or column vector for the input data.'])
	Message = ['Scalar for input data entered. Please use a numeric row or column vector for the input data.'];
	StatusFlag = 1;
	return
	
elseif all(size(Input) > 1) == 1

	% Trap for scalar value(s) of input data. Ok 11/12/2006
	disp(['Matrix for input data entered. Please use a numeric row or column vector for the input data.'])
	Message = ['Matrix for input data entered. Please use a numeric row or column vector for the input data.'];
	StatusFlag = 1;
	return
	
end
% End code for error trap on Input data

% Start code to error-trap the bandwidth type specification 
if isempty(BandInfo) == 1
	
	% Trap for empty value of BandInfo Ok 11/12/2006
	disp(['Empty value for bandwidth spec (wide vs. narrow) entered. Please enter ''wide'' or ''narrow''. Mixed, lower or upper case is ok.'])
	Message = ['Empty value for bandwidth spec (wide vs. narrow) entered. Please enter ''wide'' or ''narrow''. Mixed, lower or upper case is ok.'];
	StatusFlag = 1;
	return
	
elseif ischar(BandInfo) == 0

	% Trap for non-character value of BandInfo Ok 11/12/2006
	disp(['Non character value for bandwidth spec (wide vs. narrow) entered. Please enter ''wide'' or ''narrow'' *as a text string*. Mixed, lower or upper case is ok.'])
	Message = ['Non character value for bandwidth spec (wide vs. narrow) entered. Please enter ''wide'' or ''narrow'' *as a text string*. Mixed, lower or upper case is ok.'];
	StatusFlag = 1;
	return
	
else

	BandInfo = lower(BandInfo);
	
	if isempty(strmatch(BandInfo, strvcat('narrow','wide') , 'exact')) == 1
		
		% Trap for non-character value of BandInfo Ok 11/12/2006
		disp(['Invalid option for bandwidth spec (wide vs. narrow) entered. Please enter ''wide'' or ''narrow'' *as a text string*. Mixed, lower or upper case is ok.'])
		Message = ['Invalid option for bandwidth spec (wide vs. narrow) entered. Please enter ''wide'' or ''narrow'' *as a text string*. Mixed, lower or upper case is ok.'];
		StatusFlag = 1;
		return
	
	end
	
end
% End code to error-trap the bandwidth type specification

% Start code for output options error trapping
if isempty(OutOptions) == 1;
	
	OutOptions = ['raw'];
	
elseif ischar(OutOptions) == 0;

		% Trap for non-character value of OutOptions Ok 11/12/2006
		disp(['Non-character value entered for OutOptions. Please enter ''raw'', ''norm'', ''dB10'', ''dB20'', ''psd'', ''log10'', or ''ln''. Mixed, lower or upper case is ok. ']);
		Message = ['Non-character value entered for OutOptions. Please enter ''raw'', ''norm'', ''dB10'', ''dB20'', ''psd'', ''log10'', or ''ln''. Mixed, lower or upper case is ok. '];
		StatusFlag = 1;
		return
		
	else
		
	OutOptions = lower(OutOptions);	
	
	if isempty(strmatch(OutOptions, OutOptionsList , 'exact')) == 1
				
		% Trap for invalid value of OutOptions
		disp(['Invalid output option. Please enter ''raw'', ''norm'', ''dB10'', ''dB20'', ''psd'', ''log10'', or ''ln''. Mixed, lower or upper case is ok. ']);
		Message = ['Invalid output option. Please enter ''raw'', ''norm'', ''dB10'', ''dB20'', ''psd'', ''log10'', or ''ln''. Mixed, lower or upper case is ok. '];
		StatusFlag = 1;
		return
	end
end

% End code for output options error trapping

% Make Input a column vector

if size(Input,1) > 1 
	Input = Input';	
end

N = length(Input); % Compute the number of input data points

MidPoint = fix(N/2); % Computer midpoint of input data

% Start code to estimate power spectrum

PwrSpecEst = abs(fft(Input)).^2; % Compute raw power spectrum as Real^2 + Imag^2 i.e. raw periodogram
Cep1 = ifft(log(PwrSpecEst)); % Compute cepstrum

% Start code to threshold cepstrum via equation 36 in Stocia and Sandgren paper

% Start code to determine value of Mu via
% equations 37 and 38 in Stocia and Sandgren paper

if strcmp(BandInfo,'wide') == 1
	
	% Start code for the wideband case
	if N < 500
		
		% Case where signal < 500 points
		Mu = 4;
		
	else

		% Case where signal = or > 500 points
		Mu = 5; 
		
	end	
	% End code for the wideband case		
	
elseif strcmp(BandInfo,'narrow') == 1

	% Start code for the narrow band case
	if N < 500
		
		% Case where signal < 500 points
		Mu = 2;
		
	else

		% Case where signal = or > 500 points
		Mu = 3; 
		
	end	
	% End code for the narrow band case	
	
end
% End code to determine value of Mu

Message = strvcat(Message,['Signal is designated as ' BandInfo '-band.'], ['Mu = ' num2str(Mu)], ['Number of Data Points = ' num2str(N)]);

% Start code for thresholding cepstrum

% Compute thresholds for first, last and (N/2) element of 
% cepstrum. Mu is from eqns 37 and 38 in Stocia and Sandgren paper
% Equation for threshold is equation 36 in Stocia and Sandgren paper
FirstAndLastThreshold = (Mu*pi)/sqrt(3*N);

% Compute threshold for the rest of the cepstrum
MiddleThreshold = (Mu*pi)/sqrt(6*N);

% Form vector of threshold values for threshold comparisons

% Enter the proper threshold levels for the first,
% and last cepstrum values into ThresholdVector
ThresholdVector(1) = FirstAndLastThreshold;
ThresholdVector(N) = FirstAndLastThreshold;

% Compute threshold for values of cepstrum from
% 2 to N - 1
ThresholdVector(2:1:N-1) = MiddleThreshold;

% Enter the proper threshold levels for the midpoint 
% cepstrum value into ThresholdVector
ThresholdVector(MidPoint) = FirstAndLastThreshold;
 
% Compute a masking vector. All places where 
% abs(real(Cep1)) < or = threshold will be zero 
% in the masking vector. If  abs(real(Cep1)) > Threshold
% then the corresponding place in masking vector = 1

MaskVector = abs(real(Cep1)) > ThresholdVector;

% Multiply by mask to set the right coeffs = 0
Cep1 = MaskVector.*Cep1;

% End code to threshold cepstrum

% Start code to compute power spectrum estimate from thresholded cepstrum
Index =[1:1:MidPoint]; % Define a index vector [1.....N2]

PwrSpecEstCep = exp(real(fft(Cep1))); % Power spectrum estimate from thresholded cepstrum

% Compute scale factor (equation 41 in Stocia and Sandgren paper)
ScaleConstant = sum(PwrSpecEst(Index).*PwrSpecEstCep(Index))/ sum(PwrSpecEstCep(Index).^2);

SpectrumOut = ScaleConstant*PwrSpecEstCep(Index); % Scale to get final result

% End code to compute power spectrum estimate from thresholded cepstrum
% End code to estimate power spectrum

% Start code to make the output the PSD, DB, normalized, etc
switch OutOptions;

	case {'raw'}
		
		% Do nothing to power spectrum
		Message = strvcat(Message, 'Raw output option selected.', 'Power spectrum has not been altered');
		
	case {'norm'}

		% User wants output spectra normalized by highest value
		
		% Normalize power spectrum by dividing all values by max(power spectrum)
		SpectrumOut = SpectrumOut./max(SpectrumOut);
			
		% Give a message
		Message = strvcat(Message, 'Normalized output option selected.', 'Power spectrum has normalized by its maximum.');
		
	case {'db10'}

		% User wants output spectra in power dB 
		% i.e. PowerSpectrumdB10(i) =  10*log10[PowerSpectrum(i) / max(PowerSpectrum)
		
		% Compute power spectrum for the dB10 case i.e.
		% i.e. PowerSpectrumdB10(i) =  10*log10[PowerSpectrum(i) / max(PowerSpectrum)
		SpectrumOut = 10*log10(SpectrumOut./max(SpectrumOut));
			
		% Give a message
		Message = strvcat(Message, 'Decibel (power ratio i.e. 10*log10[PowerSpectrum(i) / max(PowerSpectrum) ) output option selected.', 'Power spectrum is in decibels.');
	
	case {'db20'}

		% User wants output spectra in voltage or current dB 
		% i.e. PowerSpectrumdB20(i) =  20*log10[PowerSpectrum(i) / max(PowerSpectrum)
		
		% Compute power spectrum for the dB20 case i.e.
		% i.e. PowerSpectrumdB10(i) =  20*log10[PowerSpectrum(i) / max(PowerSpectrum)
		SpectrumOut = 20*log10(SpectrumOut./max(SpectrumOut));
			
		% Give a message
		Message = strvcat(Message, 'Decibel (voltage or current ratio i.e. 20*log10[PowerSpectrum(i) / max(PowerSpectrum) ) output option selected.', 'Power spectrum is in decibels.');

	case {'psd'}

		% User wants output as probablity density function i.e the power spectral density function
		SpectrumOut = SpectrumOut./sum(SpectrumOut);
		
		% Give a message
		Message = strvcat(Message, 'Power spectral density option selected', 'Power spectrum is a probability density function.');
	
	case{'log10'}

		% User wants output as PowerSpectrum_log10(i) = log10(PowerSpectrum(i))
		SpectrumOut = log10(SpectrumOut);
		
		% Give a message
		Message = strvcat(Message, 'Log to base 10 option selected (PowerSpectrumOut(i) = log10(PowerSpectrum(i))', 'Power spectrum is in log to the base 10 units.');
	
	case{'ln'}

		% User wants output as PowerSpectrum_n(i) = ln(PowerSpectrum(i))
		SpectrumOut = log(SpectrumOut);
		
		% Give a message
		Message = strvcat(Message, 'Log to base e (natural log) option selected (PowerSpectrumOut(i) = ln(PowerSpectrum(i))', 'Power spectrum is in log to the base e (natural log) units.');

end

% End code to make the output the PSD, DB, normalized, etc



Contact us at files@mathworks.com