diff m-toolbox/classes/+utils/@math/cpf.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/cpf.m	Wed Nov 23 19:22:13 2011 +0100
@@ -0,0 +1,399 @@
+% CPF finds the partial fraction expansion of the ratio of two polynomials A(s)/B(s).
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DESCRIPTION: CPF finds the residues, poles and direct terms of the
+% partial fraction expansion of the ratio of two polynomials A(s)/B(s).
+% This function assumes that the input continous filter is written in the
+% rational form or in poles, zeros and gain factorization:
+%
+%         A(s)    a(1)s^m + a(2)s^{m-1} + ... + a(m+1)
+%   H(s)= ---- = --------------------------------------
+%         B(s)    b(1)s^n + b(2)s^{n-1} + ... + b(n+1)
+%
+% or
+%
+%         A(s)     (s-z1)...(s-zn)
+%   H(s)= ---- = g ----------------
+%         B(s)     (s-p1)...(s-pn)
+%
+%
+%   It inputs a plist containing the coefficients vectors and the
+%   repeated-root tolerance.
+%   Eg:
+%       A = [a(1), a(2), ..., a(m+1)]
+%       B = [b(1), b(2), ..., b(m+1)]
+%
+% or
+%
+%       Z = [z(1), z(2), ..., z(m)]
+%       P = [p(1), p(2), ..., p(m)]
+%       G = g (Gain is a scalar)
+%
+%
+%   If there are no multiple roots,
+%
+%      A(s)       R(1)       R(2)             R(n)
+%      ----  =  -------- + -------- + ... + -------- + K(s)
+%      B(s)     s - P(1)   s - P(2)         s - P(n)
+%
+%   The number of poles is n = length(B)-1 = length(R) = length(P).
+%   The direct term coefficient vector is empty if length(A) < length(B),
+%   otherwise length(K) = length(A)-length(B)+1.
+%   K(s) is returned in the form:
+%
+%       K(s) = k(1)*s^(m-n) + ... + k(m-n)*s + k(m-n+1)
+%
+%   so that the output vector of direct terms is:
+%       K = [k(1), ..., k(m-n), k(m-n+1)]
+%
+%   If P(j) = ... = P(j+m-1) is a pole of multplicity m, then the
+%   expansion includes terms of the form
+%                R(j)        R(j+1)                R(j+m-1)
+%              -------- + ------------   + ... + ------------
+%              s - P(j)   (s - P(j))^2           (s - P(j))^m
+%
+%   The function is also capable to convert a partial fraction expanded
+%   function to its rational form by setting the 'PARFRACT' input option.
+%   In this case the output is composed by a plist containing the vactors
+%   of numerator and denominator polynomial coefficients.
+%
+%
+%
+% CALL:     varargout = cpf(varargin)
+%
+%
+%
+% INPUTS:
+%           Input options are:
+%           'INOPT' define the input function type
+%             'RAT' input the continous function in rational form.
+%             then you have to input the vector of coefficients:
+%               'NUM' is the vector with numerator coefficients.
+%               'DEN' is the vector of denominator coefficienets.
+%             'PZ' input the continuous function in poles and
+%             zeros form. Then you have to input the vectors with poles
+%             and zeros:
+%               'POLES' the vector with poles
+%               'ZEROS' the vector with zeros
+%               'GAIN' the value of the gain
+%             'PF' input the coefficients of a partial fraction
+%             expansion of the transfer function. When this option is
+%             setted the function performs the conversion from partial
+%             fraction to rational transfer function. You have to input the
+%             vectors containing the residues, poles and direct terms:
+%               'RES' the vector with residues
+%               'POLES' the vector with poles
+%               'DTERMS' the vector with direct terms
+%           'MODE' Is the used mode for the calculation of the roots of a
+%           polynomial. It is an useful option only with rational functions
+%           at the input. Admitted values are:
+%             'SYM' uses symbolic roots calculation (you need symbolic
+%             math toolbox to use this option)
+%             'DBL' uses the standard numerical matlab style roots
+%             claculation (double precision)
+%           'RRTOL' the repeated-root tolerance default value is
+%           1e-15. If two roots differs less than rrtolerance value, they
+%           are reported as multiple roots
+%
+%
+%
+% OUTPUTS:
+%
+%           When 'INOPT' is set to 'RAT' or 'PZ', outputs
+%           are:
+%           RES vector of residues coefficients
+%           POLES vector of poles coefficients
+%           DTERMS vector of direct terms coefficients
+%           PMul vector of poles multiplicity
+%
+%           When 'INOPT' is setted  to 'PF', outputs are:
+%           NUM the vector with the numerator polynomial
+%           coefficients
+%           DEN the vector with the denominator polynomial
+%           coefficents
+%
+%
+%
+% NOTE:
+%         - 'SYM' option for 'MODE' requires the Symblic Math Toolbox. It
+%         is used only for rational function input
+%
+%
+%
+% EXAMPLES:
+%           - Input a function in rational form and output the partial
+%           fraction expansion
+%           [Res, Poles, DTerms, PMul] = cpf('INOPT', 'RAT',
+%           'NUM', [], 'DEN', [], 'MODE','SYM',  'RRTOL', 1e-15)
+%           - Input a function in poles and zeros and output the partial
+%           fraction expansion
+%           [Res, Poles, DTerms, PMul] = cpf('INOPT', 'PZ',
+%           'POLES', [], 'ZEROS', [], 'GAIN', #,  'RRTOL', 1e-15)
+%           - Input a function in partial fractions and output the rational
+%           expression
+%           [Num, Den] = cpf('INOPT', 'PF', 'POLES', [], 'RES',
+%           [], 'DTERMS', [],  'RRTOL', 1e-15)
+%
+%
+%
+% REFERENCES:
+%         [1] Alan V. Oppenheim, Allan S. Willsky and Ian T. Young, Signals
+%         and Systems, Prentice-Hall Signal Processing Series, Prentice
+%         Hall (June 1982), ISBN-10: 0138097313. Pages 767 - 776.
+%
+%
+%
+% VERSION:     $Id: cpf.m,v 1.4 2008/11/19 11:37:51 luigi Exp $
+%
+%
+% HISTORY:     16-04-2008 L Ferraioli
+%                 Creation
+%
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+function varargout = cpf(varargin)
+  
+  %% VERSION
+  
+  VERSION  = '$Id: cpf.m,v 1.4 2008/11/19 11:37:51 luigi Exp $';
+  
+  %% Extracting parameters
+  
+  % default parameters
+  inopt = 'RAT';
+  mode = 'DBL';
+  tol = 1e-15;
+  
+  % Finding input parameters
+  if ~isempty(varargin)
+    for j=1:length(varargin)
+      if strcmp(varargin{j},'INOPT')
+        inopt = varargin{j+1};
+      end
+      if strcmp(varargin{j},'MODE')
+        mode = varargin{j+1};
+      end
+      if strcmp(varargin{j},'RRTOL')
+        tol = varargin{j+1};
+      end
+    end
+  end
+  
+  % Switching between input options and setup inputs for proper calculation
+  switch inopt;
+    case 'RAT'
+      % etracting numerator and denominator vectors
+      for jj=1:length(varargin)
+        if strcmp(varargin{jj},'NUM')
+          u = varargin{jj+1};
+        end
+        if strcmp(varargin{jj},'DEN')
+          v = varargin{jj+1};
+        end
+      end
+      
+      % For the conversion we need the denominator factored in poles and the
+      % numerator in polynomial form
+      switch mode
+        case 'DBL'
+          % adopt numerical calculation
+          poles_vect = roots(v);
+        case 'SYM'
+          % adopt symbolic calculation
+%           syms s
+%           % Construct the symbolic polynomial
+%           numel = length(v);
+%           PowerVector = [];
+%           for ii=1:numel
+%             PowerVector = [PowerVector v(ii)*s^(numel-ii)];
+%           end
+%           PowerMatrix = diag(PowerVector);
+%           Polyv = trace(PowerMatrix);
+%           % Solve the polynomial in order to find the roots
+%           Sp = solve(Polyv,s);
+%           % output of the poles vector in Matlab double format
+%           numpoles = length(Sp);
+%           poles_vect = zeros(1,numpoles);
+%           for jj=1:numpoles
+%             poles_vect(jj) = double(Sp(jj));
+%           end
+
+        n = length(v);
+        cN = -1.*v(2:end)./v(1);
+        A = sym(diag(ones(1,n-2),-1));
+        A(1,:) = cN;
+        sol = eig(A);
+        poles_vect = double(sol);
+      end
+      % setting the output option to residues and poles
+      outopt = 'RP';
+      
+    case 'PZ'
+      % Extracting zeros, poles and gain from inputs
+      for jj=1:length(varargin)
+        if strcmp(varargin{jj},'ZEROS')
+          zeros_vect = varargin{jj+1};
+        end
+        if strcmp(varargin{jj},'POLES')
+          poles_vect = varargin{jj+1};
+        end
+        if strcmp(varargin{jj},'GAIN')
+          gain = varargin{jj+1};
+        end
+      end
+      
+      u = poly(zeros_vect).*gain;
+      v = poly(poles_vect);
+      if ((~isempty(v)) && (v(1)~=1))
+        u = u ./ v(1); v = v ./ v(1);   % Normalize.
+      end
+      % setting the output option to residues and poles
+      outopt = 'RP';
+      
+    case 'PF'
+      % Calculate numerator and denominator of a transfer function expanded
+      % in partial fractions
+      % etracting residues, poles and direct terms
+      for jj=1:length(varargin)
+        if strcmp(varargin{jj},'RES')
+          u = varargin{jj+1};
+        end
+        if strcmp(varargin{jj},'POLES')
+          v = varargin{jj+1};
+        end
+        if strcmp(varargin{jj},'DTERMS')
+          k = varargin{jj+1};
+        end
+      end
+      % setting the output option to Transfer Function
+      outopt = 'TF';
+  end
+  
+  
+  
+  %% Partial fractions expansion
+  
+  % Switching between output cases
+  % Note: rational input and poles zeros are equivalent from this point on,
+  % so PF expansion is calculated in the same way
+  switch outopt
+    
+    case 'RP'
+      % Direct terms calculation
+      if length(u) >= length(v)
+        [dterms,new_u]=deconv(u,v);
+      else
+        dterms = 0;
+        new_u = u;
+      end
+      
+      % identification of multiple poles
+      poles_vect = sort(poles_vect); % sort the poles in ascending order
+      mul = mpoles(poles_vect,tol,0); % find the multiplicity
+      
+      mmul = mul;
+      for kk=1:length(mmul)
+        if mmul(kk)>1
+          for hh=1:mmul(kk)
+            mmul(kk-hh+1)=mmul(kk);
+          end
+        end
+      end
+      
+      % finding the residues
+      resids = zeros(length(poles_vect),1);
+      
+      for ii=1:length(poles_vect)
+        
+        den = v;
+        p = [1 -poles_vect(ii)];
+        for hh=1:mmul(ii)
+          den = deconv(den,p);
+        end
+        
+        dnum = new_u;
+        dden = den;
+        
+        c = 1;
+        if mmul(ii)>mul(ii)
+          c = prod(1:(mmul(ii)-mul(ii)));
+          
+          for jj=1:(mmul(ii)-mul(ii))
+            [dnum,dden] = polyder(dnum,dden);
+          end
+          
+        end
+        
+        resids(ii)=(polyval(dnum,poles_vect(ii))./polyval(dden,poles_vect(ii)))./c;
+      end
+      
+      % Converting from partial fractions to rational function
+    case 'TF'
+      % This code is directly taken from matlab 'residue' function
+      [mults,i]=mpoles(v,tol,0);
+      p=v(i); r=u(i);
+      n = length(p);
+      q = [p(:).' ; mults(:).'];   % Poles and multiplicities.
+      v = poly(p); u = zeros(1,n,class(u));
+      for indx = 1:n
+        ptemp = q(1,:);
+        i = indx;
+        for j = 1:q(2,indx), ptemp(i) = nan; i = i-1; end
+        ptemp = ptemp(find(~isnan(ptemp))); temp = poly(ptemp);
+        j = length(temp);
+        if j < n, temp = [zeros(1,n-j) temp]; end
+        u = u + (r(indx) .* temp);
+      end
+      if ~isempty(k)
+        if any(k ~= 0)
+          u = [zeros(1,length(k)) u];
+          k = k(:).';
+          temp = conv(k,v);
+          u = u + temp;
+        end
+      end
+      num = u; den = v;    % Rename.
+  end
+  
+  %% Output data
+  
+  switch outopt
+    case 'RP'
+      if nargout == 1
+        varargout{1} = [resids poles_vect dterms mul];
+      elseif nargout == 2
+        varargout{1} = resids;
+        varargout{2} = poles_vect;
+      elseif nargout == 3
+        varargout{1} = resids;
+        varargout{2} = poles_vect;
+        varargout{3} = dterms;
+      elseif nargout == 4
+        varargout{1} = resids;
+        varargout{2} = poles_vect;
+        varargout{3} = dterms;
+        varargout{4} = mul;
+      else
+        error('Unespected number of outputs! Set 1, 2, 3 or 4')
+      end
+      %     plout = plist('RESIDUES', resids, 'POLES', poles_vect, 'PMul', mul, 'DIRECT_TERMS', dterms);
+      % %     plout = combine(plout, pl);
+    case 'TF'
+      if nargout == 1
+        varargout{1} = [num den];
+      elseif nargout == 2
+        varargout{1} = num;
+        varargout{2} = den;
+      else
+        error('Unespected number of outputs! Set 1 or 2')
+      end
+      %     plout = plist('NUMERATOR', num, 'DENOMINATOR', den);
+  end
+  
+end
+% END
+
+
+
+