Code covered by the BSD License  

Highlights from
ARMASA

from ARMASA by Piet M T Broersen
Automatic program to estimate the power spectral density with only statistically significant details

arma2psd(varargin)
function [psd,sample,ASAcontrol] = arma2psd(varargin)
%ARMA2PSD ARMA parameters to power spectral density
%   [F,SAMPLE] = ARMA2PSD(AR,MA,N) determines N values of the normalized 
%   power spectral density function of an ARMA process determined by the 
%   parameter vectors AR and MA and returns the result in F. The function 
%   is restricted to a domain from 0 to 0.5/T Hz, corresponding with a 
%   time domain sampling period T. T = 1 is used by default. The values 
%   in the frequency domain, at which the function is evaluated, are 
%   returned in SAMPLE. N can also be omitted from the input list, in 
%   which case 129 values are returned.
%   
%   ARMA2PSD(AR,MA,SAMPLE) evaluates the function at those values in its 
%   frequency domain that are supplied by the row vector SAMPLE.
%   
%   ARMA2PSD(AR,MA,N,T,K) or ARMA2PSD(AR,MA,SAMPLE,T,K) allows to set the 
%   sampling period T. N or SAMPLE can also be empty arrays, in which 
%   case the default number of samples is chosen. A positive integer K, 
%   that serves as a multiplier of the sampling period T, can optionally 
%   be added to the input argument list. In combination with a default 
%   number of frequency values, these values will be determined, such 
%   that they will coincide with frequency values earlier determined in 
%   combination with other values of K.
%   
%   By omitting all output arguments, the function generates a graph. 
%   After turning on the HOLD status, additional graphs can be plotted in 
%   the same figure, while line colors are automatically varied.
%   
%   ARMA2PSD is an ARMASA main function.
%   
%   See also: ARMA2COR

%   References: S. M. Kay and S. L. Marple, Spectrum Analysis - a Modern
%               Perspective, Proceedings IEEE, Vol. 69, 1981,
%               pp. 1380-1419, section D.

%Header
%==============================================================================

%Declaration of variables
%------------------------

%Declare and assign values to local variables
%according to the input argument pattern
[ar,ma,sample,T,k,ASAcontrol]=ASAarg(varargin, ...
{'ar'       ;'ma'       ;'sample'   ;'T'        ;'k'        ;'ASAcontrol'}, ...
{'isnumeric';'isnumeric';'isnumeric';'isnumeric';'isnumeric';'isstruct'  }, ...
{'ar'       ;'ma'                                                        }, ...
{'ar'       ;'ma'       ;'sample'                                        }, ...
{'ar'       ;'ma'       ;'sample'   ;'T'                                 }, ...
{'ar'       ;'ma'       ;'sample'   ;'T'        ;'k'                     });

if isequal(nargin,1) & ~isempty(ASAcontrol)
      %ASAcontrol is the only input argument
   ASAcontrol.error_chk = 0;
   ASAcontrol.run = 0;
end

%ARMASA-function version information
%-----------------------------------

%This ARMASA-function is characterized by
%its current version,
ASAcontrol.is_version = [2001 3 19 14 30 0];
%and its compatability with versions down to,
ASAcontrol.comp_version = [2000 12 30 20 0 0];

%This function calls other functions of the ARMASA
%toolbox. The versions of these other functions must
%be greater than or equal to:
ASAcontrol.req_version.arma2cor = [2000 12 30 20 0 0];

%Checks
%------

