view scripts/control/d2c.m @ 3383:ebf69eb3d07d

[project @ 1999-12-15 21:27:56 by jwe]
author jwe
date Wed, 15 Dec 1999 21:28:01 +0000
parents 69b167451491
children 10f21f7ccc7f
line wrap: on
line source

## Copyright (C) 1996,1998 Auburn University.  All Rights Reserved
##
## This file is part of Octave. 
##
## Octave is free software; you can redistribute it and/or modify it 
## under the terms of the GNU General Public License as published by the 
## Free Software Foundation; either version 2, or (at your option) any 
## later version. 
## 
## Octave is distributed in the hope that it will be useful, but WITHOUT 
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
## FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License 
## for more details.
## 
## You should have received a copy of the GNU General Public License 
## along with Octave; see the file COPYING.  If not, write to the Free 
## Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. 

## -*- texinfo -*-
## @deftypefn {Function File } {@var{csys} =} d2c (@var{sys}@{,@var{tol}@})
## @deftypefnx {Function File } {@var{csys} =} d2c (@var{sys}, @var{opt})
## Convert discrete (sub)system to a purely continuous system.  Sampling
## time used is @code{sysgettsam(@var{sys})}
## 
## @strong{Inputs}
## @table @var
## @item   sys
##  system data structure with discrete components
## @item   tol
## Scalar value.
##  tolerance for convergence of default @code{"log"} option (see below)
## @item   opt
##  conversion option.  Choose from:
## @table @code
## @item         "log"
##  (default) Conversion is performed via a matrix logarithm.
## Due to some problems with this computation, it is
## followed by a steepest descent algorithm to identify continuous time 
## @var{A}, @var{B}, to get a better fit to the original data.  
## 
## If called as @code{d2c}(@var{sys},@var{tol}), @var{tol=}positive scalar, 
## 	the @code{"log"} option is used.  The default value for @var{tol} is 
## 	@code{1e-8}.
## @item        "bi"
##  Conversion is performed via bilinear transform 
## @math{z = (1 + s T / 2)/(1 - s T / 2)} where @var{T} is the 
## system sampling time (see @code{sysgettsam}).
## 
## FIXME: bilinear option exits with an error if @var{sys} is not purely discrete
## 
## @end table
## @end table
## @strong{Outputs} @var{csys} continuous time system (same dimensions and
## signal names as in @var{sys}).
## @end deftypefn
## 

 
function csys = d2c(sys,opt)

  ## Written by R. Bruce Tenison August 23, 1994
  ## Updated by John Ingram for system data structure  August 1996
  ## SYS_INTERNAL accesses members of system data structure

  if( (nargin != 1) & (nargin != 2) )
    usage("csys = d2c(sys[,tol]), csys = d2c(sys,opt)");
  elseif (!is_struct(sys))
    error("sys must be in system data structure");
  elseif(nargin == 1)
    opt = "log";
    tol = 1e-12;
  elseif(isstr(opt))   # all remaining cases are for nargin == 2
    tol = 1e-12;
    if( !(strcmp(opt,"log") | strcmp(opt,"bi") ) )
      error(["d2c: illegal opt passed=",opt]);
    endif
  elseif(!is_sample(opt))
    error("tol must be a postive scalar")
  elseif(opt > 1e-2)
    warning(["d2c: ridiculous error tolerance passed=",num2str(opt); ...
	", intended c2d call?"])
  else
    tol = opt;
    opt = "log";
  endif
  T = sysgettsam(sys);

  if(strcmp(opt,"bi"))
    ## bilinear transform
    ## convert with bilinear transform
    if (! is_digital(sys) )
       error("d2c requires a discrete time system for input")
    endif
    [a,b,c,d,tsam,n,nz,stname,inname,outname,yd] = sys2ss(sys);

    poles = eig(a);
    if( find(abs(poles-1) < 200*(n+nz)*eps) )
      warning("d2c: some poles very close to one.  May get bad results.");
    endif

    I = eye(size(a));
    tk = 2/sqrt(T);
    A = (2/T)*(a-I)/(a+I);
    iab = (I+a)\b;
    B = tk*iab;
    C = tk*(c/(I+a));
    D = d- (c*iab);
    stnamec = strappend(stname,"_c");
    csys = ss2sys(A,B,C,D,0,rows(A),0,stnamec,inname,outname);
  elseif(strcmp(opt,"log"))
    sys = sysupdate(sys,"ss");
    [n,nz,m,p] = sysdimensions(sys);
  
    if(nz == 0)
      warning("d2c: all states continuous; setting outputs to agree");
      csys = syssetsignals(sys,"yd",zeros(1,1:p));
      return;
    elseif(n != 0)
      warning(["d2c: n=",num2str(n),">0; performing c2d first"]);
      sys = c2d(sys,T);
    endif
    [a,b] = sys2ss(sys);
  
    [ma,na] = size(a);
    [mb,nb] = size(b);
  
    if(isempty(b) )
      warning("d2c: empty b matrix");
      Amat = a;
    else
      Amat = [a, b; zeros(nb,na), eye(nb)];
    endif
  
    poles = eig(a);
    if( find(abs(poles) < 200*(n+nz)*eps) )
      warning("d2c: some poles very close to zero.  logm not performed");
      Mtop = zeros(ma, na+nb);
    elseif( find(abs(poles-1) < 200*(n+nz)*eps) )
      warning("d2c: some poles very close to one.  May get bad results.");
      logmat = real(logm(Amat)/T);
      Mtop = logmat(1:na,:);
    else
      logmat = real(logm(Amat)/T);
      Mtop = logmat(1:na,:);
    endif
  
    ## perform simplistic, stupid optimization approach.
    ## should re-write with a Davidson-Fletcher CG approach
    mxthresh = norm(Mtop);
    if(mxthresh == 0)
      mxthresh = 1;
    endif
    eps1 = mxthresh;	#gradient descent step size
    cnt = max(20,(n*nz)*4);	#max number of iterations
    newgrad=1;	#signal for new gradient
    while( (eps1/mxthresh > tol) & cnt)
      cnt = cnt-1;
      ## calculate the gradient of error with respect to Amat...
      geps = norm(Mtop)*1e-8;
      if(geps == 0)
        geps = 1e-8;
      endif
      DMtop = Mtop;
      if(isempty(b))
        Mall = Mtop;
        DMall = DMtop;
      else
        Mall = [Mtop; zeros(nb,na+nb)];
        DMall = [DMtop; zeros(nb,na+nb) ];
      endif
  
      if(newgrad)
        GrMall = zeros(size(Mall));
        for ii=1:rows(Mtop)
          for jj=1:columns(Mtop)
  	  DMall(ii,jj) = Mall(ii,jj) + geps;
            GrMall(ii,jj) = norm(Amat - expm(DMall*T),'fro') ...
  	    - norm(Amat-expm(Mall*T),'fro');
      	  DMall(ii,jj) = Mall(ii,jj);
          endfor
        endfor
        GrMall = GrMall/norm(GrMall,1);
        newgrad = 0;
      endif
  
      ## got a gradient, now try to use it
      DMall = Mall-eps1*GrMall;
  
      FMall = expm(Mall*T);
      FDMall = expm(DMall*T);
      FmallErr = norm(Amat - FMall);
      FdmallErr = norm(Amat - FDMall);
      if( FdmallErr < FmallErr)
        Mtop = DMall(1:na,:);
        eps1 = min(eps1*2,1e12);
        newgrad = 1;
      else
        eps1 = eps1/2;
      endif
  
      if(FmallErr == 0)
        eps1 = 0;
      endif
      
    endwhile
  
    [aa,bb,cc,dd,tsam,nn,nz,stnam,innam,outnam,yd] = sys2ss(sys);
    aa = Mall(1:na,1:na);
    if(!isempty(b))
      bb = Mall(1:na,(na+1):(na+nb));
    endif
    csys = ss2sys(aa,bb,cc,dd,0,na,0,stnam,innam,outnam);
    
    ## update names
    nn = sysdimensions(sys);
    for ii = (nn+1):na
      strval = sprintf("%s_c",sysgetsignals(csys,"st",ii,1));
      csys = syssetsignals(csys,"st",strval,ii);
    endfor
  endif

endfunction