if ~isfield(ASAcontrol,'error_chk') | ASAcontrol.error_chk
      %Perform standard error checks
   %Input argument format checks
   ASAcontrol.error_chk = 1;
   if ~isnum(ar)
      error(ASAerr(11,'ar'))
   elseif ~isavector(ar)
      error(ASAerr(15,'ar'))
   elseif size(ar,1)>1
      ar = ar';
      warning(ASAwarn(25,{'column';'ar';'row'},ASAcontrol))         
   end
   if ~isnum(ma)
      error(ASAerr(11,'ma'))
   elseif ~isavector(ma)
      error(ASAerr(15,'ma'))
   elseif size(ma,1)>1
      ma = ma';
      warning(ASAwarn(25,{'column';'ma';'row'},ASAcontrol))         
   end
   if ~isempty(sample)
      if ~isnum(sample) | ~isavector(sample) |...
            sample(1)<0 | ~isascending(sample)
         error(ASAerr(31,{'sample of frequencies';'sample'}))
      elseif size(sample,1)>1
         sample = sample';
         warning(ASAwarn(25,{'column';'sample';'row'},ASAcontrol))
      end
   end
   if ~isempty(T) & (~isnum(T) | ...
         ~isascalar(T) | T<=0)
      error(ASAerr(32,'T'))
   end
   if ~isempty(k) & (~isnum(k) | ...
         ~isintscalar(k) | k<=0)
      error(ASAerr(17,'k'))
   end
   
   %Input argument value checks
   if ~(isreal(ar) & isreal(ma))
      error(ASAerr(13))
   end
   if ar(1)~=1
      error(ASAerr(23,{'ar','parameter'}))
   end
   if ma(1)~=1
      error(ASAerr(23,{'ma','parameter'}))
   end
   if ~isempty(sample) & ~isreal(sample)
      error(ASAerr(13))
   end
   if ~isempty(T) & ~isreal(T)
      error(ASAerr(13))
   end
   if ~isempty(sample) 
      if isascalar(sample)
         if sample==0
            error(ASAerr(33,{'number of frequency samples';'sample';'zero'}))
         end
      else
         if isempty(T)
            T_test=1;
         else
            T_test=T;
         end
         if isempty(k)
            k_test=1;
         else
            k_test=k;
         end
         freq_lim_test = 0.5/(T_test*k_test);
         if sample(end)>freq_lim_test
            error(ASAerr(34,{'sample';num2str(freq_lim_test)}))
         end
      end
   end
end

if ~isfield(ASAcontrol,'version_chk') | ASAcontrol.version_chk
      %Perform version check
   ASAcontrol.version_chk = 1;
      
   %Make sure the requested version of this function
   %complies with its actual version
   ASAversionchk(ASAcontrol);
   
   %Make sure the requested versions of the called
   %functions comply with their actual versions
   arma2cor(ASAcontrol);
end

if ~isfield(ASAcontrol,'run') | ASAcontrol.run
   ASAcontrol.run = 1;
end

if ASAcontrol.run %Run the computational kernel   
   ASAcontrol.version_chk = 0;
   ASAcontrol.error_chk = 0;

%Main   
%=====================================================
    
%Initializations
%---------------

if isempty(k)
   k = 1;
end

if isempty(T)
   T = 1;
end

%Determine the scale factor of the frequency axis
%with respect to a range from 0 to 1
freq_scale = 1/(2*k*T);

if isempty(sample) %Asess a default value of 'sample'
   %Asess the number of frequency samples in excess
   %of one
   pow2 = 128; %any power of two can be used
   n_sample = pow2/k;
   
   if n_sample == fix(n_sample) %The frequencies
         %coincide with the sample grid determined
         %for k=1
      sample = n_sample+1;
   else 
      %Determine frequencies that coincide with the
      %sample grid up to the frequency limit and
      %append 'sample' with this frequency limit
      n_sample = 2^ceil(log2(pow2/k));
      step = pow2/n_sample;
      sample = [0:1:fix(pow2/k)]/(step*n_sample);
      sample(end+1) = pow2/(k*step*n_sample);
      sample = k*freq_scale*sample;
   end
end

%Asess an appropriate computational mode:
%mode==0 : via FFT
%mode==1 : direct polynomial evaluation
if length(sample)==1
   if 0.5*length(ar)+1 > sample
      den_mode = 0;
   else
      den_mode = 1;
   end
   if 0.5*length(ma)+1 > sample
      num_mode = 0;
   else
      num_mode = 1;
   end
   if sample==1
      sample = 0;
   else
      n_fft = (sample-1)*2;
      sample = (freq_scale/(sample-1))*[0:sample-1];
   end
else
   num_mode = 0;
   den_mode = 0;
end

%Determination of the sampled normalized power
%spectral density function
%---------------------------------------------

%Determine the power gain (variance_sig/variance_noise)
%of the ARMA proces
[cor,gain] = arma2cor(ar,ma,0,ASAcontrol);

if ~num_mode | ~den_mode
   z = exp((pi*i/freq_scale)*sample);
end
if num_mode
   numerator = fft(ma,n_fft);
   numerator = numerator(1:n_fft/2+1);
else
   numerator = polyval(ma,z);
end
if den_mode
   denominator = fft(ar,n_fft);
   denominator = denominator(1:n_fft/2+1);
else
   denominator = polyval(ar,z);
end
h = abs(numerator./denominator).^2;
psd = h/(2*freq_scale*gain);

%Graphical representation
%------------------------

if nargout==0
   handle_spec = semilogy(sample,psd,'-b.');
   hold_state = ishold;
   hold on;
   y_lim = get(gca,'ylim');
   handle_lim = ...
      plot(freq_scale*[1 1],[y_lim(1) y_lim(2)],':');
   string = strvcat(' ',['T = ' num2str(T) ' '],...
      ['k = ' num2str(k)]);
   handle_tag = text(freq_scale,y_lim(2),string,...
      'FontName','FixedWidth','Units','data',...
      'VerticalAlignment','top',...
      'HorizontalAlignment','right');
   set(handle_tag,'Units','data')
   string = '\rightarrow frequency f  [Hz]          ';
   handle_xlabel=xlabel(string,...
      'HorizontalAlignment','right','Units',...
      'normalized');
   pos_xlabel=get(handle_xlabel,'Position');
   set(handle_xlabel,'Units','normalized',...
      'Position',[1 pos_xlabel(2)*1.2])
   string = ['\rightarrow normalized power '...
         'spectral density F( f )'];
   handle_ylabel=ylabel(string,...
      'HorizontalAlignment','right','Units',...
      'normalized');
   pos_ylabel=get(handle_ylabel,'position');
   set(handle_ylabel,'units','normalized',...
      'Position',[pos_ylabel(1) 1])
   title(['Right-sided Frequency Spectrum '...
      '( log-scale )'])

   persistent color_entry
   if isempty(color_entry)
      color_entry = 0;
   end
   if hold_state
      color_entry = color_entry+1;
      color_mtx = get(gca,'ColorOrder');
      s_color = size(color_mtx);
      s_color = s_color(1);
      color_entry = max(1,s_color*(color_entry/...
         s_color-fix(color_entry/s_color)));
      color = color_mtx(color_entry,:);
      set(handle_spec,'color',color);
      set(handle_lim,'color',color);
      set(handle_tag,'color',color);
   else
      color_entry=1;
      set(handle_tag,'color',[0 0 1]);
   end
   
   if ~hold_state
      hold
   end
   
   clear psd
end

%Footer
%=====================================================

else %Skip the computational kernel
   %Return ASAcontrol as the first output argument
   if nargout>1
      warning(ASAwarn(9,mfilename,ASAcontrol))
   end
   psd = ASAcontrol;
   ASAcontrol = [];
   sample = [];
end

%Program history
%================================================================================
%
% Version                Programmer(s)          E-mail address
% -------                -------------          --------------
% former versions        P.M.T. Broersen        p.m.t.broersen@tudelft.nl
%                        S. de Waele            
% [2000 12 30 20  0 0]   W. Wunderink           
% [2001  3 19 14 30 0]         ,,                

Contact us at files@mathworks.com