# HG changeset patch # User jwe # Date 910368991 0 # Node ID ba1c7cdc60907bb21131bcdeb70844259b49e410 # Parent bf61c443a366d4696496617a9a44276992094468 [project @ 1998-11-06 16:15:36 by jwe] diff --git a/scripts/control/DEMOcontrol.m b/scripts/control/DEMOcontrol.m new file mode 100644 --- /dev/null +++ b/scripts/control/DEMOcontrol.m @@ -0,0 +1,58 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function DEMOcontrol() +# Controls toolbox demo. +# Demo programs: bddemo.m, frdemo.m, analdemo.m, moddmeo.m, rldemo.m +# +# Written by David Clem August 15, 1994 +# $Revision: 1.2 $ + + disp(' O C T A V E C O N T R O L S Y S T E M S T O O L B O X') + + while (1) + clc + k = 0; + while (k > 8 || k < 1), + k = menu("Octave Controls System Toolbox Demo", ... + 'System representation', ... + 'Block diagram manipulations ', ... + 'Frequency response functions ', ... + 'State space analysis functions ', ... + 'Root locus functions ', ... + 'LQG/H2/Hinfinity functions ', ... + 'End'); + + endwhile + if(k == 1) + sysrepdemo + elseif (k == 2) + bddemo + elseif (k == 3) + frdemo + elseif (k == 4) + analdemo + elseif (k == 5) + rldemo + elseif (k == 6) + dgkfdemo + elseif (k == 7) + return + endif + endwhile +endfunction diff --git a/scripts/control/abcddim.m b/scripts/control/abcddim.m --- a/scripts/control/abcddim.m +++ b/scripts/control/abcddim.m @@ -1,71 +1,121 @@ -## Copyright (C) 1996, 1997 John W. Eaton -## -## 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-1307, USA. - -## Usage: [n, m, p] = abcddim (a, b, c, d) -## -## Check for compatibility of the dimensions of the matrices defining -## the linear system (a, b, c, d). -## -## Returns n = number of system states, -## m = number of system inputs, -## p = number of system outputs. -## -## Returns n = m = p = -1 if the system is not compatible. - -## Author: A. S. Hodel -## Created: August 1993 -## Adapted-By: jwe +# Copyright (C) 1993, 1994, 1995 John W. Eaton +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. function [n, m, p] = abcddim (a, b, c, d) +# Usage: [n, m, p] = abcddim (a, b, c, d) +# +# Check for compatibility of the dimensions of the matrices defining +# the linear system (a, b, c, d). +# +# Returns n = number of system states, +# m = number of system inputs, +# p = number of system outputs. +# +# Note: n = 0 (pure gain block) is returned without warning. +# +# Returns n = m = p = -1 if the system is not compatible. +# +# See also: is_abcd + +# Written by A. S. Hodel (scotte@eng.auburn.edu) August 1993. +# a s hodel: modified to accept pure-gain systems aug 1996 +# $Revision: 1.15 $ +# $Log: abcddim.m,v $ +# Revision 1.15 1998-11-06 16:15:36 jwe +# *** empty log message *** +# +# Revision 1.1.1.1 1998/05/19 20:24:05 jwe +# +# Revision 1.4 1997/12/01 16:44:22 scotte +# *** empty log message *** +# +# Revision 1.3 1997/02/12 15:38:14 hodel +# *** empty log message *** +# +# +# fixed typo +# +# Revision 1.1 1997/02/12 11:34:53 hodel +# Initial revision +# +# Revision 1.7 1997/02/07 15:29:56 scotte +# fixed is_square check to allow for empty a matrix +# (this allows for pure gain blocks) +# + if (nargin != 4) - error ("usage: abcddim (a, b, c, d)"); + error ("abcddim: four arguments required"); endif n = m = p = -1; - [an, am] = size(a); - if (an != am) - error ("abcddim: a is not square"); + [a,an,am] = abcddims(a); + [b,bn,bm] = abcddims(b); + [c,cn,cm] = abcddims(c); + [d,dn,dm] = abcddims(d); + + if ( (!is_square(a)) & (!isempty(a)) ) + warning (["abcddim: a is not square (",num2str(an),"x",num2str(am),")"]); + return endif - [bn, bm] = size(b); - if (bn != am) - error ("abcddim: a and b are not compatible"); + if( (bm == 0) & (dm == 0) ) + warning("abcddim: no inputs"); + elseif (bn != am) + warning (["abcddim: a(",num2str(an),"x",num2str(am), ... + " and b(",num2str(bn),"x",num2str(bm),") are not compatible"]); + return endif - [cn, cm] = size(c); - if (cm != an) - error ("abcddim: a and c are not compatible"); + if( (cn == 0) & (dn == 0 ) ) + warning("abcddim: no outputs"); + elseif (cm != an) + warning (["abcddim: a(",num2str(an),"x",num2str(am), ... + " and c(",num2str(cn),"x",num2str(cm),") are not compatible"]); + return endif - [dn, dm] = size(d); - if (cn != dn) - error ("abcddim: c and d are not compatible"); + have_connections = (bn*cn != 0); + + if( (dn == 0) & have_connections) + warning("abcddim: empty d matrix passed; setting compatibly with b, c"); + [d,dn,dm] = abcddims(zeros(cn,bm)); endif - if (bm != dm) - error ("abcddim: b and d are not compatible"); + if(an > 0) + [dn, dm] = size(d); + if ( (cn != dn) & have_connections ) + warning (["abcddim: c(",num2str(cn),"x",num2str(cm), ... + " and d(",num2str(dn),"x",num2str(dm),") are not compatible"]); + return + endif + + if ( (bm != dm) & have_connections ) + warning (["abcddim: b(",num2str(bn),"x",num2str(bm), ... + " and d(",num2str(dn),"x",num2str(dm),") are not compatible"]); + return + endif + + m = bm; + p = cn; + else + [p,m] = size(d); endif - n = an; - m = bm; - p = cn; - endfunction diff --git a/scripts/control/abcddims.m b/scripts/control/abcddims.m new file mode 100644 --- /dev/null +++ b/scripts/control/abcddims.m @@ -0,0 +1,34 @@ +# Copyright (C) 1993, 1994, 1995 John W. Eaton +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [y,my,ny] = abcddims (x) + +# Usage: [y,my,ny] = abcddims (x) +# +# Used internally in abcddim. If x is a zero-size matrix, both dimensions +# get set to 0. my and ny are the row and column dimensions of the result. + +# Written by A. S. Hodel (scotte@eng.auburn.edu) Feb 1997 +# $Revision: 1.1.1.1 $ + + y = x; + if(isempty(y)) + y = []; + endif + [my,ny] = size(y); +endfunction diff --git a/scripts/control/analdemo.m b/scripts/control/analdemo.m new file mode 100644 --- /dev/null +++ b/scripts/control/analdemo.m @@ -0,0 +1,238 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function analdemo() +# Octave Controls toolbox demo: State Space analysis demo +# Written by David Clem August 15, 1994 +# Updated by John Ingram December 1996 +# $Revision: 1.4 $ + + while (1) + clc + k=0; + while(k > 8 || k < 1) + k = menu("Octave State Space Analysis Demo", ... + "System grammians (gram, dgram)", ... + "System zeros (tzero)", ... + "Continuous => Discrete and Discrete => Continuous conversions (c2d,d2c)", ... + "Algebraic Riccati Equation (are, dare)", ... + "Balanced realizations (balreal, dbalreal)", ... + "Open loop truncations via Hankel singular values (balreal, dbalreal)", ... + "SISO pole placement", ... + "Return to main demo menu"); + endwhile + if (k == 1) + clc + help dgram + prompt + + clc + disp("System Grammians: (see Moore, IEEE T-AC, 1981) \n"); + disp("Example #1, consider the discrete time state space system:\n"); + a=[1 5 -8.4;1.2 -3 5;1 7 9] + b=[1 5;2 6;-4.4 5] + c=[1 -1.5 2;6 -9.8 1] + d=0 + prompt + disp("\nThe discrete controllability grammian is computed as follows:"); + cmd = "grammian = dgram(a, b);"; + run_cmd; + disp("Results:\n"); + grammian = dgram(a,b) + disp("Variable Description:\n"); + disp("grammian => discrete controllability grammian"); + disp("a, b => a and b matrices of discrete time system\n"); + disp("A dual approach may be used to compute the observability grammian."); + prompt + clc + + help gram + prompt + clc + + disp("Example #2, consider the continuous state space system:\n"); + a=[1 3 -10.2;3.7 -2 9;1 3 7] + b=[1 12;6 2;-3.8 7] + c=[1 -1.1 7;3 -9.8 2] + d=0 + prompt + disp("\nThe continuous controllability grammian is computed as follows:"); + cmd = "grammian = gram(a, b);"; + run_cmd; + disp("Results:\n"); + grammian = gram(a,b) + disp("Variable Description:\n"); + disp("grammian => continuous controllability grammian"); + disp("a, b => a and b matrices of continuous time system\n"); + disp("A dual approach may be used to compute the observability grammian."); + prompt + clc + + + elseif (k == 2) + clc + help tzero + prompt + + disp("System zeros (tzero) example\n"); + disp("Example #1, consider the state space system:\n"); + a=[0 1 0;-10 -2 0;-10 0 -8] + b=[0;1;9] + c=[-10 0 -4] + d=1 + prompt + disp("\nTo compute the zeros of this system, enter the following command:\n"); + cmd = "zer = tzero(a,b,c,d);"; + run_cmd; + disp("Results:\n"); + zer = tzero(a,b,c,d) + disp("Variable Description:\n"); + disp("zer => zeros of state space system"); + disp("a, b, c, d => state space system used as input argument"); + prompt + clc + + disp("Example #2, consider the state space system from example 1 again:"); + cmd = "sys = ss2sys(a,b,c,d);"; + disp(cmd); + eval(cmd); + sysout(sys); + disp("\nThe zeros of this system can also be calculated directly from the"); + disp("system variable:"); + cmd = "zer = tzero(sys);"; + run_cmd; + disp("Results:\n") + zer = tzero(sys) + disp("Variable Description:\n"); + disp("zer => zeros of state space system"); + disp("sys => state space system used as input argument"); + prompt + clc + + elseif (k == 3) + clc + help c2d + prompt + + clc + disp("Continuous => Discrete and Discrete => Continuous conversions (c2d,d2c)"); + disp("\nExample #1, consider the following continuous state space system"); + cmd = "sys_cont = ss2sys([-11 6;-15 8], [1;2], [2 -1], 0);"; + eval(cmd); + disp(cmd); + disp("Examine the poles and zeros of the continuous system:"); + sysout(sys_cont,"all"); + disp("\nTo convert this to a discrete system, a sampling time is needed:"); + cmd = "Tsam = 0.5;"; + run_cmd; + disp("\nNow convert to a discrete system with the command:"); + cmd = "sys_disc = c2d(sys_cont,Tsam);"; + run_cmd; + disp("Examine the poles and zeros of the discrete system:"); + sysout(sys_disc,"all"); + prompt + clc + + disp("\nNow we will convert the discrete system back to a continuous system"); + disp("using the d2c command:"); + help d2c + prompt + cmd = "new_sys_cont = d2c(sys_disc);"; + run_cmd; + disp("\nExamine the poles and zeros of the discrete system:"); + sysout(new_sys_cont,"all"); + prompt + + elseif (k == 4) + clc + help are + prompt + clc + + disp("Algebraic Riccati Equation (are, dare)"); + + disp("\nExample #1, consider the continuous state space system:\n"); + a=[1 3 -10.2;3.7 -2 9;1 3 7] + b=[1 12;6 2;-3.8 7] + c=[1 -1.1 7;3 -9.8 2] + d=0 + prompt + disp("\nThe solution to the continuous algebraic riccati equation"); + disp("is computed as follows:"); + cmd = "x_cont = are(a, b, c);"; + run_cmd; + disp("Results:\n") + x_cont = are(a,b,c) + disp("Variable Description:\n") + disp("x_cont => solution to the continuous algebraic riccati equation"); + disp("a, b, c => a, b, and c matrices of continuous time system\n"); + prompt + + clc + help dare + prompt + clc + + disp("Example #2, consider the discrete time state space system:\n"); + a=[1 5 -8.4;1.2 -3 5;1 7 9] + b=[1 5;2 6;-4.4 5] + c=[1 -1.5 2;6 -9.8 1] + d=0 + r=eye(columns(b)) + prompt + disp("\nThe solution to the continuous algebraic riccati equation"); + disp("is computed as follows:"); + cmd = "x_disc = dare(a, b, c, r);"; + run_cmd; + disp("Results:\n") + x_disc = dare(a,b,c,r) + disp("Variable Description:\n"); + disp("x_disc => solution to the discrete algebraic riccati equation"); + disp("a, b, c => a, b and c matrices of discrete time system\n"); + prompt + clc + + elseif (k == 5) + disp("--- Balanced realization: not yet implemented") + elseif (k == 6) + disp("--- Open loop balanced truncation: not yet implemented") + elseif (k == 7) + disp("SISO pole placement example:") + cmd = "sys=tf2sys(1,[1 -2 1]);"; + run_cmd + disp("System in zero-pole form is:") + cmd = "sysout(sys,\"zp\");"; + run_cmd + disp("and in state space form:") + cmd = "sysout(sys,\"ss\");"; + run_cmd + disp("Desired poles at -1, -1"); + cmd = "K=place(sys,[-1 -1])"; + run_cmd + disp("Check results:") + cmd = "[A,B] = sys2ss(sys);"; + run_cmd + cmd = "poles=eig(A-B*K)"; + run_cmd + prompt + elseif (k == 8) + return + endif + endwhile +endfunction + diff --git a/scripts/control/are.m b/scripts/control/are.m --- a/scripts/control/are.m +++ b/scripts/control/are.m @@ -1,44 +1,42 @@ -## Copyright (C) 1996, 1997 John W. Eaton -## -## 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-1307, USA. - -## Usage: x = are (a, b, c {,opt}) -## -## Solves algebraic riccati equation -## -## a' x + x a - x b x + c = 0 -## -## for identically dimensioned square matrices a, b, c. If b (c) is not -## square, then the function attempts to use b * b' (c' * c) instead. -## -## Solution method: apply Laub's Schur method (IEEE Trans. Auto. Contr, -## 1979) to the appropriate Hamiltonian matrix. -## -## opt is an option passed to the eigenvalue balancing routine default is "B". -## -## See also: balance - -## Author: A. S. Hodel -## Created: August 1993 -## Adapted-By: jwe +# Copyright (C) 1993, 1994, 1995 John W. Eaton +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. function x = are (a, b, c, opt) +# Usage: x = are (a, b, c {,opt}) +# +# Solves algebraic riccati equation +# +# a' x + x a - x b x + c = 0 +# +# for identically dimensioned square matrices a, b, c. If b (c) is not +# square, then the function attempts to use b * b' (c' * c) instead. +# +# Solution method: apply Laub's Schur method (IEEE Trans. Auto. Contr, +# 1979) to the appropriate Hamiltonian matrix. +# +# opt is an option passed to the eigenvalue balancing routine default is "B". +# +# See also: balance + +# Written by A. S. Hodel (scotte@eng.auburn.edu) August 1993. +# $Revision: 1.18 $ + if (nargin == 3 || nargin == 4) if (nargin == 4) if (! (strcmp (opt, "N") || strcmp (opt, "P") ... @@ -73,11 +71,11 @@ error ("are: a, b, c not conformably dimensioned."); endif - ## Should check for controllability/observability here - ## use Boley-Golub (Syst. Contr. Letters, 1984) method, not the - ## - ## n-1 - ## rank ([ B A*B ... A^ *B]) method +# Should check for controllability/observability here +# use Boley-Golub (Syst. Contr. Letters, 1984) method, not the +# +# n-1 +# rank ([ B A*B ... A^ *B]) method [d, h] = balance ([a, -b; -c, -a'], opt); [u, s] = schur (h, "A"); @@ -86,7 +84,7 @@ n2 = 2 * n; x = u (n1:n2, 1:n) / u (1:n, 1:n); else - usage ("x = are (a, b, c)"); + usage ("x = are (a, b, c)") endif endfunction diff --git a/scripts/control/axis2dlim.m b/scripts/control/axis2dlim.m new file mode 100644 --- /dev/null +++ b/scripts/control/axis2dlim.m @@ -0,0 +1,57 @@ +# Copyright (C) 1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function axvec = axis2dlim(axdata) +# function axvec = axis2dlim(axdata) +# determine axis limits for 2-d data; leaves a 10% margin around the plots. +# puts in margins of +/- 0.1 if data is one dimensional (or a single point) +# inputs: +# axdata: nx2 matrix of data [x,y] +# outputs: +# vector of axis limits appropriate for call to axis() function + + if(isempty(axdata)) + axdata = 0; + endif + + # compute axis limits + minv = min(axdata); + maxv = max(axdata); + delv = (maxv-minv)/2; # breadth of the plot + midv = (minv + maxv)/2; # midpoint of the plot + axmid = [midv(1), midv(1), midv(2), midv(2)]; + axdel = [-0.1, 0.1,-0.1,0.1]; # default plot width (if less than 2-d data) + if(max(delv) == 0) + if(midv(1) != 0) + axdel(1:2) = [-0.1*midv(1),0.1*midv(1)]; + endif + if(midv(2) != 0) + axdel(3:4) = [-0.1*midv(2),0.1*midv(2)]; + endif + else + # they're at least one-dimensional + if(delv(1) != 0) + axdel(1:2) = 1.1*[-delv(1),delv(1)]; + endif + if(delv(2) != 0) + axdel(3:4) = 1.1*[-delv(2),delv(2)]; + endif + endif + axvec = axmid + axdel; +endfunction + diff --git a/scripts/control/bddemo.m b/scripts/control/bddemo.m new file mode 100644 --- /dev/null +++ b/scripts/control/bddemo.m @@ -0,0 +1,622 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function bddemo() +# Octave Controls toolbox demo: Block Diagram Manipulations demo +# Written by David Clem August 15, 1994 +# Modified by A S Hodel Summer-Fall 1996 + +# Revision 1.6 1998/05/05 17:02:45 scotte +# updated May 5 by Kai Meuller +# +# Revision 1.2 1998/05/05 13:21:51 mueller +# buildssic command description +# buildssic example added +# +# Revision 1.1 1998/05/05 13:20:26 mueller +# Initial revision +# + + str_sav = implicit_str_to_num_ok; + sav_page = page_screen_output; + implicit_str_to_num_ok = 1; + page_screen_output = 1; + + while (1) + clc + k=0; + while(k > 14 || k < 1) + k = menu("Octave Block Diagram Manipulations Demo", ... + "sysadd/syssub: F(s) = G(s) +/- H(s)", ... + "sysappend: add new inputs/outputs", ... + "syschnames: change names of inputs, outputs, and/or states", ... + "sysconnect: connect specified system inputs/outputs", ... + "syscont/sysdisc: extract the continuous (discrete) part of a system", ... + "sysdup: duplicate specified inputs/outputs", ... + "sysgroup: group two systems into a single system,", ... + "sysmult: F(s) = G(s)*H(s) (series connection)", ... + "sysprune: keep only specified inputs/outputs", ... + "sysscale: scale inputs/outputs by specified gain matrices", ... + "parallel: parallel connection of two systems", ... + "buildssic: the combination of all", ... + "Design examples:", ... + "Return to main demo menu"); + endwhile + if (k == 1) + clc + disp("sysadd: add two systems together") + disp("syssub: subtract F = G - H") + prompt + help sysadd + prompt + help syssub + prompt + disp('Example #1, \n') + cmd = "sys1 = tf2sys([1 -1],[1 2 1]);"; + run_cmd + cmd = "sys2 = tf2sys([1 -1],[1 2 3]);"; + run_cmd + disp("sys1=") + sysout(sys1); + prompt + disp("sys2=") + sysout(sys2); + cmd = "sys_sum1 = sysadd(sys1,sys1);"; + run_cmd + disp("This example adds sys1 to itself.") + cmd = "sysout(sys_sum1)"; + run_cmd + disp("Notice that the numerator is twice what it used to be.") + prompt + disp("Example 2:") + cmd = "sys_sub1 = syssub(sys1,sys2);"; + run_cmd + disp("Notice that sysadd (actually sysgroup, called by sysadd) lets you") + disp("know that your two systems had identical names for their states,") + disp("inputs and outputs. The warning message is perfectly harmless,") + disp("and is provided for user information only.") + disp("sys_sub1=") + sysout(sys_sub1) + disp("Notice that, since the two transfer functions had different") + disp("denominators, syssub converted the primary system type to ") + disp("state space. This is the typical behavior unless the") + disp("the two systems are both SISO, they both have the same poles,") + disp("and at least one of them has tf for its primary system type."); + prompt + elseif (k == 2) + disp("sysappend: add new inputs and/or outputs to a system") + help sysappend + prompt + disp("Consider a double-integrator system:") + sys = tf2sys(1,[1 0 0]); + sys=sysupdate(sys,"ss"); + sysout(sys,"ss"); + disp("We add a velocity disturbance input as follows:") + cmd = "sys1=sysappend(sys,[1;0]);"; + run_cmd + sysout(sys1,"ss"); + disp("Names of inputs can be included as follows:") + cmd = "sys1=sysappend(sys,[1;0], [],[],[],\"Disturb\");"; + run_cmd + disp("Notice that empty matrices can be listed for the D matrix if") + disp("all entries are zeros.") + disp(" ") + disp("sys1 is thus:") + sysout(sys1); + prompt + elseif (k == 3) + disp("syschnames:") + help syschnames + disp("Example system"); + a = rand(3,3); + b = rand(3,2); + c = rand(2,3); + sys = ss2sys(a,b,c); + sysout(sys); + prompt + disp("Change state names to larry, moe, and curly as follows:") + sys = syschnames(sys,"st",1:3,["larry";"moe " ; "curly"]); + cmd = "sys = syschnames(sys,\"st\",1:3,[\"larry\";\"moe \" ; \"curly\"]);"; + run_cmd + disp("Indicate that output 2 is discrete-time:") + cmd = "sys = syschnames(sys,\"yd\",2,1);"; + run_cmd + disp("Resulting system is:") + sysout(sys); + prompt + elseif (k == 4) + help sysconnect + prompt + disp("********* N O T E *********") + disp("sysconnect is demonstrated fully in the design examples (option 13)"); + prompt + elseif (k == 5) + disp("syscont and sysdisc: ") + disp("Example block diagram 1:") + disp(" ------------------ ---------------------"); + disp(" u_in ->| Discrete system |--->| Continuous system | ---> y_out"); + disp(" ------------------ ---------------------"); + sys1 = tf2sys([1 2],[1 2 1], 1,"u_in","y_disc"); + sys2 = tf2sys([1 0],[1 -3 -2],0,"c_in","y_out"); + sys = sysmult(sys2,sys1); + disp("Consider the hybrid system") + sysout(sys); + prompt + help syscont + disp("The continuous part of the system can be extracted with syscont") + cmd = "[csys,Acd,Ccd] = syscont(sys);"; + run_cmd + disp("The resulting csys is") + sysout(csys); + disp("Notice that B is 0; there is no purely continuous path from the") + disp("input to the output"); + prompt + help sysdisc + disp("The discrete part of the system can be extracted with sysdisc") + cmd = "[dsys,Adc,Cdc] = sysdisc(sys)"; + run_cmd + disp("The resulting dsys is") + sysout(dsys); + disp("sysdisc returns dsys=empty since sys has no discrete outputs."); + prompt + disp("Example block diagram 2:") + sys1 = tf2sys([1 2],[1 2 1], 1,"u_in","y_disc"); + sys2 = tf2sys([1 0],[1 -3 -2],0,"c_in","y_out"); + disp(" ---------------------") + disp(" u_in -->o-->| Discrete system | --------> y_disc") + disp(" ^ --------------------- |") + disp(" | | "); + disp(" -----------------------------|---") + disp(" | |") + disp(" ------------------------------ |") + disp(" | |") + disp(" v --------------------- |") + disp(" c_in -->o-->| continuous system | --------> y_out") + disp(" ---------------------") + disp("repeat the above example with sys=") + sys = sysgroup(sys1, sys2); + sysout(sys) + prompt + sys = sysconnect(sys,[1 2],[2 1]); + sysout(sys); + cmd = "[csys,Acd,Bcd] = syscont(sys);"; + run_cmd + cmd = "[dsys,Acd,Bcd] = sysdisc(sys);"; + run_cmd + disp("csys is now") + sysout(csys) + disp("dsys is now") + sysout(dsys); + prompt + elseif (k == 6) + help sysdup + prompt + disp("********* N O T E *********") + disp("sysdup is fully demonstrated in the design examples (option 13)") + prompt + elseif (k == 7) + help sysgroup + disp(" ") + prompt + disp("Example: combine two SISO systems together:") + cmd = "sys_a=tf2sys([1 2],[3 4]);"; + run_cmd + cmd = "sys_b=tf2sys([5 6],[7 8],1);"; + run_cmd + cmd = "sys_g=sysgroup(sys_a,sys_b);"; + run_cmd + disp("Notice that sysgroup warns you when you join a purely continuous") + disp("system to a purely discrete system. sysgroup also warns when") + disp("you join two systems that have common state, input, or output names.") + cmd = "sysout(sys_g)"; + run_cmd + disp("Since the grouped system is a multiple-input multiple-output system,") + disp("the output system is by default in state-space format.") + disp(" ") + disp("********* N O T E *********") + disp("sysgroup is further demonstrated in the design examples (option 13)") + prompt + elseif (k == 8) + help sysmult + disp("sysmult performs a series connection of two systems.") + disp("Example 1") + disp(" ") + disp(" ---------- ----------") + disp(" u --->| Bsys |---->| Asys |---> y") + disp(" ---------- ----------") + disp(" ") + Asys = tf2sys(1,[1 2 1],0,"a_in","a_out"); + Bsys = tf2sys([2 3],[1 3 2],0,"b_in","b_out"); + disp("Asys=") + sysout(Asys); + disp("Bsys="); + sysout(Bsys); + cmd = "sys = sysmult(Asys,Bsys);"; + run_cmd + disp("sys =") + sysout(sys); + disp("Notice that sysmult automatically transforms to state space") + disp("internal representation. This is to avoid numerical problems") + disp("when multiplying polynomials"); + prompt + disp("Example 2: same system, except that Bsys is discrete-time"); + Bsys = tf2sys([2 3],[1 3 2],1e-2,"b_in","b_out"); + sysout(Bsys); + cmd = "sys = sysmult(Asys,Bsys);"; + run_cmd + disp("sys =") + sysout(sys); + prompt + elseif (k == 9) + help sysprune + prompt + disp("********* N O T E *********") + disp("sysprune is demonstrated in the design examples (option 13)"); + prompt + elseif (k == 10) + help sysscale + disp(" ") + prompt + disp("********* N O T E *********") + disp("See the design examples (option 13) for use of sysscale.") + prompt + elseif ( k == 11) + help parallel + disp("parallel operates by making a call to sysgroup and sysscale.") + disp("Example:") + sys1 = tf2sys(1,[1 1],0,"in1","out1"); + sys2 = tf2sys(2,[1 2],0,"in2","out2"); + disp("sys1=") + sysout(sys1); + disp("sys2=") + sysout(sys2); + cmd = "sysp = parallel(sys1,sys2);"; + run_cmd + disp("sysp=") + sysout(sysp); + prompt + disp("parallel can be used for multiple input systems as well:") + + in1 = ["u1.1";"u1.2"]; + in2 = ["u2.1";"u2.2"]; + out1 = ["y1.1";"y1.2"]; + out2 = ["y2.1";"y2.2"]; + + sys1 = ss2sys([-1,0;0 -2],eye(2),eye(2),[]); + sys2 = ss2sys([-2,0;0 -4],eye(2),eye(2),[]); + + sys1 = syschnames(sys1,"in",1:2,in1); + sys1 = syschnames(sys1,"out",1:2,out1); + + sys2 = syschnames(sys2,"in",1:2,in2); + sys2 = syschnames(sys2,"out",1:2,out2); + + disp("sys1=") + sysout(sys1); + disp("sys2=") + sysout(sys2); + cmd = "sysp = parallel(sys1,sys2);"; + run_cmd + disp("sysp=") + sysout(sysp); + prompt + elseif (k == 12) + # buildssic description + disp(" ") + disp(" ---------------------------------------") + disp(" b u i l d s s i c") + disp(" (BUILD State Space InterConnections)") + disp(" ---------------------------------------") + disp(" ") + disp("buildssic builds a single system from up to 8 systems.") + disp("It's primary pupose is the forming of interconnections") + disp("for H2/H_inf designs and the building of closed loop") + disp("systems.") + disp("The interconnections may be of arbitrary complexity.") + disp("The use of buildssic is an alternative to sysgroup,") + disp("sysadd/syssub, sysappend, sysconnect, sysdup, sysmult") + disp("sysprune, sysscale, parallel etc.") + disp("In contrast to these functions buildssic does not") + disp("handle mixed continuous and discrete systems. However,") + disp("discrete systems can be connected as long as their") + disp("sampling times are identical. Another drawback: the") + disp("names of input/output and state variables are clobbered.") + disp("Of course, buildsysic is useful in combination with sysgroup,") + disp("sysmult etc.") + prompt + disp("********* N O T E *********") + disp("buildssic is demonstrated in the design examples (option 13)"); + prompt + elseif (k == 13) + disp("Design examples") + disp("Packed system matrices may be connected and manipulated") + disp("With the functions listed below:") + disp(" sysdup: duplicate selected inputs/outputs") + disp(" sysconnect: connect selected inputs/outputs") + disp(" sysgroup: group two systems together") + disp(" sysprune: prune a system to keep only selected inputs and outputs") + disp(" sysscale:pre/post multiply a system by constant matrices") + disp(" buildssic: connect systems with arbitrary complexity.") + prompt + disp("As a simple example, we will construct the system block ") + disp("diagram shown below ") + disp(" ") + disp(" + -------- --------"); + disp(" r(t) ---> (+) --->| K(s) |--->| P(s) | ----> y(t)"); + disp(" -^ -------- -------- |"); + disp(" | |"); + disp(" ------------------------------"); + disp(" ") + disp("where P(s) is the plant, K(s) is the controller.") + prompt + disp("Simple example: P(s) is a first order lag, K(s) is a PI ") + disp("controller") + nump = 1; + denp = [1 1]; + disp("P(s)=") + tfout(nump,denp) + numk = [1 1]; + denk = [1 0]; + disp("\nK(s)=") + tfout(numk,denk); + prompt + disp("We'll show three approaches. ") + P = tf2sys(nump,denp,0,"plant input","plant output"); + K = tf2sys(numk, denk,0,"controller input","controller output"); + + meth = 0; + while(meth != 5) + disp("The first method consists of the following steps:") + disp(" step 1: create systems P and K") + disp(" step 2: group P and K together") + disp(" step 3: create a summing junction") + disp(" step 4: connect outputs to respective inputs") + disp(" step 5: prune the desired i/o connections") + disp("The second method is done as follows:") + disp(" step 1: create systems P and K and a summing block S") + disp(" step 2: connect P, K, and S in series") + disp(" step 3: connect y to inverted summing connection") + disp(" step 4: prune the desired i/o connections") + disp("The third method uses buildssic:") + disp(" step 1: GW = buildssic(...,K,P)") + disp(" ") + disp("Other design examples are in dgkfdemo (controldemo option 7)") + disp(" ") + meth = menu("Select design example method", ... + "Method 1 ", ... + "Method 1 (w/o algebraic loop warning)", ... + "Method 2", ... + "Method 3", ... + "Exit design examples"); + if(meth == 1) + disp(" * * * Method 1 * * *") + disp(" ") + disp(" + -------- --------"); + disp(" r(t) ---> (+) --->| K(s) |--->| P(s) | ----> y(t)"); + disp(" -^ -------- -------- |"); + disp(" | |"); + disp(" ------------------------------"); + disp(" ") + disp("Step 1: put plants in system format:"); + nump + denp + cmd = "P = tf2sys(nump,denp,0,""plant input"",""plant output"");"; + run_cmd + disp("P=") + sysout(P) + prompt + numk + denk + cmd = "K = tf2sys(numk, denk,0,""controller input"",""controller output"");"; + run_cmd + sysout(K) + prompt + disp("Step 2: group the systems together") + cmd = "PK = sysgroup(P,K);"; + run_cmd + disp("PK=") + sysout(PK); + prompt + disp(" ") + disp(" y2 u1") + disp(" + -------- --------"); + disp(" r(t) ---> (+) --->| K(s) |--->| P(s) | ----> y(t)"); + disp(" u2 -^ -------- -------- | y1"); + disp(" u3 | |"); + disp(" --------------------------------"); + disp(" ") + disp("The controller has two inputs summed together, r(t)") + disp("and the negative feedback of y(t)") + disp("Step 3a: duplicate controller input: (input 2 of PK)") + prompt + cmd = "PK = sysdup(PK,[],2);"; + run_cmd + disp("PK=") + sysout(PK); + disp("Notice that PK now has three inputs (input 3 is a duplicate "); + prompt("of input 2). Press return to go on") + disp("Step 3b: scale input 3 by -1") + cmd = "PK = sysscale(PK,[],diag([1,1,-1]));"; + run_cmd + disp("PK=") + sysout(PK); + prompt + disp("Step 4: connect:") + disp(" y(t) (output 1) to the negative sum junction (input 3)") + disp(" u(t) (output 2) to plant input (input 1)") + disp("and prune extraneous inputs/outputs (retain input 2, output 1)") + prompt + out_connect = [1 2] + in_connect = [3 1] + cmd = "PK0 = sysconnect(PK,out_connect,in_connect);"; + run_cmd + prompt + disp("Notice that sysconnect detects the possibility of algebraic") + disp("connections when connecting inputs. Option 2 (Method 1 ") + disp("without algebraic loops) shows how to avoid this warning") + disp("by performing connections one at a time.") + prompt + disp("PK0=") + sysout(PK0); + disp("Notice that the connected inputs now have stars on their") + disp("names. This is how the Octave controls toolbox reminds you") + disp("that the loop has been closed around these inputs.") + prompt("Press return to prune extra inputs/outputs from system") + disp("Only keep plant output (output 1) and r(t) (input 2)") + cmd = "PK0 = sysprune(PK0,1,2);"; + run_cmd + disp("PK0=") + sysout(PK0); + prompt + disp("The resulting closed-loop transfer function is obtained as follows:") + cmd = "[num,den] = sys2tf(PK0);"; + run_cmd + prompt + disp("Transfer function is now") + tfout(num,den) + disp("You can check this: Pk0=PK/(1+PK), as expected") + prompt + elseif(meth == 2) + disp("Method 1 without algebraic loops") + disp(" ") + disp(" y2 u1") + disp(" + -------- --------"); + disp(" r(t) ---> (+) --->| K(s) |--->| P(s) | ----> y(t)"); + disp(" u2 -^ -------- -------- | y1"); + disp(" u3 | |"); + disp(" --------------------------------"); + disp(" ") + disp("Recall that sysconnect checks for algebraic loops. Although") + disp("Design option 1 gets a warning message about a possible"); + disp("algebraic loop, such a loop does not exist.") + disp("This can be seen by performing the connections one at a time"); + cmd = "PK = sysgroup(P,K);"; + run_cmd + disp("PK=") + sysout(PK); + disp("Create an additial inverted input to the controller.") + cmd = "PK = sysdup(PK,[],2);"; + run_cmd + cmd = "PK = sysscale(PK,[],diag([1,1,-1]));"; + run_cmd + disp("PK=") + sysout(PK); + disp("Connect controller to plant:") + cmd = "PK0 = sysconnect(PK,2,1);"; + run_cmd + disp("Plant output to negative control input") + cmd = "PK0 = sysconnect(PK0,1,3);"; + run_cmd + disp("Only keep plant output (output 1) and r(t) (input 2)") + cmd = "PK0 = sysprune(PK0,1,2);"; + run_cmd + disp("PK0=") + sysout(PK0); + prompt + disp("The transfer function form of PK0 is:") + sysout(PK0,"tf"); + prompt + elseif(meth == 3) + disp(" * * * Method 2 * * *") + disp(" ") + disp(" + -------- --------"); + disp(" r(t) ---> (+) --->| K(s) |--->| P(s) | ----> y(t)"); + disp(" -^ -------- -------- |"); + disp(" | |"); + disp(" ------------------------------"); + disp(" ") + disp("Step 1: We've already created systems P and K. Create a sum ") + disp("block as follows:") + implicit_str_to_num_ok = "warn"; + cmd = "S = ss2sys([],[],[],[1 -1],0,0,0,[],[""r(t)"";""y(t)""],""e(t)"");"; + run_cmd + implicit_str_to_num_ok = 1; + disp("You may avoid the string conversion warning by setting the ") + disp("Octave global variables implicit_str_to_num_ok = 1"); + disp(" "); + disp("(You may wish to look at help ss2sys to see what the above does)"); + disp("S=") + sysout(S) + disp("notice that this is just a gain block that outputs e = r - y") + prompt + disp("Step 2: series connections of P, K, and S") + cmd = "PKS = sysmult(P,sysmult(K,S));"; + run_cmd + disp("PKS=") + sysout(PKS) + disp("Step 3: connect y to inverted input") + cmd = "PKcl = sysconnect(PKS,1,2);"; + run_cmd + disp("PKcl=") + sysout(PKcl) + disp("Step 4: prune desired inputs/outputs") + cmd = "PKcl=sysprune(PKcl,1,1);"; + run_cmd + disp("PKcl=") + sysout(PKcl) + prompt + elseif(meth == 4) + disp(" * * * Method 3 * * *") + disp(" ") + disp(" + -------- --------"); + disp(" r(t) ---> (+) --->| K(s) |--->| P(s) | ----> y(t)"); + disp(" -^ -------- -------- |"); + disp(" | |"); + disp(" ------------------------------"); + disp(" ") + disp("Step 1: We've already created systems P and K.") + disp(" Let us call buildssic:") + disp(" PKcl = buildssic([1 2;2 -1],[],[1],[2],P,K)") + disp(" ") + disp(" ^ ^ ^ ^ ^ ^") + disp(" | | | | | |") + disp(" Connection list ----+ | | | | |") + disp(" internal input list -----------+ | | | +-- controller") + disp(" output list --------------+ | |") + disp(" input list ------------------+ +---- plant") + disp(" ") + disp(" Connection list: connect input 1 (P) with output 2 (K)") + disp(" connect input 2 (K) with neg. outp. 1 (P)") + disp(" ") + disp(" int. inp. list: do not append internal inputs") + disp(" (e.g. the internal input of K (r-y))") + disp(" ") + disp(" output list: the only output is 1 (P), positive") + disp(" ") + disp(" input list: the only input is 2 (K), positive") + disp(" ") + cmd = "PKcl = buildssic([1 2;2 -1],[],[1],[2],P,K);" + run_cmd + sysout(PKcl) + prompt + disp("The transfer function form of PKcl is:") + sysout(PKcl,"tf"); + disp("You can check this: PKcl = PK / (1 + PK), as expected") + prompt + elseif(meth != 5) + disp("Illegal selection") + endif + endwhile + + elseif (k == 14) + return + endif + endwhile + implict_str_to_num_ok = str_sav; + page_screen_output = sav_page; +endfunction diff --git a/scripts/control/bode.m b/scripts/control/bode.m new file mode 100644 --- /dev/null +++ b/scripts/control/bode.m @@ -0,0 +1,180 @@ +# Copyright (C) 1996,1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [mag,phase,w] = bode(sys,w,outputs,inputs) +# [mag,phase,w] = bode(sys[,w,outputs,inputs]) +# Produce Bode plots of a system +# +# Compute the frequency response of a system. +# inputs: +# sys: system data structure (must be either purely continuous or discrete; +# see is_digital) +# w: frequency values for evaluation. +# if sys is continuous, then bode evaluates G(jw) +# if sys is discrete, then bode evaluates G(exp(jwT)), where T=sys.tsam +# (the system sampling time) +# default: the default frequency range is selected as follows: (These +# steps are NOT performed if w is specified) +# (1) via routine bodquist, isolate all poles and zeros away from +# w=0 (jw=0 or exp(jwT)=1) and select the frequency +# range based on the breakpoint locations of the frequencies. +# (2) if sys is discrete time, the frequency range is limited +# to jwT in [0,2p*pi] +# (3) A "smoothing" routine is used to ensure that the plot phase does +# not change excessively from point to point and that singular +# points (e.g., crossovers from +/- 180) are accurately shown. +# outputs, inputs: the indices of the output(s) and input(s) to be used in +# the frequency response; see sysprune. +# outputs: +# mag, phase: the magnitude and phase of the frequency response +# G(jw) or G(exp(jwT)) at the selected frequency values. +# w: the vector of frequency values used +# If no output arguments are given, bode plots the results to the screen. +# Descriptive labels are automatically placed. See xlabel, ylable, title, +# and replot. +# +# Note: if the requested plot is for an MIMO system, mag is set to +# ||G(jw)|| or ||G(exp(jwT))|| and phase information is not computed. + +# Written by John Ingram July 10th, 1996 +# Based on previous code +# By R. Bruce Tenison, July 13, 1994 +# Modified by David Clem November 13, 1994 +# again by A. S. Hodel July 1995 (smart plot range, etc.) +# Modified by Kai P. Mueller September 28, 1997 (multiplot mode) +# $Revision: 1.6 $ +# $Log: bode.m,v $ +# Revision 1.6 1998/09/04 20:57:18 hodelas +# fixed bodquist bug (use reshape instead of transpose); removed extraneous +# output from bode. +# +# Bodquist is now much faster +# +# Revision 1.4 1998/08/24 15:50:03 hodelas +# updated documentation +# +# Revision 1.3 1998/08/13 20:11:27 hodelas +# Added calls to axis2dlim for flat-plots +# +# Revision 1.2 1998/07/24 18:16:51 hodelas +# rewrote bodquist as a function call. nyquist interactive plot now optional +# +# Revision 1.1.1.1 1998/05/19 20:24:05 jwe +# +# Revision 1.7 1998/02/09 13:04:11 scotte +# fixed oneplot/gset nokey to function only if gnuplot_has_multiplot +# +# Revision 1.6 1997/12/01 16:51:50 scotte +# updated by Mueller 27 Nov 97 +# +# Revision 1.2 1997/11/24 18:53:01 mueller +# gset autoscale prevents the following error message: +# line 0: x range must be greater than 0 for log scale! +# gset nokey and call to oneplot() added +# +# Revision 1.1 1997/11/11 17:31:27 mueller +# Initial revision +# + + # check number of input arguments given + if (nargin < 1 | nargin > 4) + usage("[mag,phase,w] = bode(sys[,w,outputs,inputs])"); + endif + if(nargin < 2) + w = []; + endif + if(nargin < 3) + outputs = []; + endif + if(nargin < 4) + inputs = []; + endif + + [f, w] = bodquist(sys,w,outputs,inputs,"bode"); + + [stname,inname,outname] = sysgetsignals(sys); + systsam = sysgettsam(sys); + + # Get the magnitude and phase of f. + mag = abs(f); + phase = arg(f)*180.0/pi; + + if (nargout < 1), + # Plot the information + if(gnuplot_has_multiplot) + oneplot(); + endif + gset autoscale; + if(gnuplot_has_multiplot) + gset nokey; + endif + clearplot(); + gset data style lines; + if(is_digital(sys)) + xlstr = ["Digital frequency w=rad/sec. pi/T=",num2str(pi/systsam)]; + tistr = "(exp(jwT)) "; + else + xlstr = "Frequency in rad/sec"; + tistr = "(jw)"; + endif + xlabel(xlstr); + ylabel("Gain in dB"); + if(is_siso(sys)) + if (gnuplot_has_multiplot) + subplot(2,1,1); + endif + title(["|[Y/U]",tistr,"|, u=", inname, ... + ", y=",outname]); + else + title([ "||Y(", tistr, ")/U(", tistr, ")||"]); + disp("MIMO plot from") + outlist(inname," "); + disp("to") + outlist(outname," "); + endif + wv = [min(w), max(w)]; + md = 20*log10(mag); + + axvec = axis2dlim([vec(w),vec(md)]); + axvec(1:2) = wv; + axis(axvec); + grid("on"); + semilogx(w,md); + if (is_siso(sys)) + if (gnuplot_has_multiplot) + subplot(2,1,2); + else + prompt('Press any key for phase plot'); + endif + axvec = axis2dlim([vec(w),vec(phase)]); + axvec(1:2) = wv; + axis(axvec); + xlabel(xlstr); + ylabel("Phase in deg"); + title([ "phase([Y/U]", tistr, ... + "), u=", (inname),", y=",(outname)]); + grid("on"); + semilogx(w,phase); + # This should be the default for subsequent plot commands. + if(gnuplot_has_multiplot) + oneplot(); + endif + endif + mag = phase = w = []; + endif +endfunction diff --git a/scripts/control/bode_bounds.m b/scripts/control/bode_bounds.m new file mode 100644 --- /dev/null +++ b/scripts/control/bode_bounds.m @@ -0,0 +1,87 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [wmin,wmax] = bode_bounds(zer,pol,DIGITAL,tsam) +# function [wmin,wmax] = bode_bounds(zer,pol,DIGITAL{,tsam}) +# get default range of frequencies for system zeros and poles +# +# frequency range is the interval [10^wmin,10^wmax] +# +# used internally in freqresp + +# $Revision: 1.4 $ +# $Log: bode_bounds.m,v $ +# Revision 1.4 1998/10/05 17:12:56 hodelas +# various bug changes +# +# Revision 1.2 1998/08/18 21:21:19 hodelas +# updated for simpler interface +# +# +# Revision 1.2 1997/11/24 15:39:38 mueller +# floating overflow on digital systems fixed +# The overflow occurs if the system has poles or zeros at 0 (log(0)/tsamp) +# +# Revision 1.1 1997/11/24 15:36:31 mueller +# Initial revision +# + + # make sure zer,pol are row vectors + if(!isempty(pol)) pol = reshape(pol,1,length(pol)); endif + if(!isempty(zer)) zer = reshape(zer,1,length(zer)); endif + +# check for natural frequencies away from omega = 0 + if (DIGITAL) + # The 2nd conditions prevents log(0) in the next log command + iiz = find(abs(zer - 1) > norm(zer) * eps && abs(zer) > norm(zer) * eps); + iip = find(abs(pol - 1) > norm(pol) * eps && abs(pol) > norm(pol) * eps); + + # avoid dividing empty matrices, it would work but looks nasty + if (!isempty(iiz)) czer = log(zer(iiz))/tsam; + else czer = []; endif + + if (!isempty(iip)) cpol = log(pol(iip))/tsam; + else cpol = []; endif + + else + # continuous + iip = find((abs(pol)) > (norm(pol) * eps)); + iiz = find((abs(zer)) > (norm(zer) * eps)); + + if(!isempty(zer)) czer = zer(iiz); + else czer = []; endif + if(!isempty(pol)) cpol = pol(iip); + else cpol = []; endif + endif + + if(max(size(iip)) + max(size(iiz)) ) + wmin = floor(log10(min(abs([cpol,czer])))); + wmax = ceil(log10(max(abs([cpol,czer])))); + else + # no poles/zeros away from omega = 0; pick defaults + wmin = -1; + wmax = 3; + endif + + # expand to show the entirety of the "interesting" portion of the plot + wmin--; + wmax++; + + # run digital frequency all the way to pi + if (DIGITAL) wmax = log10(pi/tsam); endif +endfunction diff --git a/scripts/control/bodquist.m b/scripts/control/bodquist.m new file mode 100644 --- /dev/null +++ b/scripts/control/bodquist.m @@ -0,0 +1,127 @@ +# Copyright (C) 1996,1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [f,w] = bodquist(sys,w,outputs,inputs,rname) +# [f,w] = bodquist(sys,w,outputs,inputs) +# used by bode, nyquist +# inputs: +# sys: input system structure +# w: range of frequencies; empty if user wants default +# outputs:list of outputs; empty if user wants all +# inputs: list of inputs; empty if user wants all +# rname: name of routine that called bodquist ("bode" or "nyquist") +# outputs: +# w: list of frequencies +# f: frequency response of sys; f(ii) = f(omega(ii)) +# +# Both bode and nyquist share the same introduction, so the common parts are +# in this file bodquist.m. It contains the part that finds the +# number of arguments, determines whether or not the system is SISO, and +# computes the frequency response. Only the way the response is plotted is +# different between the two functions. + + # check number of input arguments given + if (nargin != 5) + usage("[f,w] = bodquist(sys,w,outputs,inputs,rname)"); + endif + + # check each argument to see if it's in the correct form + if (!is_struct(sys)) + error("sys must be a system data structure"); + endif + + # let freqresp determine w if it's not already given + USEW = freqchkw(w); + + # get initial dimensions (revised below if sysprune is called) + [nn,nz,mm,pp ] = sysdimensions(sys); + + # check for an output vector and to see whether it`s correct + if (!isempty(outputs)) + if (isempty(inputs)) + inputs = 1:mm; # use all inputs + warning([rname,": outputs specified but not inputs"]); + endif + sys = sysprune(sys,outputs,inputs); + [nn,nz,mm,pp ] = sysdimensions(sys); + endif + + # for speed in computation, convert local copy of + # SISO state space systems to zero-pole form + if( is_siso(sys) & strcmp( sysgettype(sys), "ss") ) + [zer,pol,k,tsam,inname,outname] = sys2zp(sys); + sys = zp2sys(zer,pol,k,tsam,inname,outname); + endif + + # get system frequency response + [f,w] = freqresp(sys,USEW,w); + + phase = arg(f)*180.0/pi; + + if(!USEW) + # smooth plots + pcnt = 5; # max number of refinement steps + dphase = 5; # desired max change in phase + dmag = 0.2; # desired max change in magnitude + while(pcnt) + pd = abs(diff(phase)); # phase variation + pdbig = vec(find(pd > dphase)); + + lp = length(f); lp1 = lp-1; # relative variation + fd = abs(diff(f)); + fm = max(abs([f(1:lp1); f(2:lp)])); + fdbig = vec(find(fd > fm/10)); + + bigpts = union(fdbig, pdbig); + + if(isempty(bigpts) ) + pcnt = 0; + else + pcnt = pcnt - 1; + wnew = []; + crossover_points = find ( phase(1:lp1).*phase(2:lp) < 0); + pd(crossover_points) = abs(359.99+dphase - pd(crossover_points)); + np_pts = ceil(pd/dphase)+2; # phase points + nm_pts = ceil(log(fd./fm)/log(dmag))+2; # magnitude points + npts = min(5,max(np_pts, nm_pts)); + w1 = log10(w(1:lp1)); + w2 = log10(w(2:lp)); + for ii=bigpts + if(npts(ii)) + wseg(ii,1:npts(ii)) = logspace(w1(ii),w2(ii),npts(ii)); + endif + endfor + wnew = reshape(wseg,1,rows(wseg)*columns(wseg)); # make a row vector + wnew = wnew(find(wnew != 0)); + wnew = sort(wnew); + wnew = create_set(wnew); + if(isempty(wnew)) # all small crossovers + pcnt = 0; + else + [fnew,wnew] = freqresp(sys,1,wnew); # get new freq resp points + w = [w,wnew]; # combine with old freq resp + f = [f,fnew]; + [w,idx] = sort(w); # sort into order + f = f(idx); + phase = arg(f)*180.0/pi; + endif + endif + endwhile + endif + +endfunction diff --git a/scripts/control/buildssic.m b/scripts/control/buildssic.m new file mode 100644 --- /dev/null +++ b/scripts/control/buildssic.m @@ -0,0 +1,299 @@ +# Copyright (C) 1998 Kai P. Mueller +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [sys] = buildssic(Clst,Ulst,Olst,Ilst,s1,s2,s3,s4,s5,s6,s7,s8) +# +# [sys] = buildssic(Clst,Ulst,Olst,Ilst,s1,s2,s3,s4,s5,s6,s7,s8) +# +# Form an arbitrary complex (open or closed loop) system in +# state-space form from several systems. "buildssic" can +# easily (despite it's cryptic syntax) integrate transfer functions +# from a complex block diagram into a single system with one call. +# This function is especially useful for building open loop +# interconnections for H_infinity and H2 designs or for closing +# loops with these controllers. +# +# Although this function is general purpose, the use of "sysgroup" +# "sysmult", "sysconnect" and the like ist recommended for standard +# operations since they can handle mixed discrete and continuous +# systems and also the names of inputs, outputs, and states. +# +# The parameters consist of 4 lists which describe the connections +# outputs and inputs and up to 8 systems s1-s8. +# Format of the lists: +# +# Clst: connection list, describes the input signal of +# each system. The maximum number of rows of Clst is +# equal to the sum of all inputs of s1-s8. +# Example: +# [1 2 -1; 2 1 0] ==> new input 1 is old inpout 1 +# + output 2 - output 1, new input 2 is old input 2 +# + output 1. The order of rows is arbitrary. +# +# Ulst: if not empty the old inputs in vector Ulst will +# be appended to the outputs. You need this if you +# want to "pull out" the input of a system. Elements +# are input numbers of s1-s8. +# +# Olst: output list, specifiy the outputs of the resulting +# systems. Elements are output numbers of s1-s8. +# The numbers are alowed to be negative and may +# appear in any order. An empty matrix means +# all outputs. +# +# Ilst: input list, specifiy the inputs of the resulting +# systems. Elements are input numbers of s1-s8. +# The numbers are alowed to be negative and may +# appear in any order. An empty matrix means +# all inputs. +# +# Example: Very simple closed loop system. +# +# w e +-----+ u +-----+ +# --->o--*-->| K |--*-->| G |--*---> y +# ^ | +-----+ | +-----+ | +# - | | | | +# | | +----------------> u +# | | | +# | +-------------------------|---> e +# | | +# +----------------------------+ +# +# The closed loop system GW can be optained by +# +# GW = buildssic([1 2; 2 -1], [2], [1 2 3], [2], G, K); +# +# Clst: (1. row) connect input 1 (G) with output 2 (K). +# (2. row) connect input 2 (K) with neg. output 1 (G). +# Ulst: append input of (2) K to the number of outputs. +# Olst: Outputs are output of 1 (G), 2 (K) and appended +# output 3 (from Ulst). +# Ilst: the only input is 2 (K). +# +# Here is a real example: +# +----+ +# -------------------->| W1 |---> v1 +# z | +----+ +# ----|-------------+ || GW || => min. +# | | vz infty +# | +---+ v +----+ +# *--->| G |--->O--*-->| W2 |---> v2 +# | +---+ | +----+ +# | | +# | v +# u y +# +# The closed loop system GW from [z; u]' to [v1; v2; y]' can be +# obtained by (all SISO systems): +# +# GW = buildssic([1 4;2 4;3 1],[3],[2 3 5],[3 4],G,W1,W2,One); +# +# where "One" is a unity gain (auxillary) function with order 0. +# (e.g. One = ugain(1);) +# + +# Written by Kai Mueller April 1998 +# $Revision: 1.2 $ +# $Log: buildssic.m,v $ +# Revision 1.2 1998/10/12 10:04:14 mueller +# bugfix: build of discrete systems corrected. +# The previous version did not sum up the discrete states. +# +# Revision 1.1 1998/10/12 08:51:49 mueller +# Initial revision +# +# Revision 1.1.1.1 1998/05/19 20:24:05 jwe +# +# Revision 1.1 1998/05/05 17:02:56 scotte +# Initial revision +# +# Revision 1.2 1998/05/05 08:19:59 mueller +# minor corrections +# +# Revision 1.1 1998/05/04 15:09:32 mueller +# Initial revision +# + + if((nargin < 5) || (nargin > 12)) + usage("[sys] = buildssic(Clst,Ulst,Olst,Ilst,s1,s2,s3,s4,s5,s6,s7,s8)"); + endif + if (nargin >= 5) + if (!is_struct(s1)) + error("---> s1 must be a structed system."); + endif + s1 = sysupdate(s1, "ss"); + [n, nz, m, p] = sysdimensions(s1); + if (!n && !nz) + error("---> pure static system must not be the first in list."); + endif + if (n && nz) + error("---> cannot handle mixed continuous and discrete systems."); + endif + D_SYS = (nz > 0); + [A,B,C,D,tsam] = sys2ss(s1); + nt = n + nz; + endif + for ii = 6:nargin + eval(["ss = s" num2str(ii-4) ";"]); + if (!is_struct(ss)) + error("---> Parameter must be a structed system."); + endif + ss = sysupdate(ss, "ss"); + [n1, nz1, m1, p1] = sysdimensions(ss); + if (n1 && nz1) + error("---> cannot handle mixed continuous and discrete systems."); + endif + if (D_SYS) + if (n1) + error("---> cannot handle mixed cont. and discr. systems."); + endif + if (tsam != sysgettsam(ss)) + error("---> sampling time of all systems must match."); + endif + endif + [as,bs,cs,ds] = sys2ss(ss); + nt1 = n1 + nz1; + if (!nt1) + # pure gain (pad B, C with zeros) + B = [B zeros(nt,m1)]; + C = [C; zeros(p1,nt)]; + else + A = [A zeros(nt,nt1); zeros(nt1,nt) as]; + B = [B zeros(nt,m1); zeros(nt1,m) bs]; + C = [C zeros(p,nt1); zeros(p1,nt) cs]; + endif + D = [D zeros(p,m1); zeros(p1,m) ds]; + n = n + n1; + nz = nz + nz1; + nt = nt + nt1; + m = m + m1; + p = p + p1; + endfor + + # check maximum dimensions + [nx, mx] = size(Clst); + if (nx > m) + error("---> more rows in Clst than total number of inputs."); + endif + if (mx > p+1) + error("---> more cols in Clst than total number of outputs."); + endif + # empty vector Ulst is OK + lul = length(Ulst); + if (lul) + if (!is_vector(Ulst)) + error("---> Input u list Ulst must be a vector."); + endif + if (lul > m) + error("---> more values in Ulst than number of inputs."); + endif + endif + if (!length(Olst)) Olst = [1:(p+lul)]; endif + if (!length(Ilst)) Ilst = [1:m]; endif + if (!is_vector(Olst)) + error("---> Output list Olst must be a vector."); + endif + if (!is_vector(Ilst)) + error("---> Input list Ilst must be a vector."); + endif + + # build the feedback "K" from the interconnection data Clst + K = zeros(m, p); + inp_used = zeros(m,1); + for ii = 1:nx + xx = Clst(ii,:); + iu = xx(1); + if ((iu < 1) || (iu > m)) + error("---> Illegal value in first col of Clst."); + endif + if (inp_used(iu)) + error("---> Input specified more than once."); + endif + inp_used(iu) = 1; + for kk = 2:mx + it = xx(kk); + if (abs(it) > p) + error("---> Illegal row value in Clst."); + elseif (it) + K(iu,abs(it)) = sign(it); + endif + endfor + endfor + + # form the "closed loop", i.e replace u in + # . + # x = Ax + Bu + # ~ + # y = Cx + Du by u = K*y+u + # + # -1 + # R = (I-D*K) must exist. + # + R = eye(p) - D*K; + if (rank(R) < m) + error("---> singularity in algebraic loop."); + else + R = inv(R); + endif + A = A + B*K*R*C; + B = B + B*K*R*D; + C = R*C; + D = R*D; + + # append old inputs u to the outputs (if lul > 0) + kc = K*C; + kdi = eye(m) + K*D; + for ii = 1:lul + it = Ulst(ii); + if ((it < 1) || (it > m)) + error("---> Illegal value in Ulst."); + endif + C = [C; kc(it,:)]; + D = [D; kdi(it,:)]; + endfor + + # select and rearrange outputs + nn = length(A); + lol = length(Olst); + Cnew = zeros(lol,nn); + Dnew = zeros(lol,m); + for ii = 1:lol + iu = Olst(ii); + if (!iu || (abs(iu) > p+lul)) + error("---> Illegal value in Olst."); + endif + Cnew(ii,:) = sign(iu)*C(abs(iu),:); + Dnew(ii,:) = sign(iu)*D(abs(iu),:); + endfor + C = Cnew; + D = Dnew; + lil = length(Ilst); + Bnew = zeros(nn,lil); + Dnew = zeros(lol,lil); + for ii = 1:lil + iu = Ilst(ii); + if (!iu || (abs(iu) > m)) + error("---> Illegal value in Ilst."); + endif + Bnew(:,ii) = sign(iu)*B(:,abs(iu)); + Dnew(:,ii) = sign(iu)*D(:,abs(iu)); + endfor + + sys = ss2sys(A, Bnew, C, Dnew, tsam, n, nz); + +endfunction diff --git a/scripts/control/c2d.m b/scripts/control/c2d.m --- a/scripts/control/c2d.m +++ b/scripts/control/c2d.m @@ -1,59 +1,179 @@ -## Copyright (C) 1996, 1997 John W. Eaton -## -## 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-1307, USA. +# Copyright (C) 1993, 1994, 1995 John W. Eaton +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function dsys = c2d (sys, opt, T) -## Usage: [Ad, Bd] = c2d (Ac, Bc, T) -## -## converts the continuous time system described by: -## . -## x = Ac x + Bc u -## -## into a discrete time equivalent model via the matrix exponential -## -## x[n+1] = Ad x[n] + Bd u[n] -## -## assuming a zero-order hold on the input and sample time T. +# Usage: dsys = c2d (sys[, T]) +# Usage: dsys = c2d (sys[, opt[, T]]) +# inputs: +# sys: system data structure (may be mixed discrete/continiuous time) +# optional arguments: +# opt: conversion option: +# "ex" - use the matrix exponential (default) +# "bi" - use the bilinear transformation +# 2(z-1) +# s = ----- +# T(z+1) +# FIXME: This option exits with an error if sys is not purely +# continuous. (The ex can handle mixed systems.) +# +# T: sampling time; required if sys is purely continuous. +# outputs: +# dsys: discrete time equivalent via zero-order hold, sample each T sec. +# +# converts the system described by: +# . +# x = Ac x + Bc u +# +# into a discrete time equivalent model via the matrix exponential +# +# x[n+1] = Ad x[n] + Bd u[n] +# +# Note: This function adds _d to the names of the new discrete states. -## Author: R.B. Tenison -## Created: October 1993 -## Adapted-By: jwe +# Written by R.B. Tenison (btenison@eng.auburn.edu) +# October 1993 +# Updated by John Ingram for system data structure August 1996 +# SYS_INTERNAL accesses members of system data structure +# $Log: c2d.m,v $ +# Revision 1.13 1998-11-06 16:15:36 jwe +# *** empty log message *** +# +# Revision 1.3 1998/07/21 14:53:08 hodelas +# use isempty instead of size tests; use sys calls to reduce direct +# access to system structure elements +# +# Revision 1.2 1998/07/01 16:23:35 hodelas +# Updated c2d, d2c to perform bilinear transforms. +# Updated several files per bug updates from users. +# +# $Revision: 1.13 $ -function [Ad, Bd] = c2d (Ac, Bc, T) + save_val = implicit_str_to_num_ok; # save for later + implicit_str_to_num_ok = 1; - ## check args - if (nargin != 3) - usage ("c2d (Ac, Bc, T)"); +# parse input arguments + if(nargin < 1 | nargin > 3) + usage("dsys=c2d(sys[,T])"); + elseif (!is_struct(sys)) + error("sys must be a system data structure"); + elseif (nargin == 1) + opt = "ex"; + elseif (nargin == 2 & !isstr(opt) ) + T = opt; + opt = "ex"; endif - [ma, na] = size (Ac); - [mb, nb] = size (Bc); + # check if sampling period T was passed. + Ts = sysgettsam(sys); + if(!exist("T")) + T = Ts; + if(T == 0) + error("sys is purely continuous; no sampling period T provided"); + endif + elseif (T != Ts & Ts > 0) + warning(["c2d: T=",num2str(T),", system tsam==",num2str(Ts), ... + ": using T=", num2str(min(T,Ts))]); + T = min(T,Ts); + endif - if (ma != na) - error ("c2d: Ac must be square"); + if (!is_sample(T)) + error("sampling period T must be a postive, real scalar"); + elseif( ! (strcmp(opt,"ex") | strcmp(opt,"bi") ) ) + error(["illegal option passed: ",opt]) endif - if (ma != mb) - error ("c2d: Ac and Bc must have the same number of rows"); - endif + sys = sysupdate(sys,"ss"); + [n,nz,m,p] = sysdimensions(sys); + + if (n == 0) + warning("c2d: sys has no continuous states; setting outputs to discrete"); + dsys = syschnames(sys,"yd",1:p,ones(1:p)); + elseif(strcmp(opt,"ex")) + # construct new state-space (a,b,c,d) for continuous subsystem + [csys,Acd] = syscont(sys); # extract continuous subsystem + [csys_a, csys_b, csys_c, csys_d] = sys2ss(csys); + [ sys_a, sys_b, sys_c, sys_d] = sys2ss( sys); + if(isempty(Acd)) + Bmat = sys_b; + elseif(isempty(csys_b)) + Bmat = Acd; + else + Bmat = [Acd csys_b]; + endif + + row_zer = columns(Bmat); + col_zer = csys.n + row_zer; + + if(isempty(Bmat) ) + warning("c2d: no inputs to continuous subsystem."); + mat = csys.a; + else + mat = [csys.a Bmat ; zeros( row_zer,col_zer) ]; + endif + + matexp = expm(mat * T); + + Abar = matexp( 1:csys.n , 1:(csys.n + columns(Acd)) ); + Bbar = matexp( 1:csys.n , (columns(Abar) + 1):columns(matexp) ); + + dsys = sys; + + dsys.a(1:csys.n , : ) = Abar; + dsys.b(1:csys.n , : ) = Bbar; - matexp = expm ([[Ac, Bc] * T; (zeros (nb, na+nb))]); + dsys.sys = [2 0 0 1]; + + dsys.tsam = T; + dsys.n = 0; + dsys.nz = rows(dsys.a); + + dsys.yd = ones(1,rows(dsys.c)); + + for ii = 1:csys.n + strval = [dezero((dsys.stname(ii,:))),"_d"]; + dsys.stname(ii,(1:length(strval))) = [strval]; + endfor - Ad = matexp (1:na, 1:na); - Bd = matexp (1:na, na+1:na+nb); + elseif(strcmp(opt,"bi")) + if(is_digital(sys)) + error("c2d: system is already digital") + else + # convert with bilinear transform + [a,b,c,d,tsam,n,nz,stname,inname,outname,yd] = sys2ss(sys); + IT = (2/T)*eye(size(a)); + A = (IT+a)/(IT-a); + iab = (IT-a)\b; + tk=2/sqrt(T); + B = tk*iab; + C = tk*(c/(IT-a)); + D = d + (c*iab); + stnamed=""; + for kk=1:rows(stname) + tmp = [dezero(stname(kk,:)),"_d"]; + stnamed(kk,1:length(tmp)) = tmp; + endfor + dsys = ss2sys(A,B,C,D,T,0,rows(A),stnamed,inname,outname); + endif + else + error(["Bad option=",opt]) + endif + + implicit_str_to_num_ok = save_val; # restore value endfunction diff --git a/scripts/control/com2str.m b/scripts/control/com2str.m new file mode 100644 --- /dev/null +++ b/scripts/control/com2str.m @@ -0,0 +1,76 @@ +# Copyright (C) 1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function retval = com2str(zz,flg) +# usage retval = com2str(zz{,flg}) +# +# convert complex number to a string +# zz: complex number +# flg: format flag +# 0 (default): -1, 0, 1, 1i, 1 + 0.5i +# 1 (for use with zpout): -1, 0, + 1, + 1i, + 1 + 0.5i +# +# $Revision: 1.1 $ + + if (nargin < 1 | nargin > 2) + usage("com2str(zz{,flg})"); + endif + if(nargin == 1) + flg = 0; + endif + + if( !(is_scalar(zz) & is_scalar(flg) ) ) + error("com2str: arguments must be a scalar."); + endif + + if(flg != 0 & flg != 1) + error(["Illegal flg value: ",num2str(flg)]); + endif + + sgns = "+-"; + rz = real(zz); + iz = imag(zz); + az = abs(zz); + if(iz == 0) + # strictly a real number + switch(flg) + case(0) + retval = num2str(rz); + case(1) + retval = [ sgns(1+(rz< 0))," ", num2str(abs(rz))]; + endswitch + elseif(rz == 0) + # strictly an imaginary number + switch(flg) + case(0) + retval = num2str(iz); + case(1) + retval = [ sgns(1+(iz< 0))," ", num2str(abs(iz)),"i"]; + endswitch + else + # complex number + # strictly an imaginary number + switch(flg) + case(0) + retval = [num2str(rz)," ",com2str(i*iz,1)]; + case(1) + retval = [ sgns(1+(rz< 0))," ", num2str(abs(rz))," ",com2str(i*iz,1)]; + endswitch + endif + +endfunction diff --git a/scripts/control/controldemo.m b/scripts/control/controldemo.m new file mode 100644 --- /dev/null +++ b/scripts/control/controldemo.m @@ -0,0 +1,61 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function DEMOcontrol() +# Controls toolbox demo. +# Demo programs: bddemo.m, frdemo.m, analdemo.m, moddmeo.m, rldemo.m +# +# Written by David Clem August 15, 1994 +# $Revision: 1.1 $ + + disp(' O C T A V E C O N T R O L S Y S T E M S T O O L B O X') + + while (1) + clc + k = 0; + while (k > 8 || k < 1), + k = menu("Octave Controls System Toolbox Demo", ... + 'System representation', ... + 'Block diagram manipulations ', ... + 'Frequency response functions ', ... + 'State space analysis functions ', ... + 'System model manipulations ', ... + 'Root locus functions ', ... + 'LQG/H2/Hinfinity functions ', ... + 'End'); + + endwhile + if(k == 1) + sysrepdemo + elseif (k == 2) + bddemo + elseif (k == 3) + frdemo + elseif (k == 4) + analdemo + elseif (k == 5) + moddemo + elseif (k == 6) + rldemo + elseif (k == 7) + dgkfdemo + elseif (k == 8) + return + endif + endwhile +endfunction diff --git a/scripts/control/ctrb.m b/scripts/control/ctrb.m new file mode 100644 --- /dev/null +++ b/scripts/control/ctrb.m @@ -0,0 +1,72 @@ +# Copyright (C) 1997 Kai P. Mueller +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function Qs = ctrb(sys, b) + # ------------------------------------------------------ + # Qs = ctrb(sys [, b]) + # Build controllability matrix + # + # 2 n-1 + # Qs = [ B AB A B ... A B + # + # of a system data structure or the pair (A, B). + # + # Note: ctrb() forms the controllability matrix. + # The numerical properties of is_controllable() + # are much better for controllability tests. + # See also: obsv, is_observable, is_controllable + # ------------------------------------------------------ + + # Written by Kai P. Mueller November 4, 1997 + # based on is_controllable.m of Scottedward Hodel + # modified by + # $Revision: 1.1.1.1 $ + # $Log: ctrb.m,v $ + # Revision 1.1.1.1 1998/05/19 20:24:06 jwe + # + # Revision 1.2 1997/12/01 16:51:50 scotte + # updated by Mueller 27 Nov 97 + # +# Revision 1.2 1997/11/25 11:15:54 mueller +# name confict with function mb removed +# + + if (nargin == 2) + a = sys; + elseif (nargin == 1 && is_struct(sys)) + sysupdate(sys,"ss"); + a = sys.a; + b = sys.b; + else + usage("ctrb(sys [, b])") + endif + + if (!is_abcd(a,b)) + Qs = []; + else + # no need to check dimensions, we trust is_abcd(). + [na, ma] = size(a); + # using imb avoids name conflict with the "mb" function + [inb, imb] = size(b); + Qs = zeros(na, ma*imb); + for i = 1:na + Qs(:, (i-1)*imb+1:i*imb) = b; + b = a * b; + endfor + endif +endfunction diff --git a/scripts/control/d2c.m b/scripts/control/d2c.m new file mode 100644 --- /dev/null +++ b/scripts/control/d2c.m @@ -0,0 +1,243 @@ +# Copyright (C) 1996,1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function csys = d2c(sys,opt) +# csys = d2c(sys[,tol]) +# csys = d2c(sys,opt) +# +# inputs: +# sys: system data structure with discrete components +# tol: tolerance for convergence of default "log" option (see below) +# +# opt: conversion option. Choose from: +# "log": (default) Conversion is performed via a matrix logarithm. +# Due to some problems with this computation, it is +# followed by a steepest descent to identify continuous time +# A, B, to get a better fit to the original data. +# +# If called as d2c(sys,tol), tol=positive scalar, the log +# option is used. The default value for tol is 1e-8. +# "bi": Conversion is performed via bilinear transform +# 1 + s T/2 +# z = --------- +# 1 - s T/2 +# where T is the system sampling time (see syschtsam). +# +# FIXME: exits with an error if sys is not purely discrete +# +# D2C converts the real-coefficient discrete time state space system +# +# x(k+1) = A x(k) + B u(k) +# +# to a continuous time state space system +# . +# x = A1 x + B1 u +# +# The sample time used is that of the system. (see syschtsam). + +# 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 +# $Revision: 1.3 $ +# $Log: d2c.m,v $ +# Revision 1.3 1998/08/13 16:27:21 hodelas +# Fixed warning message +# +# Revision 1.2 1998/07/01 16:23:36 hodelas +# Updated c2d, d2c to perform bilinear transforms. +# Updated several files per bug updates from users. +# +# Revision 1.4 1997/02/20 16:18:52 hodel +# added warning about poles near 1. +# +# Revision 1.3 1997/02/20 16:07:26 hodel +# Added gradient descent code so that d2c returns the same function +# as c2d started with a.s.hodel@eng.auburn.edu +# + + save_val = implicit_str_to_num_ok; # save for later + implicit_str_to_num_ok = 1; + + 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 = sys.tsam; + + if(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); + stnamed=""; + for kk=1:rows(stname) + tmp = [dezero(stname(kk,:)),"_c"]; + stnamec(kk,1:length(tmp)) = tmp; + endfor + csys = ss2sys(A,B,C,D,0,rows(A),0,stnamec,inname,outname); + elseif(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 = syschnames(sys,"yd",1:p,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 + + csys = sys; + csys.a = Mall(1:na,1:na); + if(!isempty(b)) + csys.b = Mall(1:na,(na+1):(na+nb)); + endif + + csys.n = na; + csys.nz = 0; + + csys.sys = [2 0 0 1]; + + csys.yd = zeros(1,rows(csys.c)); + + for ii = (sys.n + 1):rows(sys.stname) + strval = [(csys.stname(ii,:)),"_c"]; + csys.stname(ii,(1:length(strval))) = [strval]; + endfor + csys = syschtsam(csys,0); + endif + + implicit_str_to_num_ok = save_val; # restore value +endfunction diff --git a/scripts/control/damp.m b/scripts/control/damp.m new file mode 100644 --- /dev/null +++ b/scripts/control/damp.m @@ -0,0 +1,93 @@ +# Copyright (C) 1993, 1994, 1995 John W. Eaton +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function damp(p, tsam) +# Usage: damp(p[, tsam]) +# Displays eigenvalues, natural frequencies and damping ratios +# of the eigenvalues of a matrix p or the A-matrix of a +# system p, respectively. +# If p is a system, tsam must not be specified. +# If p is a matrix and tsam is specified, eigenvalues +# of p are assumed to be in z-domain. +# +# See also: eig + +# Written by Kai P. Mueller September 29, 1997. +# Update +# $Revision: 1.1.1.1 $ +# $Log: damp.m,v $ +# Revision 1.1.1.1 1998/05/19 20:24:06 jwe +# +# Revision 1.3 1997/12/01 16:51:50 scotte +# updated by Mueller 27 Nov 97 +# +# Revision 1.1 1997/11/11 17:32:13 mueller +# Initial revision +# + + # assume a continuous system + DIGITAL = 0; + if(nargin < 1 || nargin > 2) + usage("damp(p,[ tsamp])") + endif + if(is_struct(p)) + if (nargin != 1) + error("damp: when p is a system, tsamp parameter is not allowed."); + endif + [aa, b, c, d, t_samp] = sys2ss(p); + DIGITAL = is_digital(p); + else + aa = p; + if (nargin == 2) + DIGITAL = 1; + t_samp = tsam; + endif + endif + if (!is_square(aa)) + error("damp: Matrix p is not square.") + endif + if (DIGITAL && t_samp <= 0.0) + error("damp: Sampling time tsam must not be <= 0.") + endif + + # all checks done. + e = eig(aa); + [n, m] = size(aa); + if (DIGITAL) + printf(" (discrete system with sampling time %f)\n", t_samp); + endif + printf("............... Eigenvalue ........... Damping Frequency\n"); + printf("--------[re]---------[im]--------[abs]----------------------[Hz]\n"); + for i = 1:n + pole = e(i); + cpole = pole; + if (DIGITAL) + cpole = log(pole) / t_samp; + endif + d0 = -cos(atan2(imag(cpole), real(cpole))); + f0 = 0.5 / pi * abs(cpole); + if (abs(imag(cpole)) < eps) + printf("%12f --- %12f %10f %12f\n", + real(pole), abs(pole), d0, f0); + else + printf("%12f %12f %12f %10f %12f\n", + real(pole), imag(pole), abs(pole), d0, f0); + endif + endfor + +endfunction diff --git a/scripts/control/dare.m b/scripts/control/dare.m --- a/scripts/control/dare.m +++ b/scripts/control/dare.m @@ -32,8 +32,8 @@ ## ## If c is not square, then the function attempts to use c'*c instead. ## -## Solution method: Laub's Schur method (IEEE Trans Auto Contr, 1979) applied -## to the appropriate symplectic matrix. +## Solution method: Generalized eigenvalue approach (Van Dooren; SIAM J. +## Sci. Stat. Comput., Vol 2) applied to the appropriate symplectic pencil. ## ## See also: Ran and Rodman, "Stable Hermitian Solutions of Discrete ## Algebraic Riccati Equations," Mathematics of Control, Signals and @@ -47,10 +47,15 @@ ## Author: A. S. Hodel ## Created: August 1993 ## Adapted-By: jwe +## $Revision: 1.15 $ +## $Log: dare.m,v $ +## Revision 1.15 1998-11-06 16:15:36 jwe +## *** empty log message *** +## function x = dare (a, b, c, r, opt) - if (nargin == 4 || nargin == 5) + if (nargin == 4 | nargin == 5) if (nargin == 5) if (opt != "N" || opt != "P" || opt != "S" || opt != "B") warning ("dare: opt has an invalid value -- setting to B"); @@ -60,26 +65,10 @@ opt = "B"; endif - ## Check a matrix dimensions - if ((n = is_square (a)) == 0) - error ("dare: a is not square"); - endif - - ## Check a,b compatibility. - - [n1, m] = size (b); - - if (n1 != n) - warning ("dare: a,b are not conformable"); - endif - + # dimension checks are done in is_controllable, is_observable if (is_controllable (a, b) == 0) warning ("dare: a,b are not controllable"); - endif - - ## Check a,c compatibility. - - if (is_observable (a, c) == 0) + elseif (is_observable (a, c) == 0) warning ("dare: a,c are not observable"); endif @@ -88,22 +77,19 @@ p = rows (c); endif - if (n != p) - error ("dare: a,c are not conformable"); - endif - ## Check r dimensions. - + n = rows(a); + m = columns(b); if ((m1 = is_square (r)) == 0) warning ("dare: r is not square"); elseif (m1 != m) warning ("b,r are not conformable"); endif - brb = (b/r)*b'; - atc = a'\c; - [d, sy] = balance ([a + brb*atc, -brb/(a'); -atc, (inv (a'))], opt); - [u, s] = schur (sy, 'D'); + s1 = [a, zeros(n) ; -c, eye(n)]; + s2 = [eye(n), (b/r)*b' ; zeros(n), a']; + [c,d,s1,s2] = balance(s1,s2,opt); + [aa,bb,u,lam] = qz(s1,s2,"S"); u = d*u; n1 = n+1; n2 = 2*n; diff --git a/scripts/control/dcgain.m b/scripts/control/dcgain.m new file mode 100644 --- /dev/null +++ b/scripts/control/dcgain.m @@ -0,0 +1,57 @@ +# Copyright (C) 1993, 1994, 1995 John W. Eaton +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function gm = dcgain(sys, tol) +# Usage: gm = dcgain(sys[, tol]) +# Returns dc-gain matrix. If dc-gain is infinity +# an empty matrix is returned. +# The argument tol is an optional tolerance for the condition +# number of A-Matrix in sys (default tol = 1.0e-10) +# +# See also: (nothing) + +# Written by Kai P Mueller (mueller@ifr.ing.tu-bs.de) October 1, 1997 +# Updated +# $Revision: 1.1.1.1 $ +# $Log: dcgain.m,v $ +# Revision 1.1.1.1 1998/05/19 20:24:06 jwe +# +# Revision 1.3 1997/12/01 16:51:50 scotte +# updated by Mueller 27 Nov 97 +# +# Revision 1.1 1997/11/11 17:32:46 mueller +# Initial revision +# + + if((nargin < 1) || (nargin > 2) || (nargout > 1)) + usage("[gm, ok] = dcgain(sys[, tol])"); + endif + if(!is_struct(sys)) + error("dcgain: first argument is not a system data structure.") + endif + sys = sysupdate(sys, "ss"); + aa = sys.a; + if (is_digital(sys)) aa = aa - eye(size(aa)); endif + if (nargin == 1) tol = 1.0e-10; endif + r = rank(aa, tol); + if (r < rows(aa)) + gm = []; + else + gm = -sys.c / aa * sys.b + sys.d; + endif +endfunction diff --git a/scripts/control/demomarsyas.m b/scripts/control/demomarsyas.m new file mode 100644 --- /dev/null +++ b/scripts/control/demomarsyas.m @@ -0,0 +1,116 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +page_screen_output = 1; +opt = 0; +QUITOPT = 7; +while (opt != QUITOPT) + opt = menu("Marsyas interface update demo:", ... + "run Marsyas on the magnetically suspended ball example", ... + "load continuous time marsyas example system", ... + "load discrete-time marsyas example system", ... + "bode plot of loaded system (MIMO)", ... + "bode plot of loaded system (SISO)", ... + "Design example", ... + "Quit"); + + if(opt == 1) + cmd = "system(""marsyas mag1d.mar"")"; + run_cmd + cmd = "system(""marplot -i"")"; + run_cmd + elseif(opt == 2) + cmd = "ballsys = margetsys();"; + run_cmd; + cmd = "sysout(ballsys);" + run_cmd + elseif(opt == 3) + cmd = "ballsys = margetsys(""disc"");"; + run_cmd + cmd = "sysout(ballsys);" + run_cmd + elseif(opt == 4) + cmd = "bode(ballsys);"; + run_cmd + elseif(opt == 5) + cmd = "bode(ballsys,[],1,1);"; + run_cmd + elseif(opt == 6) + if(!exist("ballsys")) + warning("You didn't load a system yet (option 2 or 3)"); + else + disp("Design LQG controller"); + cmd = "sysout(ballsys)"; + run_cmd + disp("add noise inputs to system...") + if(ballsys.n) + disp("continuous system:") + cmd = "ballsys1 = sysappend(ballsys,eye(ballsys.n));"; + else + disp("discrete system:") + cmd = "ballsys1 = sysappend(ballsys,eye(ballsys.nz));"; + endif + run_cmd + cmd = "sysout(ballsys1)"; + run_cmd + disp("Notice the two additional inputs, u_2, and u_3. These are the "); + disp("""entry points"" for the gaussian noise disturbance."); + disp(" "); + disp("We'll design the controller to use only position feedback:") + cmd = "ballsys1=sysprune(ballsys1,1,[]);"; + run_cmd + cmd = "sysout(ballsys1)"; + run_cmd + disp("Now design an LQG controller: Sigw: input noise") + Sigw = eye(2) + disp("Now design an LQG controller: Sigv: measurement noise") + Sigv = eye(rows(ballsys1.c)) + disp("State and input penalties:") + Q = eye(2) + R = 1 + disp("Controlled input is input 1"); + cmd="Ksys = lqg(ballsys1,Sigw,Sigv,Q,R,1);"; + run_cmd + disp("sysout(Ksys);"); + sysout(Ksys); + + disp("marsyas conversion: output in scalar form:") + cmd = "maroutsys(Ksys, ""ball_controller"",""scalar"");"; + run_cmd + disp("here's the output file:") + prompt + system("more ball_controller.mar"); + + disp("marsyas conversion: output in state space form: (default option;") + disp("the ""ss"" in the command below is not needed)") + cmd = "maroutsys(Ksys, ""ball_controller_ss"",""ss"");"; + run_cmd + disp("here's the output file:") + prompt + system("more ball_controller_ss.mar"); + + disp("marsyas conversion: output in transfer function form:") + cmd = "maroutsys(Ksys, ""ball_controller_tf"",""tf"")" + run_cmd + disp("here's the output file:") + prompt + system("more ball_controller_tf.mar"); + + endif + endif +endwhile diff --git a/scripts/control/dezero.m b/scripts/control/dezero.m new file mode 100644 --- /dev/null +++ b/scripts/control/dezero.m @@ -0,0 +1,88 @@ +## Copyright (C) 1996 Kurt Hornik +## +## 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-1307, USA. + +## usage: dezero (s) +## +## Remove trailing blank entries and all zero entries from the string s. + +## Author: Kurt Hornik +## Adapted-By: jwe +## Adapted from deblank by A. S. Hodel (a.s.hodel@eng.auburn.edu) +## (the name dezero is a reference to the Fermilab D0 experiment, +## where my sister did her PhD research) +## $Revision: 1.1.1.1 $ +## $Log: dezero.m,v $ +## Revision 1.1.1.1 1998/05/19 20:24:13 jwe +## +## Revision 1.3 1997/03/11 14:42:41 scotte +## fixed implicit_str_to_num_ok bug a.s.hodel@eng.auburn.edu +## +## Revision 1.2 1997/03/03 22:52:20 hodel +## fixed problem with conversion to/from numerical value +## a.s.hodel@eng.auburn.edu +## +## Revision 1.1 1997/02/12 11:34:56 hodel +## Initial revision +## +## Revision 1.3 1997/02/07 15:24:35 scotte +## fixed to remove all null characters, then call deblank +## + +function t = dezero (s) + + if (nargin != 1) + usage ("dezero (s)"); + elseif (isstr (s)) + + save_val = implicit_str_to_num_ok; + implicit_str_to_num_ok = 1; + + #disp("dezero: entry, s=") + #s + #disp("/dezero") + + [nr, nc] = size (s); + len = nr * nc; + + if (len == 0) + t = s; + else + + #disp("dezero: 1, s=") + #s + #disp("/dezero") + + s = reshape (s, 1, len); + + #disp("dezero: 2, s=") + #s + #disp("/dezero") + + # need to remove zeros first, then call deblank + s = 1*s; + t = deblank(setstr(s(find(s != 0) ))); + endif + + implicit_str_to_num_ok = save_val; + + else + error ("dezero: expecting string argument"); + endif + +endfunction diff --git a/scripts/control/dgkfdemo.m b/scripts/control/dgkfdemo.m new file mode 100644 --- /dev/null +++ b/scripts/control/dgkfdemo.m @@ -0,0 +1,363 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function dgkfdemo() +# Octave Controls toolbox demo: H2/Hinfinity options demos +# Written by A. S. Hodel June 1995 +# $Revision: 1.2 $ +# $Log: dgkfdemo.m,v $ +# Revision 1.2 1998/08/24 15:50:06 hodelas +# updated documentation +# +# Revision 1.1.1.1 1998/05/19 20:24:06 jwe +# +# Revision 1.5 1998/05/05 17:03:18 scotte +# update 5 May 1998 by Kai Mueller +# +# Revision 1.2 1998/05/05 10:02:46 mueller +# new H_inf demo (jet707 MIMO design) +# +# Revision 1.1 1998/05/05 09:59:28 mueller +# Initial revision +# + + save_val = page_screen_output; + page_screen_output = 1; + while (1) + clc + menuopt=0; + while(menuopt > 10 || menuopt < 1) + menuopt = menu('Octave H2/Hinfinity options demo', ... + 'LQ regulator', ... + 'LG state estimator', ... + 'LQG optimal control design', ... + 'H2 gain of a system', ... + 'H2 optimal controller of a system', ... + 'Hinf gain of a system', ... + 'Hinf optimal controller of a SISO system', ... + 'Hinf optimal controller of a MIMO system', ... + 'Discrete-time Hinf optimal control by bilinear transform', ... + 'Return to main demo menu'); + endwhile + if (menuopt == 1) + disp('Linear/Quadratic regulator design:') + disp('Compute optimal state feedback via the lqr command...') + help lqr + disp(' ') + disp('Example:') + A = [0 1; -2 -1] + B = [0; 1] + Q = [1 0; 0 0] + R = 1 + disp("Q = state penalty matrix; R = input penalty matrix") + prompt + disp('Compute state feedback gain k, ARE solution P, and closed-loop') + disp('poles as follows:'); + cmd = "[k p e] = lqr(A,B,Q,R)"; + run_cmd + prompt + disp("A similar approach can be used for LTI discrete-time systems") + disp("by using the dlqr command in place of lqr (see LQG example).") + elseif (menuopt == 2) + disp('Linear/Gaussian estimator design:') + disp('Compute optimal state estimator via the lqe command...') + help lqe + disp(' ') + disp('Example:') + A = [0 1; -2 -1] + disp("disturbance entry matrix G") + G = eye(2) + disp("Output measurement matrix C") + C = [0 1] + SigW = [1 0; 0 1] + SigV = 1 + disp("SigW = input disturbance intensity matrix;") + disp("SigV = measurement noise intensity matrix") + prompt + disp('Compute estimator feedback gain k, ARE solution P, and estimator') + disp('poles via the command: ') + cmd = "[k p e] = lqe(A,G,C,SigW,SigV)"; + run_cmd + disp("A similar approach can be used for LTI discrete-time systems") + disp("by using the dlqe command in place of lqe (see LQG example).") + elseif (menuopt == 3) + disp('LQG optimal controller of a system:') + disp('Input accepted as either A,B,C matrices or in system data structure form') + disp('in both discrete and continuous time.') + disp("Example 1: continuous time design:") + prompt + help lqg + disp("Example system") + A = [0 1; .5 .5]; + B = [0 ; 2]; + G = eye(2) + C = [1 1]; + sys = ss2sys(A,[B G],C); + sys = syschnames(sys,"in",1:3, \ + ["control input"; "disturbance 1"; "disturbance 2"]); + sysout(sys) + prompt + disp("Filtering/estimator parameters:") + SigW = eye(2) + SigV = 1 + prompt + disp("State space (LQR) parameters Q and R are:") + Q = eye(2) + R = 1 + cmd = "[K,Q1,P1,Ee,Er] = lqg(sys,SigW,SigV,Q,R,1);"; + run_cmd + disp("Check: closed loop system A-matrix is") + disp(" [A B*Cc]") + disp(" [Bc*C Ac ]") + cmd = "[Ac,Bc,Cc] = sys2ss(K);"; + run_cmd + cmd = "Acl = [A , B*Cc ; Bc*C Ac]"; + run_cmd + disp("Check: poles of Acl:") + Acl_poles = sortcom(eig(Acl)) + disp("Predicted poles from design = union(Er,Ee)") + cmd = "pred_poles = sortcom([Er;Ee])"; + run_cmd + disp("Example 2: discrete-time example") + cmd1 = "Dsys = ss2sys(A,[G B],C,[0 0 0],1);"; + cmd2 = "[K,Q1,P1,Ee,Er] = lqg(Dsys,SigW, SigV,Q,R);"; + disp("Run commands:") + cmd = cmd1; + run_cmd + cmd = cmd2; + run_cmd + prompt + disp("Check: closed loop system A-matrix is") + disp(" [A B*Cc]") + disp(" [Bc*C Ac ]") + [Ac,Bc,Cc] = sys2ss(K); + Acl = [A , B*Cc ; Bc*C Ac] + prompt + disp("Check: poles of Acl:") + Acl_poles = sortcom(eig(Acl)) + disp("Predicted poles from design = union(Er,Ee)") + pred_poles = sortcom([Er;Ee]) + elseif (menuopt == 4) + disp('H2 gain of a system: (Energy in impulse response)') + disp('Example 1: Stable plant:') + cmd = "A = [0 1; -2 -1]; B = [0 ; 1]; C = [1 0]; sys_poles = eig(A)"; + run_cmd + disp("Put into Packed system form:") + cmd = "Asys = ss2sys(A,B,C);"; + run_cmd + disp("Evaluate system 2-norm (impulse response energy):"); + cmd = "AsysH2 = h2norm(Asys)"; + run_cmd + disp("Compare with a plot of the system impulse response:") + tt = 0:0.1:20; + for ii=1:length(tt) + ht(ii) = C*expm(A*tt(ii))*B; + endfor + plot(tt,ht) + title("impulse response of example plant") + prompt + disp('Example 2: unstable plant') + cmd = "A = [0 1; 2 1]"; + eval(cmd); + cmd = "B = [0 ; 1]"; + eval(cmd); + cmd = "C = [1 0]"; + eval(cmd); + cmd = "sys_poles = eig(A)"; + run_cmd + prompt + disp('Put into system data structure form:') + cmd="Bsys = ss2sys(A,B,C);"; + run_cmd + disp('Evaluate 2-norm:') + cmd = "BsysH2 = h2norm(Bsys)"; + run_cmd + disp(' ') + prompt('NOTICE: program returns a value without an error signal.') + disp('') + + elseif (menuopt == 5) + disp('H2 optimal controller of a system: command = h2syn:') + prompt + help h2syn + prompt + disp("Example system: double integrator with output noise and") + disp("input disturbance:") + disp(" "); + disp(" -------------------->y2"); + disp(" | _________"); + disp("u(t)-->o-->| 1/s^2 |-->o-> y1"); + disp(" ^ --------- ^"); + disp(" | |"); + disp(" w1(t) w2(t)"); + disp(" ") + disp("w enters the system through B1, u through B2") + disp("z = [y1 ; y2] is obtained through C1, y=y1 through C2"); + disp(" ") + cmd = "A = [0 1; 0 0]; B1 = [0 0;1 0]; B2 = [0;1];"; + disp(cmd) + eval(cmd); + cmd = "C1 = [1 0; 0 0]; C2 = [1 0]; D11 = zeros(2);"; + disp(cmd) + eval(cmd); + cmd = "D12 = [0;1]; D21 = [0 1]; D22 = 0; D = [D11 D12; D21 D22];"; + disp(cmd) + eval(cmd); + disp("Design objective: compute U(s)=K(s)Y1(s) to minimize the closed") + disp("loop impulse response from w(t) =[w1; w2] to z(t) = [y1; y2]"); + prompt + disp("First: pack system:") + cmd="Asys = ss2sys(A,[B1 B2], [C1;C2] , D);"; + run_cmd + disp("Open loop multivariable Bode plot: (will take a moment)") + cmd="bode(Asys);"; + run_cmd + prompt("Press a key to close plot and continue"); + closeplot + disp("Controller design command: (only need 1st two output arguments)") + cmd="[K,gain, Kc, Kf, Pc, Pf] = h2syn(Asys,1,1);"; + run_cmd + disp("Controller is:") + cmd = "sysout(K)"; + run_cmd + disp(["returned gain value is: ",num2str(gain)]); + disp("Check: close the loop and then compute h2norm:") + prompt + cmd="K_loop = sysgroup(Asys,K);"; + run_cmd + cmd = "Kcl = sysconnect(K_loop,[3,4],[4,3]);"; + run_cmd + cmd = "Kcl = sysprune(Kcl,[1,2],[1,2]);"; + run_cmd + cmd="gain_Kcl = h2norm(Kcl)"; + run_cmd + cmd="gain_err = gain_Kcl - gain"; + run_cmd + disp("Check: multivarible bode plot:") + cmd="bode(Kcl);"; + run_cmd + prompt + disp("Related functions: is_dgkf, is_controllable, is_stabilizable,") + disp(" is_observable, is_detectable") + elseif (menuopt == 6) + disp('Hinfinity gain of a system: (max gain over all j-omega)') + disp('Example 1: Stable plant:') + cmd = "A = [0 1; -2 -1]; B = [0 ; 1]; C = [1 0]; sys_poles = eig(A)"; + run_cmd + disp('Pack into system format:') + cmd = "Asys = ss2sys(A,B,C);"; + run_cmd + disp('The infinity norm must be computed iteratively by') + disp('binary search. For this example, we select tolerance tol = 0.01, ') + disp('min gain gmin = 1e-2, max gain gmax=1e4.') + disp('Search quits when upper bound <= (1+tol)*lower bound.') + cmd = "tol = 0.01; gmin = 1e-2; gmax = 1e+4;"; + run_cmd + cmd = "AsysH2 = hinfnorm(Asys,tol,gmin,gmax)" + run_cmd + disp("Check: look at max value of magntude Bode plot of Asys:"); + [M,P,w] = bode(Asys); + loglog(w,M); + xlabel('Omega') + ylabel('|Asys(j omega)|') + disp(["Max magnitude is ",num2str(max(M)), ... + ", compared with gmin=",num2str(gmin)," and gmax=", ... + num2str(gmax),"."]) + prompt + disp('Example 2: unstable plant') + cmd = "A = [0 1; 2 1]; B = [0 ; 1]; C = [1 0]; sys_poles = eig(A)"; + run_cmd + disp("Pack into system format:") + cmd = "Bsys = ss2sys(A,B,C);"; + run_cmd + disp('Evaluate with BsysH2 = hinfnorm(Bsys,tol,gmin,gmax)') + BsysH2 = hinfnorm(Bsys,tol,gmin,gmax) + disp(' ') + disp('NOTICE: program returns a value without an error signal.') + disp('') + + elseif (menuopt == 7) + disp('Hinfinity optimal controller of a system: command = hinfsyn:') + prompt + help hinfsyn + prompt + disp("Example system: double integrator with output noise and") + disp("input disturbance:") + A = [0 1; 0 0] + B1 = [0 0;1 0] + B2 = [0;1] + C1 = [1 0; 0 0] + C2 = [1 0] + D11 = zeros(2); + D12 = [0;1]; + D21 = [0 1]; + D22 = 0; + D = [D11 D12; D21 D22] + prompt + disp("First: pack system:") + cmd="Asys = ss2sys(A,[B1 B2], [C1;C2] , D)"; + run_cmd + prompt + disp("Open loop multivariable Bode plot: (will take a moment)") + cmd="bode(Asys);"; + run_cmd + prompt + disp("Controller design command: (only need 1st two output arguments)") + gmax = 1000 + gmin = 0.1 + gtol = 0.01 + cmd="[K,gain] = hinfsyn(Asys,1,1,gmin,gmax,gtol)"; + run_cmd + disp("Check: close the loop and then compute h2norm:") + prompt + cmd="K_loop = sysgroup(Asys,K);"; + run_cmd + cmd = "Kcl = sysconnect(K_loop,[3,4],[4,3]);"; + run_cmd + cmd = "Kcl = sysprune(Kcl,[1,2],[1,2]);"; + run_cmd + cmd="gain_Kcl = hinfnorm(Kcl)"; + run_cmd + cmd="gain_err = gain_Kcl - gain"; + run_cmd + disp("Check: multivarible bode plot:") + cmd="bode(Kcl);"; + run_cmd + prompt + disp("Related functions: is_dgkf, is_controllable, is_stabilizable,") + disp(" is_observable, is_detectable, buildssic") + elseif (menuopt == 8) + disp('Hinfinity optimal controller of MIMO system: command = hinfsyn:') + prompt + help hinfsyn + prompt + disp("Example system: Boeing 707-321 airspeed/pitch angle control") + disp(" ") + hinfdemo + elseif (menuopt == 9) + disp("Discrete time H-infinity control via bilinear transform"); + prompt + dhinfdemo + elseif (menuopt == 10) + return + endif + prompt + endwhile + page_screen_output = save_val; +endfunction + diff --git a/scripts/control/dgram.m b/scripts/control/dgram.m --- a/scripts/control/dgram.m +++ b/scripts/control/dgram.m @@ -1,38 +1,32 @@ -## Copyright (C) 1996, 1997 John W. Eaton -## -## 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-1307, USA. +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function m = dgram(a,b) + # m = dgram(a,b) + # Return controllability grammian of discrete time system + # + # x(k+1) = a x(k) + b u(k) + # + # a m a' - m + b*b' = 0 -## Usage: gramian = dgram (A, B) -## -## Returns the discrete controllability and observability gramian. -## -## dgram (A, B) returns the discrete controllability gramian. -## dgram (A', C') returns the observability gramian. + # Written by A. S. Hodel July 1995 + # $Revision: 1.11 $ -## Author: R. Bruce Tenison -## Created: October 1993 -## Adapted-By: jwe - - -function gramian = dgram (A, B) - - [U, Sig, V] = svd (B); - - gramian = U * dlyap (U'*A*U, Sig*Sig') * U'; - + # let dlyap do the error checking... + m = dlyap(a,b*b'); endfunction diff --git a/scripts/control/dhinfdemo.m b/scripts/control/dhinfdemo.m new file mode 100644 --- /dev/null +++ b/scripts/control/dhinfdemo.m @@ -0,0 +1,127 @@ +# ------------------------------------------------------------ +# dhinfdemo Design of a discrete H_infinity controller. +# This is not a true discrete design. The design +# is carried out in continuous time while the +# effect of sampling is described by a bilinear +# transformation of the sampled system. +# This method works quite well if the sampling +# period is "small" compared to the plant time +# constants. +# +# This is a script file for OCTAVE. +# ------------------------------------------------------------ +# +# continuous plant: +# 1 +# G(s) = -------------- +# (s + 2)(s + 1) +# +# discretised plant with ZOH (Sampling period = Ts = 1 second) +# +# 0.39958z + 0.14700 +# G(s) = -------------------------- +# (z - 0.36788)(z - 0.13533) +# +# +----+ +# -------------------->| W1 |---> v1 +# z | +----+ +# ----|-------------+ || T || => min. +# | | vz infty +# | +---+ v +----+ +# *--->| G |--->O--*-->| W2 |---> v2 +# | +---+ | +----+ +# | | +# | +---+ | +# -----| K |<------- +# +---+ +# +# W1 and W2 are the robustness and performancs weighting +# functions + +# K. Mueller, +# Technical University of Braunschweig, IfR +# $Revision: 1.2 $ $Date: 1998/10/12 10:11:13 $ +# + +echo off +disp(" "); +disp(" --------------------------------------------------"); +disp(" Discrete H_infinity optimal control for the plant:"); +disp(" "); +disp(" 0.39958z + 0.14700"); +disp(" G(s) = --------------------------"); +disp(" (z - 0.36788)(z - 0.13533)"); +disp(" --------------------------------------------------"); +disp(" "); + +disp("sampling time:") +cmd = "Ts = 1.0;"; +disp(cmd); +eval(cmd); +disp("weighting on actuator value u"); +cmd = "W1 = wgt1o(0.1, 200.0, 50.0);"; +disp(cmd); +eval(cmd); +disp("weighting on controlled variable y"); +cmd = "W2 = wgt1o(350.0, 0.05, 0.0002);"; +disp(cmd); +eval(cmd); +# omega axis (column vector) +ww = vec(logspace(-4.99, 3.99, 100)); + +disp("Create ZOH equivalent model of a continuous plant"); +cmd = "G = tf2sys(2,[1 3 2]); Gd = c2d(G, Ts);"; +run_cmd + +# w-plane (continuous representation of the sampled system) +disp("W-plane transform of discrete time system:"); +cmd = "Gw = d2c(Gd, \"bi\");"; +run_cmd + +disp(" "); +disp(" o building P..."); +# need One as the pseudo transfer function One = 1 +cmd = "One = ugain(1);"; +disp(cmd); +eval(cmd); +cmd = " psys = buildssic([1 4;2 4;3 1],[3],[2 3 5],[3 4],Gw,W1,W2,One);"; +run_cmd; +disp(" o controller design..."); +cmd = "[K, gfin, GWC] = hinfsyn(psys, 1, 1, 0.1, 10.0, 0.02);"; +run_cmd + +disp(" "); +fig_n = 1; +yn = input(" * Plot magnitudes of W1KS and W2S? [n]: ","S"); +if (length(yn) >= 1) + if ((yn(1) == "y") || (yn(1) == 'Y')) + disp(" o magnitudes of W1KS and W2S..."); + gwx = sysprune(GWC, 1, 1); + mag1 = bode(gwx, ww); + if (columns(mag1) > 1); mag1 = mag1'; endif + gwx = sysprune(GWC, 2, 1); + mag2 = bode(gwx, ww); + if (columns(mag2) > 1); mag2 = mag2'; endif + figure(fig_n) + fig_n = fig_n + 1; + gset grid + loglog(ww, [mag1 mag2]); + endif +endif + +Kd = c2d(K, "bi", Ts); +GG = buildssic([1 2; 2 1], [], [1 2], [-2], Gd, Kd); +disp(" o closed loop poles..."); +damp(GG); + +disp(" "); +yn = input(" * Plot closed loop step responses? [n]: ","S"); +if (length(yn) >= 1) + if ((yn(1) == "y") || (yn(1) == 'Y')) + disp(" o step responses of T and KS..."); + figure(fig_n) + step(GG, 1, 10); + endif +endif + +# --------- End of dhinfdemo/kpm diff --git a/scripts/control/dlqe.m b/scripts/control/dlqe.m --- a/scripts/control/dlqe.m +++ b/scripts/control/dlqe.m @@ -1,67 +1,66 @@ -## Copyright (C) 1996, 1997 John W. Eaton -## -## 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-1307, USA. +# Copyright (C) 1993, 1994, 1995 John W. Eaton +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [l, m, p, e] = dlqe (a, g, c, sigw, sigv, s) -## Usage: [l, m, p, e] = dlqe (A, G, C, SigW, SigV {,Z}) -## -## Linear quadratic estimator (Kalman filter) design for the -## discrete time system -## -## x[k+1] = A x[k] + B u[k] + G w[k] -## y[k] = C x[k] + D u[k] + w[k] -## -## where w, v are zero-mean gaussian noise processes with respective -## intensities SigW = cov (w, w) and SigV = cov (v, v). -## -## Z (if specified) is cov(w,v); otherwise cov(w,v) = 0. -## -## Observer structure is -## z[k+1] = A z[k] + B u[k] + k(y[k] - C z[k] - D u[k]). -## -## Returns: -## -## l = observer gain, (A - A L C) is stable -## m = Ricatti equation solution -## p = the estimate error covariance after the measurement update -## e = closed loop poles of (A - A L C) +# Usage: [l, m, p, e] = dlqe (A, G, C, SigW, SigV {,S}) +# +# Linear quadratic estimator (Kalman filter) design for the +# discrete time system +# +# x[k+1] = A x[k] + B u[k] + G w[k] +# y[k] = C x[k] + D u[k] + w[k] +# +# where w, v are zero-mean gaussian noise processes with respective +# intensities SigW = cov (w, w) and SigV = cov (v, v). +# +# S (if specified) is cov(w,v); otherwise cov(w,v) = 0. +# +# Observer structure is +# z[k+1] = A z[k] + B u[k] + k(y[k] - C z[k] - D u[k]). +# +# Returns: +# +# l = observer gain, (A - L C) is stable +# m = Ricatti equation solution +# p = the estimate error covariance after the measurement update +# e = closed loop poles of (A - L C) -## Author: A. S. Hodel -## R. Bruce Tenison -## Created: August 1993 -## Adapted-By: jwe +# Written by A. S. Hodel (scotte@eng.auburn.edu) August, 1993. +# Modified for discrete time by R. Bruce Tenison (btenison@eng.auburn.edu) +# October, 1993 -function [l, m, p, e] = dlqe (a, g, c, sigw, sigv, zz) +# $Revision: 1.14 $ if (nargin != 5 && nargin != 6) error ("dlqe: invalid number of arguments"); endif - ## The problem is dual to the regulator design, so transform to lqr - ## call. +# The problem is dual to the regulator design, so transform to dlqr call. if (nargin == 5) [k, p, e] = dlqr (a', c', g*sigw*g', sigv); - m = p'; - l = (m*c')/(c*m*c'+sigv); + m = p; + l = k'; else - [k, p, e] = dlqr (a', c', g*sigw*g', sigv, g*zz); - m = p'; - l = (m*c'+a\g)/(c*m*c'+sigv); + [k, p, e] = dlqr (a', c', g*sigw*g', sigv, g*s); + m = p; + l = k'; a = a-g*t/sigv*c; sigw = sigw-t/sigv; endif diff --git a/scripts/control/dlqg.m b/scripts/control/dlqg.m new file mode 100644 --- /dev/null +++ b/scripts/control/dlqg.m @@ -0,0 +1,125 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [K,Q,P,Ee,Er] = dlqg(A,B,C,G,Sigw, Sigv,Q,R) +# +# +# O B S O L E T E * * * D O N O T U S E! +# +# Use lqg instead. +# +# function [K,Q,P,Ee,Er] = dlqg(A,B,C,G,Sigw,Sigv,Q,R) +# function [K,Q,P,Ee,Er] = dlqg(Sys,Sigw,Sigv,Q,R) +# +# design a discrete-time linear quadratic gaussian optimal controller +# for the system +# +# x(k+1) = A x(k) + B u(k) + G w(k) [w]=N(0,[Sigw 0 ]) +# y(k) = C x(k) + v(k) [v] ( 0 Sigv ]) +# +# Outputs: +# K: system data structure format LQG optimal controller +# P: Solution of control (state feedback) algebraic Riccati equation +# Q: Solution of estimation algebraic Riccati equation +# Ee: estimator poles +# Es: controller poles +# inputs: +# A,B,C,G, or Sys: state space representation of system. +# Sigw, Sigv: covariance matrices of independent Gaussian noise processes +# (as above) +# Q, R: state, control weighting matrices for dlqr call respectively. +# +# See also: lqg, dlqe, dlqr + +# Written by A. S. Hodel August 1995 +# $Revision: 1.2 $ + +warning("dlqg: obsolete. use lqg instead (system data structure format)"); + +if (nargin == 5) + # system data structure format + + # check that it really is system data structure + if(! is_struct(A) ) + error("dlqg: 5 arguments, first argument is not a system data structure structure") + endif + + sys = sysupdate(sys,"ss"); # make sure in proper form + [ndstates,nin,nout] = abcddim(sys.a, sys.b, sys.c, sys.d); + if(ndstates == -1) + error("this message should never appear: bad system dimensions"); + endif + + if(sys.n) + error("dlqg: system has continuous-time states (try lqg?)") + elseif(sys.nz < 1) + error("dlqg: system has no discrete time states") + elseif(nin <= columns(Sigw)) + error(["dlqg: ",num2str(nin)," inputs provided, noise dimension is ", ... + num2str(columns(Sigw))]) + elseif(nout != columns(Sigv)) + error(["dlqg: number of outputs (",num2str(nout),") incompatible with ", ... + "dimension of Sigv (",num2str(columns(Sigv)),")"]) + endif + + # put parameters into correct variables + R = Sigw; + Q = G; + Sigv = C; + Sigw = B; + [A,B,C,D] = sys2ss(Sys) + [n,m] = size(B) + m1 = columns(Sigw); + m2 = m1+1; + G = B(:,1:m1); + B = B(:,m2:m); + +elseif (nargin == 8) + # state-space format + m = columns(B); + m1 = columns(G); + p = rows(C); + n = abcddim(A,B,C,zeros(p,m)); + n1 = abcddim(A,G,C,zeros(p,m1)); + if( (n == -1) || (n1 == -1)) + error("dlqg: A,B,C,G incompatibly dimensioned"); + elseif(p != columns(Sigv)) + error("dlqg: C, Sigv incompatibly dimensioned"); + elseif(m1 != columns(Sigw)) + error("dlqg: G, Sigw incompatibly dimensioned"); + endif +else + error("dlqg: illegal number of arguments") +endif + +if (! (is_square(Sigw) && is_square(Sigv) ) ) + error("dlqg: Sigw, Sigv must be square"); +endif + +# now we can just do the design; call dlqr and dlqe, since all matrices +# are not given in Cholesky factor form (as in h2syn case) +[Ks P Er] = dlqr(A,B,Q,R); +[Ke Q jnk Ee] = dlqe(A,G,C,Sigw,Sigv); +Ac = A - Ke*C - B*Ks; +Bc = Ke; +Cc = -Ks; +Dc = zeros(rows(Cc),columns(Bc)); +K = ss2sys(Ac,Bc,Cc,Dc,1); +disp("HODEL: need to add names to this guy!") + +endfunction diff --git a/scripts/control/dlqr.m b/scripts/control/dlqr.m --- a/scripts/control/dlqr.m +++ b/scripts/control/dlqr.m @@ -1,98 +1,102 @@ -## Copyright (C) 1996, 1997 John W. Eaton -## -## 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-1307, USA. +# Copyright (C) 1993, 1994, 1995 John W. Eaton +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [k, p, e] = dlqr (a, b, q, r, s) -## Usage: [k, p, e] = dlqr (A, B, Q, R {,Z}) -## -## Linear quadratic regulator design for the discrete time system -## -## x[k+1] = A x[k] + B u[k] -## -## to minimize the cost functional -## -## J = Sum { x' Q x + u' R u } Z omitted -## -## or -## -## J = Sum { x' Q x + u' R u +2 x' Z u} Z included -## -## Returns: -## -## k = state feedback gain, (A - B K) is stable -## p = solution of algebraic Riccati equation -## e = closed loop poles of (A - B K) - -## Author: A. S. Hodel -## R. B. Tenison -## Created: August 1993 -## Adapted-By: jwe - -function [k, p, e] = dlqr (a, b, q, r, zz) +# Usage: [k, p, e] = dlqr (A, B, Q, R {,S}) +# +# Linear quadratic regulator design for the discrete time system +# +# x[k+1] = A x[k] + B u[k] +# +# to minimize the cost functional +# +# J = Sum { x' Q x + u' R u } S omitted +# +# or +# +# J = Sum { x' Q x + u' R u +2 x' S u} S included +# +# Returns: +# +# k = state feedback gain, (A - B K) is stable +# p = solution of algebraic Riccati equation +# e = closed loop poles of (A - B K) +# +# References: +# Anderson and Moore, Optimal Control: Linear Quadratic Methods, +# Prentice-Hall, 1990, pp. 56-58 +# Kuo, Digital Control Systems, Harcourt Brace Jovanovich, 1992, +# section 11-5-2. +# Written by A. S. Hodel (scotte@eng.auburn.edu) August 1993. +# Converted to discrete time by R. B. Tenison +# (btenison@eng.auburn.edu) October 1993 +# $Revision: 1.15 $ if (nargin != 4 && nargin != 5) error ("dlqr: invalid number of arguments"); endif - ## Check a. +# Check a. if ((n = is_square (a)) == 0) error ("dlqr: requires 1st parameter(a) to be square"); endif - ## Check b. +# Check b. [n1, m] = size (b); if (n1 != n) error ("dlqr: a,b not conformal"); endif - ## Check q. - +# Check q. + if ((n1 = is_square (q)) == 0 || n1 != n) error ("dlqr: q must be square and conformal with a"); endif - ## Check r. +# Check r. if((m1 = is_square(r)) == 0 || m1 != m) error ("dlqr: r must be square and conformal with column dimension of b"); endif - ## Check if n is there. +# Check if n is there. if (nargin == 5) - [n1, m1] = size (zz); + [n1, m1] = size (s); if (n1 != n || m1 != m) error ("dlqr: z must be identically dimensioned with b"); endif - ## Incorporate cross term into a and q. +# Incorporate cross term into a and q. - ao = a - (b/r)*zz'; - qo = q - (zz/r)*zz'; + ao = a - (b/r)*s'; + qo = q - (s/r)*s'; else - zz = zeros (n, m); + s = zeros (n, m); ao = a; qo = q; endif - ## Check that q, (r) are symmetric, positive (semi)definite +# Check that q, (r) are symmetric, positive (semi)definite if (is_symmetric (q) && is_symmetric (r) ... && all (eig (q) >= 0) && all (eig (r) > 0)) p = dare (ao, b, qo, r); - k = (r+b'*p*b)\b'*p*ao + r\zz'; + k = (r+b'*p*b)\b'*p*a + r\s'; e = eig (a - b*k); else error ("dlqr: q (r) must be symmetric positive (semi) definite"); diff --git a/scripts/control/dlyap.m b/scripts/control/dlyap.m --- a/scripts/control/dlyap.m +++ b/scripts/control/dlyap.m @@ -1,43 +1,41 @@ -## Copyright (C) 1996, 1997 John W. Eaton -## -## 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-1307, USA. - -## Usage: x = dlyap (a, b) -## -## Solve a x a' - x + b = 0 (discrete Lyapunov equation) for square -## matrices a and b. If b is not square, then the function attempts -## to solve either -## -## a x a' - x + b b' = 0 -## -## or -## -## a' x a - x + b' b = 0 -## -## whichever is appropriate. Uses Schur decomposition as in Kitagawa -## (1977). - -## Author: A. S. Hodel -## Created: August 1993 -## Adapted-By: jwe +# Copyright (C) 1993, 1994, 1995 John W. Eaton +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. function x = dlyap (a, b) +# Usage: x = dlyap (a, b) +# +# Solve a x a' - x + b = 0 (discrete Lyapunov equation) for square +# matrices a and b. If b is not square, then the function attempts +# to solve either +# +# a x a' - x + b b' = 0 +# +# or +# +# a' x a - x + b' b = 0 +# +# whichever is appropriate. Uses Schur decomposition as in Kitagawa +# (1977). + +# Written by A. S. Hodel (scotte@eng.auburn.edu) August 1993. +# $Revision: 1.13 $ + if ((n = is_square (a)) == 0) warning ("dlyap: a must be square"); endif @@ -57,7 +55,7 @@ warning ("dlyap: a,b not conformably dimensioned"); endif - ## Solve the equation column by column. + # Solve the equation column by column. [u, s] = schur (a); b = u'*b*u; @@ -66,7 +64,7 @@ while (j > 0) j1 = j; - ## Check for Schur block. +# Check for Schur block. if (j == 1) blksiz = 1; @@ -97,7 +95,7 @@ endwhile - ## Back-transform to original coordinates. +# Back-transform to original coordinates. x = u*x*u'; diff --git a/scripts/control/dmr2d.m b/scripts/control/dmr2d.m new file mode 100644 --- /dev/null +++ b/scripts/control/dmr2d.m @@ -0,0 +1,260 @@ +# Copyright (C) 1998 A. S. Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [dsys,fidx] = dmr2d (sys, idx, sprefix, Ts2,cuflg) + +# Usage: [dsys,fidx] = dmr2d (sys, idx, sprefix, Ts2 {,cuflg}) +# convert a multirate digital system to a single rate digital system +# states specified by idx, sprefix are sampled at Ts2, all others +# are sampled at Ts1 = sysgettsam(sys). +# inputs: +# sys: discrete time system; +# dmr2d exits with an error if sys is not discrete +# idx: list of states with sampling time sys.tsam (may be empty) +# sprefix: string prefix of states with sampling time sys.tsam (may be empty) +# Ts2: sampling time of states not specified by idx, sprefix +# must be an integer multiple of sys.tsam +# cuflg: "constant u flag" if cuflg is nonzero then the system inputs are +# assumed to be constant over the revised sampling interval Ts2. +# Otherwise, since the inputs can change during the interval +# t in [k Ts2, (k+1) Ts2], an additional set of inputs is +# included in the revised B matrix so that these intersample inputs +# may be included in the single-rate system. +# default: cuflg = 1. +# +# outputs: +# dsys: equivalent discrete time system with sampling time Ts2. +# +# The sampling time of sys is updated to Ts2. +# +# if cuflg=0 then a set of additional inputs is added to +# the system with suffixes _d1, ..., _dn to indicate their +# delay from the starting time k Ts2, i.e. +# u = [u_1; u_1_d1; ..., u_1_dn] where u_1_dk is the input +# k*Ts1 units of time after u_1 is sampled. (Ts1 is +# the original sampling time of discrete time sys and +# Ts2 = (n+1)*Ts1) +# +# fidx: indices of "formerly fast" states specified by idx and sprefix; +# these states are updated to the new slower) sampling interval +# +# +# WARNING: Not thoroughly tested yet; especially when cuflg == 0. + +# Adapted from c2d by a.s.hodel@eng.auburn.edu +# $Log: dmr2d.m,v $ +# Revision 1.1 1998/07/21 14:40:55 hodelas +# First attempt at multirate systems analysis +# +# Revision 1.2 1998/07/21 14:13:44 hodel +# squaring A to get a11^nstp +# +# Revision 1.1 1998/07/21 12:41:46 hodel +# Initial revision +# +# + save_val = implicit_str_to_num_ok; # save for later + implicit_str_to_num_ok = 1; + + # parse input arguments + if(nargin != 4 | nargout > 2) + usage("[dsys,fidx] = dmr2d (sys, idx, sprefix, Ts2 {,cuflg})"); + + elseif (!is_struct(sys)) + error("sys must be in system data structure form"); + + elseif(!is_digital(sys)) + error("sys must be discrete-time; continuous time passed"); + + elseif (!(is_vector(idx) | isempty(idx))) + error(["idx(",num2str(rows(idx)),"x",num2str(columns(idx)), ... + ") must be a vector"]); + + elseif (any(idx <= 0)) + idv = find(idx <= 0); + ii = idv(1); + error(["idx(",num2str(ii),")=",num2str(idx(ii)), ... + "; entries of idx must be positive"]); + + elseif(!(isstr(sprefix) | isempty(sprefix))) + error("sprefix must be a string or empty"); + + elseif(!is_sample(Ts2)) + error(["Ts2=",num2str(Ts2),"; invalid sampling time"]); + + endif + + # optional argument: cuflg + if(nargin <= 4) + cuflg = 1; # default: constant inputs over Ts2 sampling interv. + elseif( !is_scalar(cuflg) ) + error("cuflg must be a scalar") + elseif( cuflg != 0 | cuflg != 1) + error(["cuflg = ",num2str(cuflg),", should be 0 or 1"]); + endif + + # extract state space information + [da,db,dc,dd,Ts1,nc,nz,stname,inname,outname,yd] = sys2ss(sys); + + # compute number of steps + if(Ts1 > Ts2) + error(["Current sampling time=",num2str(Ts1),"< Ts2=",num2str(Ts2)]); + endif + nstp = floor(Ts2/Ts1+0.5); + if(abs((Ts2 - Ts1*nstp)/Ts1) > 1e-12) + warning(["dmr2d: Ts1=",num2str(Ts1),", Ts2=",num2str(Ts2), ... + ", selecting nsteps=",num2str(nstp),"; mismatch"]); + endif + + if(isempty(sprefix) & isempty(idx)) + warning("both sprefix and idx are empty; returning dsys=sys"); + fidx = []; + dsys = sys; + return + elseif(isempty(sprefix)) + fidx = idx; + else + fidx = reshape(idx,1,length(idx)); + # find states whose name begins with any strings in sprefix. + ns = rows(sprefix); + for kk=1:ns + spk = dezero(sprefix(kk,:)); # get next prefix and length + spl = length(spk); + + # check each state name + for ii=1:nz + sti = dezero(stname(ii,:)); # compare spk with this state name + if(length(sti) >= spl) + # if the prefix matches and ii isn't already in the list, add ii + if(strcmp(sti(1:spl),spk) & !any(fidx == ii) ) + fidx = sort([fidx,ii]); + endif + endif + endfor + endfor + endif + + if(nstp == 0) + warning("dmr2d: nstp = 0; setting tsam and returning"); + dsys = syschtsam(sys,Ts2); + return + elseif(nstp < 0) + error(["nstp = ", num2str(nstp)," < 0; this shouldn't be!"]); + endif + + # permute system matrices + pv = sysreorder(nz,fidx); + pv = pv(nz:-1:1); # reverse order to put fast modes in leading block + + # construct inverse permutation + Inz = eye(nz); + pvi = (Inz(pv,:)'*[1:nz]')'; + + # permute A, B (stname permuted for debugging only) + da = da(pv,pv); + db = db(pv,:); + stname = stname(pv,:); + + # partition A, B: + lfidx = length(fidx); + bki = 1:lfidx; + a11 = da(bki,bki); + b1 = db(bki,:); + + if(lfidx < nz) + lfidx1 = lfidx+1; + bki2 = (lfidx1):nz; + a12 = da(bki,bki2); + b2 = db(bki2,:); + else + warning("dmr2d: converting entire A,B matrices to new sampling rate"); + lfidx1 = -1; + bki2 = []; + endif + + ##################################### + # begin system conversion: nstp steps + ##################################### + + # compute abar_{n-1}*a12 and appropriate b matrix stuff + a12b = a12; # running total of abar_{n-1}*a12 + a12w = a12; # current a11^n*a12 (start with n = 0) + if(cuflg) + b1b = b1; + b1w = b1; + else + # cuflg == 0, need to keep track of intersample inputs too + nzdx = find(max(abs(b1)) != 0); # FIXME: check tolerance relative to ||b1|| + b1w = b1(nzdx); + innamenz = inname(nzdx); + b1b = b1; # initial b1 must match columns in b2 + endif + + ###################################### + # compute a11h = a11^nstp by squaring + a11h = eye(size(a11)); + p2 = 1; + a11p2 = a11; #a11^p2 + + nstpw = nstp; # workspace for computing a11^nstp + while(nstpw > 0.5) + oddv = rem(nstpw,2); + if(oddv) + a11h = a11h*a11p2; + endif + nstpw = (nstpw-oddv)/2; + if(nstpw > 0.5) + a11p2 = a11p2*a11p2; # a11^(next power of 2) + endif + endwhile + + # FIXME: this part should probably also use squaring, but + # that would require exponentially growing memory. What do do? + for kk=2:nstp + # update a12 block to sum(a12 + ... + a11^(kk-1)*a12) + a12w = a11*a12w; + a12b = a12b + a12w; + + # similar for b1 block (checking for cuflg first!) + b1w = a11*b1w; + if(cuflg) + b1b = b1b + b1w; # update b1 block just like we did a12 + else + b1b = [b1b, b1w]; # append new inputs + newin = strappend(innamenz,["_d",num2str(kk-1)]); + inname = str2mat(inname,newin); + endif + endfor + + # reconstruct system and return + da(bki,bki) = a11h; + db(bki,1:columns(b1b)) = b1b; + if(!isempty(bki2)) + da(bki,bki2) = a12b; + endif + + da = da(pvi,pvi); + db = db(pvi,:); + stname = stname(pvi,:); + + # construct new system and return + dsys = ss2sys(da,db,dc,dd,Ts2,0,nz,stname,inname,outname,find(yd == 1)); + + implicit_str_to_num_ok = save_val; # restore value + +endfunction diff --git a/scripts/control/fir2sys.m b/scripts/control/fir2sys.m new file mode 100644 --- /dev/null +++ b/scripts/control/fir2sys.m @@ -0,0 +1,102 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function sys = fir2sys (num,tsam,inname,outname) + # + # outsys = fir2sys(num,{tsam,inname,outname}) + # construct a system data structure from FIR description + # inputs: + # num: vector of coefficients [c0 c1 ... cn] of the SISO FIR transfer + # function C(z) = c0 + c1*z^{-1} + c2*z^{-2} + ... + znz^{-n} + # tsam: sampling time (default: 1) + # inname: name of input signal + # outname: name of output signal + # outputs: sys (system data structure) + + # Written by R. Bruce Tenison July 29, 1994 + # Name changed to TF2SYS July 1995 + # updated for new system data structure format July 1996 + # adapted from tf2sys july 1996 + # $Revision: 1.1.1.1 $ + + save_val = implicit_str_to_num_ok; + implicit_str_to_num_ok = 1; + + # Test for the correct number of input arguments + if ((nargin < 2) || (nargin > 4)) + usage('sys=fir2sys(num[,tsam,inname,outname])'); + return + endif + + # check input format + if( !is_vector(num) ) + error(['num (',num2str(rows(num)),'x',num2str(columns(num)), ... + ') must be a vector']) + endif + + den = [1,zeros(1,length(num)-1)]; + + # check sampling interval (if any) + if(nargin <= 1) + tsam = 1; # default + elseif (isempty(tsam)) + tsam = 1; + endif + if ( (! (is_scalar(tsam) && (imag(tsam) == 0) )) || (tsam <= 0) ) + error('fir tsam must be a positive real scalar') + endif + + # Set name of input + if(nargin < 3) + inname = "u"; + elseif(isempty(inname)) + inname = "u"; + endif + if (rows(inname) > 1) + warning(['fir2sys:,' num2str(rows(inname)),' input names given, 1st used.']) + inname = (inname(1,:)); + endif + + # Set name of output + if(nargin < 4) + outname = "y"; + elseif(isempty(outname)) + outname = "y"; + endif + if (rows(outname) > 1) + warning(['fir2sys: ',num2str(rows(outname)),... + ' output names given, 1st used.']) + outname = (outname(1,:)); + endif + + sys.num = num; + sys.den = den; + + # Set the system vector: active = 0(tf), updated = [1 0 0]; + sys.sys = [0 1 0 0]; + + # Set defaults + sys.tsam = tsam; + sys.inname = inname; + sys.outname = outname; + sys.nz = length(den)-1; + sys.n = 0; + sys.yd = 1; + + implicit_str_to_num_ok = save_val; +endfunction diff --git a/scripts/control/frdemo.m b/scripts/control/frdemo.m new file mode 100644 --- /dev/null +++ b/scripts/control/frdemo.m @@ -0,0 +1,583 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function frdemo() +# Octave Controls toolbox demo: Frequency Response demo +# Written by David Clem August 15, 1994 + +# $Revision: 1.3 $ +# a s hodel: updated to match new order of ss2zp outputs +# J Ingram: updated for system data structure format August 1996 + + disp("") + clc + j = 0; + while (j != 3) + disp(""); + j = menu("Octave Controls Systems Toolbox Frequency Response Demo",... + 'Bode analysis (bode)',... + 'Nyquist analysis (nyquist)',... + 'Return to main demo menu'); + + if (j == 1) + k1 = 0; + while (k1 != 4) + disp("\n"); + clc + + k1 = menu("Bode analysis (bode)",... + 'Continuous system bode analysis',... + 'Discrete system bode analysis',... + 'Bode command description', ... + 'Return to frdemo menu'); + + if( k1 == 1 ) + disp(" ") + clc + disp("\nContinuous system bode analysis\n"); + disp("Example #1:") + disp("\nConsider the system sys1="); + sys1=tf2sys([1 1],[1 0 -1]); + sysout(sys1); + disp("\nPole-zero form can be obtained as follows:") + cmd = "sysout(sys1,""zp"");"; + run_cmd; + disp("The systems bode plot is obtained as follows:"); + cmd = "bode(sys1);"; + run_cmd; + disp("\nNotice that bode automatically labels the plots according to") + disp("the selected input/output combinations.") + disp(" ") + disp("If the frequency range is not specified, bode automatically") + disp("selects a frequency range based on the natural frequencies of") + disp("of all poles away from s=0 (or z=1 in discrete time). Bode") + disp("then checks to make sure that the phase plot is sufficiently") + disp("smooth that relevant plot behavior is captured.") + disp("") + disp("Bode exits with an error if the system is mixed (both continuous") + disp("and discrete; see is_digital for conditions)") + prompt + disp("\nIf the plot magnitude, phase and frequency data is desired, the"); + disp("user can enter the following command:"); + disp("\n[Mag,Phase,w] = bode(sys);"); + disp("\nThis will return three vectors containing the magnitude,"); + disp("phase and frequency.\n"); + prompt; + + disp("") + clc + disp("Example #2, sys2=") + cmd = "sys2=zp2sys([1],[-1 -5],10);"; + eval(cmd); + cmd = "sysout(sys2);"; + eval(cmd); + disp("\nThe bode plot command is identical to the tf form:") + cmd = "bode(sys2);"; + run_cmd; + disp("\nThe internal representation of the system is not important;") + disp("bode automatically sorts it out internally.") + prompt; + + disp("") + clc + disp("Example #3, Consider the following state space system sys3=:\n"); + cmd = "sys3=ss2sys([0 1; -1000 -1001], [0;1], [0 -891], 1);"; + eval(cmd); + cmd = "sysout(sys3);"; + eval(cmd); + disp("\nOnce again, the bode plot command is the same:"); + cmd = "bode(sys3);"; + run_cmd; + disp("\nSuppose the user is interested in the response of the system"); + disp("defined over the input frequency range of 1 - 1000 rad/s.\n"); + disp("First, a frequency vector is required. It can be created"); + disp("with the command:\n"); + cmd = "wrange = logspace(log10(1),log10(1000),100);"; + disp(cmd); + eval(cmd); + disp("\nThis creates a logarithmically scaled frequency vector with"); + disp("100 values between 1 and 1000 rad/s\n"); + disp("Then, the bode command includes wrange in the input arguments"); + disp("like this:"); + cmd = "bode(sys3,wrange);"; + run_cmd; + prompt; + + disp("") + clc + disp("\nExample #4, The state-space system from example 3 will be"); + disp("grouped with the system from example 2 to form a MIMO system"); + disp("The commands to do this grouping are as follows:"); + sys2=sysupdate(sys2,"ss"); + [nn,nz,mm,pp] = sysdimensions(sys2); + sys2 = syschnames(sys2,"out",1:pp,"y_sys2"); + sys2 = syschnames(sys2,"in",1:mm,"u_sys2"); + sys2 = syschnames(sys2,"st",1:nn,sysdefioname(nn+nz,"x_sys2")); + cmd = "sys_mimo = sysgroup(sys2,sys3);"; + disp(cmd); + eval(cmd); + disp("The resulting state-space system (after changing signal names"); + disp("in sys2) is"); + cmd = "sysout(sys_mimo)"; + eval(cmd); + disp("\nNotice that there are now 2 inputs and 2 outputs, and that it did"); + disp("not matter what form the two systems were in when they were grouped."); + disp(["\nTo view the system\'s bode plots, execute the",... + " following command:\n"]) + cmd = "bode(sys_mimo);"; + run_cmd; + prompt + disp("\nTo view the bode plots for selected channels, the command form changes:") + cmd = "wrange = [];"; + disp(cmd) + eval(cmd); + cmd = "out = 1;"; + disp(cmd) + eval(cmd); + cmd = "in = 1;"; + disp(cmd) + eval(cmd); + cmd = "bode(sys_mimo,wrange,out,in);"; + run_cmd; + disp("\nNotice that this bode plot is the same as the plot from example 2."); + prompt + closeplot + + elseif( k1 == 2 ) + disp("") + clc + disp("\nDiscrete system bode analysis\n"); + disp("Display bode plots of a discrete SISO system (dbode)\n") + disp("Example #1, Consider the following discrete transfer"); + disp(" function:\n"); + cmd = "sys1 = tf2sys([0.00100502 -0.00099502],[1 -2 1],0.001);"; + disp(cmd); + eval(cmd); + cmd = "sysout(sys1)"; + disp(cmd); + eval(cmd); + disp("\nTo examine open loop zeros and poles of the system,"); + disp("use the command:\n") + cmd = "sysout(sys1,""zp"");"; + run_cmd; + disp("\nTo view the system's bode plots, execute the following"); + disp("command:\n") + cmd = "bode(sys1);"; + run_cmd; + disp("\nNotice (1) the plot label uses exp(jwT) for its title axis. This") + disp(" allows the user to determine what kind of system was") + disp(" used to generate the bode plot"); + disp(" (2) the system poles are both at z=1, (break frequency at") + disp(" jwT = 0); pure integrator poles like this are discarded") + disp(" by Octave when computing the plot frequency range.") + + disp("\nIf magnitude, phase, and frequency data are also desired,"); + disp(" perform the following command instead:\n"); + disp("[M,P,w]=dbode(num,den,T,wrange).\n Where:"); + disp("M => Bode magnitude response data"); + disp("P => Bode phase response data"); + disp("w => frequencies that M and P were evaluated at"); + disp("sys1 => system data structure") + disp("T => sample period") + disp("wrange => optional vector of frequencies") + disp(" if wrange is entered in the argument list, the"); + disp(" system will be evaluated at these specific"); + disp(" frequencies\n"); + + prompt + disp("") + clc + disp("Example #2, Consider the following set of discrete poles and"); + disp("zeros:\n") + cmd = "sys2 = zp2sys([0.99258;0.99745],[0.99961;0.99242],1,0.001);"; + disp(cmd); + eval(cmd); + cmd = "sysout(sys2)"; + disp(cmd); + eval(cmd); + disp("\nTo view the system's bode plots, execute the following"); + disp("command:\n") + cmd = "bode(sys2);"; + run_cmd; + disp("Notice that the bode command is the same in both of the previous"); + disp("examples. The bode command is also the same for the continuous case."); + disp("The function, dbode, is no longer used."); + + prompt + disp("") + clc + disp("\nExample #3, Now consider the following state space system:\n"); + cmd = "sys3 = ss2sys([.857 .0011;0 .99930],[1;1],[-.6318 .0057096],5.2, .001);"; + disp(cmd); + eval(cmd); + cmd = "sysout(sys3);"; + disp(cmd); + eval(cmd); + disp("\nTo view the system's bode plots, execute the following command:\n") + cmd = "bode(sys3);"; + run_cmd; + disp("\nAgain, notice that the bode command is the same regardless of the form"); + disp("of the system."); + disp("\nSuppose the user is interested in the response of the system"); + disp("defined over the input frequency range of 1 - 1000 rad/s.\n"); + disp("First, a frequency vector is required. It can be created"); + disp("with the command:\n"); + cmd = "wrange = logspace(log10(1),log10(1000),100);"; + disp(cmd); + eval(cmd); + disp("\nThis creates a logrithmetically scaled frequency vector with"); + disp("100 values between 1 and 1000 rad/s\n"); + disp("Then, the bode command includes wrange in the input arguments"); + disp("like this:"); + cmd = "bode(sys3,wrange);"; + run_cmd; + prompt; + + disp("") + clc + disp("\nExample #4, We will now examine a MIMO state-space system. Systems"); + disp("two and three will be grouped."); + sys2=sysupdate(sys2,"ss"); + [nn,nz,mm,pp] = sysdimensions(sys2); + sys2 = syschnames(sys2,"out",1:pp,"y_sys2"); + sys2 = syschnames(sys2,"in",1:mm,"u_sys2"); + sys2 = syschnames(sys2,"st",1:nn,sysdefioname(nn+nz,"x_sys2")); + cmd = "sys_mimo = sysgroup(sys2,sys3);"; + disp(cmd); + eval(cmd); + cmd = "sysout(sys_mimo);"; + disp(cmd); + eval(cmd); + disp("\nTo view the system's bode plots, execute the following command:\n") + cmd = "bode(sys_mimo);"; + run_cmd; + prompt + + disp("\nThe bode plot of a single channel is viewed as follows:") + cmd = "wrange = [];"; + disp(cmd) + eval(cmd); + cmd = "out = 1;"; + disp(cmd) + eval(cmd); + cmd = "in = 1;"; + disp(cmd) + eval(cmd); + cmd = "bode(sys_mimo,wrange,out,in);"; + run_cmd; + disp("\nNotice that this bode plot is the same as the plot from example 2."); + prompt + closeplot + + elseif( k1 == 3 ) + help bode + prompt + endif + endwhile + elseif (j == 2) + k2 = 0; + disp(""); + while (k2 != 4) + disp("\n"); + help nyquist + prompt; + disp("") + clc; + + k2 = menu("Nyquist analysis (Nyquist)",... + 'Continuous system nyquist analysis',... + 'Discrete system nyquist analysis',... + 'Mixed system nyquist analysis',... + 'Return to frdemo menu'); + + if( k2 == 1 ) + disp("") + clc + disp("\nContinuous system nyquist analysis\n"); + disp("Display Nyquist plots of a SISO system (nyquist)\n") + disp("Example #1, Consider the following transfer function:\n") + cmd = "sys1 = tf2sys([1],[1 0.8 1]);"; + disp(cmd); + eval(cmd); + disp("To examine the transfer function, use the command:"); + cmd = "sysout(sys1);"; + disp(cmd); + eval(cmd); + disp("\nTo examine the open loop zeros and poles, use the command:"); + cmd = "sysout(sys1,""zp"");"; + run_cmd; + disp("\nTo view the system""s nyquist plot, execute the following"); + disp("command:\n") + cmd = "nyquist(sys1);"; + run_cmd; + disp("\nIf the real and imaginary parts of the response are desired,"); + disp("use the following command:"); + disp("command: [R,I,w]=nyquist(sys1);\n"); + disp("If the user desires to evaluate the response in a certain"); + disp("frequency range, he may do so by entering the following:"); + disp("command: [M,P,w]=nyquist(num,den,wrange).\n") + disp("wrange is a vector of frequencies that spans the desired"); + disp("viewing range.\n"); + disp("This will be illustrated in the third nyquist example.\n") + disp("Variable Description:\n") + disp("R => real part of response") + disp("I => imaginary part of response") + disp("w => frequencies that the transfer function was evaluated at") + disp("sys1 => system data structure") + disp("wrange => optional vector of frequencies") + disp(" if wrange is entered in the argument list, the"); + disp(" system will be evaluated at these specific"); + disp(" frequencies\n") + prompt + + disp("") + clc + disp("Example #2, Consider the following set of poles and zeros:\n") + cmd = "sys2 = zp2sys([-1;-4],[-2+1.4142i;-2-1.4142i],1);"; + disp(cmd); + eval(cmd); + disp("\nTo examine the poles and zeros, use the command:"); + cmd = "sysout(sys2)"; + disp(cmd); + eval(cmd); + disp("\nTo view the system""s nyquist plot, execute the following"); + disp("command:\n") + cmd = "nyquist(sys2);"; + run_cmd; + prompt + + disp("") + clc + disp("\nExample #3, Consider the following state space system:\n") + cmd = "sys3 = ss2sys([0 1 0 0;0 0 1 0;0 0 0 1;0 0 -20 -12],[0;0;0;1],[50 100 0 0],0);"; + disp(cmd); + eval(cmd); + disp("\nTo examine the state-space system, use the command:"); + cmd = "sysout(sys3)"; + disp(cmd); + eval(cmd); + disp("\nTo examine the poles and zeros, use the command:"); + cmd = "sysout(sys3,""zp"")"; + run_cmd; + disp("\nTo view the system""s nyquist plot, execute the following"); + disp("commands:\n") + cmd = "nyquist(sys3);"; + run_cmd; + prompt + + disp("Example #3 (continued), If the user wishes to evaluate the"); + disp("system response over a desired frequency range, he must first"); + disp("create a frequency vector.\n") + disp("For example, suppose the user is interested in the response"); + disp("of the system defined above over input frequency range of"); + disp("3 - 100 rad/s.\n") + disp("A frequency vector can be created using the command:\n"); + cmd = "wrange = logspace(log10(3),log10(100),100);"; + disp(cmd); + eval(cmd); + disp("\nNyquist can be run again using the frequency vector as"); + disp("follows:\n") + cmd = "nyquist(sys3,wrange);"; + run_cmd; + prompt + + disp("") + clc + disp("Example #4, Nyquist can be used for MIMO systems if the system has"); + disp("an equal number of inputs and outputs. Otherwise, nyquist returns"); + disp("an error. To examine a MIMO system, systems 2 and 3 will be grouped"); + sys2=sysupdate(sys2,"ss"); + [nn,nz,mm,pp] = sysdimensions(sys2); + sys2 = syschnames(sys2,"out",1:pp,"y_sys2"); + sys2 = syschnames(sys2,"in",1:mm,"u_sys2"); + sys2 = syschnames(sys2,"st",1:nn,sysdefioname(nn+nz,"x_sys2")); + cmd = "sys_mimo = sysgroup(sys2,sys3);"; + disp(cmd); + eval(cmd); + cmd = "sysout(sys_mimo);"; + disp(cmd); + eval(cmd); + disp("\nTo view the system's nyquist plot, execute the following command:\n") + cmd = "nyquist(sys_mimo);"; + run_cmd; + prompt + disp("\nTo view the nyquist plots for selected channels, the command form changes:") + cmd = "nyquist(sys_mimo,[],1,1);"; + run_cmd; + disp("\nNotice that this bode plot is the same as the plot from example 2."); + prompt + closeplot + + + + elseif( k2 == 2 ) + disp("") + clc + disp("\nDiscrete system nyquist analysis\n"); + disp("Display Nyquist plots of a discrete SISO system (nyquist)\n") + disp("We will first define a sampling time, T"); + cmd = "T = 0.01;"; + disp(cmd); + eval(cmd); + disp("\nExample #1, Consider the following transfer function:\n") + cmd = "sys1 = tf2sys([2 -3.4 1.5],[1 -1.6 0.8],T);"; + disp(cmd); + eval(cmd); + disp("To examine the transfer function, use the command:"); + cmd = "sysout(sys1);"; + disp(cmd); + eval(cmd); + disp("\nTo examine the open loop zeros and poles, use the command:"); + cmd = "sysout(sys1,""zp"")"; + disp(cmd); + eval(cmd); + disp("\nTo view the system""s nyquist plot, execute the following"); + disp("command:") + cmd = "nyquist(sys1);"; + run_cmd; + disp("To change the range used for the frequency, a frequency"); + disp("is needed. Suppose the user would like to examine the"); + disp("nyquist plot in the frequency range of 0.01 - 31.6 rad/s."); + disp("\nThe frequency vector needed to do this is created with the"); + disp("command:"); + cmd = "wrange = logspace(-2,1.5,200);"; + disp(cmd); + eval(cmd); + disp("\nNyquist can be run again with this frequency vector"); + cmd = "nyquist(sys1,wrange);"; + run_cmd; + disp("\nIf the real and imaginary parts of the response are desired,"); + disp("perform the following command:\n"); + disp("[R,I,w]=nyquist(sys,wrange)\n") + disp("Variable Description:\n") + disp("R => real part of response") + disp("I => imaginary part of response") + disp("w => frequencies that the transfer function was evaluated at") + disp("sys => The system data structure"); + disp("wrange => optional vector of frequencies") + disp(" if wrange is entered in the argument list, the"); + disp(" system will be evaluated at these specific"); + prompt + + disp("") + clc + disp("\nExample #2, Consider the following set of poles and zeros:\n") + cmd = "sys2 = zp2sys([0.98025 + 0.01397i;0.98025 - 0.01397i],[0.96079;0.99005],1,T);"; + disp(cmd); + eval(cmd); + disp("\nTo examine the open loop zeros and poles, use the command:"); + cmd = "sysout(sys2)"; + disp(cmd); + eval(cmd); + disp("\nTo view the system's nyquist plot between the frequencies"); + disp("0.01 - 100 rad/s, execute the following commands:\n") + cmd = "wrange = logspace(-2,2,100);"; + disp(cmd); + eval(cmd); + cmd = "nyquist(sys2,wrange);"; + run_cmd; + prompt; + + disp("") + clc + disp("\nExample #3, Consider the following discrete state space"); + disp("system:\n"); + disp("This example will use the same system used in the third"); + disp("example in the continuous nyquist demo. First, that system"); + disp("will have to be re-entered useing the following commands:\n"); + cmd = "sys3 = ss2sys([0 1 0 0;0 0 1 0;0 0 0 1;0 0 -20 -12],[0;0;0;1],[50 100 0 0],0);"; + disp(cmd); + eval(cmd); + disp("\nTo examine the state-space system, use the command:"); + cmd = "sysout(sys3)"; + disp(cmd); + eval(cmd); + disp("\nTo examine the poles and zeros, use the command:"); + cmd = "sysout(sys3,""zp"")"; + disp(cmd); + eval(cmd); + disp("\nTo convert the system to discrete time, we need a sampling"); + disp("time which can be entered like this:"); + cmd = "T = 0.01"; + disp(cmd); + eval(cmd); + disp("\nNow the command, c2d, is used to convert the system from"); + disp("continuous to discrete time, with the following command"); + cmd = "dsys3 = c2d(sys3,T);"; + run_cmd; + disp("\nTo examine the new discrete state-space system, use the"); + disp("command"); + cmd = "sysout(dsys3);"; + disp(cmd); + eval(cmd); + disp("\nTo examine the new discrete poles and zeros, use the command:"); + cmd = "sysout(dsys3,""zp"")"; + disp(cmd); + eval(cmd); + disp("\nTo view the system's nyquist plot, execute the following"); + disp("commands:\n"); + cmd = "gset xrange [-4:2];"; + disp(cmd); eval(cmd); + cmd = "gset yrange [-2.5:2.5];"; + disp(cmd); eval(cmd); + cmd = "nyquist(dsys3);"; + run_cmd; + disp("Notice that the asymptotes swamp out the behavior of the plot") + disp("near the origin. You may use interactive nyquist plots") + disp("to \"zoom in\" on a plot as follows:") + + cmd = "atol = 1;"; + disp(cmd) + eval(cmd) + cmd = "nyquist(dsys3,[],[],[],atol);"; + run_cmd + prompt + + + disp("") + clc + disp("MIMO SYSTEM: Nyquist cannot be used for discrete MIMO systems"); + disp("at this time."); +# cmd = "dsys_mimo = sysgroup(sys2,dsys3);"; +# disp(cmd); +# eval(cmd); +# cmd = "sysout(dsys_mimo);"; +# disp(cmd); +# eval(cmd); +# disp("\nTo view the system's nyquist plot, execute the following command:\n") +# cmd = "nyquist(dsys_mimo);"; +# run_cmd; +# prompt +# disp("\nTo view the nyquist plots for selected channels, the command form changes:") +# cmd = "nyquist(dsys_mimo,[],1,1);"; +# run_cmd; +# disp("\nNotice that this bode plot is the same as the plot from example 2."); + prompt + closeplot + + + elseif( k2 == 3 ) + disp("\nMixed system nyquist analysis\n"); + disp("Nyquist exits with an error if it is passed a ""mixed"" system (one") + disp("with both continuous and discrete states). Use c2d or d2c to") + disp("convert the system to either pure digital or pure continuous form"); + endif + endwhile + endif + endwhile +endfunction diff --git a/scripts/control/freqchkw.m b/scripts/control/freqchkw.m new file mode 100644 --- /dev/null +++ b/scripts/control/freqchkw.m @@ -0,0 +1,37 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function USEW = freqchkw(w) + # function USEW = freqchkw(w) + # used by freqresp to check that input frequency vector is legal + + # A S Hodel July 1996 + # $Revision: 1.1.1.1 $ + + if(isempty(w)) + USEW = 0; + elseif(!is_vector(w)) + error(["w (",num2str(rows(w)),"x",num2str(columns(w)), ... + "): must be [], a vector or a scalar"]); + elseif( (max(abs(imag(w))) != 0) && (min(real(w)) <= 0) ) + error("w must have real positive entries"); + else + w = sort(w); + USEW = 1; # vector provided (check values later) + endif +endfunction diff --git a/scripts/control/freqresp.m b/scripts/control/freqresp.m new file mode 100644 --- /dev/null +++ b/scripts/control/freqresp.m @@ -0,0 +1,118 @@ +# Copyright (C) 1996,1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [ff,w] = freqresp(sys,USEW,w); + # function [ff,w] = freqresp(sys,USEW{,w}); + # Frequency response function - used internally by bode, nyquist. + # minimal argument checking; "do not attempt to do this at home" + # USEW returned by freqchkw + # w: optional, must be present if USEW is given + # + # returns: ff = vector of finite G(j*w) entries (or || G(j*w) || for MIMO) + # w = vector of frequencies used + # ff and w are both returned as row vectors + + # Written by: R. Bruce Tenison July 11, 1994 + # $Revision: 1.6 $ + # SYS_INTERNAL accesses members of system data structure + + save_val = empty_list_elements_ok; + empty_list_elements_ok = 1; + + # Check Args + if( (nargin < 2) || (nargin > 4) ) + usage ("[ff,w] = freqresp(sys,USEW{,w})"); + elseif( USEW & (nargin < 3) ) + error("USEW=1 but w was not passed."); + elseif( USEW & isempty(w)) + warning("USEW=1 but w is empty; setting USEW=0"); + USEW=0; + endif + + DIGITAL = is_digital(sys); + + # compute default w if needed + if(!USEW) + if(is_siso(sys)) + sys = sysupdate(sys,"zp"); + [zer,pol] = sys2zp(sys); + else + zer = tzero(sys); + pol = eig(sys2ss(sys)); + endif + + # get default frequency range + [wmin,wmax] = bode_bounds(zer,pol,DIGITAL,sysgettsam(sys)); + w = logspace(wmin,wmax,50); + else + w = reshape(w,1,length(w)); # make sure it's a row vector + endif + + # now get complex values of s or z + if(DIGITAL) + jw = exp(i*w*sysgettsam(sys)); + else + jw = i*w; + endif + + [nn,nz,mm,pp] = sysdimensions(sys); + + # now compute the frequency response - divide by zero yields a warning + if (strcmp(sysgettype(sys),"zp")) + # zero-pole form (preferred) + [zer,pol,sysk] = sys2zp(sys); + ff = ones(size(jw)); + l1 = min(length(zer)*(1-isempty(zer)),length(pol)*(1-isempty(pol))); + for ii=1:l1 + ff = ff .* (jw - zer(ii)) ./ (jw - pol(ii)); + endfor + + # require proper transfer function, so now just get poles. + for ii=(l1+1):length(pol) + ff = ff ./ (jw - pol(ii)); + endfor + ff = ff*sysk; + + elseif (strcmp(sysgettype(sys),"tf")) + # transfer function form + [num,den] = sys2tf(sys); + ff = polyval(num,jw)./polyval(den,jw); + elseif (mm==pp) + # The system is square; do state-space form bode plot + [sysa,sysb,sysc,sysd,tsam,sysn,sysnz] = sys2ss(sys); + n = sysn + sysnz; + for ii=1:length(jw); + ff(ii) = det(sysc*((jw(ii).*eye(n)-sysa)\sysb)+sysd); + endfor; + else + # Must be state space... bode + [sysa,sysb,sysc,sysd,tsam,sysn,sysnz] = sys2ss(sys); + n = sysn + sysnz; + for ii=1:length(jw); + ff(ii) = norm(sysc*((jw(ii)*eye(n)-sysa)\sysb)+sysd); + endfor + + endif + + w = reshape(w,1,length(w)); + ff = reshape(ff,1,length(ff)); + + # restore global variable + empty_list_elements_ok = save_val; +endfunction + diff --git a/scripts/control/gram.m b/scripts/control/gram.m new file mode 100644 --- /dev/null +++ b/scripts/control/gram.m @@ -0,0 +1,32 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function m = gram(a,b) + # m = gram(a,b) + # Return controllability grammian of continuous time system + # + # dx/dt = a x + b u + # + # a m + a' + b*b' = 0 + + # Written by A. S. Hodel + # $Revision: 1.2 $ + + # let lyap do the error checking... + m = lyap(a,b*b'); +endfunction diff --git a/scripts/control/h2norm.m b/scripts/control/h2norm.m new file mode 100644 --- /dev/null +++ b/scripts/control/h2norm.m @@ -0,0 +1,58 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function out = h2norm(sys) + # Usage: out = h2norm(sys) + # + # Computes the H2 norm system data structure (continuous time only) + # sys = system data structure [see ss2sys()] + # returns out = Inf if system is unstable + # + # Reference: + # Doyle, Glover, Khargonekar, Francis, "State Space Solutions to Standard + # H2 and Hinf Control Problems", IEEE TAC August 1989 + # + + # A. S. Hodel Aug 1995 + # updated for system data structure by John Ingram November 1996 + # $Revision: 1.2 $ + + if((nargin != 1)) + usage("out = h2norm(sys)"); + elseif(!is_struct(sys)) + error("Sys must be in system data structure"); + end + dflg = is_digital(sys); + + if(!is_stable(sys)) + warning("h2norm: unstable input system; returning Inf"); + out = Inf; + else + # compute gain + [a,b,c,d] = sys2ss(sys); + if(dflg) + M = dlyap(a,b*b'); + else + M = lyap (a,b*b'); + endif + if( min(real(eig(M))) < 0) + error("h2norm: grammian not >= 0 (lightly damped modes?)") + endif + out = sqrt(d'*d + trace(c*M*c')); + endif +endfunction diff --git a/scripts/control/h2syn.m b/scripts/control/h2syn.m new file mode 100644 --- /dev/null +++ b/scripts/control/h2syn.m @@ -0,0 +1,179 @@ +# Copyright (C) 1996,1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [K,gain, Kc, Kf, Pc, Pf] = h2syn(Asys,nu,ny,tol) + # [K,gain, Kc, Kf, Pc, Pf] = h2syn(Asys,nu,ny,tol) + # + # Design H2 optimal controller per procedure in + # Doyle, Glover, Khargonekar, Francis, "State Space Solutions to Standard + # H2 and Hinf Control Problems", IEEE TAC August 1989 + # + # Discrete time control per Zhou, Doyle, and Glover, ROBUST AND OPTIMAL + # CONTROL, Prentice-Hall, 1996 + # + # inputs: + # Asys: system data structure. + # nu: number of controlled inputs + # ny: number of measured outputs + # tol: threshhold for 0. Default: 200*eps + # outputs: + # K: system controller (system data structure) + # gain: optimal closed loop gain (see Kc, Kf warning below) + # Kc: full information control (system data structure) + # Kf: state estimator (system data structure) + # WARNING: incorporation of the is_dgkf nonsingular transformations + # Ru and Ry into Kc and Kf has not been tested. + # Pc: ARE solution matrix for regulator subproblem + # Pf: ARE solution matrix for filter subproblem + + # Updated for System structure December 1996 by John Ingram + # $Revision: 1.2 $ + # $Log: h2syn.m,v $ + # Revision 1.2 1998/07/01 16:23:36 hodelas + # Updated c2d, d2c to perform bilinear transforms. + # Updated several files per bug updates from users. + # + # Revision 1.4 1997/03/11 15:24:28 scotte + # fixed bugs in return system data structure signal names a.s.hodel@eng.auburn.edu + # + # Revision 1.3 1997/03/03 22:54:42 hodel + # fixed details for update to octave 2.0.x + # a.s.hodel@eng.auburn.edu + + if ((nargin < 3) | (nargin > 4)) + usage("[K,gain, Kc, Kf, Pc, Pf] = h2syn(Asys,nu,ny[,tol])"); + elseif(nargin == 3 ) + [chkdgkf,dgs] = is_dgkf(Asys,nu,ny); + elseif(nargin == 4) + [chkdgkf,dgs] = is_dgkf(Asys,nu,ny,tol); + endif + + if (!chkdgkf ) + disp("h2syn: system does not meet required assumptions") + help is_dgkf + error("h2syn: exit"); + endif + + # extract dgs information + nw = dgs.nw; nu = dgs.nu; + A = dgs.A; Bw = dgs.Bw; Bu = dgs.Bu; + Cz = dgs.Cz; Dzw = dgs.Dzw; Dzu = dgs.Dzu; nz = dgs.nz; + Cy = dgs.Cy; Dyw = dgs.Dyw; Dyu = dgs.Dyu; ny = dgs.ny; + d22nz = dgs.Dyu_nz; + dflg = dgs.dflg; + + if(norm(Dzw,Inf) > norm([Dzw Dzu ; Dyw Dyu],Inf)*1e-12) + warning("h2syn: Dzw nonzero; feedforward not implemented") + Dzw + D = [Dzw Dzu ; Dyw Dyu] + endif + + # recover i/o transformations + Ru = dgs.Ru; Ry = dgs.Ry; + [ncstates, ndstates, nout, nin] = sysdimensions(Asys); + Atsam = sysgettsam(Asys); + [Ast, Ain, Aout] = sysgetsignals(Asys); + + if(dgs.dflg == 0) + Pc = are(A,Bu*Bu',Cz'*Cz); # solve control, filtering ARE's + Pf = are(A',Cy'*Cy,Bw*Bw'); + F2 = -Bu'*Pc; # calculate feedback gains + L2 = -Pf*Cy'; + + AF2 = A + Bu*F2; + AL2 = A + L2*Cy; + CzF2 = Cz + (Dzu/Ru)*F2; + BwL2 = Bw+L2*(Ry\Dyw); + + else + # discrete time solution + error("h2syn: discrete-time case not yet implemented") + Pc = dare(A,Bu*Bu',Cz'*Cz); + Pf = dare(A',Cy'*Cy,Bw*Bw'); + endif + + nn = ncstates + ndstates; + In = eye(nn); + KA = A + Bu*F2 + L2*Cy; + Kc1 = ss2sys(AF2,Bw,CzF2,zeros(nz,nw)); + Kf1 = ss2sys(AL2,BwL2,F2,zeros(nu,nw)); + + g1 = h2norm(Kc1); + g2 = h2norm(Kf1); + + # compute optimal closed loop gain + gain = sqrt ( g1*g1 + g2*g2 ); + + if(nargout) + Kst = strappend(Ast,"_K"); + Kin = strappend(Aout((nout-ny+1):(nout),:),"_K"); + Kout = strappend(Ain((nin-nu+1):(nin),:),"_K"); + + # compute systems for return + K = ss2sys(KA,-L2/Ru,Ry\F2,zeros(nu,ny),Atsam,ncstates,ndstates,Kst,Kin,Kout); + endif + + if (nargout > 2) + #system full information control state names + for ii=1:rows(Ast) + tmp = [dezero(Ast(ii,:)),"_FI"]; + stname2(ii,1:length(tmp)) = tmp; + endfor + + #system full information control input names + for ii=1:rows(Ast) + tmp = [dezero(Ast(ii,:)),"_FI_in"]; + inname2(ii,1:length(tmp)) = tmp; + endfor + + #system full information control output names + for ii=1:(rows(Aout)-ny) + tmp = [dezero(Aout(ii,:)),"_FI_out"]; + outname2(ii,1:length(tmp)) = tmp; + endfor + + nz = rows (Cz); + nw = columns (Bw); + + Kc = ss2sys(AF2, In, CzF2, zeros(nz,nn), Atsam, ... + ncstates, ndstates, stname2, inname2, outname2); + endif + + if (nargout >3) + #fix system state estimator state names + for ii=1:rows(Ast) + tmp = [dezero(Ast(ii,:)),"_Kf"]; + stname3(ii,1:length(tmp)) = tmp; + endfor + + #fix system state estimator input names + for ii=1:rows(Ast) + tmp = [dezero(Ast(ii,:)),"_Kf_noise"]; + inname3(ii,1:length(tmp)) = tmp; + endfor + + #fix system state estimator output names + for ii=1:rows(Ast) + tmp = [dezero(Ast(ii,:)),"_est"]; + outname3(ii,1:length(tmp)) = tmp; + endfor + + Kf = ss2sys(AL2, BwL2, In, zeros(nn,nw),Atsam, ... + ncstates, ndstates, stname3, inname3,outname3); + endif +endfunction diff --git a/scripts/control/hinf_ctr.m b/scripts/control/hinf_ctr.m new file mode 100644 --- /dev/null +++ b/scripts/control/hinf_ctr.m @@ -0,0 +1,140 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function K = hinf_ctr(dgs,F,H,Z,g) + # K = hinf_ctr(dgs,F,H,Z,g) + # + # Called by hinfsyn to compute the H_inf optimal controller. + # + # inputs: + # dgs: data structure returned by is_dgkf + # F, H: feedback and filter gain (not partitioned) + # g: final gamma value + # outputs: + # controller K (system data structure) + # + # Do not attempt to use this at home; no argument checking performed. + + # A. S. Hodel August 1995 + # $Revision: 1.2 $ + # Revised by Kai P Mueller April 1998 to solve the general H_infinity + # problem using unitary transformations Q (on w and z) + # and non-singular transformations R (on u and y). + # $Revision: 1.2 $ + # $Log: hinf_ctr.m,v $ + # Revision 1.2 1998/07/10 21:44:31 hodelas + # d11 -> D11 (was uninitialized) + + nw = dgs.nw; + nu = dgs.nu; + nz = dgs.nz; + ny = dgs.ny; + d22nz = dgs.Dyu_nz; + + B1 = dgs.Bw; + B2 = dgs.Bu; + C1 = dgs.Cz; + C2 = dgs.Cy; + C = [C1; C2]; + D11 = dgs.Dzw; + D12 = dgs.Dzu; + D21 = dgs.Dyw; + D22 = dgs.Dyu; + A = dgs.A; + Ru = dgs.Ru; + Ry = dgs.Ry; + + nout = nz + ny; + nin = nw + nu; + nstates = size(A, 1); + + F11 = F(1:(nw-ny),:); + F12 = F((nw-ny+1):nw,:); + F2 = F((nw+1):nin,:); + H11 = H(:,1:(nz-nu)); + H12 = H(:,(nz-nu+1):nz); + H2 = H(:,(nz+1):nout); + + # D11 partitions + D1111 = D11(1:(nz-nu),1:(nw-ny)); + D1112 = D11(1:(nz-nu),(nw-ny+1):nw); + D1121 = D11((nz-nu+1):nz,1:(nw-ny)); + D1122 = D11((nz-nu+1):nz,(nw-ny+1):nw); + + # D11ik may be the empty matrix, don't calculate with empty matrices + [nd1111,md1111] = size(D1111); + md1112 = length(D1112); + md1121 = length(D1121); + + if ((nd1111 == 0) || (md1112 == 0)) + d11hat = -D1122; + else + xx = inv(g*g*eye(nz-nu) - D1111*D1111'); + d11hat = -D1121*D1111'*xx*D1112 - D1122; + endif + if (md1112 == 0) + d21hat = eye(ny); + elseif (nd1111 == 0) + d21hat = chol(eye(ny) - D1112'*D1112/g/g); + else + xx = inv(g*g*eye(nz-nu) - D1111*D1111'); + xx = eye(ny) - D1112'*xx*D1112; + d21hat = chol(xx); + endif + if (md1121 == 0) + d12hat = eye(nu); + elseif (md1111 == 0) + d12hat = chol(eye(nu) - D1121*D1121'/g/g)'; + else + xx = inv(g*g*eye(nw-ny) - D1111'*D1111); + xx = eye(nu)-D1121*xx*D1121'; + d12hat = chol(xx)'; + endif + + b2hat = (B2+H12)*d12hat; + c2hat = -d21hat*(C2+F12)*Z; + b1hat = -H2 + (b2hat/d12hat)*d11hat; + c1hat = F2*Z + (d11hat/d21hat)*c2hat; + ahat = A + H*C + (b2hat/d12hat)*c1hat; + + # rescale controller by Ru and Ry + b1hat = b1hat/Ry; + c1hat = Ru\c1hat; + bhat = [b1hat b2hat]; + chat = [c1hat; c2hat]; + dhat = [Ru\d11hat/Ry Ru\d12hat; d21hat/Ry 0*d11hat']; + + # non-zero D22 is a special case + if (d22nz) + if (rank(eye(nu) + d11hat*D22) < nu) + error(" *** cannot compute controller for D22 non-zero."); + endif + + d22new = [D22 zeros(ny,ny); zeros(nu,nu) 0*D22']; + xx = inv(eye(nu+ny) + d22new*dhat); + mhat = inv(eye(nu+ny) + dhat*d22new); + ahat = ahat - bhat*((eye(nu+ny)-xx)/dhat)*chat; + bhat = bhat*xx; + chat = mhat*chat; + dhat = dhat*xx; + + endif + + K = ss2sys(ahat,bhat(:,1:ny),chat(1:nu,:),dhat(1:nu,1:ny)); + +endfunction diff --git a/scripts/control/hinfdemo.m b/scripts/control/hinfdemo.m new file mode 100644 --- /dev/null +++ b/scripts/control/hinfdemo.m @@ -0,0 +1,328 @@ +# hinfdemo H_infinity design demos for continuous SISO and MIMO +# systems and a discrete system. +# The SISO system is difficult to control because +# it is non minimum phase and unstable. The second +# design example controls the "jet707" plant, the +# linearized state space model of a Boeing 707-321 +# aircraft at v=80m/s (M = 0.26, Ga0 = -3 deg, +# alpha0 = 4 deg, kappa = 50 deg). +# inputs: (1) thrust and (2) elevator angle +# outputs: (1) airspeed and (2) pitch angle. +# The discrete system is a stable and second order. +# +# This is a script file for Octave. +# +# SISO plant: +# +# s - 2 +# G(s) = -------------- +# (s + 2)(s - 1) +# +# +----+ +# -------------------->| W1 |---> v1 +# z | +----+ +# ----|-------------+ || T || => min. +# | | vz infty +# | +---+ v y +----+ +# u *--->| G |--->O--*-->| W2 |---> v2 +# | +---+ | +----+ +# | | +# | +---+ | +# -----| K |<------- +# +---+ +# +# W1 und W2 are the robustness and performance weighting +# functions +# +# MIMO plant: +# The optimal controller minimizes the H_infinity norm of the +# augmented plant P (mixed-sensitivity problem): +# +# w +# 1 -----------+ +# | +----+ +# +---------------------->| W1 |----> z1 +# w | | +----+ +# 2 ------------------------+ +# | | | +# | v +----+ v +----+ +# +--*-->o-->| G |-->o--*-->| W2 |---> z2 +# | +----+ | +----+ +# | | +# ^ v +# u (from y (to K) +# controller +# K) +# +# +# + + + + +# | z | | w | +# | 1 | | 1 | +# | z | = [ P ] * | w | +# | 2 | | 2 | +# | y | | u | +# + + + + +# +# DISCRETE SYSTEM: +# This is not a true discrete design. The design is carried out +# in continuous time while the effect of sampling is described by +# a bilinear transformation of the sampled system. +# This method works quite well if the sampling period is "small" +# compared to the plant time constants. +# +# The continuous plant: +# 1 +# G (s) = -------------- +# k (s + 2)(s + 1) +# +# is discretised with a ZOH (Sampling period = Ts = 1 second): +# +# 0.199788z + 0.073498 +# G(s) = -------------------------- +# (z - 0.36788)(z - 0.13534) +# +# +----+ +# -------------------->| W1 |---> v1 +# z | +----+ +# ----|-------------+ || T || => min. +# | | vz infty +# | +---+ v +----+ +# *--->| G |--->O--*-->| W2 |---> v2 +# | +---+ | +----+ +# | | +# | +---+ | +# -----| K |<------- +# +---+ +# +# W1 and W2 are the robustness and performancs weighting +# functions + + +# Kai P. Mueller 30-APR-1998 = 1) + if ((yn(1) == "y") || (yn(1) == 'Y')) + disp(" o step responses of T and KS..."); + GW = buildssic([1 2; 2 1], [], [1 2], [-2], G, K); + figure(1); + step(GW, 1, 10); + endif + endif + + case (2) + # mimo + disp(" "); + disp(" -----------------------------------------------"); + disp(" H_inf optimal control for the jet707 plant"); + disp(" -----------------------------------------------"); + disp(" "); + + # Weighting function on u (robustness weight) + ww1 = wgt1o(0.01,5,0.9); + ww2 = wgt1o(0.01,5,2.2); + W1 = buildssic([1 0;2 0],[],[1 2],[1 2],ww1,ww2); + # Weighting function on y (performance weight) + ww1 = wgt1o(250,0.1,0.0001); + ww2 = wgt1o(250,0.1,0.0002); + W2 = buildssic([1 0;2 0],[],[1 2],[1 2],ww1,ww2); + # plant (2 x 2 system) + G = jet707; + + disp(" o forming P..."); + One = ugain(2); + Clst = [1 7; 2 8; 3 7; 4 8; 5 1; 6 2]; + P = buildssic(Clst,[5 6],[3:6 9 10],[1 2 5:8],G,W1,W2,One); + + disp(" "); + disp(" o controller design..."); + K = hinfsyn(P, 2, 2, 0.25, 10.0, 0.005); + + disp(" "); + yn = input(" * Plot closed loop step responses? [n]: ","S"); + if (length(yn) >= 1) + if ((yn(1) == "y") || (yn(1) == 'Y')) + disp(" o step responses of T and KS..."); + GW = buildssic([1 3;2 4;3 1;4 2],[],[1 2 3 4],[-3 -4],G,K); + + disp(" "); + disp(" FIGURE 1: speed refence => 1, pitch angle ref. => 0"); + disp(" ==================================================="); + disp(" y1: speed (should be 1)"); + disp(" y2: pitch angle (should remain 0)"); + disp(" y3: thrust (should be a slow transient)"); + disp(" y6: elevator (should be a faster transient)"); + disp(" "); + disp(" FIGURE 2: speed refence => 0, pitch angle ref. => 1"); + disp(" ==================================================="); + disp(" y1: speed (should remain 0)"); + disp(" y2: pitch angle (should be 1)"); + disp(" y3: thrust (should be a slow transient)"); + disp(" y6: elevator (should be a faster transient)"); + disp(" "); + figure(1) + step(GW); + figure(2) + step(GW,2); + endif + endif + + case (3) + # discrete + disp(" "); + disp(" --------------------------------------------------"); + disp(" Discrete H_infinity optimal control for the plant:"); + disp(" "); + disp(" 0.199788z + 0.073498"); + disp(" G(s) = --------------------------"); + disp(" (z - 0.36788)(z - 0.13533)"); + disp(" --------------------------------------------------"); + disp(" "); + + # sampling time + Ts = 1.0; + # weighting on actuator value u + W1 = wgt1o(0.1, 200.0, 50.0); + # weighting on controlled variable y + W2 = wgt1o(350.0, 0.05, 0.0002); + # omega axis + ww = logspace(-4.99, 3.99, 100); + if (columns(ww) > 1); ww = ww'; endif + + # continuous plant + G = tf2sys(2,[1 3 2]); + # discrete plant with zoh + Gd = c2d(G, Ts); + # w-plane (continuous representation of the sampled system) + Gw = d2c(Gd, "bi"); + + disp(" "); + disp(" o building P..."); + # need One as the pseudo transfer function One = 1 + One = ugain(1); + psys = buildssic([1 4;2 4;3 1],[3],[2 3 5],[3 4],Gw,W1,W2,One); + disp(" o controller design..."); + [K, gfin, GWC] = hinfsyn(psys, 1, 1, 0.1, 10.0, 0.02); + + disp(" "); + fig_n = 1; + yn = input(" * Plot magnitudes of W1KS and W2S? [n]: ","S"); + if (length(yn) >= 1) + if ((yn(1) == "y") || (yn(1) == 'Y')) + disp(" o magnitudes of W1KS and W2S..."); + gwx = sysprune(GWC, 1, 1); + mag1 = bode(gwx, ww); + if (columns(mag1) > 1); mag1 = mag1'; endif + gwx = sysprune(GWC, 2, 1); + mag2 = bode(gwx, ww); + if (columns(mag2) > 1); mag2 = mag2'; endif + figure(fig_n) + fig_n = fig_n + 1; + gset grid + loglog(ww, [mag1 mag2]); + endif + endif + + Kd = c2d(K, "bi", Ts); + GG = buildssic([1 2; 2 1], [], [1 2], [-2], Gd, Kd); + disp(" o closed loop poles..."); + damp(GG); + + disp(" "); + yn = input(" * Plot closed loop step responses? [n]: ","S"); + if (length(yn) >= 1) + if ((yn(1) == "y") || (yn(1) == 'Y')) + disp(" o step responses of T and KS..."); + figure(fig_n) + step(GG, 1, 10); + endif + endif + +endswitch + +disp(" o hinfdemo terminated successfully."); + +# KPM-hinfdemo/End diff --git a/scripts/control/hinfnorm.m b/scripts/control/hinfnorm.m new file mode 100644 --- /dev/null +++ b/scripts/control/hinfnorm.m @@ -0,0 +1,135 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [g gmin gmax] = hinfnorm(sys,tol,gmin,gmax,ptol) + # Usage: [g gmin gmax] = hinfnorm(sys[,tol,gmin,gmax,ptol]) + # + # Computes the H infinity norm of a system data structure + # sys = system data structure + # tol = H infinity norm search tolerance (default: 0.001) + # gmin = minimum value for norm search (default: 1e-9) + # gmax = maximum value for norm search (default: 1e+9) + # ptol: pole tolerance: + # if sys is continuous, poles with + # |real(pole)| < ptol*||H|| (H is appropriate Hamiltonian) + # are considered to be on the imaginary axis. + # if sys is discrete, poles with + # |abs(pole)-1| < ptol*||[s1,s2]|| (appropriate symplectic pencil) + # are considered to be on the unit circle + # Default: 1e-9 + # + # References: + # Doyle, Glover, Khargonekar, Francis, "State space solutions to standard + # H2 and Hinf control problems", IEEE TAC August 1989 + # Iglesias and Glover, "State-Space approach to discrete-time Hinf control," + # Int. J. Control, vol 54, #5, 1991 + # Zhou, Doyle, Glover, "Robust and Optimal Control," Prentice-Hall, 1996 + # $Revision: 1.6 $ + + if((nargin == 0) || (nargin > 4)) + usage("[g gmin gmax] = hinfnorm(sys[,tol,gmin,gmax,ptol])"); + elseif(!is_struct(sys)) + error("Sys must be a system data structure"); + endif + + # set defaults where applicable + if(nargin < 5) + ptol = 1e-9; # pole tolerance + endif + if(nargin < 4) + gmax = 1e9; # max gain value + endif + + dflg = is_digital(sys); + sys = sysupdate(sys,"ss"); + [A,B,C,D] = sys2ss(sys); + [n,nz,m,p] = sysdimensions(sys); + + # eigenvalues of A must all be stable + if(!is_stable(sys)) + warning(["hinfnorm: unstable system (is_stable, ptol=",num2str(ptol), ... + "), returning Inf"]); + endif + + Dnrm = norm(D); + if(nargin < 3) + gmin = max(1e-9,Dnrm); # min gain value + elseif(gmin < Dnrm) + warning(["hinfnorm: setting Gmin=||D||=",num2str(Dnrm)]); + endif + + if(nargin < 2) + tol = 0.001; # convergence measure for gmin, gmax + endif + + # check for scalar input arguments 2...5 + if( ! (is_scalar(tol) && is_scalar(gmin) + && is_scalar(gmax) && is_scalar(ptol)) ) + error("hinfnorm: tol, gmin, gmax, ptol must be scalars"); + endif + + In = eye(n+nz); + Im = eye(m); + Ip = eye(p); + # find the Hinf norm via binary search + while((gmax/gmin - 1) > tol) + g = (gmax+gmin)/2; + + if(dflg) + # multiply g's through in formulas to avoid extreme magnitudes... + Rg = g^2*Im - D'*D; + Ak = A + (B/Rg)*D'*C; + Ck = g^2*C'*((g^2*Ip-D*D')\C); + + # set up symplectic generalized eigenvalue problem per Iglesias & Glover + s1 = [Ak , zeros(nz) ; -Ck, In ]; + s2 = [In, -(B/Rg)*B' ; zeros(nz) , Ak' ]; + + # guard against roundoff again: zero out extremely small values + # prior to balancing + s1 = s1 .* (abs(s1) > ptol*norm(s1,"inf")); + s2 = s2 .* (abs(s2) > ptol*norm(s2,"inf")); + [cc,dd,s1,s2] = balance(s1,s2); + [qza,qzb,zz,pls] = qz(s1,s2,"S"); # ordered qz decomposition + eigerr = abs(abs(pls)-1); + normH = norm([s1,s2]); + Hb = [s1 s2]; + + # check R - B' X B condition (Iglesias and Glover's paper) + X = zz((nz+1):(2*nz),1:nz)/zz(1:nz,1:nz); + dcondfailed = min(real( eig(Rg - B'*X*B)) < ptol); + else + Rinv = inv(g*g*Im - (D' * D)); + H = [A + B*Rinv*D'*C, B*Rinv*B'; ... + -C'*(Ip + D*Rinv*D')*C, -(A + B*Rinv*D'*C)']; + # guard against roundoff: zero out extremely small values prior + # to balancing + H = H .* (abs(H) > ptol*norm(H,"inf")); + [DD,Hb] = balance(H); + pls = eig(Hb); + eigerr = abs(real(pls)); + normH = norm(H); + dcondfailed = 0; # digital condition; doesn't apply here + endif + if( (min(eigerr) <= ptol * normH) | dcondfailed) + gmin = g; + else + gmax = g; + endif + endwhile +endfunction diff --git a/scripts/control/hinfsyn.m b/scripts/control/hinfsyn.m new file mode 100644 --- /dev/null +++ b/scripts/control/hinfsyn.m @@ -0,0 +1,390 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [K,g,GW,Xinf,Yinf] = hinfsyn(Asys,nu,ny,gmin,gmax,gtol,ptol,tol) + # [K,g,GW,Xinf,Yinf] = hinfsyn(Asys,nu,ny,gmin,gmax,gtol[,ptol,tol]) + # + # [1] Doyle, Glover, Khargonekar, Francis, "State Space Solutions + # to Standard H2 and Hinf Control Problems," IEEE TAC August 1989 + # + # [2] Maciejowksi, J.M.: "Multivariable feedback design," + # Addison-Wesley, 1989, ISBN 0-201-18243-2 + # + # [3] Keith Glover and John C. Doyle: "State-space formulae for all + # stabilizing controllers that satisfy and h-infinity-norm bound + # and relations to risk sensitivity," + # Systems & Control Letters 11, Oct. 1988, pp 167-172. + # + # inputs: input system is passed as either + # Asys: system data structure (see ss2sys, sys2ss) + # - controller is implemented for continuous time systems + # - controller is NOT implemented for discrete time systems + # nu: number of controlled inputs + # ny: number of measured outputs + # gmin: initial lower bound on H-infinity optimal gain + # gmax: initial upper bound on H-infinity optimal gain + # gtol: gain threshhold. Routine quits when gmax/gmin < 1+tol + # ptol: poles with abs(real(pole)) < ptol*||H|| (H is appropriate + # Hamiltonian) are considered to be on the imaginary axis. + # Default: 1e-9 + # tol: threshhold for 0. Default: 200*eps + # + # gmax, gmin, gtol, and tol must all be postive scalars. + # + # outputs: + # K: system controller + # g: designed gain value + # GW: closed loop system + # Xinf: ARE solution matrix for regulator subproblem + # Yinf: ARE solution matrix for filter subproblem + + + # A. S. Hodel August 1995 + # Updated for Packed system structures December 1996 by John Ingram + # $Revision: 1.5 $ + # + # Revised by Kai P Mueller April 1998 to solve the general H_infinity + # problem using unitary transformations Q (on w and z) + # and non-singular transformations R (on u and y). + # $Revision: 1.5 $ + # $Log: hinfsyn.m,v $ + # Revision 1.5 1998/08/24 15:50:10 hodelas + # updated documentation + # + # Revision 1.3 1998/07/01 16:23:37 hodelas + # Updated c2d, d2c to perform bilinear transforms. + # Updated several files per bug updates from users. + # + # Revision 1.2 1998/06/25 12:35:38 hodelas + # fixed controller input/output signal name code + # + # Revision 1.4 1998/06/25 12:40:35 hodel + # fixed error in input/output names in the controller + # + # Revision 1.3 1998/05/05 17:03:59 scotte + # update 5 May 1998 by Kai Mueller + # +# Revision 1.2 1998/05/05 08:57:16 mueller +# comments added +# + + old_page_val = page_screen_output; + page_screen_output = 0; + + if( (nargin < 1) | (nargin > 8) ) + usage("[K,g,GW,Xinf,Yinf] = hinfsyn(Asys,nu,ny,gmin,gmax,gtol,ptol,tol)"); + endif + # set default arguments + if(nargin < 8) + tol = 200*eps; + elseif(!is_sample(tol)) + error("tol must be a positive scalar.") + endif + if(nargin < 7) + ptol = 1e-9; + elseif(!is_sample(ptol)) + error("hinfsyn: ptol must be a positive scalar"); + endif + + if(!is_sample(gmax) | !is_sample(gmin) | !is_sample(gtol) ) + error(["hinfsyn: gmax=",num2str(gmax),", gmin=",num2str(gmin), ... + "gtol=",num2str(gtol), " must be positive scalars."]) + endif + + [chkdgkf,dgs] = is_dgkf(Asys,nu,ny,tol); + + if (! chkdgkf ) + disp("hinfsyn: system does not meet required assumptions") + help is_dgkf + error("hinfsyn: exit"); + endif + + # extract dgs information + nw = dgs.nw; nu = dgs.nu; + A = dgs.A; B1 = dgs.Bw; B2 = dgs.Bu; + C1 = dgs.Cz; D11 = dgs.Dzw; D12 = dgs.Dzu; nz = dgs.nz; + C2 = dgs.Cy; D21 = dgs.Dyw; D22 = dgs.Dyu; ny = dgs.ny; + d22nz = dgs.Dyu_nz; + dflg = dgs.dflg; + + # recover i/o transformations + R12 = dgs.Ru; R21 = dgs.Ry; + [ncstates, ndstates, nin, nout] = sysdimensions(Asys); + Atsam = sysgettsam(Asys); + [Ast, Ain, Aout] = sysgetsignals(Asys); + + BB = [B1 B2]; + CC = [C1 ; C2]; + DD = [D11 D12 ; D21 D22]; + + if (dflg == 0) + n = ncstates; + # perform binary search to find gamma min + ghi = gmax; + # derive a lower lower bound for gamma from D matrices + xx1 = norm((eye(nz) - (D12/(D12'*D12))*D12')*D11); + xx2 = norm(D11*(eye(nw)-(D21'/(D21*D21'))*D21)); + glo = max(xx1, xx2); + if (glo > gmin) + disp(" *** D matrices indicate a greater value of gamma min."); + fprintf(" gamma min (%f) superseeded by %f.", gmin, glo); + glo = xx1; + else + glo = gmin; + endif + if (glo > ghi) + fprintf(" *** lower bound of gamma greater than upper bound(%f)", ... + glo, ghi); + disp(" *** unable to continue, Goodbye."); + return; + endif + + de = ghi - glo; + g = glo; + search_state = 0; + iteration_finished = 0; + disp(" o structural tests passed, start of iteration..."); + disp(" o <-> test passed # <-> test failed - <-> cannot test"); + printf("----------------------------------------"); + printf("--------------------------------------\n"); + + # ------123456789012345678901234567890123456789012345678901234567890 + printf(" .........X......... .........Y......... "); + printf(".Z. PASS REMARKS\n"); + printf(" ga iax nev ene sym pos iax nev ene sym pos "); + printf("rho y/n ======>\n"); + printf("----------------------------------------"); + printf("--------------------------------------\n"); + + # now do the search + while (!iteration_finished) + switch (search_state) + case (0) + g = ghi; + case (1) + g = glo; + case (2) + g = 0.5 * (ghi + glo); + otherwise + error(" *** This should never happen!"); + endswitch + printf("%10.4f ", g); + + # computing R and R~ + d1dot = [D11 D12]; + R = zeros(nin, nin); + R(1:nw,1:nw) = -g*g*eye(nw); + R = R + d1dot' * d1dot; + ddot1 = [D11; D21]; + Rtilde = zeros(nout, nout); + Rtilde(1:nz,1:nz) = -g*g*eye(nz); + Rtilde = Rtilde + ddot1 * ddot1'; + + # build hamiltonian Ha for X_inf + xx = ([BB; -C1'*d1dot]/R) * [d1dot'*C1 BB']; + Ha = [A 0*A; -C1'*C1 -A'] - xx; + x_ha_err = 0; + # copied from from are(...)... + [d, Ha] = balance(Ha); + [u, s] = schur(Ha, "A"); + rev = real(eig(s)); + if (any(abs(rev) <= ptol)) + # eigenvalues near the imaginary axis + x_ha_err = 1; + elseif (sum(rev > 0) != sum(rev < 0)) + # unequal number of positive and negative eigenvalues + x_ha_err = 2; + else + # compute positive Riccati equation solution + u = d * u; + Xinf = u(n+1:2*n,1:n) / u(1:n,1:n); + if (!all(all(finite(Xinf)))) + x_ha_err = 3; + elseif (norm(Xinf-Xinf') >= 10*ptol) + # solution not symmetric + x_ha_err = 4; + else + # positive semidefinite? + rev = eig(Xinf); + if (any(rev <= -ptol)) + x_ha_err = 5; + endif + endif + endif + + # build hamiltonian Ha for Y_inf + xx = ([CC'; -B1*ddot1']/Rtilde) * [ddot1*B1' CC]; + Ha = [A' 0*A; -B1*B1' -A] - xx; + y_ha_err = 0; + # copied from from are(...)... + [d, Ha] = balance(Ha); + [u, s] = schur(Ha, "A"); + rev = real(eig(s)); + if (any(abs(rev) <= ptol)) + # eigenvalues near the imaginary axis + y_ha_err = 1; + elseif (sum(rev > 0) != sum(rev < 0)) + # unequal number of positive and negative eigenvalues + y_ha_err = 2; + else + # compute positive Riccati equation solution + u = d * u; + Yinf = u(n+1:2*n,1:n) / u(1:n,1:n); + if (!all(all(finite(Yinf)))) + y_ha_err = 3; + elseif (norm(Yinf-Yinf') >= 10*ptol) + # solution not symmetric + x_ha_err = 4; + else + # positive semidefinite? + rev = eig(Yinf); + if (any(rev <= -ptol)) + y_ha_err = 5; + endif + endif + endif + + # assume failure for this gamma + passed = 0; + if (!x_ha_err && !y_ha_err) + # test spectral radius condition + rho = max(abs(eig(Xinf * Yinf))); + if (rho < g*g) + # spectral radius condition passed + passed = 1; + endif + endif + + switch (x_ha_err) + case (0) + # iax nev ene sym pos + printf(" o o o o o "); + xerr = " "; + case (1) + printf(" # - - - - "); + xerr = "X im.eig."; + case (2) + printf(" o # - - - "); + xerr = "Hx not Ham."; + case (3) + printf(" o o # - - "); + xerr = "X inf.eig."; + case (4) + printf(" o o o # - "); + xerr = "X not symm."; + case (5) + printf(" o o o o # "); + xerr = "X not pos."; + otherwise + error(" *** Xinf fail: this should never happen!"); + endswitch + switch (y_ha_err) + case (0) + # iax nev ene sym pos rho + if (passed) + printf(" o o o o o o y all tests passed.\n"); + elseif (x_ha_err) + printf(" o o o o o - n %s\n", xerr); + else + printf(" o o o o o # n rho=%f.\n", rho); + endif + case (1) + printf(" # - - - - - n %s/Y im. eig.", xerr); + case (2) + printf(" o # - - - - n %s/Hy not Ham.", xerr); + case (3) + printf(" o o # - - - n %s/Y inf.eig.", xerr); + case (4) + printf(" o o o # - - n %s/Y not symm.", xerr); + case (5) + printf(" o o o o # - n %s/Y not pos.", xerr); + otherwise + error(" *** Yinf fail: this should never happen!"); + endswitch + + + if (passed && (de/g < gtol)) + search_state = 3; + endif + + switch (search_state) + case (0) + if (!passed) + # upper bound must pass but did not + fprintf(" *** the upper bound of gamma (%f) is too small.\n", g); + iteration_finished = 2; + else + search_state = 1; + endif + case (1) + if (!passed) + search_state = 2; + else + # lower bound must not pass but passed + fprintf(" *** the lower bound of gamma (%f) passed.\n", g); + iteration_finished = 3; + endif + case (2) + if (!passed) + glo = g; + else + ghi = g; + endif + de = ghi - glo; + case (3) + # done + iteration_finished = 1; + otherwise + error(" *** This should never happen!"); + endswitch + endwhile + + printf("----------------------------------------"); + printf("--------------------------------------\n"); + if (iteration_finished != 1) + K = []; + else + # success: compute controller + fprintf(" hinfsyn final: glo=%f ghi=%f, test gain g=%f\n", \ + glo, ghi, g); + printf("----------------------------------------"); + printf("--------------------------------------\n"); + Z = inv(eye(ncstates) - Yinf*Xinf/g/g); + F = -R \ (d1dot'*C1 + BB'*Xinf); + H = -(B1*ddot1' + Yinf*CC') / Rtilde; + K = hinf_ctr(dgs,F,H,Z,g); + + Kst = strappend(Ast,"_K"); + Kin = strappend(Aout((nout-ny+1):(nout),:),"_K"); + Kout = strappend(Ain((nin-nu+1):(nin),:),"_K"); + [Ac, Bc, Cc, Dc] = sys2ss(K); + K = ss2sys(Ac,Bc,Cc,Dc,Atsam,ncstates,ndstates,Kst,Kin,Kout); + if (nargout >= 3) + GW = starp(Asys, K); + endif + endif + + elseif(ndstates) + + # discrete time solution + error("hinfsyn: discrete-time case not yet implemented") + + endif + + page_screen_output = old_page_val; +endfunction diff --git a/scripts/control/hinfsyn_chk.m b/scripts/control/hinfsyn_chk.m new file mode 100644 --- /dev/null +++ b/scripts/control/hinfsyn_chk.m @@ -0,0 +1,88 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [retval,Pc,Pf] = hinfsyn_chk(A,B1,B2,C1,C2,D12,D21,g,ptol) + # [retval,Pc,Pf] = hinfsyn_chk(A,B1,B2,C1,C2,D12,D21,g,ptol) + # + # Called by hinfsyn to see if gain g satisfies conditions in Theorem 3 of + # Doyle, Glover, Khargonekar, Francis, "State Space Solutions to Standard + # H2 and Hinf Control Problems", IEEE TAC August 1989 + # + # inputs: + # g = candidate gain level + # ptol: as in hinfsyn + # remaining parameters as returned by is_dgkf + # outputs: + # retval: = 1 if g exceeds optimal Hinf closed loop gain, else 0 + # Pc: solution of "regulator" H-inf ARE + # Pf: solution of "filter" H-inf ARE + # + # Do not attempt to use this at home; no argument checking performed. + + # A. S. Hodel August 1995 + # $Revision: 1.1 $ + + Pc = Pf = []; + + # Construct the two Hamiltonians + g2 = 1/(g*g); + Hc = [ A , g2*B1*B1' - B2*B2'; -C1'*C1 , -A']; + Hf = [ A' , g2*C1'*C1 - C2'*C2; -B1*B1' , -A]; + + # check if Hc, Hf are in dom(Ric) + Hcminval = min(abs(real(eig(Hc)))); + Hfminval = min(abs(real(eig(Hf)))); + if(Hcminval < ptol); + disp("hinfsyn_chk: Hc is not in dom(Ric)"); + retval = 0; + return + endif + if(Hfminval < ptol) + disp("hinfsyn_chk: Hf is not in dom(Ric)"); + retval = 0; + return + endif + + #Solve ARE's + Pc = are(A, B2*B2'-g2*B1*B1',C1'*C1); + Pf = are(A',C2'*C2-g2*C1'*C1,B1*B1'); + + Pceig = eig(Pc); + Pfeig = eig(Pf); + Pcfeig = eig(Pc*Pf); + + if(min(Pceig) < -ptol) + disp("hinfsyn_chk: Pc is not >= 0"); + retval = 0; + return + endif + if(min(Pfeig) < -ptol) + disp("hinfsyn_chk: Pf is not >= 0"); + retval = 0; + return + endif + if(max(abs(Pcfeig)) >= g*g) + disp("hinfsyn_chk: rho(Pf*Pc) is not < g^2"); + retval = 0; + return + endif + + # all conditions met. + retval = 1; + +endfunction diff --git a/scripts/control/impulse.m b/scripts/control/impulse.m new file mode 100644 --- /dev/null +++ b/scripts/control/impulse.m @@ -0,0 +1,86 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [y, t] = impulse(sys, inp, tstop, n) +# step: Impulse response for a linear system. +# The system can be discrete or multivariable (or both). +# +# [y, t] = impulse(sys[, inp, tstop, n]) +# Produces a plot or the step response data for system sys. +# +# The argument tstop (scalar value) denotes the time when the +# simulation should end. The Parameter n is the number of data values. +# Both parameters tstop and n can be ommitted and will be +# computed from the eigenvalues of the A-Matrix. +# +# When the step function is invoked with the output parameter y +# a plot is not displayed. +# +# See also: step, stepimp + +# Written by Kai P. Mueller October 2, 1997 +# based on lsim.m of Scottedward Hodel +# modified by +# $Revision: 1.1.1.1 $ +# $Log: impulse.m,v $ +# Revision 1.1.1.1 1998/05/19 20:24:07 jwe +# +# Revision 1.3 1997/12/01 16:51:50 scotte +# updated by Mueller 27 Nov 97 +# +# Revision 1.1 1997/11/11 17:33:06 mueller +# Initial revision +# + + if((nargin < 1) || (nargin > 4)) + usage("[y, u] = impulse(sys[, inp, tstop, n])"); + endif + + if(nargout > 2) + usage("[y, u] = impulse(sys[, inp, tstop, n])"); + endif + + if(!is_struct(sys)) + error("impulse: sys must be a system data structure."); + endif + + if (nargout == 0) + switch (nargin) + case (1) + stepimp(2, sys); + case (2) + stepimp(2, sys, inp); + case (3) + stepimp(2, sys, inp, tstop); + case (4) + stepimp(2, sys, inp, tstop, n); + endswitch + else + switch (nargin) + case (1) + [y, t] = stepimp(2, sys); + case (2) + [y, t] = stepimp(2, sys, inp); + case (3) + [y, t] = stepimp(2, sys, inp, tstop); + case (4) + [y, t] = stepimp(2, sys, inp, tstop, n); + endswitch + endif + +endfunction diff --git a/scripts/control/is_abcd.m b/scripts/control/is_abcd.m new file mode 100644 --- /dev/null +++ b/scripts/control/is_abcd.m @@ -0,0 +1,106 @@ +# Copyright (C) 1997 Kai P. Mueller +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function retval = is_abcd(a, b, c, d) + # ------------------------------------------------------ + # retval = is_abcd(a [, b, c, d]) + # Returns retval = 1 if the dimensions of a, b, c, d + # are compatible, otherwise retval = 0. + # The matrices b, c, or d may be omitted. + # ------------------------------------------------------ + # + # see also: abcddim + + # Written by Kai P. Mueller November 4, 1997 + # based on is_controllable.m of Scottedward Hodel + # modified by + # $Revision: 1.1.1.1 $ + # $Log: is_abcd.m,v $ + # Revision 1.1.1.1 1998/05/19 20:24:07 jwe + # + # Revision 1.1 1997/12/01 16:51:50 scotte + # Initial revision + # +# Revision 1.1 1997/11/25 11:17:41 mueller +# Initial revision +# + + retval = 0; + switch (nargin) + case (1) + # A only + [na, ma] = size(a); + if (na != ma) + disp("Matrix A ist not square.") + endif + case (2) + # A, B only + [na, ma] = size(a); [nb, mb] = size(b); + if (na != ma) + disp("Matrix A ist not square.") + return; + endif + if (na != nb) + disp("A and B column dimension different.") + return; + endif + case (3) + # A, B, C only + [na, ma] = size(a); [nb, mb] = size(b); [nc, mc] = size(c); + if (na != ma) + disp("Matrix A ist not square.") + return; + endif + if (na != nb) + disp("A and B column dimensions not compatible.") + return; + endif + if (ma != mc) + disp("A and C row dimensions not compatible.") + return; + endif + case (4) + # all matrices A, B, C, D + [na, ma] = size(a); [nb, mb] = size(b); + [nc, mc] = size(c); [nd, md] = size(d); + if (na != ma) + disp("Matrix A ist not square.") + return; + endif + if (na != nb) + disp("A and B column dimensions not compatible.") + return; + endif + if (ma != mc) + disp("A and C row dimensions not compatible.") + return; + endif + if (mb != md) + disp("B and D row dimensions not compatible.") + return; + endif + if (nc != nd) + disp("C and D column dimensions not compatible.") + return; + endif + otherwise + usage("retval = is_abcd(a [, b, c, d])") + endswitch + # all tests passed, signal ok. + retval = 1; +endfunction diff --git a/scripts/control/is_controllable.m b/scripts/control/is_controllable.m --- a/scripts/control/is_controllable.m +++ b/scripts/control/is_controllable.m @@ -1,76 +1,96 @@ -## Copyright (C) 1996, 1997 John W. Eaton -## -## 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-1307, USA. - -## Usage: is_controllable (a, b {,tol}) -## -## Returns 1 if the pair (a, b) is controllable, or 0 if not. -## -## See also: size, rows, columns, length, is_matrix, is_scalar, is_vector -## -## This should really use the method below, but I'm being lazy for now: -## -## Controllability is determined by applying Arnoldi iteration with -## complete re-orthogonalization to obtain an orthogonal basis of the -## Krylov subspace. -## -## (FIX ME... The Krylov subspace approach is not done yet!) -## n-1 -## span ([b,a*b,...,a^ b]). -## -## tol is a roundoff paramter, set to 2*eps if omitted. +# Copyright (C) 1993, 1994, 1995 John W. Eaton +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. -## Author: A. S. Hodel -## Created: August 1993 -## Adapted-By: jwe - -function retval = is_controllable (a, b, tol) - - if (nargin == 2 || nargin == 3) - - n = is_square (a); - [nr, nc] = size (b); - - if (n == 0 || n != nr || nc == 0) - retval = 0; - else +function [retval,U] = is_controllable (a, b, tol) +# [retval, U] = is_controllable (a, b {,tol}) +# = is_controllable (sys{, tol}) +# Returns retval=1 if the system sys or the pair (a, b) is controllable +# 0 if not. +# U is an orthogonal basis of the controllable subspace. +# +# Controllability is determined by applying Arnoldi iteration with +# complete re-orthogonalization to obtain an orthogonal basis of the +# Krylov subspace. +# +# span ([b,a*b,...,a^ b]). +# +# tol is a roundoff paramter, set to 10*eps if omitted. +# +# See also: size, rows, columns, length, is_matrix, is_scalar, is_vector +# is_observable, is_stabilizable, is_detectable, krylov, krylovb - m = b; - tmp = b; - for ii = 1:(n-1) - tmp = a * tmp; - m = [m, tmp]; - endfor - - ## If n is of any significant size, m will be low rank, so be careful! +# Written by A. S. Hodel (scotte@eng.auburn.edu) August, 1993. +# Updated by A. S. Hodel (scotte@eng.auburn.edu) Aubust, 1995 to use krylovb +# Updated by John Ingram (ingraje@eng.auburn.edu) July, 1996 for packed systems +# SYS_INTERNAL accesses members of packed system structure +# $Revision: 1.14 $ - if (nargin == 3) - if (is_scalar (tol)) - retval = (rank (m, tol) == n); - else - error ("is_controllable: tol must be a scalar"); - endif - else - retval = (rank (m) == n); - endif + deftol = 1; # assume default tolerance + if(nargin < 1 | nargin > 3) + usage(sprintf("[retval,U] = %s\n\t%s", "is_controllable(a {, b ,tol})", ... + "is_controllable(sys{,tol})")); + elseif(is_struct(a)) + # system structure passed. + sys = sysupdate(a,"ss"); + [a,bs] = sys2ss(sys); + if(nargin > 2) + usage("[retval,U] = is_controllable(sys{,tol})"); + elseif(nargin == 2) + tol = b; % get tolerance + deftol = 0; endif + b = bs; else - usage ("is_controllable (a, b)"); + # a,b arguments sent directly. + if(nargin < 2) + usage("[retval,U] = is_controllable(a {, b ,tol})"); + else + deftol = 1; + endif endif + # check for default tolerance + if(deftol) tol = 1000*eps; endif + + # check tol dimensions + if( !is_sample(tol) ) + error("is_controllable: tol must be a positive scalar!"); + endif + + # check dimensions compatibility + n = is_square (a); + [nr, nc] = size (b); + + if (n == 0 | n != nr | nc == 0) + warning(["is_controllable: a=(",num2str(rows(a)),"x", ... + num2str(columns(a)),"), b=(",num2str(nr),"x",num2str(nc),")"]) + retval = 0; + else + # call block-krylov subspace routine to get an orthogonal basis + # of the controllable subspace. + if(nc == 1) + [U,H,Ucols] = krylov(a,b,n,tol); + U = U(:,1:Ucols); + else + [U,Ucols] = krylovb(a,b,n,tol); + U = U(:,1:Ucols); + endif + + retval = (Ucols == n); + endif endfunction diff --git a/scripts/control/is_detectable.m b/scripts/control/is_detectable.m new file mode 100644 --- /dev/null +++ b/scripts/control/is_detectable.m @@ -0,0 +1,60 @@ +# Copyright (C) 1993, 1994, 1995 John W. Eaton +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [retval,U] = is_detectable (a,c,tol) + +# [retval,U] = is_detectable (a,c,tol) +# usage: is_detectable (a , c {,tol}) +# or is_detectable (sys {,tol}) +# +# Default: tol = 10*norm(a,'fro')*eps +# +# Returns 1 if the system, a, is detectable, 1 if the pair (a, c) is +# detectable, or 0 if not. +# +# See also: size, rows, columns, length, is_matrix, is_scalar, is_vector. + +# Written by A. S. Hodel (scotte@eng.auburn.edu) August 1993. +# Updated by John Ingram (ingraje@eng.auburn.edu) July 1996. +# SYS_INTERNAL accesses members of system structure +# $Revision: 1.1.1.1 $ + + if( nargin < 1) + usage("[retval,U] = is_detectable(a , c {, tol})"); + elseif(is_struct(a)) + # system form + if(nargin == 2) + tol = c; + elseif(nargin > 2) + usage("[retval,U] = is_detectable(sys {, tol})"); + endif + a = sysupdate(a,"ss"); + c = a.c; + a = a.a; + elseif(nargin > 3) + usage("[retval,U] = is_detectable(a , c {, tol})"); + endif + if(exist("tol")) + [retval,U] = is_stabilizable (a', c', tol); + else + [retval,U] = is_stabilizable (a', c'); + endif + + +endfunction + diff --git a/scripts/control/is_dgkf.m b/scripts/control/is_dgkf.m new file mode 100644 --- /dev/null +++ b/scripts/control/is_dgkf.m @@ -0,0 +1,240 @@ +# Copyright (C) 1996,1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [retval,dgkf_struct] = is_dgkf(Asys,nu,ny,tol) + # function [retval,dgkf_struct] = is_dgkf(Asys,nu,ny{,tol}) + # Determine whether a continuous time state space system meets + # assumptions of DGKF algorithm. + # Partitions system into: [z] = [A | Bw Bu ][w] + # [y] [Cz | Dzw Dzu ][u] + # [Cy | Dyw Dyu ] + #If necessary, orthogonal transformations Qw, Qz and nonsingular + # transformations Ru, Ry are applied to respective vectors w, z, u, y + # in order to satisfy DGKF assumptions. Loop shifting is used if Dyu block + # is nonzero. + # + # inputs: + # Asys: structured system + # nu: number of controlled inputs + # ny: number of measured outputs + # tol: threshhold for 0. Default: 200*eps + # outputs: + # retval: true(1) if system passes check, false(0) otherwise + # dgkf_struct: data structure of is_dgkf results. Entries: + # nw, nz: dimensions of w, z + # A: system A matrix + # Bw: (n x nw) Qw-transformed disturbance input matrix + # Bu: (n x nu) Ru-transformed controlled input matrix; + # Note: B = [Bw Bu] + # Cz: (nz x n) Qz-transformed error output matrix + # Cy: (ny x n) Ry-transformed measured output matrix + # Note: C = [Cz; Cy] + # Dzu, Dyw: off-diagonal blocks of transformed D matrix that enter + # z, y from u, w respectively + # Ru: controlled input transformation matrix + # Ry: observed output transformation matrix + # Dyu_nz: nonzero if the Dyu block is nonzero. + # Dyu: untransformed Dyu block + # dflg: nonzero if the system is discrete-time + # + # is_dgkf exits with an error if the system is mixed discrete/continuous + # + # [1] Doyle, Glover, Khargonekar, Francis, "State Space Solutions + # to Standard H2 and Hinf Control Problems," IEEE TAC August 1989 + # + # [2] Maciejowksi, J.M.: "Multivariable feedback design," + # Addison-Wesley, 1989, ISBN 0-201-18243-2 + # + # [3] Keith Glover and John C. Doyle: "State-space formulae for all + # stabilizing controllers that satisfy and h-infinity-norm bound + # and relations to risk sensitivity," + # Systems & Control Letters 11, Oct. 1988, pp 167-172. + # [4] P. A. Iglesias and K. Glover, "State-space approach to discrete-time + # H-infinity control." Int. J. Control, 1991, V. 54, #5, 1031-1073. + # + + # Written by A. S. Hodel + # Updated by John Ingram July 1996 to accept structured systems + # $Revision: 1.7 $ + # + # Revised by Kai P Mueller April 1998 to solve the general H_infinity + # problem using unitary transformations Q (on w and z) + # and non-singular transformations R (on u and y) such + # that the Dzu and Dyw matrices of the transformed plant + # + # ~ + # P (the variable Asys here) + # + # become + # + # ~ -1 T + # D = Q D R = [ 0 I ] or [ I ], + # 12 12 12 12 + # + # ~ T + # D = R D Q = [ 0 I ] or [ I ]. + # 21 21 21 21 + # + # This transformation together with the algorithm in [1] solves + # the general problem (see [2] for example). + # $Revision: 1.7 $ + # $Log: is_dgkf.m,v $ + # Revision 1.7 1998/08/19 19:11:18 hodelas + # uniform interface for is_dgkf among h2syn, hinfsyn + # + # Revision 1.6 1998/07/13 21:05:03 hodelas + # Updated for discrete time systems + # + # fixed typo in error messages; use system functions instead of + # direct access to system structure + # + + if (nargin < 3) | (nargin > 4) + usage("[retval,dgkf_struct] = is_dgkf(Asys,nu,ny{,tol})"); + elseif (! is_scalar(nu) | ! is_scalar(ny) ) + error("is_dgkf: arguments 2 and 3 must be scalars") + elseif (! is_struct(Asys) ) + error("Argument 1 must be a system data structure"); + endif + if(nargin < 4) + tol = 200*eps; + elseif( !is_sample(tol) ) + error("is_dgkf: tol must be a positive scalar") + endif + + retval = 1; # assume passes test + + dflg = is_digital(Asys); + [Anc, Anz, nin, nout ] = sysdimensions(Asys); + + if( Anz == 0 & Anc == 0 ) + error("is_dgkf: no system states"); + elseif( nu >= nin ) + error("is_dgkf: insufficient number of disturbance inputs"); + elseif( ny >= nout ) + error("is_dgkf: insufficient number of regulated outputs"); + endif + + nw = nin - nu; nw1 = nw + 1; + nz = nout - ny; nz1 = nz + 1; + + [A,B,C,D] = sys2ss(Asys); + # scale input/output for numerical reasons + if(norm(C,'fro')*norm(B,'fro') == 0) + error("||C||*||B|| = 0; no dynamic connnection from inputs to outputs"); + endif + xx = sqrt(norm(B, Inf) / norm(C, Inf)); + B = B / xx; C = C * xx; + + # partition matrices + Bw = B(:,1:nw); Bu = B(:,nw1:nin); + Cz = C(1:nz,:); Dzw = D(1:nz,1:nw); Dzu = D(1:nz,nw1:nin); + Cy = C(nz1:nout,:); Dyw = D(nz1:nout,1:nw); Dyu = D(nz1:nout,nw1:nin); + + # Check for loopo shifting + Dyu_nz = (norm(Dyu,Inf) != 0); + if (Dyu_nz) + warning("is_dgkf: D22 nonzero; performing loop shifting"); + endif + + # 12 - rank condition at w = 0 + xx =[A Bu; Cz Dzu]; + [nr, nc] = size(xx); + irank = rank(xx); + if (irank != nc) + retval = 0; + warning(sprintf("rank([A Bu; Cz Dzu]) = %d, need %d; n=%d, nz=%d, nu=%d", ... + irank,nc,(Anc+Anz),nz,nu)); + warning(" *** 12-rank condition violated at w = 0."); + endif + + # 21 - rank condition at w = 0 + xx =[A Bw; Cy Dyw]; + [nr, nc] = size(xx); + irank = rank(xx); + if (irank != nr) + retval = 0; + warning(sprintf("rank([A Bw; Cy Dyw]) = %d, need %d; n=%d, ny=%d, nw=%d", ... + irank,nr,(Anc+Anz),ny,nw)); + warning(" *** 21-rank condition violated at w = 0."); + endif + + # can Dzu be transformed to become [0 I]' or [I]? + # This ensures a normalized weight + [Qz, Ru] = qr(Dzu); + irank = rank(Ru); + if (irank != nu) + retval = 0; + warning(sprintf("*** rank(Dzu(%d x %d) = %d", nz, nu, irank)); + warning(" *** Dzu does not have full column rank."); + endif + if (nu >= nz) + Qz = Qz(:,1:nu)'; + else + Qz = [Qz(:,(nu+1):nz) Qz(:,1:nu)]'; + endif + Ru = Ru(1:nu,:); + + # can Dyw be transformed to become [0 I] or [I]? + # This ensures a normalized weight + [Qw, Ry] = qr(Dyw'); + irank = rank(Ry); + if (irank != ny) + retval = 0; + warning(sprintf("*** rank(Dyw(%d x %d) = %d", ny, nw, irank)); + warning(" *** Dyw does not have full row rank."); + endif + + if (ny >= nw) + Qw = Qw(:,1:ny); + else + Qw = [Qw(:,(ny+1):nw) Qw(:,1:ny)]; + endif + Ry = Ry(1:ny,:)'; + + # transform P by Qz/Ru and Qw/Ry + Bw = Bw*Qw; + Bu = Bu/Ru; + B = [Bw Bu]; + Cz = Qz*Cz; + Cy = Ry\Cy; + C = [Cz; Cy]; + Dzw = Qz*Dzw*Qw; + Dzu = Qz*Dzu/Ru; + Dyw = Ry\Dyw*Qw; + + # pack the return structure + dgkf_struct.nw = nw; + dgkf_struct.nu = nu; + dgkf_struct.nz = nz; + dgkf_struct.ny = ny; + dgkf_struct.A = A; + dgkf_struct.Bw = Bw; + dgkf_struct.Bu = Bu; + dgkf_struct.Cz = Cz; + dgkf_struct.Cy = Cy; + dgkf_struct.Dzw = Dzw; + dgkf_struct.Dzu = Dzu; + dgkf_struct.Dyw = Dyw; + dgkf_struct.Dyu = Dyu; + dgkf_struct.Ru = Ru; + dgkf_struct.Ry = Ry; + dgkf_struct.Dyu_nz = Dyu_nz; + dgkf_struct.dflg = dflg; + +endfunction diff --git a/scripts/control/is_digital.m b/scripts/control/is_digital.m new file mode 100644 --- /dev/null +++ b/scripts/control/is_digital.m @@ -0,0 +1,39 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function DIGITAL = is_digital(sys) +# function DIGITAL = is_digital(sys) +# retrurn nonzero if system is digital +# exits with an error of sys is a mixed (continuous and discrete) system + +# a s hodel July 1996 +# $Revision: 1.1.1.1 $ +# SYS_INTERNAL accesses members of system structure + + # checked for sampled data system (mixed) + # discrete system + cont = sum(sys.yd == 0) + sys.n; + dig = sum(sys.yd != 0) + sys.nz + sys.tsam; + if( cont*dig != 0) + sysout(sys); + error("continuous/discrete system; use syscont, sysdisc, or c2d first"); + else + DIGITAL = (sys.tsam > 0); + endif + +endfunction diff --git a/scripts/control/is_observable.m b/scripts/control/is_observable.m --- a/scripts/control/is_observable.m +++ b/scripts/control/is_observable.m @@ -1,40 +1,59 @@ -## Copyright (C) 1996, 1997 John W. Eaton -## -## 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-1307, USA. +# Copyright (C) 1993, 1994, 1995 John W. Eaton +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [retval,U] = is_observable (a,c,tol) -## usage: is_observable (a, c {,tol}) -## -## Returns 1 if the pair (a, c) is observable, or 0 if not. -## -## See also: size, rows, columns, length, is_matrix, is_scalar, is_vector. +# [retval,U] = is_observable (a,c,tol) +# usage: is_observable (a , c {,tol}) +# or is_observable (sys {,tol}) +# +# Default: tol = 10*norm(a,'fro')*eps +# +# Returns 1 if the system, a, is observable, 1 if the pair (a, c) is +# observable, or 0 if not. +# +# See also: size, rows, columns, length, is_matrix, is_scalar, is_vector. + +# Written by A. S. Hodel (scotte@eng.auburn.edu) August 1993. +# Updated by John Ingram (ingraje@eng.auburn.edu) July 1996. +# SYS_INTERNAL accesses members of system structure +# $Revision: 1.14 $ -## Author: A. S. Hodel -## Created: August 1993 -## Adapted-By: jwe - -function retval = is_observable (a,c,tol) - - if (nargin == 2) - retval = is_controllable (a', c'); - elseif (nargin == 3) - retval = is_controllable (a', c', tol); + if( nargin < 1) + usage("[retval,U] = is_observable(a , c {, tol})"); + elseif(is_struct(a)) + # system form + if(nargin == 2) + tol = c; + elseif(nargin > 2) + usage("[retval,U] = is_observable(sys {, tol})"); + endif + a = sysupdate(a,"ss"); + c = a.c; + a = a.a; + elseif(nargin > 3) + usage("[retval,U] = is_observable(a , c {, tol})"); + endif + if(exist("tol")) + [retval,U] = is_controllable (a', c', tol); else - usage ("is_observable (a, c {,tol})"); + [retval,U] = is_controllable (a', c'); endif endfunction + diff --git a/scripts/control/is_sample.m b/scripts/control/is_sample.m new file mode 100644 --- /dev/null +++ b/scripts/control/is_sample.m @@ -0,0 +1,29 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function out = is_sample(Ts) +# +# out = is_sample(Ts): return true if Ts is a legal sampling time +# (real,scalar, > 0) + +# A. S. Hodel July 1995 +# $Revision: 1.1 $ + +out = (is_scalar(Ts) && (Ts == abs(Ts)) && (Ts != 0) ); + +endfunction diff --git a/scripts/control/is_siso.m b/scripts/control/is_siso.m new file mode 100644 --- /dev/null +++ b/scripts/control/is_siso.m @@ -0,0 +1,37 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function SISO = is_siso(sys) +# function SISO = is_siso(sys) +# return nonzero if the system sys is single-input, single-output. + +# a s hodel July 1996, 1998 +# $Revision: 1.2 $ +# SYS_INTERNAL accesses members of system structure + + if(nargin != 1) + usage("SISO = is_siso(sys)"); + elseif( !is_struct(sys)) + error("input must be a system structure (see ss2sys, tf2sys, zp2sys)"); + endif + + [n,nz,m,p] = sysdimensions(sys); + + SISO = (m == 1 & p == 1); + +endfunction diff --git a/scripts/control/is_stabilizable.m b/scripts/control/is_stabilizable.m new file mode 100644 --- /dev/null +++ b/scripts/control/is_stabilizable.m @@ -0,0 +1,96 @@ +# Copyright (C) 1993, 1994, 1995 John W. Eaton +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [retval,U] = is_stabilizable (a, b, tol) + +# Usage: [retval,U] = is_stabilizable (a {, b, tol}) +# +# Returns retval = 1 if the system, a, is stabilizable, if the pair (a, b) is +# stabilizable, or 0 if not. +# U = orthogonal basis of controllable subspace. +# +# Controllable subspace is determined by applying Arnoldi iteration with +# complete re-orthogonalization to obtain an orthogonal basis of the +# Krylov subspace. +# +# span ([b,a*b,...,a^ b]). +# +# tol is a roundoff paramter, set to 200*eps if omitted. +# +# See also: size, rows, columns, length, is_matrix, is_scalar, is_vector +# is_observable, is_stabilizable, is_detectable, krylov, krylovb + +# Written by A. S. Hodel (scotte@eng.auburn.edu) August, 1993. +# Updated by A. S. Hodel (scotte@eng.auburn.edu) Aubust, 1995 to use krylovb +# Updated by John Ingram (ingraje@eng.auburn.edu) July, 1996 to accept systems +# SYS_INTERNAL accesses members of system structure +# $Revision: 1.1.1.1 $ + + if(nargin < 1) + usage("[retval,U] = is_stabilizable(a {, b ,tol})"); + elseif(is_struct(a)) + # sustem passed. + if(nargin == 2) + tol = b; % get tolerance + elseif(nargin > 2) + usage("[retval,U] = is_stabilizable(sys{,tol})"); + endif + sys = sysupdate(a,"ss"); + a = sys.a; + b = sys.b; + else + # a,b arguments sent directly. + if(nargin > 3) + usage("[retval,U] = is_stabilizable(a {, b ,tol})"); + endif + endif + + if(exist("tol")) + [retval,U] = is_controllable(a,b,tol); + else + [retval,U] = is_controllable(a,b); + tol = 1e2*rows(b)*eps; + endif + + #disp("is_stabilzable: is_controllable returns") + #retval + #U + #disp("/is_stabilzable: is_controllable returns") + + if( !retval & columns(U) > 0) + # now use an ordered Schur decomposition to get an orthogonal + # basis of the unstable subspace... + n = rows(a); + [ua,s] = schur(-(a+eye(n)*tol),'A'); + k = sum( real(eig(a)) >= 0 ); # count unstable poles + + #disp("is_stabilizable: unstable poles found:") + #k + #s + #disp("/is_stabilizable: unstable poles found:") + + if( k > 0 ) + ua = ua(:,1:k); + # now see if span(ua) is contained in span(U) + retval = (norm(ua - U*U'*ua) < tol); + else + retval = 1; # all poles stable + endif + endif + +endfunction diff --git a/scripts/control/is_stable.m b/scripts/control/is_stable.m new file mode 100644 --- /dev/null +++ b/scripts/control/is_stable.m @@ -0,0 +1,73 @@ +# Copyright (C) 1993, 1994, 1995 John W. Eaton +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function retval = is_stable (a, tol, disc) + +# Usage: retval = is_stable (a {,tol,disc}) +# or retval = is_stable (sys{,tol}) +# +# Returns retval = 1 if the matrix a or the system a is stable, or 0 if not. +# +# tol is a roundoff paramter, set to 200*eps if omitted. +# disc != 0: stable if eig(a) in unit circle +# 0: stable if eig(a) in open LHP (default) +# +# See also: size, rows, columns, length, is_matrix, is_scalar, is_vector +# is_observable, is_stabilizable, is_detectable, krylov, krylovb + +# Written by A. S. Hodel (scotte@eng.auburn.edu) August, 1993. +# Updated by A. S. Hodel (scotte@eng.auburn.edu) Aubust, 1995 to use krylovb +# Updated by John Ingram (ingraje@eng.auburn.edu) July, 1996 for systems +# SYS_INTERNAL accesses members of system structure +# $Revision: 1.1.1.1 $ + + if( (nargin < 1) | (nargin > 3) ) + usage("is_stable(a {,tol,disc})"); + elseif(is_struct(a)) + # system was passed + if(nargin < 3) + disc = is_digital(a); + elseif(disc != is_digital(a)) + warning(["is_stable: disc =",num2str(disc),", does not match system"]) + endif + sys = sysupdate(a,"ss"); + a = sys.a; + else + if(nargin < 3) + disc = 0; + endif + if(is_square(a) == 0) + error("non-square a matrix passed.") + endif + endif + + if( nargin < 2) + tol = 200*eps; + elseif( !is_scalar(tol) ) + error("is_stable: tol must be a scalar!"); + endif + + l = eig(a); + if(disc) + nbad = sum(abs(l)*(1+tol) > 1); + else + nbad = sum(real(l)+tol > 0); + endif + retval = (nbad == 0); + +endfunction diff --git a/scripts/control/jet707.m b/scripts/control/jet707.m new file mode 100644 --- /dev/null +++ b/scripts/control/jet707.m @@ -0,0 +1,64 @@ +# Copyright (C) 1997 Kai P. Mueller +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function outsys = jet707() + # function outsys = jet707() + # Creates linearized state space model of a Boeing 707-321 aircraft + # at v=80m/s. (M = 0.26, Ga0 = -3 deg, alpha0 = 4 deg, kappa = 50 deg) + # System inputs: (1) thrust and (2) elevator angle + # System outputs: (1) airspeed and (2) pitch angle + # Ref: R. Brockhaus: Flugregelung (Flight Control), Springer, 1994 + # + # see also: ord2 + + # Written by Kai P. Mueller September 28, 1997 + # Updates + # $Revision: 1.1.1.1 $ + # $Log: jet707.m,v $ + # Revision 1.1.1.1 1998/05/19 20:24:07 jwe + # + # Revision 1.3 1997/12/01 16:51:50 scotte + # updated by Mueller 27 Nov 97 + # +# Revision 1.1 1997/11/11 17:33:24 mueller +# Initial revision +# + + if (nargin != 0) + usage("outsys = jet707()") + endif + if (nargin > 1) + usage("outsys = jet707()") + endif + + a = [ -0.46E-01, 0.10681415316, 0.0, -0.17121680433; + -0.1675901504661613, -0.515, 1.0, 0.6420630320636088E-02; + 0.1543104215347786, -0.547945, -0.906, -0.1521689385990753E-02; + 0.0, 0.0, 1.0, 0.0 ]; + b = [ 0.1602300107479095, 0.2111848453E-02; + 0.8196877780963616E-02, -0.3025E-01; + 0.9173594317692437E-01, -0.75283075; + 0.0, 0.0 ]; + c = [ 1.0, 0.0, 0.0, 0.0; + 0.0, 0.0, 0.0, 1.0 ]; + d=zeros(2,2); + inam = ["thrust"; "rudder"]; + onam = ["speed"; "pitch"]; + snam = ["x1"; "x2"; "x3"; "x4"]; + outsys = ss2sys(a, b, c, d, 0.0, 4, 0, snam, inam, onam); +endfunction diff --git a/scripts/control/lqe.m b/scripts/control/lqe.m --- a/scripts/control/lqe.m +++ b/scripts/control/lqe.m @@ -1,55 +1,66 @@ -## Copyright (C) 1996, 1997 John W. Eaton -## -## 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-1307, USA. - -## Usage: [k, p, e] = lqe (A, G, C, SigW, SigV {,Z}) -## -## Linear quadratic estimator (Kalman filter) design for the -## continuous time system -## -## dx/dt = A x + B u + G w -## y = C x + D u + v -## -## where w, v are zero-mean gaussian noise processes with respective -## intensities SigW = cov (w, w) and SigV = cov (v, v). -## -## Z (if specified) is cov(w,v); otherwise cov(w,v) = 0. -## -## Observer structure is dz/dt = A z + B u + k( y - C z - D u). -## -## Returns: -## -## k = observer gain, (A - K C) is stable -## p = solution of algebraic Riccati equation -## e = closed loop poles of (A - K C) - -## Author: A. S. Hodel -## Created: August 1993 -## Adapted-By: jwe +# Copyright (C) 1993, 1994, 1995 John W. Eaton +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. function [k, p, e] = lqe (a, g, c, sigw, sigv, zz) - if (nargin != 5 && nargin != 6) +# Usage: [k, p, e] = lqe (A, G, C, SigW, SigV {,Z}) +# +# Linear quadratic estimator (Kalman filter) design for the +# continuous time system +# +# dx/dt = A x + B u + G w +# y = C x + D u + v +# +# where w, v are zero-mean gaussian noise processes with respective +# intensities SigW = cov (w, w) and SigV = cov (v, v). +# +# Z (if specified) is cov(w,v); otherwise cov(w,v) = 0. +# +# Observer structure is dz/dt = A z + B u + k( y - C z - D u). +# +# Returns: +# +# k = observer gain, (A - K C) is stable +# p = solution of algebraic Riccati equation +# e = closed loop poles of (A - K C) + +# Written by A. S. Hodel (scotte@eng.auburn.edu) August, 1993. +# $Revision: 1.14 $ +# $Log: lqe.m,v $ +# Revision 1.14 1998-11-06 16:15:36 jwe +# *** empty log message *** +# +# Revision 1.1.1.1 1998/05/19 20:24:07 jwe +# +# Revision 1.3 1997/09/19 21:36:54 scotte +# *** empty log message *** +# +# Revision 1.2 1997/02/28 23:03:45 hodel +# added parenthesis in if command (just in case) +# a.s.hodel@eng.auburn.edu +# + + if ( (nargin != 5) && (nargin != 6)) error ("lqe: invalid number of arguments"); endif - ## The problem is dual to the regulator design, so transform to lqr - ## call. +# The problem is dual to the regulator design, so transform to lqr +# call. if (nargin == 5) [k, p, e] = lqr (a', c', g*sigw*g', sigv); diff --git a/scripts/control/lqg.m b/scripts/control/lqg.m new file mode 100644 --- /dev/null +++ b/scripts/control/lqg.m @@ -0,0 +1,156 @@ +# Copyright (C) 1996, 1997 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [K,Q1,P1,Ee,Er] = lqg(sys,Sigw,Sigv,Q,R,input_list) +# +# function [K,Q,P,Ee,Er] = lqg(sys,Sigw,Sigv,Q,R,input_list) +# design a linear-quadratic-gaussian optimal controller for the system +# +# dx/dt = A x + B u + G w [w]=N(0,[Sigw 0 ]) +# y = C x + v [v] ( 0 Sigv ]) +# +# or +# +# x(k+1) = A x(k) + B u(k) + G w(k) [w]=N(0,[Sigw 0 ]) +# y(k) = C x(k) + v(k) [v] ( 0 Sigv ]) +# +# inputs: +# sys: system data structure +# Sigw, Sigv: intensities of independent Gaussian noise processes (as above) +# Q, R: state, control weighting respectively. Control ARE is +# input_list: indices of controlled inputs +# default: last dim(R) inputs are assumed to be controlled inputs, all +# others are assumed to be noise inputs. +# Outputs: +# K: system data structure LQG optimal controller +# (Obtain A,B,C matrices with sys2ss, sys2tf, or sys2zp as appropriate) +# P: Solution of control (state feedback) algebraic Riccati equation +# Q: Solution of estimation algebraic Riccati equation +# Ee: estimator poles +# Es: controller poles +# +# See also: h2syn, lqe, lqr + +# Written by A. S. Hodel August 1995; revised for new system format +# August 1996 +# $Revision: 1.1.1.1 $ +# $Log: lqg.m,v $ +# Revision 1.1.1.1 1998/05/19 20:24:07 jwe +# +# Revision 1.2 1997/03/01 00:21:33 hodel +# fixed some string manipulation problems. +# + +sav_val = implicit_str_to_num_ok; +implicit_str_to_num_ok = 1; + +if ( (nargin < 5) | (nargin > 6)) + usage("[K,Q1,P1,Ee,Er] = lqg(sys,Sigw, Sigv,Q,R{,input_list})"); + +elseif(!is_struct(sys) ) + error("sys must be in system data structure"); +endif + +DIG = is_digital(sys); +[A,B,C,D,tsam,n,nz,stname,inname,outname] = sys2ss(sys); +nout = rows(outname); +nin = rows(inname); +if(nargin == 5) + #construct default input_list + input_list = (columns(Sigw)+1):nin; +endif + +if( !(n+nz) ) + error(["lqg: 0 states in system"]); + +elseif(nin != columns(Sigw)+ columns(R)) + error(["lqg: sys has ",num2str(nin)," inputs, dim(Sigw)=", ... + num2str(columns(Sigw)),", dim(u)=",num2str(columns(R))]) + +elseif(nout != columns(Sigv)) + error(["lqg: sys has ",num2str(nout)," outputs, dim(Sigv)=", ... + num2str(columns(Sigv)),")"]) +elseif(length(input_list) != columns(R)) + error(["lqg: length(input_list)=",num2str(length(input_list)), ... + ", columns(R)=", num2str(columns(R))]); +endif + +varname = ["Sigw";"Sigv";"Q ";"R "]; +for kk=1:rows(varname); + stval = dezero(varname(kk,:)); + cmd = ["chk = is_square(",stval,");"]; + eval(cmd); + if(! chk ) + error(["lqg: ",stval," is not square"]); + endif +endfor + +# permute (if need be) +if(nargin == 6) + all_inputs = sysreorder(nin,input_list); + B = B(:,all_inputs); + inname = inname(all_inputs,:); +endif + +# put parameters into correct variables +m1 = columns(Sigw); +m2 = m1+1; +G = B(:,1:m1); +B = B(:,m2:nin); + +# now we can just do the design; call dlqr and dlqe, since all matrices +# are not given in Cholesky factor form (as in h2syn case) +if(DIG) + [Ks P1 Er] = dlqr(A,B,Q,R); + [Ke Q1 jnk Ee] = dlqe(A,G,C,Sigw,Sigv); +else + [Ks P1 Er] = lqr(A,B,Q,R); + [Ke Q1 Ee] = lqe(A,G,C,Sigw,Sigv); +endif +Ac = A - Ke*C - B*Ks; +Bc = Ke; +Cc = -Ks; +Dc = zeros(rows(Cc),columns(Bc)); + +# fix state names +for ii=1:rows(stname) + newst = [dezero(stname(ii,:)),"\\e"]; + stname1(ii,1:length(newst)) = newst; +endfor + +# fix controller output names +inname = inname(m2:nin,:); +for ii=1:rows(inname) + newst = [dezero(inname(ii,:)),"\\K"]; + outname1(ii,1:length(newst)) = newst; +endfor + +# fix controller input names +for ii=1:rows(outname) + newst = [dezero(outname(ii,:)),"\\K"]; + inname1(ii,1:length(newst)) = newst; +endfor + +if(DIG) + K = ss2sys(Ac,Bc,Cc,Dc,tsam,n,nz,stname1,inname1,outname1,1:rows(Cc)); +else + K = ss2sys(Ac,Bc,Cc,Dc,tsam,n,nz,stname,inname1,outname1); +endif + +implicit_str_to_num_ok = sav_val; +endfunction diff --git a/scripts/control/lqr.m b/scripts/control/lqr.m --- a/scripts/control/lqr.m +++ b/scripts/control/lqr.m @@ -1,100 +1,124 @@ -## Copyright (C) 1996, 1997 John W. Eaton -## -## 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-1307, USA. +# Copyright (C) 1993, 1994, 1995 John W. Eaton +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [k, p, e] = lqr (a, b, q, r, s) -## Usage: [k, p, e] = lqr (A, B, Q, R {,Z}) -## -## Linear quadratic regulator design for the continuous time system -## -## dx/dt = A x + B u -## -## to minimize the cost functional -## -## J = int_0^\infty{ x' Q x + u' R u } Z omitted -## -## or -## -## J = int_0^\infty{ x' Q x + u' R u +2 x' Z u} Z included -## -## Returns: -## -## k = state feedback gain, (A - B K) is stable -## p = solution of algebraic Riccati equation -## e = closed loop poles of (A - B K) +# Usage: [k, p, e] = lqr (A, B, Q, R {,S}) +# +# Linear quadratic regulator design for the continuous time system +# dx/dt = A x + B u +# to minimize the cost functional +# +# J = int_0^\infty{ [x' u'] [Q S'; S R] [x ; u] dt} +# +# inputs: +# A, B: coefficient matrices for continuous time system +# Q, R, S: cost functional coefficient matrices. +# Q: must be nonnegative definite. +# R: must be positive definite +# S: defaults to 0 +# +# if S is omitted, the optimization simplifies to the usual +# +# J = int_0^\infty{ x' Q x + u' R u } +# +# Returns: +# +# k = state feedback gain, (A - B K) is stable and minimizes the +# cost functional +# p = solution of algebraic Riccati equation +# e = closed loop poles of (A - B K) +# +# reference: Anderson and Moore, OPTIMAL CONTROL: LINEAR QUADRATIC METHODS, +# Prentice-Hall, 1990, pp. 56-58 -## Author: A. S. Hodel -## Created: August 1993 -## Adapted-By: jwe +# $Revision: 1.13 $ -function [k, p, e] = lqr (a, b, q, r, zz) +# Written by A. S. Hodel (scotte@eng.auburn.edu) August 1993. +# $Log: lqr.m,v $ +# Revision 1.13 1998-11-06 16:15:36 jwe +# *** empty log message *** +# +# Revision 1.1.1.1 1998/05/19 20:24:07 jwe +# +# Revision 1.4 1997/09/19 21:37:24 scotte +# added references for feedback matrices +# , +# +# +# Revision 1.2 1997/02/28 23:02:14 hodel +# added parenthesis around if commands (just to be sure) +# a.s.hodel@eng.auburn.edu - if (nargin != 4 && nargin != 5) + # disp("lqr: entry"); + + if ((nargin != 4) && (nargin != 5)) error ("lqr: invalid number of arguments"); endif - ## Check a. +# Check a. if ((n = is_square (a)) == 0) error ("lqr: requires 1st parameter(a) to be square"); endif - ## Check b. +# Check b. [n1, m] = size (b); if (n1 != n) error ("lqr: a,b not conformal"); endif - ## Check q. - - if ((n1 = is_square (q)) == 0 || n1 != n) +# Check q. + + if ( ((n1 = is_square (q)) == 0) || (n1 != n)) error ("lqr: q must be square and conformal with a"); endif - ## Check r. - if((m1 = is_square(r)) == 0 || m1 != m) +# Check r. + if ( ((m1 = is_square(r)) == 0) || (m1 != m)) error ("lqr: r must be square and conformal with column dimension of b"); endif - ## Check if n is there. +# Check if n is there. if (nargin == 5) - [n1, m1] = size (zz); - if (n1 != n || m1 != m) + [n1, m1] = size (s); + if ( (n1 != n) || (m1 != m)) error ("lqr: z must be identically dimensioned with b"); endif - ## Incorporate cross term into a and q. - - ao = a - (b/r)*zz'; - qo = q - (zz/r)*zz'; +# Incorporate cross term into a and q. + ao = a - (b/r)*s'; + qo = q - (s/r)*s'; else - zz = zeros (n, m); + s = zeros (n, m); ao = a; qo = q; endif - ## Check that q, (r) are symmetric, positive (semi)definite +# Check that q, (r) are symmetric, positive (semi)definite if (is_symmetric (q) && is_symmetric (r) ... && all (eig (q) >= 0) && all (eig (r) > 0)) p = are (ao, (b/r)*b', qo); - k = r\(b'*p + zz'); + k = r\(b'*p + s'); e = eig (a - b*k); else error ("lqr: q (r) must be symmetric positive (semi) definite"); endif + # disp("lqr: exit"); endfunction diff --git a/scripts/control/lsim.m b/scripts/control/lsim.m new file mode 100644 --- /dev/null +++ b/scripts/control/lsim.m @@ -0,0 +1,114 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [y,x] = lsim(sys,u,t,x0) +# lsim: Produce output for a linear simulation of a system +# +# lsim(sys,u,t,[x0]) +# Produces a plot for the output of the system, sys. +# +# U is an array that contains the system's inputs. Each column in u +# corresponds to a different time step. Each row in u corresponds to a +# different input. T is an array that contains the time index of the +# system. T should be regularly spaced. If initial conditions are required +# on the system, the x0 vector should be added to the argument list. +# +# When the lsim function is invoked with output parameters: +# [y,x] = lsim(sys,u,t,[x0]) +# a plot is not displayed, however, the data is returned in y = system output +# and x = system states. + +# Written by David Clem, A. S. Hodel July 1995 +# modified by John Ingram for system format August 1996 +# $Revision: 1.1.1.1 $ +# $Log: lsim.m,v $ +# Revision 1.1.1.1 1998/05/19 20:24:07 jwe +# +# Revision 1.3 1997/12/01 16:51:50 scotte +# updated by Mueller 27 Nov 97 +# +# Revision 1.2 1997/11/24 16:13:39 mueller +# old style c2d call fixed +# + + if((nargin < 3)||(nargin > 4)) + usage("[y,x] = lsim(sys,u,t[,x0])"); + endif + + if(!is_struct(sys)) + error("sys must be in system data structure"); + endif + + sys = sysupdate(sys,"ss"); + + nout = rows(sys.c); + nin = columns(sys.b); + ncstates = sys.n; + ndstates = sys.nz; + + a = sys.a; + b = sys.b; + c = sys.c; + d = sys.d; + + if (nargin == 3) + x0 = zeros(columns(a),1); + endif + + if(rows(u) ~= length(t)) + error("lsim: There should be an input value (row) for each time instant"); + endif + if(columns(u) ~= columns(d)) + error("lsim: U and d should have the same number of inputs"); + endif + if(columns(x0) > 1) + error("lsim: Initial condition vector should have only one column"); + endif + if(rows(x0) > rows(a)) + error("lsim: Initial condition vector is too large"); + endif + + Ts = 0; + t(2)-t(1); + u=u'; + n = max(size(t)); + for ii = 1:(n-1) + + # check if step size changed + if (t(ii+1) - t(ii) != Ts) + Ts = t(ii+1) - t(ii); + # [F,G] = c2d(a,b,Ts); + dsys = c2d(sys, Ts); + F = dsys.a; + G = dsys.b; + endif + + x(:,ii) = x0; + x0 = F*x0 + G*u(:,ii); + endfor + + # pick up last point + x(:,n) = x0; + + y = c*x + d*u; + if(nargout == 0) + plot(t,y); + y=[]; + x=[]; + endif +endfunction diff --git a/scripts/control/ltifr.m b/scripts/control/ltifr.m new file mode 100644 --- /dev/null +++ b/scripts/control/ltifr.m @@ -0,0 +1,85 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function out = ltifr(a,b,w) + #ltifr: Linear time invarient frequency response of SISO systems + # out = ltifr(A,B,w) + # user enters the A and B matrices + # + # out = ltifr(sys,w) + # user enters the system, SYS + # + # this function takes the system matrices, A and B and + # returns: -1 + # G(s) = (jw I-A) B + # + # for complex frequencies s = jw. + + # R. B. Tenison, D. Clem, A. S. Hodel, July 1995 + # updated by John Ingram August 1996 for system format + # $Revision: 1.1.1.1 $ + + if ((nargin < 2) || (nargin > 3)) + error("incorrect number of input arguments"); + endif + + if (nargin == 2) + sys = a; + w = b; + + if (!is_vector(w)) + error("w must be a vector"); + endif + + sys = sysupdate(sys,"ss"); + + if(columns(sys.b) != 1) + error("sys is not an SISO system"); + endif + + a = sys.a; + b = sys.b; + + else + + if (columns(a) != rows(b)), + error("ltifr: A, B not compatibly dimensioned"); + endif + + if(columns(b) != 1) + error("ltifr: 2nd argument must be a single column vector"); + endif + + if (!is_square(a)) + error("ltifr: A must be square.") + endif + + endif + + if (!is_vector(w)) + error("w must be a vector"); + endif + + ey = eye(size(a)); + lw = length(w); + out = ones(columns(a),lw); + + for ii=1:lw, + out(:,ii) = (w(ii)*ey-a)\b; + endfor +endfunction diff --git a/scripts/control/mb.m b/scripts/control/mb.m new file mode 100644 --- /dev/null +++ b/scripts/control/mb.m @@ -0,0 +1,40 @@ +# $Revision: 1.1 $ + +Ap = [0 1;1960 0]; +Bp = [0;-6261]; +Cp = [1 0]; +Dp = 0; + +Gp = ss2sys(Ap,Bp,Cp,Dp,0,2,0,[],"delta_i","delta_y"); +Gp = syschnames(Gp,"st",1,"delta_x1"); +Gp = syschnames(Gp,"st",2,"delta_x2"); + +Ak = [-20 1;-22160 -200]; +Bk = [-20;-2160]; +Ck = [-3.5074 -0.0319]; +Dk = 0; + +Gk = ss2sys(Ak,Bk,Ck,Dk,0,2,0,[],"y","i"); +Gk = syschnames(Gk,"st",1,"x1"); +Gk = syschnames(Gk,"st",2,"x2"); + +Gc = sysgroup(Gp,Gk); + +Gc = sysdup(Gc,[],[1 2]); +# Gc = sysscale(Gc,[],diag([1,1,1,1])); + +Gc = sysconnect(Gc,[1 2],[4 3]); +Gc = sysprune(Gc,1,[1 2]); + +disp("after pruning, closed loop system is") +sysout(Gc) + +# Gc = sysdup(Gc,[],2); +# Gc = sysconnect(Gc,1,3); +# Gc = sysprune(Gc,1,1); + +is_stable(Gc) +eig(Gc.a) + +Acl = [Gp.a, -Gp.b*Gk.c; Gk.b*Gp.c, Gk.a] +eig(Acl) diff --git a/scripts/control/minfo.m b/scripts/control/minfo.m new file mode 100644 --- /dev/null +++ b/scripts/control/minfo.m @@ -0,0 +1,85 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [systype, nout, nin, ncstates, ndstates] = minfo(inmat) + # function [systype, nout, nin, ncstates, ndstates] = minfo(inmat) + # + # MINFO: Determines the type of system matrix. INMAT can be + # a varying(*), system, constant, and empty matrix. + # + # Returns: + # systype can be one of: + # varying, system, constant, and empty + # nout is the number of outputs of the system + # nin is the number of inputs of the system + # ncstates is the number of continuous states of the system + # ndstates is the number of discrete states of the system + + # Written by R. Bruce Tenison July 29, 1994 + # Modified by David Clem November 13, 1994 + # Modified by A. S. Hodel July 1995 + # $Revision: 1.1.1.1 $ + + warning("minfo: obsolete. Use sys2ss, sys2tf, or sys2zp."); + + if (nargin ~= 1 ) + disp('MINFO: Wrong number of arguments') + systype = nout = nin = ncstates = ndstates = []; + endif + + [rr,cc] = size(inmat); + + # Check for empty matrix first! + if (isempty(inmat)) + systype = "empty"; + nout = nin = ncstates = ndstates = 0; + return + + # Check for Constant matrix + + elseif (rr == 1 || cc == 1) + systype = "constant"; + nout = nin = ncstates = ndstates = 1; + return + + # Check for system type matrix + elseif (inmat(rr,cc) == -Inf) + systype = "system"; + ncstates = inmat(1,cc); + ndstates = inmat(rr,1); + nstates = ncstates + ndstates; + nout = rr - nstates - 1; + nin = cc - nstates - 1; + + # Check for Varying type matrix + elseif (inmat(rr,cc) == Inf) + systype = "varying"; + npoints = inmat(rr,cc-1); + nin = cc - 1; + nout = rr / npoints; + nstates = 0; + + # Must be a standard matrix + else + systype = "constant"; + nin = cc; + nout = rr; + ncstates = 0; + ndstates = 0; + endif +endfunction diff --git a/scripts/control/moddemo.m b/scripts/control/moddemo.m new file mode 100644 --- /dev/null +++ b/scripts/control/moddemo.m @@ -0,0 +1,201 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function moddemo() +# Octave Controls toolbox demo: Model Manipulations demo +# Written by David Clem August 15, 1994 + +# $Revision: 1.1 $ +# a s hodel: updated to reflect updated output order in ss2zp + + while (1) + clc + disp('Octave Model Manipulations Demo') + disp('=======================================') + disp(' 1) Perform continuous to discrete time conversion (c2d)') + disp(' 2) Convert from state space to zero / pole form (ss2zp)') + disp(' Convert from zero / pole to state space form (zp2ss)') + disp(' 3) Convert from state space to transfer function form (ss2tf)') + disp(' Convert from transfer function to state space form (tf2ss)') + disp(' 4) Convert from transfer function to zero / pole form (tf2zp)') + disp(' Convert from zero / pole to transfer function form (zp2tf)') + disp(' 5) Return to main demo menu') + disp(' ') + k=6; + while(k > 5 || k < 1) + k = input('Please enter a number:'); + endwhile + if (k == 1) + clc + disp('Perform continuous to discrete time conversion (c2d)\n') + disp('Example #1, Consider the following continuous time state space system:\n') + a=[0 1;-25 -4] + b=[0;1] + c=[1 1] + d=1 + prompt + disp('\nTo convert this to a discrete time system (using a zero order hold),') + disp('use the following commands:\n') + cmd="sys=ss2sys(a,b,c,d);"; + run_cmd + cmd="dsys = c2d(sys,0.2);"; + run_cmd + cmd="sysout(dsys);"; + run_cmd + disp('Function check\n') + disp('Check the poles of sys vs dsys:\n') + cmd="[da,db]=sys2ss(dsys);"; + run_cmd + cmd="lam = eig(a);"; + run_cmd + disp('Discretize the continuous time eigenvalues using the matrix exponential:\n') + disp('lambc = exp(lam*0.2)\n') + lambc = exp(lam*0.2) + disp('Check the eigenvalues of da\n') + lambd = eig(da) + disp('Calculate the difference between lambd and lambc:\n') + cmd = 'error = sort(lambd)-sort(lambc)\n'; + run_cmd + disp("The error is on the order of roundoff noise, so we're o.k.") + prompt + clc + elseif (k == 2) + clc + disp('Convert from state space to zero / pole form (ss2zp)\n') + disp('Example #1, Consider the following state space system:\n') + a=[0 3 1;-2 -4 5;5 8 2] + b=[0;5;2.5] + c=[6 -1.9 2] + d=[-20] + prompt + disp(' ') + disp('\nTo find the poles and zeros of this sytstem, use the following command:\n') + disp('\n[zer, pol] = ss2zp(a, b, c, d)\n') + prompt + disp('Results:\n') + [zer, pol] = ss2zp(a, b, c, d) + disp('Variable Description:\n') + disp('zer, pol => zeros and poles of the state space system') + disp('a, b, c, d => state space system\n') + prompt + clc + disp('Convert from zero / pole to state space form (zp2ss)\n') + disp('Example #1, Consider the following set of zeros and poles:\n') + zer + pol + prompt + disp('\nTo find an equivalent state space representation for this set of poles') + disp('and zeros, use the following commands:\n') + k=1 + disp('\n[na, nb, nc, nd] = zp2ss(zer, pol, k)\n') + prompt + disp('Results:\n') + [na, nb, nc, nd] = zp2ss(zer, pol, k) + disp('Variable Description:\n') + disp('na, nb, nc, nd => state space system equivalent to zero / pole input') + disp('zer, pol => zeros and poles of desired state space system') + disp('k => gain associated with the zeros\n') + prompt + disp('Function check\n') + disp('Are the eigenvalues of the origonal state space system the same as the') + disp('eigenvalues of the newly constructed state space system ?\n') + disp('Find the difference between the two sets of eigenvalues') + disp('error = sort(eig(a)) - sort(eig(na))\n') + error = sort(eig(a)) - sort(eig(na)) + prompt + clc + elseif (k == 3) + clc + disp('Convert from state space to transfer function (ss2tf)\n') + disp('Example #1, Consider the following state space system:\n') + a=[0 1;-2 -3] + b=[1;1] + c=[1 9] + d=[1] + prompt + disp('\nTo find an equivalent transfer function for this system, use') + disp('the following command:\n') + disp('[num, den] = ss2tf(a, b, c, d)\n') + prompt + disp('Results:\n') + [num,den] = ss2tf(a, b, c, d) + disp('Variable Description:\n') + disp('num, den => numerator and denominator of transfer function that is') + disp(' equivalent to the state space system') + disp('a, b, c, d => state space system\n') + prompt + clc + disp('Convert from transfer function to state space form (tf2ss)\n') + disp('Example #1, Consider the following transfer function:\n') + num + den + prompt + disp('\nTo find an equivalent state space representation for this system, use') + disp('the following command:\n') + disp('[a, b, c, d] = tf2ss(num, den)\n') + prompt + disp('Results:\n') + [a, b, c, d] = tf2ss(num, den) + disp('Variable Description:\n') + disp('a, b, c, d => state space system equivalent to transfer function input') + disp('num, den => numerator and denominator of transfer function that is equivalent') + disp(' to the state space system\n') + prompt + clc + elseif (k == 4) + clc + disp('Convert from transfer function to zero / pole form (tf2zp)\n') + disp('Example #1, Consider the following transfer function:\n') + num=[1 2 3 4 5 ] + den=[1 2 3 4 5 6 7] + prompt + disp('\nTo find the zeros and poles of this system, use the following command:\n') + disp('[zer,pol] = tf2zp(num,den)\n') + prompt + disp('Results:\n') + [zer,pol] = tf2zp(num,den) + disp('Variable Description:\n') + disp('zer,pol => zeros and poles of the transfer function') + disp('num, den => numerator and denominator of transfer function\n') + prompt + clc + disp('Convert from zero / pole to transfer function (zp2tf)\n') + disp('Example #1, Consider the following set of zeros and poles:\n') + zer + pol + prompt + disp('\nTo find an equivalent transfer function representation for this set') + disp('of poles and zeros, use the following commands:\n') + k=1 + disp('\n[num, den] = zp2tf(zer, pol, k)\n') + prompt + disp('Results:\n') + [num, den] = zp2tf(zer, pol, k) + disp('Variable Description:\n') + disp('[num, den] => transfer function representation of desired set of zeros') + disp(' and poles') + disp('a, b, c, d => state space system') + disp('zer, pol => zeros and poles of desired state space system') + disp('k => gain associated with the zeros\n') + prompt + clc + elseif (k == 5) + return + endif + endwhile +endfunction diff --git a/scripts/control/nichols.m b/scripts/control/nichols.m new file mode 100644 --- /dev/null +++ b/scripts/control/nichols.m @@ -0,0 +1,114 @@ +# Copyright (C) 1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [mag,phase,w] = nichols(sys,w,outputs,inputs) +# [mag,phase,w] = nichols(sys[,w,outputs,inputs]) +# Produce Nichols plot of a system +# +# Compute the frequency response of a system. +# inputs: +# sys: system data structure (must be either purely continuous or discrete; +# see is_digital) +# w: frequency values for evaluation. +# if sys is continuous, then nichols evaluates G(jw) +# if sys is discrete, then nichols evaluates G(exp(jwT)), where T=sys.tsam +# (the system sampling time) +# default: the default frequency range is selected as follows: (These +# steps are NOT performed if w is specified) +# (1) via routine bodquist, isolate all poles and zeros away from +# w=0 (jw=0 or exp(jwT)=1) and select the frequency +# range based on the breakpoint locations of the frequencies. +# (2) if sys is discrete time, the frequency range is limited +# to jwT in [0,2p*pi] +# (3) A "smoothing" routine is used to ensure that the plot phase does +# not change excessively from point to point and that singular +# points (e.g., crossovers from +/- 180) are accurately shown. +# outputs, inputs: the indices of the output(s) and input(s) to be used in +# the frequency response; see sysprune. +# outputs: +# mag, phase: the magnitude and phase of the frequency response +# G(jw) or G(exp(jwT)) at the selected frequency values. +# w: the vector of frequency values used +# If no output arguments are given, nichols plots the results to the screen. +# Descriptive labels are automatically placed. See xlabel, ylable, title, +# and replot. +# +# Note: if the requested plot is for an MIMO system, mag is set to +# ||G(jw)|| or ||G(exp(jwT))|| and phase information is not computed. + +# $Log$ + + # check number of input arguments given + if (nargin < 1 | nargin > 4) + usage("[mag,phase,w] = nichols(sys[,w,outputs,inputs])"); + endif + if(nargin < 2) + w = []; + endif + if(nargin < 3) + outputs = []; + endif + if(nargin < 4) + inputs = []; + endif + + [f, w] = bodquist(sys,w,outputs,inputs,"nichols"); + + [stname,inname,outname] = sysgetsignals(sys); + systsam = sysgettsam(sys); + + # Get the magnitude and phase of f. + mag = abs(f); + phase = arg(f)*180.0/pi; + + if (nargout < 1), + # Plot the information + if(gnuplot_has_multiplot) + oneplot(); + endif + gset autoscale; + if(gnuplot_has_multiplot) + gset nokey; + endif + clearplot(); + grid("on"); + gset data style lines; + if(is_digital(sys)) + tistr = "(exp(jwT)) "; + else + tistr = "(jw)"; + endif + xlabel("Phase (deg)"); + ylabel("Gain in dB"); + if(is_siso(sys)) + title(["Nichols plot of |[Y/U]",tistr,"|, u=", inname, ... + ", y=",outname]); + else + title([ "||Y(", tistr, ")/U(", tistr, ")||"]); + disp("MIMO plot from") + outlist(inname," "); + disp("to") + outlist(outname," "); + endif + md = 20*log10(mag); + axvec = axis2dlim([vec(phase),vec(md)]); + axis(axvec); + plot(phase,md); + mag = phase = w = []; + endif +endfunction diff --git a/scripts/control/nyquist.m b/scripts/control/nyquist.m new file mode 100644 --- /dev/null +++ b/scripts/control/nyquist.m @@ -0,0 +1,195 @@ +# Copyright (C) 1996,1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [realp,imagp,w] = nyquist(sys,w,outputs,inputs,atol) +# [realp,imagp,w] = nyquist(sys[,w,outputs,inputs,atol]) +# Produce Nyquist plots of a system +# +# Compute the frequency response of a system. +# inputs: (pass as empty to get default values +# sys: system data structure (must be either purely continuous or discrete; +# see is_digital) +# w: frequency values for evaluation. +# if sys is continuous, then bode evaluates G(jw) +# if sys is discrete, then bode evaluates G(exp(jwT)), where +# T=sysgettsam(sys) (the system sampling time) +# default: the default frequency range is selected as follows: (These +# steps are NOT performed if w is specified) +# (1) via routine bodquist, isolate all poles and zeros away from +# w=0 (jw=0 or exp(jwT)=1) and select the frequency +# range based on the breakpoint locations of the frequencies. +# (2) if sys is discrete time, the frequency range is limited +# to jwT in [0,2p*pi] +# (3) A "smoothing" routine is used to ensure that the plot phase does +# not change excessively from point to point and that singular +# points (e.g., crossovers from +/- 180) are accurately shown. +# outputs, inputs: the indices of the output(s) and input(s) to be used in +# the frequency response; see sysprune. +# atol: for interactive nyquist plots: atol is a change-in-angle tolerance +# (in degrees) for the of asymptotes (default = 0; 1e-2 is a good choice). +# Consecutive points along the asymptotes whose angle is within atol of +# the angle between the largest two points are omitted for "zooming in" +# +# outputs: +# realp, imagp: the real and imaginary parts of the frequency response +# G(jw) or G(exp(jwT)) at the selected frequency values. +# w: the vector of frequency values used +# +# If no output arguments are given, nyquist plots the results to the screen. +# If atol != 0 and asymptotes are detected then the user is asked +# interactively if they wish to zoom in (remove asymptotes) +# Descriptive labels are automatically placed. See xlabel, ylable, title, +# and replot. +# +# Note: if the requested plot is for an MIMO system, a warning message is +# presented; the returned information is of the magnitude +# ||G(jw)|| or ||G(exp(jwT))|| only; phase information is not computed. + + # By R. Bruce Tenison, July 13, 1994 + # A. S. Hodel July 1995 (adaptive frequency spacing, + # remove acura parameter, etc.) + # Revised by John Ingram July 1996 for system format + # + # Revision 1.6 1998/02/09 13:03:37 scotte + # fixed oneplot/gset nokey to function only if gnuplot_has_multiplot + # +# Revision 1.2 1997/11/24 17:27:58 mueller +# call to oneplot() and gset nokey added +# +# Revision 1.1 1997/11/11 17:33:41 mueller +# Initial revision +# + + # Both bode and nyquist share the same introduction, so the common parts are + # in a file called bodquist.m. It contains the part that finds the + # number of arguments, determines whether or not the system is SISO, and + # computes the frequency response. Only the way the response is plotted is + # different between the two functions. + + save_val = implicit_str_to_num_ok; # save for later + implicit_str_to_num_ok = 1; + + # check number of input arguments given + if (nargin < 1 | nargin > 5) + usage("[realp,imagp,w] = nyquist(sys[,w,outputs,inputs,atol])"); + endif + if(nargin < 2) + w = []; + endif + if(nargin < 3) + outputs = []; + endif + if(nargin < 4) + inputs = []; + endif + if(nargin < 5) + atol = 0; + elseif(!(is_sample(atol) | atol == 0)) + error("atol must be a nonnegative scalar.") + endif + + # signal to bodquist who's calling + + [f,w] = bodquist(sys,w,outputs,inputs,"nyquist"); + + # Get the real and imaginary part of f. + realp = real(f); + imagp = imag(f); + + # No output arguments, then display plot, otherwise return data. + if (nargout == 0) + dnplot = 0; + while(!dnplot) + if(gnuplot_has_multiplot) + oneplot(); + gset key; + endif + clearplot(); + grid ("on"); + gset data style lines; + + if(is_digital(sys)) + tstr = " G(e^{jw}) "; + else + tstr = " G(jw) "; + endif + xlabel(["Re(",tstr,")"]); + ylabel(["Im(",tstr,")"]); + + [stn, inn, outn] = sysgetsignals(sys); + if(is_siso(sys)) + title(sprintf("Nyquist plot from %s to %s, w (rad/s) in [%e, %e]", ... + inn, outn, w(1), w(length(w))) ) + endif + + gset nologscale xy; + + axis(axis2dlim([[vec(realp),vec(imagp)];[vec(realp),-vec(imagp)]])); + plot(realp,imagp,"- ;+w;",realp,-imagp,"-@ ;-w;"); + + # check for interactive plots + dnplot = 1; # assume done; will change later if atol is satisfied + if(atol > 0 & length(f) > 2) + + # check for asymptotes + fmax = max(abs(f)); + fi = max(find(abs(f) == fmax)); + + # compute angles from point to point + df = diff(f); + th = atan2(real(df),imag(df))*180/pi; + + # get angle at fmax + if(fi == length(f)) fi = fi-1; endif + thm = th(fi); + + # now locate consecutive angles within atol of thm + ith_same = find(abs(th - thm) < atol); + ichk = union(fi,find(diff(ith_same) == 1)); + + #locate max, min consecutive indices in ichk + loval = max(complement(ichk,1:fi)); + if(isempty(loval)) loval = fi; + else loval = loval + 1; endif + + hival = min(complement(ichk,fi:length(th))); + if(isempty(hival)) hival = fi+1; endif + + keep_idx = complement(loval:hival,1:length(w)); + + if(length(keep_idx)) + resp = input("Remove asymptotes and zoom in (y or n): ",1); + if(resp(1) == "y") + dnplot = 0; # plot again + w = w(keep_idx); + f = f(keep_idx); + realp = real(f); + imagp = imag(f); + endif + endif + + endif + endwhile + w = []; + realp=[]; + imagp=[]; + endif + + implicit_str_to_num_ok = save_val; # restore value + +endfunction diff --git a/scripts/control/obsv.m b/scripts/control/obsv.m new file mode 100644 --- /dev/null +++ b/scripts/control/obsv.m @@ -0,0 +1,75 @@ +# Copyright (C) 1997 Kai P. Mueller +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function Qb = obsv(sys, c) + # ------------------------------------------------------ + # Qb = obsv(sys [, c]) + # Build observability matrix + # + # + + + # | C | + # | CA | + # Qb = | CA^2 | + # | ... | + # | CA^(n-1) | + # + + + # + # of a system data structure or the pair (A, C). + # + # Note: obsv() forms the observability matrix. + # The numerical properties of is_observable() + # are much better for observability tests. + # See also: ctrb, is_observable, is_controllable + # ------------------------------------------------------ + + # Written by Kai P. Mueller November 4, 1997 + # modified by + # $Revision: 1.1.1.1 $ + # $Log: obsv.m,v $ + # Revision 1.1.1.1 1998/05/19 20:24:07 jwe + # + # Revision 1.2 1997/12/01 16:51:50 scotte + # updated by Mueller 27 Nov 97 + # +# Revision 1.1 1997/11/25 11:17:05 mueller +# Initial revision +# + + if (nargin == 2) + a = sys; + elseif (nargin == 1 && is_struct(sys)) + sysupdate(sys,"ss"); + a = sys.a; + c = sys.c; + else + usage("obsv(sys [, c])") + endif + + if (!is_abcd(a,c')) + Qb = []; + else + # no need to check dimensions, we trust is_abcd(). + [na, ma] = size(a); + [nc, mc] = size(c); + Qb = zeros(na*nc, ma); + for i = 1:na + Qb((i-1)*nc+1:i*nc, :) = c; + c = c * a; + endfor + endif +endfunction diff --git a/scripts/control/ord2.m b/scripts/control/ord2.m new file mode 100644 --- /dev/null +++ b/scripts/control/ord2.m @@ -0,0 +1,64 @@ +# Copyright (C) 1997 Kai P. Mueller +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function outsys = ord2(nfreq, damp, gain) + # function outsys = ord2(nfreq, damp[, gain]) + # Creates a continuous 2nd order system with parameters: + # + # nfreq: natural frequency [Hz]. (not in rad/s) + # damp: damping coefficient + # gain: dc-gain + # This is steady state value only for damp > 0. + # gain is assumed to be 1.0 if ommitted. + # + # The system has representation with w = 2 * pi * nfreq: + # + # / \ + # | / -2w*damp -w \ / w \ | + # G = | | |, | |, [ 0 gain ], 0 | + # | \ w 0 / \ 0 / | + # \ / + # + # See also: jet707 (MIMO example, Boeing 707-321 aircraft model) + + # Written by Kai P. Mueller September 28, 1997 + # Updates + # $Revision: 1.1.1.1 $ + # $Log: ord2.m,v $ + # Revision 1.1.1.1 1998/05/19 20:24:07 jwe + # + # Revision 1.3 1997/12/01 16:51:50 scotte + # updated by Mueller 27 Nov 97 + # +# Revision 1.1 1997/11/11 17:34:06 mueller +# Initial revision +# + + if(nargin != 2 & nargin != 3) + usage("outsys = ord2(nfreq, damp[, gain])") + endif + if (nargout > 1) + usage("outsys = ord2(nfreq, damp[, gain])") + endif + if (nargin == 2) + gain = 1.0; + endif + + w = 2.0 * pi * nfreq; + outsys = ss2sys([-2.0*w*damp, -w; w, 0], [w; 0], [0, gain]); +endfunction diff --git a/scripts/control/outlist.m b/scripts/control/outlist.m new file mode 100644 --- /dev/null +++ b/scripts/control/outlist.m @@ -0,0 +1,77 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function outlist(lmat,tabchar,yd,ilist) +# function outlist(lmat[,tabchar,yd,ilist]) +# +# internal use only; minimal argument checking performed +# +# print an enumerated list of strings +# inputs: +# lmat: matrix of strings (one per row) +# tabchar: tab character (default: none) +# yd: indices of strings to append with the string "(discrete)" +# (used by sysout; minimal checking of this argument) +# yd = [] => all continuous +# ilist: index numbers to print with names +# default: 1:rows(lmat) +# outputs: +# prints the list to the screen, numbering each string in order. + +# A. S. Hodel Dec. 1995 +# $Revision: 1.1.1.1 $ + +save_val = implicit_str_to_num_ok; # save for later +implicit_str_to_num_ok = 1; + +#save for restore later +save_empty = empty_list_elements_ok; +empty_list_elements_ok = 1; + +if( (nargin < 1) || (nargin > 4) ) + usage("outlist(x[,tabchar,yd,ilist])"); +endif + +[m,n] = size(lmat); +if(nargin < 4) + ilist = 1:m; +endif +if(nargin ==1) + empty_list_elements_ok = 1; + tabchar = ""; +endif + +if(nargin < 3) + yd = zeros(1,m); +elseif(isempty(yd)) + yd = zeros(1,m); +endif + +if((m >= 1) && (isstr(lmat))) + for ii=1:m + str = dezero([lmat(ii,:),setstr((yd(ii)*" (discrete)"))]); + #disp(["length(str)=",num2str(length(str))]) + disp([tabchar,num2str(ilist(ii)),": ",str]) + endfor +else + disp([tabchar,"None"]) +endif + +empty_list_elements_ok = save_empty; +implicit_str_to_num_ok = save_val; # restore value +endfunction diff --git a/scripts/control/packedform.m b/scripts/control/packedform.m new file mode 100644 --- /dev/null +++ b/scripts/control/packedform.m @@ -0,0 +1,98 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +# $Revision# +save_var = page_screen_output; +page_screen_output = 1; +disp("Description of system data structure:") +disp("A linear system is stored in a structure, and may be represented in") +disp("ss (state space), tf (transfer function), and/or zp (zero-pole-gain)") +disp("form.") +disp(" ") +disp("variables in all representations:") +disp("inname: string or a matrix of strings containing name(s) of system ") +disp(" inputs"); +disp("n: number of continuous states") +disp("nz: number of discrete states") +disp("outname: string or a matrix of strings containing name(s) of system ") +disp(" outputs"); +disp(" ") +disp("variables in all representations:(cont'd)") +disp("sys: system status vector. This vector indicates both what") +disp(" representation was used to initialize the system data structure") +disp(" (called the primary system type) and which other representations") +disp(" are currently up-to-date with the primary system type.") +disp(" sys(0): primary system type") +disp(" =0 for tf form") +disp(" =1 for zp form") +disp(" =2 for ss form") +disp(" sys(1:3): indicate whether tf, zp, or ss, respectively, are") +disp(" \"up to date\" (whether it is safe to use the variables") +disp(" associated with these representations)") +disp(" sys(1): = 1 if tf variables are up to date") +disp(" = 0 else"); +disp(" sys(2): = 1 if zp variables are up to date") +disp(" = 0 else"); +disp(" sys(3): = 1 if ss variables are up to date") +disp(" = 0 else"); +disp("You can update alternative representations internally with the") +disp("sysupdate command:") +disp(" ") +help sysupdate +disp("===============================") +disp("More variables common to all representations in system data structures:"); +disp("tsam: discrete time sampling interval ") +disp(" =0 if system is purely continuous"); +disp(" >0 if system has discrete-time states or outputs"); +disp("yd: vector indicating which outputs are discrete time (i.e.,") +disp(" produced by D/A converters) and which are continuous time.") +disp(" yd(ii) = 0 if output ii is continuous, = 1 if discrete.") +disp(" ") +disp("===============================") +disp("variables in tf representations (SISO only):") +disp("num: vector of numerator coefficients") +disp("den: vector of denominator coefficients") +disp(" ") +disp("===============================") +disp("variables in zp representations (SISO only):") +disp("zer: vector of system zeros") +disp("pol: vector of system poles") +disp("k: system leading coefficient") +disp(" ") +disp("===============================") +disp("variables in ss representations:") +disp("a,b,c,d: usual state-space matrices. If a system has both") +disp(" continuous and discrete states, they are sorted so that") +disp(" continuous states come first, then discrete states") +disp(" ") +disp("WARNING: some functions (e.g., bode) will not accept systems") +disp("with both discrete and continuous states/outputs") +disp("stname: string or matrix of strings containing name(s) of system") +disp(" states"); +disp("===============================") +disp("Object oriented programming:") +disp("It is recommended that users do not directly access the internal") +disp("variables themselves, but use the interface functions") +disp(" ss2sys sys2ss syschnames") +disp(" tf2sys sys2tf") +disp(" zp2sys sys2zp") +disp("to create/access internal variables. For developmental purposes,") +disp("routines that directly access the internal structure of a system data") +disp("structure either have the string \"sys\" in their name or else") +disp("have the word SYS_INTERNAL in their revision comment block"); +page_screen_output = save_var; diff --git a/scripts/control/packsys.m b/scripts/control/packsys.m new file mode 100644 --- /dev/null +++ b/scripts/control/packsys.m @@ -0,0 +1,71 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function Asys = packsys(a,b,c,d,dflg) + # O B S O L E T E: use ss2sys instead. + # function Asys = packsys(a,b,c[,d,dflg]) + # + # dflg: 0 for continuous time system, 1 for discrete-time system. + # + # defaults: + # D: 0 matrix of appropriate dimension. + # dflg: 0 (continuous time) + # + # Note: discrete-state sampling time is not included! + # + + # Written by R. Bruce Tenison July 29, 1994 + # Modified by David Clem November 13, 1994 + # Modified by A. S. Hodel April 1995 + # $Revision: 1.2 $ + + warning("packsys is obsolete! Use ss2sys instead."); + + if (nargin < 3 || nargin > 5) + disp("packsys: Invalid number of arguments") + endif + + # check dflg + if(nargin == 5) + if( !is_scalar(dflg)) + [m,n] = size(dflg); + error(["packsys: dflg (",num2str(m),",",num2str(n), ... + ") must be a scalar."]); + elseif( (dflg != 0) && (dflg != 1)) + error(["packsys: dflg=",num2str(dflg),"must be 0 or 1"]); + endif + else + #default condition + dflg = 0; + endif + + if (nargin == 3) + # No D matrix. Form a zero one! + [brows,bcols] = size(b); + [crows,ccols] = size(c); + d = zeros(crows,bcols); + endif + + [n,m,p] = abcddim(a,b,c,d); + if (n == -1 || m == -1 || p == -1) + error("packsys: incompatible dimensions") + endif + + Asys = ss2sys(a,b,c,d,dflg); + +endfunction diff --git a/scripts/control/parallel.m b/scripts/control/parallel.m new file mode 100644 --- /dev/null +++ b/scripts/control/parallel.m @@ -0,0 +1,62 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function sysp = parallel(Asys,Bsys) +# function sysp = parallel(Asys,Bsys) +# Forms the parallel connection of two systems. +# +# ____________________ +# | ________ | +# u ----->|----> | Asys |--->|----> y1 +# | | -------- | +# | | ________ | +# |--->|----> | Bsys |--->|----> y2 +# | -------- | +# -------------------- +# Ksys + +# Written by David Clem August 15, 1994 +# completely rewritten Oct 1996 a s hodel +# SYS_INTERNAL accesses members of system structure +# $Revision: 1.1.1.1 $ + + if(nargin != 2) + usage("sysp = parallel(Asys,Bsys)"); + endif + if(! is_struct(Asys) ) + error("1st input argument is not a system data structure") + elseif (! is_struct(Bsys) ) + error("2nd input argument is not a system data structure") + endif + mA = rows(Asys.inname); + mB = rows(Bsys.inname); + if(mA != mB) + error(["Asys has ",num2str(mA)," inputs, Bsys has ",num2str(mB)," inputs"]); + endif + sysp = sysgroup(Asys,Bsys); + sysD = ss2sys([],[],[],[eye(mA);eye(mA)]); + + #disp("sysp=") + #sysout(sysp) + #disp("sysD") + #sysout(sysD) + + sysp = sysmult(sysp,sysD); + sysp = syschnames(sysp,"in",1:mA,Asys.inname); + +endfunction diff --git a/scripts/control/pinv.m b/scripts/control/pinv.m new file mode 100644 --- /dev/null +++ b/scripts/control/pinv.m @@ -0,0 +1,55 @@ +# Copyright (C) 1994 John W. Eaton +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function retval = pinv (X, tol) + +# usage: pinv (X, tol) +# +# Returns the pseudoinverse of X; singular values less than tol are +# ignored. +# +# If the second argument is omitted, it is assumed that +# +# tol = max (size (X)) * sigma_max (X) * eps, +# +# where sigma_max(X) is the maximal singular value of X. + +# Written by Kurt Hornik (hornik@neuro.tuwien.ac.at) March 1993. +# Dept of Probability Theory and Statistics TU Wien, Austria. +# $Revision: 1.1 $ + + if (nargin < 1 || nargin > 2) + error ("usage: pinv (X [, tol])"); + endif + + [U, S, V] = svd(X); + s = diag(S); + + if (nargin == 1) + tol = max (size (X)) * s (1) * eps; + endif + + r = sum (s > tol); + if (r == 0) + retval = zeros (X'); + else + D = diag (ones (r, 1) ./ s (1:r)); + retval = V (:, 1:r) * D * U (:, 1:r)'; + endif + +endfunction diff --git a/scripts/control/place.m b/scripts/control/place.m new file mode 100644 --- /dev/null +++ b/scripts/control/place.m @@ -0,0 +1,133 @@ +# Copyright (C) 1997 Jose Daniel Munoz Frias +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function K = place(sys, P) + +%PLACE K = place(sys,P) Computes the matrix K such that if the state +%is feedback with gain K, then the eigenvalues of the closed loop +%system (i.e. A-BK) are those specified in the vector P. +% +% Version: Beta (May-1997): If you have any comments, please let me know. +% (see the file place.m for my address) +% +% Written by: Jose Daniel Munoz Frias. + +% Universidad Pontificia Comillas +% ICAIdea +% Alberto Aguilera, 23 +% 28015 Madrid, Spain +% +% E-Mail: daniel@dea.icai.upco.es +% +% Phone: 34-1-5422800 Fax: 34-1-5596569 +% +% Algorithm taken from "The Control Handbook", IEEE press pp. 209-212 +# +# code adaped by A.S.Hodel (a.s.hodel@eng.auburn.edu) for use in controls +# toolbox +# $Revision: 1.1.1.1 $ +# $Log: place.m,v $ +# Revision 1.1.1.1 1998/05/19 20:24:08 jwe +# +# Revision 1.2 1997/03/10 21:41:10 scotte +# *** empty log message *** +# +# Revision 1.1 1997/03/10 20:38:53 scotte +# Initial revision +# + + sav_val = empty_list_elements_ok; + empty_list_elements_ok = 1; + # + # check arguments + # + if(!is_struct(sys)) + error("sys must be in system data structure format (see ss2sys)"); + endif + sys = sysupdate(sys,"ss"); # make sure it has state space form up to date + if(!is_controllable(sys)) + error("sys is not controllable."); + elseif( min(size(P)) != 1) + error("P must be a vector") + else + P = reshape(P,length(P),1); # make P a column vector + endif + # system must be purely continuous or discrete + is_digital(sys); + [n,nz,m,p] = sysdimensions(sys); + nx = n+nz; # already checked that it's not a mixed system. + if(m != 1) + error(["sys has ", num2str(m)," inputs; need only 1"]); + endif + + # takes the A and B matrix from the system representation + [A,B]=sys2ss(sys); + sp = length(P); + if(nx == 0) + error("place: A matrix is empty (0x0)"); + elseif(nx != length(P)) + error(["A=(",num2str(nx),"x",num2str(nx),", P has ", num2str(length(P)), ... + "entries."]) + endif + + # arguments appear to be compatible; let's give it a try! + %The second step is the calculation of the characteristic polynomial ofA + PC=poly(A); + + %Third step: Calculate the transformation matrix T that transforms the state + %equation in the controllable canonical form. + + %first we must calculate the controllability matrix M: + M=B; + AA=A; + for n = 2:nx + M(:,n)=AA*B; + AA=AA*A; + endfor + + %second, construct the matrix W + PCO=PC(nx:-1:1); + PC1=PCO; %Matrix to shift and create W row by row + + for n = 1:nx + W(n,:) = PC1; + PC1=[PCO(n+1:nx),zeros(1,n)]; + endfor + + T=M*W; + + %finaly the matrix K is calculated + PD = poly(P); %The desired characteristic polynomial + PD = PD(nx+1:-1:2); + PC = PC(nx+1:-1:2); + + K = (PD-PC)/T; + + %Check if the eigenvalues of (A-BK) are the same specified in P + Pcalc = eig(A-B*K); + + Pcalc = sortcom(Pcalc); + P = sortcom(P); + + if(max( (abs(Pcalc)-abs(P))./abs(P) ) > 0.1) + disp("Place: Pole placed at more than 10% relative error from specified"); + endif + + empty_list_elements_ok = sav_val; +endfunction + diff --git a/scripts/control/polyout.m b/scripts/control/polyout.m new file mode 100644 --- /dev/null +++ b/scripts/control/polyout.m @@ -0,0 +1,76 @@ +# Copyright (C) 1995,1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function y = polyout(c,x) +# +# usage: [y=] polyout(c[,x]) +# +# print formatted polynomial +# c(x) = c(1) * x^n + ... + c(n) x + c(n+1) +# in a string or to the screen (if y is omitted) +# x defaults to the string "s" +# +# SEE ALSO: polyval, polyvalm, poly, roots, conv, deconv, residue, +# filter, polyderiv, polyinteg + +# Written by A. Scottedward Hodel (scotte@eng.auburn.edu) May 1995) +# Nov 1998: Correctly handles complex coefficients +# $Revision$ + + if (nargin < 1 ) || (nargin > 2) || (nargout < 0 ) || (nargout > 1) + usage("[y = ] polyout(c,[x])"); + endif + + if (!is_vector(c)) + error("polyout: first argument must be a vector"); + endif + + if (nargin == 1) + x = 's'; + elseif( ! isstr(x) ) + error("polyout: second argument must be a string"); + endif + + n = length(c); + if(n > 0) + n1 = n+1; + + if( imag(c(1)) ) tmp = com2str(c(1)) + else tmp = num2str(c(1)); endif + + for ii=2:n + if(real(c(ii)) < 0) ns = ' - '; c(ii) = -c(ii); + else ns = ' + '; endif + + if( imag(c(ii)) ) nstr = sprintf("(%s)",com2str(c(ii)) ); + else nstr = num2str(c(ii)); endif + + tmp = sprintf("%s*%s^%d%s%s",tmp,x,n1-ii,ns,nstr); + + endfor + else + tmp = " "; + endif + + if(nargout == 0) + disp(tmp) + else + y = tmp; + endif + +endfunction diff --git a/scripts/control/prompt.m b/scripts/control/prompt.m new file mode 100644 --- /dev/null +++ b/scripts/control/prompt.m @@ -0,0 +1,40 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function prompt(str) +# function prompt([str]) +# Prompt user to continue +# str: input string. Default value: "\n ---- Press a key to continue ---" +# Written by David Clem August 15, 1994 +# Modified A. S. Hodel June 1995 + +# $Revision: 1.2 $ + +if(nargin > 1) + usage("prompt([str])"); +elseif(nargin == 0) + str = "\n ---- Press a key to continue ---"; +elseif ( !isstr(str) ) + error("prompt: input must be a string"); +endif + +disp(str); +fflush(stdout); +kbhit; + +endfunction diff --git a/scripts/control/pzmap.m b/scripts/control/pzmap.m new file mode 100644 --- /dev/null +++ b/scripts/control/pzmap.m @@ -0,0 +1,89 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [zer,pol]=pzmap(sys) +# function [zer,pol]=pzmap(sys) +# Plots the zeros and poles of a system in the complex plane. +# +# inputs: sys: system data structure +# outputs: if omitted, the poles and zeros are plotted on the screen. +# otherwise, pol, zer are returned as the system poles and zeros. +# (see sys2zp for a preferable function call) + +# $Log: pzmap.m,v $ +# Revision 1.2 1998/07/14 22:01:28 hodelas +# Changed to use axis2dlim (new function) to getplot limits; +# Changed gnuplot plotting commands +# + + save_val = implicit_str_to_num_ok; # save for later + save_emp = empty_list_elements_ok; + + implicit_str_to_num_ok = 1; + empty_list_elements_ok = 1; + + if(nargin != 1) + usage("pzmap(sys) or [zer,pol] = pzmap(sys)"); + elseif (!is_struct(sys)); + error("sys must be in system format"); + endif + + [zer,pol] = sys2zp(sys); + + # force to column vectors, split into real, imaginary parts + zerdata = poldata = []; + if(length(zer)) + zer = reshape(zer,length(zer),1); + zerdata = [real(zer(:,1)) imag(zer(:,1))]; + endif + if(length(pol)) + pol = reshape(pol,length(pol),1); + poldata = [real(pol(:,1)) imag(pol(:,1))]; + endif + + # determine continuous or discrete plane + vars = "sz"; + varstr = vars(is_digital(sys) + 1); + + # Plot the data + gset nologscale xy; + if(is_siso(sys)) + title(["Pole-zero map from ",sys.inname," to ", sys.outname]); + endif + xlabel(["Re(",varstr,")"]); + ylabel(["Im(",varstr,")"]); + grid; + + # compute axis limits + axis(axis2dlim([zerdata;poldata])); + grid + # finally, plot the data + if(length(zer) == 0) + plot(poldata(:,1), poldata(:,2),"@12 ;poles (no zeros);"); + elseif(length(pol) == 0) + plot(zerdata(:,1), zerdata(:,2),"@31 ;zeros (no poles);"); + else + plot(zerdata(:,1), zerdata(:,2),"@31 ;zeros;", ... + poldata(:,1), poldata(:,2),"@12 ;poles;"); + endif + replot + + implicit_str_to_num_ok = save_val; # restore value + empty_list_elements_ok = save_emp; + +endfunction diff --git a/scripts/control/qzval.m b/scripts/control/qzval.m new file mode 100644 --- /dev/null +++ b/scripts/control/qzval.m @@ -0,0 +1,33 @@ +# Copyright (C) 1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function lam = qzval(A,B) +# X = qzval (A, B) +# +# compute generalized eigenvalues of the matrix pencil (A - lambda B). +# A and B must be real matrices. +# +# This function is superseded by qz. You should use qz instead. +# + +# A. S. Hodel July 1998 + + warning("qzval is obsolete; calling qz instead") + lam = qz(A,B); +endfunction + diff --git a/scripts/control/rldemo.m b/scripts/control/rldemo.m new file mode 100644 --- /dev/null +++ b/scripts/control/rldemo.m @@ -0,0 +1,297 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function rldemo() +# Octave Controls toolbox demo: Root Locus demo +# Written by David Clem August 15, 1994 +# Updated by John Ingram December 1996 +# $Revision: 1.2 $ + + while (1) + clc + k = menu("Octave Root Locus Demo", ... + "Display continuous system's open loop poles and zeros (pzmap)", ... + "Display discrete system's open loop poles and zeros (pzmap)", ... + "Display root locus diagram of SISO continuous system (rlocus)", ... + "Display root locus diagram of SISO discrete system (rlocus)", ... + "Return to main demo menu"); + gset autoscale + if (k == 1) + clc + help pzmap + prompt + + clc + disp("Display continuous system's open loop poles and zeros (pzmap)\n"); + disp("Example #1, Consider the following continuous transfer function:"); + cmd = "sys1 = tf2sys([1.5 18.5 6],[1 4 155 302 5050]);"; + disp(cmd); + eval(cmd); + cmd ="sysout(sys1);"; + disp(cmd); + eval(cmd); + disp("\nPole-zero form can be obtained as follows:"); + cmd = "sysout(sys1,""zp"");"; + disp(cmd); + eval(cmd); + disp("View the system's open loop poles and zeros with the command:") + cmd = "pzmap(sys1);"; + run_cmd + prompt + + clc + disp("Example #2, Consider the following set of poles and zeros:"); + cmd = "sys2 = zp2sys([-1 5 -23],[-1 -10 -7+5i -7-5i],5);"; + disp(cmd); + eval(cmd); + cmd = "sysout(sys2);"; + disp(cmd); + eval(cmd); + disp("\nThe pzmap command for the zp form is the same as the tf form:") + cmd = "pzmap(sys2);"; + run_cmd; + disp("\nThe internal representation of the system is not important;"); + disp("pzmap automatically sorts it out internally."); + prompt; + + clc + disp("Example #3, Consider the following state space system:\n"); + cmd = "sys3=ss2sys([0 1; -10 -11], [0;1], [0 -2], 1);"; + disp(cmd); + eval(cmd); + cmd = "sysout(sys3);"; + disp(cmd); + eval(cmd); + disp("\nPole-zero form can be obtained as follows:"); + cmd = "sysout(sys3,""zp"");"; + disp(cmd); + eval(cmd); + disp("\nOnce again, the pzmap command is the same:"); + cmd = "pzmap(sys3);"; + run_cmd; + prompt; + + closeplot + clc + + elseif (k == 2) + clc + help pzmap + prompt + + clc + disp("\nDisplay discrete system's open loop poles and zeros (pzmap)\n"); + disp("First we must define a sampling time, as follows:\n"); + cmd = "Tsam = 1;"; + run_cmd; + disp("Example #1, Consider the following discrete transfer function:"); + cmd = "sys1 = tf2sys([1.05 -0.09048],[1 -2 1],Tsam);"; + disp(cmd); + eval(cmd); + cmd ="sysout(sys1);"; + disp(cmd); + eval(cmd); + disp("\nPole-zero form can be obtained as follows:"); + cmd = "sysout(sys1,""zp"");"; + disp(cmd); + eval(cmd); + disp("View the system's open loop poles and zeros with the command:") + cmd = "pzmap(sys1);"; + run_cmd + prompt + + clc + disp("Example #2, Consider the following set of discrete poles and zeros:"); + cmd = "sys2 = zp2sys([-0.717],[1 -0.368],3.68,Tsam);"; + disp(cmd); + eval(cmd); + cmd = "sysout(sys2);"; + disp(cmd); + eval(cmd); + disp("\nThe pzmap command for the zp form is the same as the tf form:") + cmd = "pzmap(sys2);"; + run_cmd; + disp("\nThe internal representation of the system is not important;"); + disp("pzmap automatically sorts it out internally."); + prompt; + + clc + disp("Example #3, Consider the following discrete state space system:\n"); + cmd = "sys3=ss2sys([1 0.0952;0 0.905], [0.00484; 0.0952], [1 0], 0, Tsam);"; + disp(cmd); + eval(cmd); + cmd = "sysout(sys3);"; + disp(cmd); + eval(cmd); + disp("\nPole-zero form can be obtained as follows:"); + cmd = "sysout(sys3,""zp"");"; + disp(cmd); + eval(cmd); + disp("\nOnce again, the pzmap command is the same:"); + cmd = "pzmap(sys3);"; + run_cmd; + prompt; + + closeplot + clc + + elseif (k == 3) + clc + help rlocus + prompt; + + clc + disp("Display root locus of a continuous SISO system (rlocus)\n") + disp("Example #1, Consider the following continuous transfer function:"); + cmd = "sys1 = tf2sys([1.5 18.5 6],[1 4 155 302 5050]);"; + disp(cmd); + eval(cmd); + cmd ="sysout(sys1);"; + disp(cmd); + eval(cmd); + disp("\nPole-zero form can be obtained as follows:"); + cmd = "sysout(sys1,""zp"");"; + disp(cmd); + eval(cmd); + disp("\nWhen using rlocus, inital system poles are displayed as X's.") + disp("Moving poles are displayed as diamonds. Zeros are displayed as") + disp("boxes. The simplest form of the rlocus command is as follows:") + cmd = "rlocus(sys1);"; + run_cmd + disp("\nrlocus automatically selects the minimum and maximum gains based") + disp("on the real-axis locus breakpoints. The plot limits are chosen") + disp("to be no more than 10 times the maximum magnitude of the open") + disp("loop poles/zeros."); + prompt + + clc + disp("Example #2, Consider the following set of poles and zeros:"); + cmd = "sys2 = zp2sys([],[0 -20 -2 -0.1],5);"; + disp(cmd); + eval(cmd); + cmd = "sysout(sys2);"; + disp(cmd); + eval(cmd); + disp("\nThe rlocus command for the zp form is the same as the tf form:") + cmd = "rlocus(sys2);"; + run_cmd; + disp("\nThe internal representation of the system is not important;"); + disp("rlocus automatically sorts it out internally."); + prompt; + + clc + disp("Example #3, Consider the following state space system:\n"); + cmd = "sys3=ss2sys([0 1; -10 -11], [0;1], [0 -2], 0);"; + disp(cmd); + eval(cmd); + cmd = "sysout(sys3);"; + disp(cmd); + eval(cmd); + disp("\nPole-zero form can be obtained as follows:"); + cmd = "sysout(sys3,""zp"");"; + disp(cmd); + eval(cmd); + disp("\nOnce again, the rlocus command is the same:"); + cmd = "rlocus(sys3);"; + run_cmd; + + disp("\nNo matter what form the system is in, the rlocus command works the"); + disp("the same."); + prompt; + + closeplot + clc + + elseif (k == 4) + clc + help rlocus + prompt + + clc + disp("Display root locus of a discrete SISO system (rlocus)\n") + disp("First we must define a sampling time, as follows:\n"); + cmd = "Tsam = 1;"; + run_cmd; + disp("Example #1, Consider the following discrete transfer function:"); + cmd = "sys1 = tf2sys([1.05 -0.09048],[1 -2 1],Tsam);"; + disp(cmd); + eval(cmd); + cmd ="sysout(sys1);"; + disp(cmd); + eval(cmd); + disp("\nPole-zero form can be obtained as follows:"); + cmd = "sysout(sys1,""zp"");"; + disp(cmd); + eval(cmd); + disp("\nWhen using rlocus, inital system poles are displayed as X's.") + disp("Moving poles are displayed as diamonds. Zeros are displayed as") + disp("boxes. The simplest form of the rlocus command is as follows:") + cmd = "rlocus(sys1);"; + run_cmd + disp("\nrlocus automatically selects the minimum and maximum gains based") + disp("on the real-axis locus breakpoints. The plot limits are chosen") + disp("to be no more than 10 times the maximum magnitude of the open") + disp("loop poles/zeros."); + prompt + + clc + disp("Example #2, Consider the following set of discrete poles and zeros:"); + cmd = "sys2 = zp2sys([-0.717],[1 -0.368],3.68,Tsam);"; + disp(cmd); + eval(cmd); + cmd = "sysout(sys2);"; + disp(cmd); + eval(cmd); + disp("\nThe rlocus command for the zp form is the same as the tf form:") + cmd = "rlocus(sys2);"; + run_cmd; + disp("\nThe internal representation of the system is not important;"); + disp("rlocus automatically sorts it out internally. Also, it does not"); + disp("matter if the system is continuous or discrete. rlocus also sorts"); + disp("this out automatically"); + prompt; + + clc + disp("Example #3, Consider the following discrete state space system:\n"); + cmd = "sys3=ss2sys([1 0.0952;0 0.905], [0.00484; 0.0952], [1 0], 0, Tsam);"; + disp(cmd); + eval(cmd); + cmd = "sysout(sys3);"; + disp(cmd); + eval(cmd); + disp("\nPole-zero form can be obtained as follows:"); + cmd = "sysout(sys3,""zp"");"; + disp(cmd); + eval(cmd); + disp("\nOnce again, the rlocus command is the same:"); + cmd = "rlocus(sys3);"; + run_cmd; + + disp("\nNo matter what form the system is in, the rlocus command works the"); + disp("the same."); + + prompt; + + closeplot + clc + + elseif (k == 5) + return + endif + endwhile +endfunction diff --git a/scripts/control/rlocus.m b/scripts/control/rlocus.m new file mode 100644 --- /dev/null +++ b/scripts/control/rlocus.m @@ -0,0 +1,199 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [rldata,k_break,rlpol,gvec,real_ax_pts] = rlocus(sys,increment,min_k,max_k) + # [rldata,k_break,rlpol,gvec,real_ax_pts] = rlocus(sys,increment,min_k,max_k) + # Displays root locus plot of the specified SISO system. + # + # ----- --- -------- + # --->| + |---|k|---->| SISO |-----------> + # ----- --- -------- | + # - ^ | + # |_____________________________| + # + #inputs: sys = system data structure + # min_k, max_k,increment: minimum, maximum values of k and + # the increment used in computing gain values + # outputs: plots the root locus to the screen. + # rldata: Data points plotted column 1: real values, column 2: imaginary + # values) + # k_break: gains for real axis break points. + # rlpol: complex vector: column ii of pol is the set of poles + # corresponding to to gain gvec(ii) + # gvec: gains used to compute root locus + # real_ax_pts: breakpoints of the real axis locus. + + # Convert the input to a transfer function if necessary + # Written by Clem and Tenison + # Updated by Kristi McGowan July 1996 for intelligent gain selection + # Updated by John Ingram July 1996 for systems + # $Revision: 1.7 $ + + if (nargin < 1) | (nargin > 4) + usage("rlocus(sys[,inc,mink,maxk])"); + endif + + [num,den] = sys2tf(sys); # extract numerator/denom polyomials + lnum = length(num); lden = length(den); + if(lden < 2) + error(sprintf("length of derivative=%d, doesn't make sense",lden)); + elseif(lnum == 1) + num = [0 num]; # so that derivative is shortened by one + endif + + # root locus plot axis limits + + # compute real axis locus breakpoints + # compute the derivative of the numerator and the denominator + dern=polyderiv(num); derd=polyderiv(den); + + # compute real axis breakpoints + real_ax_pol = conv(den,dern) - conv(num,derd); + real_ax_pts = roots(real_ax_pol); + if(isempty(real_ax_pts)) + k_break = []; + maxk = 0; + else + # compute gains that achieve the breakpoints + c1 = polyval(num,real_ax_pts); + c2 = polyval(den,real_ax_pts); + k_break = -real(c2 ./ c1); + maxk = max(max(k_break,0)); + endif + + # compute gain ranges based on computed K values + if(maxk == 0) maxk = 1; + else maxk = 1.1*maxk; endif + mink = 0; + ngain = 20; + + # check for input arguments: + if (nargin > 2) mink = min_k; endif + if (nargin > 3) maxk = max_k; endif + if (nargin > 1) + if(increment <= 0) error("increment must be positive"); + else + ngain = (maxk-mink)/increment; + endif + endif + + # vector of gains + ngain = max(3,ngain); + gvec = linspace(mink,maxk,ngain); + + # Find the open loop zeros and the initial poles + rlzer = roots(num); + + # update num to be the same length as den + lnum = length(num); if(lnum < lden) num = [zeros(1,lden - lnum),num]; endif + + # compute preliminary pole sets + nroots = lden-1; + for ii=1:ngain + gain = gvec(ii); + rlpol(1:nroots,ii) = vec(sortcom(roots(den + gain*num))); + endfor + + # compute axis limits (isolate asymptotes) + olpol = roots(den); + real_axdat = union(real(rlzer), real(union(olpol,real_ax_pts)) ); + rmin = min(real_axdat); rmax = max(real_axdat); + + rlpolv = [vec(rlpol); vec(real_axdat)]; + idx = find(real(rlpolv) >= rmin & real(rlpolv) <= rmax); + axlim = axis2dlim([real(rlpolv(idx)),imag(rlpolv(idx))]); + xmin = axlim(1); + xmax = axlim(2); + + # set smoothing tolerance per axis limits + smtol = 0.01*max(abs(axlim)); + + # smooth poles if necessary, up to maximum of 1000 gain points + # only smooth points within the axis limit window + # smoothing done if max_k not specified as a command argument + done=(nargin == 4); # perform a smoothness check + while((!done) & ngain < 1000) + done = 1 ; # assume done + dp = abs(diff(rlpol'))'; + maxd = max(dp); + # search for poles in the real axis limits whose neighbors are distant + idx = find(maxd > smtol); + for ii=1:length(idx) + i1 = idx(ii); g1 = gvec(i1); p1 = rlpol(:,i1); + i2 = idx(ii)+1; g2 = gvec(i2); p2 = rlpol(:,i2); + + # isolate poles in p1, p2 that are inside the real axis limits + bidx = find( (real(p1) >= xmin & real(p1) <= xmax) ... + | (real(p2) >= xmin & real(p2) <= xmax) ); + if(!isempty(bidx)) + p1 = p1(bidx); + p2 = p2(bidx); + if( max(abs(p2-p1)) > smtol) + newg = linspace(g1,g2,5); + newg = newg(2:4); + if(isempty(newg)) + printf("rlocus: empty newg") + g1 + g2 + i1 + i2 + idx_i1 = idx(ii) + gvec_i1 = gvec(i1:i2) + delta_vec_i1 = diff(gvec(i1:i2)) + prompt + endif + gvec = [gvec,newg]; + done = 0; # need to process new gains + endif + endif + endfor + + # process new gain values + ngain1 = length(gvec); + for ii=(ngain+1):ngain1 + gain = gvec(ii); + rlpol(1:nroots,ii) = vec(sortcom(roots(den + gain*num))); + endfor + + [gvec,idx] = sort(gvec); + rlpol = rlpol(:,idx); + ngain = length(gvec); + endwhile + + # Plot the data + if(nargout == 0) + rlpolv = vec(rlpol); + idx = find(real(rlpolv) >= xmin & real(rlpolv) <= xmax); + axdata = [real(rlpolv(idx)),imag(rlpolv(idx))]; + axlim = axis2dlim(axdata); + axlim(1:2) = [xmin xmax]; + gset nologscale xy; + grid("on"); + rldata = [real(rlpolv), imag(rlpolv) ]; + axis(axlim); + [stn,inname,outname] = sysgetsignals(sys); + xlabel(sprintf("Root locus from %s to %s, gain=[%f,%f]: Real axis", ... + dezero(inname),dezero(outname),gvec(1),gvec(ngain))); + ylabel("Imag. axis"); + + plot(real(rlpolv),imag(rlpolv),".1;locus points;", ... + real(olpol),imag(olpol),"x2;open loop poles;", ... + real(rlzer),imag(rlzer),"o3;zeros;"); + rldata = []; + endif +endfunction diff --git a/scripts/control/rotg.m b/scripts/control/rotg.m new file mode 100644 --- /dev/null +++ b/scripts/control/rotg.m @@ -0,0 +1,28 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [c,s] = rotg(a,b) + #function [c,s] = rotg(a,b) + # givens rotation calculation + # + # NOTE: Use [c,s] = givens(a,b) instead. + + # $Revision: 1.1 $ + + [c,s] = givens(a,b); +endfunction diff --git a/scripts/control/run_cmd.m b/scripts/control/run_cmd.m new file mode 100644 --- /dev/null +++ b/scripts/control/run_cmd.m @@ -0,0 +1,32 @@ +# run_cmd: short script used in demos +# prints string cmd to the screen, then executes after a pause + +# $Revision: 1.1.1.1 $ +# $Log: run_cmd.m,v $ +# Revision 1.1.1.1 1998/05/19 20:24:05 jwe +# +# Revision 1.4 1997/02/20 16:07:26 hodel +# added "fflush" after disp("executing") +# +# Revision 1.3 1997/02/12 15:38:14 hodel +# added separator after command execution +# +# added blank line after eval(cmd) +# +# Revision 1.1 1997/02/12 11:35:08 hodel +# Initial revision +# +# Revision 1.3 1997/02/07 15:44:13 scotte +# Added "executing" string so that users would know that the command was +# being processed +# + +disp(["Command: ",cmd]) +puts("Press a key to execute command"); +fflush(stdout); +kbhit(); +disp(" executing"); +fflush(stdout); +eval(cmd); +disp("---") +disp(" ") diff --git a/scripts/control/series.m b/scripts/control/series.m new file mode 100644 --- /dev/null +++ b/scripts/control/series.m @@ -0,0 +1,97 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [a,b,c,d] = series(a1,b1,c1,d1,a2,b2,c2,d2) +# Forms the series connection of two systems. +# +# Superseded by sysmult. Do not use this routine! +# used internally in zp2ss +# +# Type of input: Transfer functions +# Command: [num,den]=series(num1,den1,num2,den2) +# Forms the series representation of the two transfer functions. +# +# Type of input: State space systems +# Command: [a,b,c,d]=series(a1,b1,c1,d1,a2,b2,c2,d2) +# Forms the series representation of the two state space system arguments. +# The series connected system will have the inputs of system 1 and the +# outputs of system 2. +# +# Type of input: system data structure +# Command: syst=series(syst1,syst2) +# Forms the series representation of the two mu system arguments. +# Written by David Clem August 15, 1994 + +# If two arguments input, take care of mu system case +# $Revision: 1.1.1.1 $ + + warning("series is superseded by sysmult; use sysmult instead.") + + muflag = 0; + if(nargin == 2) + temp=b1; + [a1,b1,c1,d1]=sys2ss(a1); + [a2,b2,c2,d2]=sys2ss(temp); + muflag = 1; + endif + +# If four arguments input, put two transfer functions in series + + if(nargin == 4) + a = conv(a1,c1); % was conv1 + b = conv(b1,d1); % was conv1 + c = 0; + d = 0; + +# Find series combination of 2 state space systems + + elseif((nargin == 8)||(muflag == 1)) + +# check matrix dimensions + + [n1,m1,p1] = abcddim(a1,b1,c1,d1); + [n2,m2,p2] = abcddim(a2,b2,c2,d2); + + if((n1 == -1) || (n2 == -1)) + error("Incorrect matrix dimensions"); + endif + +# check to make sure the number of outputs of system1 equals the number +# of inputs of system2 + + if(p1 ~= m2) + error("System 1 output / System 2 input connection sizes do not match"); + endif + +# put the two state space systems in series + + a = [a1 zeros(rows(a1),columns(a2));b2*c1 a2]; + b = [b1;b2*d1]; + c = [d2*c1 c2]; + d = [d2*d1]; + +# take care of mu output + + if(muflag == 1) + a=ss2sys(a,b,c,d); + b=c=d=0; + endif + endif + +endfunction + diff --git a/scripts/control/sortcom.m b/scripts/control/sortcom.m new file mode 100644 --- /dev/null +++ b/scripts/control/sortcom.m @@ -0,0 +1,79 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [yy,idx] = sortcom(xx,opt) +# [yy,idx] = sortcom(xx[,opt]): sort a complex vector +# xx: complex vector +# opt: sorting option: +# "re": real part (default) +# "mag": by magnitude +# "im": by imaginary part +# +# if opt != "im" then values with common real part/magnitude are +# sorted by imaginary part, i.e. a - jb followed by a + jb. +# [Complex conjugate pairs may not be grouped consecutively if more than 2 +# numbers share a common real part/magnitude] +# yy: sorted values +# idx: permutation vector: yy = xx(idx) + +# Written by A. S. Hodel June 1995 +# $Revision: 1.4 $ + + if( nargin < 1 | nargin > 2 ) + usage("yy = sortcom(xx[,opt]"); + elseif( !(is_vector(xx) | isempty(xx) )) + error("sortcom: first argument must be a vector"); + endif + + if(nargin == 1) opt = "re"; + else + if (!isstr(opt)) + error("sortcom: second argument must be a string"); + endif + endif + + if(strcmp(opt,"re")) datavec = real(xx); + elseif(strcmp(opt,"im")) datavec = imag(xx); + elseif(strcmp(opt,"mag")) datavec = abs(xx); + else + error(["sortcom: illegal option = ", opt]) + endif + + [datavec,idx] = sort(datavec); + yy= xx(idx); + + if(strcmp(opt,"re") | strcmp(opt,"mag")) + # sort so that complex conjugate pairs appear together + + ddiff = diff(datavec); + zidx = find(ddiff == 0); + + # sort common datavec values + if(!isempty(zidx)) + for iv=create_set(datavec(zidx)) + vidx = find(datavec == iv); + [vals,imidx] = sort(imag(yy(vidx))); + yy(vidx) = yy(vidx(imidx)); + idx(vidx) = idx(vidx(imidx)); + endfor + endif + + endif + +endfunction + diff --git a/scripts/control/ss2sys.m b/scripts/control/ss2sys.m new file mode 100644 --- /dev/null +++ b/scripts/control/ss2sys.m @@ -0,0 +1,219 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function outsys = ss2sys (a,b,c,d,tsam,n,nz,stname,inname,outname,outlist) + # outsys = ss2sys (a,b,c{,d,tsam,n,nz,stname,inname,outname,outlist}) + # Create system structure from state-space data. May be continous, + # discrete, or mixed (sampeld-data) + # inputs: + # a, b, c, d: usual state space matrices. + # default: d = zero matrix + # tsam: sampling rate. Default: tsam = 0 (continuous system) + # n, nz: number of continuous, discrete states in the system + # default: tsam = 0: n = rows(a), nz = 0 + # tsam > 0: n = 0, nz = rows(a), n + # see below for system partitioning + # stname: string matrix of state signal names + # default (stname=[] on input): x_n for continuous states, + # xd_n for discrete states + # inname: string matrix of input signal names + # default (inname = [] on input): u_n + # outname: string matrix of input signal names + # default (outname = [] on input): y_n + # outlist: list of indices of outputs y that are sampled + # default: (tsam = 0) outlist = [] + # (tsam > 0) outlist = 1:rows(c) + # Unlike states, discrete/continous outputs may appear + # in any order. + # Note: sys2ss returns a vector yd where + # yd(outlist) = 1; all other entries of yd are 0. + # + # System partitioning: Suppose for simplicity that outlist specified + # that the first several outputs were continuous and the remaining outputs + # were discrete. Then the system is partitioned as + # x = [ xc ] (n x 1) + # [ xd ] (nz x 1 discrete states) + # a = [ acc acd ] b = [ bc ] + # [ adc add ] [ bd ] + # c = [ ccc ccd ] d = [ dc ] + # [ cdc cdd ] d = [ dd ] (cdc = c(outlist,1:n), etc.) + # + # with dynamic equations: + # + # d/dt xc(t) = acc*xc(t) + acd*xd(k*tsam) + bc*u(t) + # xd((k+1)*tsam) = adc*xc(k*tsam) + add*xd(k*tsam) + bd*u(k*tsam) + # yc(t) = ccc*xc(t) + ccd*xd(k*tsam) + dc*u(t) + # yd(k*tsam) = cdc*xc(k*tsam) + cdd*xd(k*tsam) + dd*u(k*tsam) + # + # signal partitions: + # | continuous | discrete | + # ------------------------------------------------------ + # states | stname(1:n,:) | stname((n+1):(n+nz),:) | + # ------------------------------------------------------ + # outputs | outname(cout,:) | outname(outlist,:) | + # ------------------------------------------------------ + # + # where cout = list if indices in 1:rows(p) not contained in outlist. + # + + # Written by John Ingram (ingraje@eng.auburn.edu) July 20, 1996 + # $Revision: 1.3 $ + # $Log: ss2sys.m,v $ + # Revision 1.3 1998/07/01 20:55:08 hodelas + # Updated sysgroup, sys2ss, ss2sys to use system structure interface + # + # Revision 1.4 1997/03/11 15:19:27 scotte + # fixed warning message about inname dimensions a.s.hodel@eng.auburn.edu + # + + save_val = implicit_str_to_num_ok; # save for later + implicit_str_to_num_ok = 1; + + # Test for correct number of inputs + if ((nargin < 3) | (nargin > 11)) + error("Incorrect number of arguments"); + endif + + # verify A, B, C, D arguments + # If D is not specified, set it to a zero matrix of appriate dimension. + if (nargin == 3) + d = zeros(rows(c) , columns(b)); + elseif (isempty(d)) + d = zeros(rows(c) , columns(b)); + endif + + # Check the dimensions + [na,m,p] = abcddim(a,b,c,d); + + # If dimensions are wrong, exit function + if (m == -1) + error("a,b,c,d matrix dimensions are not compatible"); + endif + + # check for tsam input + if(nargin < 5) + tsam = 0; + elseif( !( is_sample(tsam) | (tsam == 0) ) ) + error("tsam must be a nonnegative real scalar"); + endif + + # check for continuous states + if( (nargin < 6) & (tsam == 0) ) + n = na; + elseif(nargin < 6) + n = 0; + elseif( (!is_scalar(n)) | (n < 0 ) | (n != round(n)) ) + if(is_scalar(n)) + error(["illegal value of n=",num2str(n)]); + else + error(["illegal value of n=(",num2str(rows(n)),"x", ... + num2str(columns(n)),")"]); + endif + endif + + # check for num discrete states + if( (nargin < 7) & (tsam == 0)) + nz = 0; + elseif(nargin < 7) + nz = na - n; + elseif( (!is_scalar(nz)) | (nz < 0 ) | (nz != round(nz)) ) + if(is_scalar(nz)) + error(["illegal value of nz=",num2str(nz)]); + else + error(["illegal value of nz=(",num2str(rows(nz)),"x", ... + num2str(columns(nz)),")"]); + endif + endif + + #check for total number of states + if( (n + nz) != na ) + error(["Illegal: a is ",num2str(na),"x",num2str(na),", n=", ... + num2str(n),", nz=",num2str(nz)]); + endif + + # check for state names + if(nargin < 8) + stname = sysdefstname(n,nz); + else + nr = rows(stname); + if(na != nr) + error(["ss2sys: ",num2str(na),"system states,", ... + num2str(nr)," state names provided"]); + endif + endif + + #check for input names + if(nargin < 9) + inname = sysdefioname(m,"u"); + elseif( !isstr(inname) ) + warning("ss2sys: inname=") + disp(inname); + error("inname must be a string or string matrix."); + elseif(rows(inname) != m ) + warning("ss2sys: inname=") + disp(inname); + error(["inname has ",num2str(rows(inname))," rows, sys has ", ... + num2str(m)," inputs."]); + endif + + #check for output names + if(nargin < 10) + outname = sysdefioname(p,"y"); + elseif( !isstr(outname) ) + warning("ss2sys: outname=") + disp(outname); + error("outname must be a string or string matrix."); + elseif(rows(outname) != p ) + warning("ss2sys: outname=") + disp(outname); + error(["outname has ",num2str(rows(outname))," rows, sys has ", ... + num2str(p)," outputs."]); + endif + + # set up yd + if(nargin < 11) + yd = ones(1,p)*(tsam > 0); + else + yd = zeros(1,p); + yd(outlist) = ones(1,length(outlist)); + if(max(outlist) > p) + error(["max outlist index=",num2str(max(outlist)), ... + " exceeds number of outputs=",num2str(p)]); + endif + endif + + # Construct the state space system + outsys.a = a; + outsys.b = b; + outsys.c = c; + outsys.d = d; + + outsys.n = n; + outsys.nz = nz; + outsys.tsam = tsam; + outsys.yd = yd; + + outsys.stname = stname; + outsys.inname = inname; + outsys.outname = outname; + + # Set the system vector: active = 2(ss), updated = [0 0 1]; + outsys.sys = [2 0 0 1]; + + implicit_str_to_num_ok = save_val; # restore value +endfunction diff --git a/scripts/control/ss2tf.m b/scripts/control/ss2tf.m new file mode 100644 --- /dev/null +++ b/scripts/control/ss2tf.m @@ -0,0 +1,74 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [num,den] = ss2tf(a,b,c,d) +# [num,den] = ss2tf(a,b,c,d) +# Conversion from tranfer function to state-space. +# The state space system +# . +# x = Ax + Bu +# y = Cx + Du +# +# is converted to a transfer function +# +# num(s) +# G(s)=------- +# den(s) +# +# used internally in system data structure manipulations + +# Written by R. Bruce Tenison (June 24, 1994) btenison@eng.auburn.edu +# a s hodel: modified to allow for pure gain blocks Aug 1996 +# $Revision: 1.1.1.1 $ + +# Check args + [n,m,p] = abcddim(a,b,c,d); + if (n == -1) + num = []; + den = []; + error("ss2tf: Non compatible matrix arguments"); + elseif ( (m != 1) | (p != 1)) + num = []; + den = []; + error(["ss2tf: not SISO system: m=",num2str(m)," p=",num2str(p)]); + endif + + if(n == 0) + # gain block only + num = d; + den = 1; + else + # First, get the denominator coefficients + den = poly(a); + + # Get the zeros of the system + [zz,g] = tzero(a,b,c,d); + + # Form the Numerator (and include the gain) + if (!isempty(zz)) + num = g * poly(zz); + else + num = g; + endif + + # the coefficients must be real + den = real(den); + num = real(num); + endif +endfunction + diff --git a/scripts/control/ss2zp.m b/scripts/control/ss2zp.m new file mode 100644 --- /dev/null +++ b/scripts/control/ss2zp.m @@ -0,0 +1,53 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [zer,pol,k] = ss2zp(a,b,c,d) +# Converts a state space representation to a set of poles and zeros. +# +# [pol,zer,k] = ss2zp(a,b,c,d) returns the poles and zeros of the state space +# system (a,b,c,d). K is a gain associated with the zeros. +# +# used internally in system data structure manipulations + +# Written by David Clem August 15, 1994 +# Hodel: changed order of output arguments to zer, pol, k. July 1996 +# a s hodel: added argument checking, allow for pure gain blocks aug 1996 +# $Revision: 1.1.1.1 $ + + if(nargin != 4) + usage("[zer,pol,k] = ss2zp(a,b,c,d)"); + endif + + [n,m,p] = abcddim(a,b,c,d); + if (n == -1) + error("ss2tf: Non compatible matrix arguments"); + elseif ( (m != 1) | (p != 1)) + error(["ss2tf: not SISO system: m=",num2str(m)," p=",num2str(p)]); + endif + + if(n == 0) + # gain block only + k = d; + zer = pol = []; + else + # First, get the denominator coefficients + [zer,k] = tzero(a,b,c,d); + pol = eig(a); + endif +endfunction + diff --git a/scripts/control/starp.m b/scripts/control/starp.m new file mode 100644 --- /dev/null +++ b/scripts/control/starp.m @@ -0,0 +1,129 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [sys] = starp(P, K, ny, nu); +# +# [sys] = starp(P, K, ny, nu) +# +# Redheffer star product or upper/lower LFT, respectively. +# +# +# +-------+ +# --------->| |---------> +# | P | +# +--->| |---+ ny +# | +-------+ | +# +-------------------+ +# | | +# +----------------+ | +# | | +# | +-------+ | +# +--->| |------+ nu +# | K | +# --------->| |---------> +# +-------+ +# +# If ny and nu "consume" all inputs and outputs of K then the result +# is a lower fractional transformation. If ny and nu "consume" all +# inputs and outputs of P then the result is an upper fractional +# transformation. +# +# ny and/or nu may be negative (= negative feedback) + +# Written by Kai Mueller May 1998 +# $Revision: 1.1.1.1 $ +# $Log: starp.m,v $ +# Revision 1.1.1.1 1998/05/19 20:24:08 jwe +# +# Revision 1.1 1998/05/05 17:04:20 scotte +# Initial revision +# +# Revision 1.1 1998/05/04 15:12:14 mueller +# Initial revision +# + + if((nargin != 2) && (nargin != 4)) + usage("[sys] = starp(P, K, ny, nu)"); + endif + if (!is_struct(P)) + error("---> P must be in system data structure"); + endif + if (!is_struct(K)) + error("---> K must be in system data structure"); + endif + + P = sysupdate(P, "ss"); + [n, nz, mp, pp] = sysdimensions(P); + np = n + nz; + K = sysupdate(K, "ss"); + [n, nz, mk, pk] = sysdimensions(K); + nk = n + nz; + ny_sign = 1; + nu_sign = 1; + if (nargin == 2) + # perform a LFT of P and K (upper or lower) + ny = min([pp mk]); + nu = min([pk mp]); + else + if (ny < 0) + ny = -ny; + ny_sign = -1; + endif + if (nu < 0) + nu = -nu; + nu_sign = -1; + endif + endif + if (ny > pp) + error("---> P has not enough outputs."); + endif + if (nu > mp) + error("---> P has not enough inputs."); + endif + if (ny > mk) + error("---> K has not enough inputs."); + endif + if (nu > pk) + error("---> K has not enough outputs."); + endif + nwp = mp - nu; + nzp = pp - ny; + nwk = mk - ny; + nzk = pk - nu; + if ((nwp + nwk) < 1) + error("---> no inputs left for star product."); + endif + if ((nzp + nzk) < 1) + error("---> no outputs left for star product."); + endif + + # checks done, form sys + if (nzp) Olst = [1:nzp]; endif + if (nzk) Olst = [Olst pp+nu+1:pp+pk]; endif + if (nwp) Ilst = [1:nwp]; endif + if (nwk) Ilst = [Ilst mp+ny+1:mp+mk]; endif + Clst = zeros(ny+nu,2); + for ii = 1:nu + Clst(ii,:) = [nwp+ii nu_sign*(pp+ii)]; + endfor + for ii = 1:ny + Clst(nu+ii,:) = [mp+ii ny_sign*(nzp+ii)]; + endfor + sys = buildssic(Clst,[],Olst,Ilst,P,K); + +endfunction diff --git a/scripts/control/step.m b/scripts/control/step.m new file mode 100644 --- /dev/null +++ b/scripts/control/step.m @@ -0,0 +1,86 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [y, t] = step(sys, inp, tstop, n) +# step: Step response for a linear system. +# The system can be discrete or multivariable (or both). +# +# [y, t] = step(sys[, inp, tstop, n]) +# Produces a plot or the step response data for system sys. +# +# The argument tstop (scalar value) denotes the time when the +# simulation should end. The Parameter n is the number of data values. +# Both parameters tstop and n can be ommitted and will be +# computed from the eigenvalues of the A-Matrix. +# +# When the step function is invoked with the output parameter y +# a plot is not displayed. +# +# See also: impulse, stepimp + +# Written by Kai P. Mueller September 30, 1997 +# based on lsim.m of Scottedward Hodel +# modified by +# $Revision: 1.1.1.1 $ +# $Log: step.m,v $ +# Revision 1.1.1.1 1998/05/19 20:24:08 jwe +# +# Revision 1.3 1997/12/01 16:51:50 scotte +# updated by Mueller 27 Nov 97 +# +# Revision 1.1 1997/11/11 17:34:34 mueller +# Initial revision +# + + if((nargin < 1) || (nargin > 4)) + usage("[y, u] = step(sys[, inp, tstop, n])"); + endif + + if(nargout > 2) + usage("[y, u] = step(sys[, inp, tstop, n])"); + endif + + if(!is_struct(sys)) + error("step: sys must be a system data structure."); + endif + + if (nargout == 0) + switch (nargin) + case (1) + stepimp(1, sys); + case (2) + stepimp(1, sys, inp); + case (3) + stepimp(1, sys, inp, tstop); + case (4) + stepimp(1, sys, inp, tstop, n); + endswitch + else + switch (nargin) + case (1) + [y, t] = stepimp(1, sys); + case (2) + [y, t] = stepimp(1, sys, inp); + case (3) + [y, t] = stepimp(1, sys, inp, tstop); + case (4) + [y, t] = stepimp(1, sys, inp, tstop, n); + endswitch + endif + +endfunction diff --git a/scripts/control/stepimp.m b/scripts/control/stepimp.m new file mode 100644 --- /dev/null +++ b/scripts/control/stepimp.m @@ -0,0 +1,302 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [y, t] = stepimp(sitype, sys, inp, tstop, n) +# step: Impulse or step response for a linear system. +# The system can be discrete or multivariable (or both). +# This m-file contains the "common code" of step and impulse. +# +# [y, t] = stepimp(sitype, sys[, inp, tstop, n]) +# Produces a plot or the response data for system sys. +# +# Limited argument checking; "do not attempt to do this at home". +# Use step or impulse instead. +# +# See also: step, impulse + +# Written by Kai P. Mueller October 2, 1997 +# based on lsim.m of Scottedward Hodel +# $Revision: 1.1.1.1 $ +# $Log: stepimp.m,v $ +# Revision 1.1.1.1 1998/05/19 20:24:08 jwe +# +# Revision 1.4 1998/05/05 17:04:35 scotte +# minor corrections by Kai Mueller 5 May 1998 +# +# Revision 1.1 1998/05/04 15:12:42 mueller +# Initial revision +# +# Revision 1.3 1997/12/01 16:51:50 scotte +# updated by Mueller 27 Nov 97 +# +# Revision 1.4 1997/11/26 17:41:18 mueller +# impulse gives now expected results for continuous and discrete systems +# +# Revision 1.3 1997/11/24 18:57:57 mueller +# gset autoscale for proper scaling +# +# Revision 1.2 1997/11/24 17:23:38 mueller +# call to oneplot() and gset nokey added +# +# Revision 1.1 1997/11/11 17:34:50 mueller +# Initial revision +# + + if (sitype == 1) + IMPULSE = 0; + elseif (sitype == 2) + IMPULSE = 1; + else + error("stepimp: illegal sitype argument.") + endif + sys = sysupdate(sys,"ss"); + + USE_DEF = 0; # default tstop and n if we have to give up + N_MIN = 50; # minimum number of points + N_MAX = 2000; # maximum number of points + T_DEF = 10.0; # default simulation time + # collect useful information about the system + NOUT = rows(sys.c); + NIN = columns(sys.b); + if (nargin < 3) + inp = 1; + elseif ((inp < 1) || (inp > NIN)) + error("Argument inp out of range") + endif + DIGITAL = is_digital(sys); + if (DIGITAL) + NSTATES = sys.nz; + TSAMPLE = sys.tsam; + if (TSAMPLE < eps) + error("stepimp: sampling time of discrete system too small.") + endif + else + NSTATES = sys.n; + endif + if (NSTATES < 1) + error("step: n < 1, step response is trivial") + endif + if (nargin < 5) + # we have to compute the time when the system reaches steady state + # and the step size + ev = eig(sys.a); + if (DIGITAL) + # perform bilinear transformation on poles in z + for i = 1:NSTATES + pole = ev(i); + if (abs(pole + 1) < 1.0e-10) + ev(i) = 0; + else + ev(i) = 2 / TSAMPLE * (pole - 1) / (pole + 1); + endif + endfor + endif + # remove poles near zero from eigenvalue array ev + nk = NSTATES; + for i = 1:NSTATES + if (abs(ev(i)) < 1.0e-10) + ev(i) = 0; + nk = nk - 1; + endif + endfor + if (nk == 0) + USE_DEF = 1; + #printf("##STEPIMP-DEBUG: using defaults.\n"); + else + ev = ev(find(ev)); + x = max(abs(ev)); + t_step = 0.2 * pi / x; + x = min(abs(real(ev))); + t_sim = 5.0 / x; + # round up + yy = 10^(ceil(log10(t_sim)) - 1); + t_sim = yy * ceil(t_sim / yy); + #printf("##STEPIMP-DEBUG: nk=%d t_step=%f t_sim=%f\n", + # nk, t_step, t_sim); + endif + endif + + if (DIGITAL) + # ---- sampled system + if (nargin == 5) + n = round(n); + if (n < 2) + error("stepimp: n must not be less than 2.") + endif + else + if (nargin == 4) + # n is unknown + elseif (nargin >= 1) + # tstop and n are unknown + if (USE_DEF) + tstop = (N_MIN - 1) * TSAMPLE; + else + tstop = t_sim; + endif + endif + n = floor(tstop / TSAMPLE) + 1; + if (n < 2) n = 2; endif + if (n > N_MAX) + n = N_MAX; + printf("Hint: number of samples limited to %d by default.\n", \ + N_MAX); + printf(" ==> increase \"n\" parameter for longer simulations.\n"); + endif + endif + tstop = (n - 1) * TSAMPLE; + t_step = TSAMPLE; + else + # ---- continuous system + if (nargin == 5) + n = round(n); + if (n < 2) + error("step: n must not be less than 2.") + endif + t_step = tstop / (n - 1); + else + if (nargin == 4) + # only n in unknown + if (USE_DEF) + n = N_MIN; + t_step = tstop / (n - 1); + else + n = floor(tstop / t_step) + 1; + endif + else + # tstop and n are unknown + if (USE_DEF) + tstop = T_DEF; + n = N_MIN; + t_step = tstop / (n - 1); + else + tstop = t_sim; + n = floor(tstop / t_step) + 1; + endif + endif + if (n < N_MIN) + n = N_MIN; + t_step = tstop / (n - 1); + endif + if (n > N_MAX) + tstop = (n - 1) * t_step; + t_step = tstop / (N_MAX - 1); + n = N_MAX; + endif + endif + tstop = (n - 1) * t_step; + B = sys.b(:,inp); + sys = c2d(sys, t_step); + endif + #printf("##STEPIMP-DEBUG: t_step=%f n=%d tstop=%f\n", t_step, n, tstop); + + F = sys.a; + G = sys.b(:,inp); + C = sys.c; + D = sys.d(:,inp); + y = zeros(NOUT, n); + t = linspace(0, tstop, n); + + if (IMPULSE) + if (!DIGITAL && (D'*D > 0)) + error("impulse: D matrix is nonzero, impulse response infinite.") + endif + if (DIGITAL) + y(:,1) = D; + x = G / t_step; + else + x = B; + y(:,1) = C * x; + x = F * x; + endif + for i = 2:n + y(:,i) = C * x; + x = F * x; + endfor + else + x = zeros(NSTATES, 1); + for i = 1:n + y(:,i) = C * x + D; + x = F * x + G; + endfor + endif + + if(nargout == 0) + # Plot the information + oneplot(); + gset nogrid + gset nologscale + gset autoscale + gset nokey + clearplot(); + if (gnuplot_has_multiplot) + if (IMPULSE) + gm = zeros(NOUT, 1); + tt = "impulse"; + else + ssys = ss2sys(F, G, C, D, t_step); + gm = dcgain(ssys); + tt = "step"; + endif + ncols = floor(sqrt(NOUT)); + nrows = ceil(NOUT / ncols); + for i = 1:NOUT + subplot(nrows, ncols, i); + title([tt, ": | ", sys.inname(inp,:), " -> ", sys.outname(i,:)]); + if (DIGITAL) + [ts, ys] = stairs(t, y(i,:)); + ts = ts(1:2*n-2)'; ys = ys(1:2*n-2)'; + if (length(gm) > 0) + yy = [ys; gm(i)*ones(size(ts))]; + else + yy = ys; + endif + grid("on"); + xlabel("time [s]"); + ylabel("y(t)"); + plot(ts, yy); + else + if (length(gm) > 0) + yy = [y(i,:); gm(i)*ones(size(t))]; + else + yy = y(i,:); + endif + grid("on"); + xlabel("time [s]"); + ylabel("y(t)"); + plot(t, yy); + endif + endfor + # leave gnuplot in multiplot mode is bad style + oneplot(); + else + # plot everything in one diagram + title([tt, " response | ", sys.inname(inp,:), " -> all outputs"]); + if (DIGITAL) + stairs(t, y(i,:)); + else + grid("on"); + xlabel("time [s]"); + ylabel("y(t)"); + plot(t, y(i,:)); + endif + endif + y=[]; + t=[]; + endif + #printf("##STEPIMP-DEBUG: gratulations, successfull completion.\n"); +endfunction diff --git a/scripts/control/strappend.m b/scripts/control/strappend.m new file mode 100644 --- /dev/null +++ b/scripts/control/strappend.m @@ -0,0 +1,34 @@ +# Copyright (C) 1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function retval = strappend(str,suffix); + # retval = strappend(str,suffix); + # append string suffix to each string in the string matrix str + + if(nargin != 2 | nargout > 1) + usage(" retval = strappend(str,suffix)"); + elseif(!isstr(str) | !isstr(suffix)) + error("Both arguments must be strings") + endif + + for ii=1:rows(str) + newst = [dezero(str(ii,:)),suffix]; + retval(ii,1:length(newst)) = (newst); + endfor + +endfunction diff --git a/scripts/control/susball.m b/scripts/control/susball.m new file mode 100644 --- /dev/null +++ b/scripts/control/susball.m @@ -0,0 +1,94 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + + cmd = "ballsys = margetsys(""disc"")"; + eval(cmd); + + disp("Design LQG controller"); + cmd = "sysout(ballsys)"; + run_cmd + disp("add noise inputs to system...") + + disp("discrete system:") + cmd = "ballsys = sysappend(ballsys,eye(ballsys.nz));"; + run_cmd + + cmd = "sysout(ballsys)"; + run_cmd + + disp("Notice the two additional inputs, u_2, and u_3. These are the "); + disp("""entry points"" for the gaussian noise disturbance."); + disp(" "); + disp("We'll design the controller to use only position feedback:") + + cmd = "ballsys=sysprune(ballsys,1,[]);"; + run_cmd + cmd = "sysout(ballsys)"; + run_cmd + + disp("Now design an LQG controller: Sigw: input noise") + Sigw = eye(2) + disp("Now design an LQG controller: Sigv: measurement noise") + Sigv = eye(rows(ballsys.c)) + + disp("State and input penalties:") + Q = eye(2) + R = 1 + disp("Controlled input is input 1"); + + cmd="Ksys = lqg(ballsys,Sigw,Sigv,Q,R,1);"; + run_cmd + + disp("sysout(Ksys);"); + sysout(Ksys); + + disp("\nGet rid of the disturbance inputs"); + cmd = "ballsys = sysprune(ballsys,1,1);" + run_cmd; + sysout(ballsys); + sysout(ballsys,"zp"); + + disp("\nGrouping the plant and the controller"); + cmd = "closed_loop = sysgroup(ballsys,Ksys);" + run_cmd; + sysout(closed_loop); + + disp("\nduplicating the plant input"); + cmd = "closed_loop = sysdup(closed_loop,[],1);" + run_cmd; + sysout(closed_loop); + +# disp("\nscaling the duplicated input by -1"); +# cmd = "closed_loop = sysscale(closed_loop,[],diag([1,1,1]));" +# run_cmd; +# sysout(closed_loop); + + disp("\nconnecting plant output to controller input and controller output"); + disp("to the duplicated plant input"); + cmd = "closed_loop = sysconnect(closed_loop,[1 2],[2 3]);" + run_cmd; + sysout(closed_loop); + + disp("\nkeeping only the original plant input and plant output"); + cmd = "closed_loop = sysprune(closed_loop,1,1);" + run_cmd; + sysout(closed_loop); + + sysout(closed_loop,"zp"); + + diff --git a/scripts/control/swap.m b/scripts/control/swap.m new file mode 100644 --- /dev/null +++ b/scripts/control/swap.m @@ -0,0 +1,30 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [a1,b1] = swap(a,b) + # [a1,b1] = swap(a,b) + # interchange a and b + + # A. S. Hodel July 24 1992 + # Conversion to Octave R. Bruce Tenison July 4, 1994 + # $Revision: 1.1 $ + + a1 = b; + b1 = a; +endfunction + diff --git a/scripts/control/swapcols.m b/scripts/control/swapcols.m new file mode 100644 --- /dev/null +++ b/scripts/control/swapcols.m @@ -0,0 +1,31 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function B = swapcols(A) + # function B = swapcols(A) + # permute columns of A into reverse order + + # A. S. Hodel July 23, 1992 + # Conversion to Octave R. Bruce Tenison July 4, 1994 + # $Revision: 1.1 $ + + m = length(A(1,:)); + idx = m:-1:1; + B = A(:,idx); +endfunction + diff --git a/scripts/control/swaprows.m b/scripts/control/swaprows.m new file mode 100644 --- /dev/null +++ b/scripts/control/swaprows.m @@ -0,0 +1,31 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function B = swaprows(A) + # function B = swaprows(A) + # permute rows of A into reverse order + + # A. S. Hodel July 23, 1992 + # Conversion to Octave R. Bruce Tenison July 4, 1994 + # $Revision: 1.1.1.1 $ + + m = rows(A); + idx = m:-1:1; + B = A(idx,:); +endfunction + diff --git a/scripts/control/sys2fir.m b/scripts/control/sys2fir.m new file mode 100644 --- /dev/null +++ b/scripts/control/sys2fir.m @@ -0,0 +1,46 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [c,tsam,input,output] = sys2fir(sys) +# function [c,tsam,input,output] = sys2fir(sys) +# extract fir system from system data structure + +# $Revision: 1.1.1.1 $ +# a s hodel July 1996 + + sys=sysupdate(sys,"tf"); # make sure it's SISO + alph = sys.den(1); # scale to get monic denominator + sys.den = sys.den/alph; + sys.num = sys.num/alph; + l = length(sys.den); + m = length(sys.num); + if( norm(sys.den(2:l)) ) + sysout(sys,"tf"); + error("denominator has poles away from origin"); + elseif( !is_digital(sys) ) + error("system must be discrete-time to be FIR"); + elseif(m != l) + warning(["sys2fir: deg(num) - deg(den) = ",num2str(m-l), ... + "; coefficients must be shifted"]); + endif + c = sys.num; + tsam = sys.tsam; + input = sys.inname; + output = sys.outname; +endfunction + diff --git a/scripts/control/sys2ss.m b/scripts/control/sys2ss.m new file mode 100644 --- /dev/null +++ b/scripts/control/sys2ss.m @@ -0,0 +1,76 @@ +# Copyright (C) 1996, 1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [a,b,c,d,tsam,n,nz,stname,inname,outname,yd] = sys2ss(sys) + # function [a,b,c,d(,tsam,n,nz,stname,inname,outname,yd)] = sys2ss(sys) + # Convert from system data structure to state space form + # inputs: + # sys: system data structure for the state space system + # + # x' = Ax + Bu + # y = Cx + Du + # + # or a similar discrete-time system. + # + # outputs: + # a,b,c,d: state space matrices for sys + # tsam: sampling time of sys (0 if continuous) + # n, nz: number of continuous, discrete states (discrete states come + # last in state vector x) + # stname, inname, outname: signal names (strings); names of states, + # inputs, and outputs, respectively + # yd: binary vector; yd(ii) is nonzero if output y is discrete. + # + # A warning message is printed if the system is a mixed + # continuous/discrete system. + + # Written by David Clem August 19, 1994 + # Updates by John Ingram July 14, 1996 + # $Revision: 1.4 $ + + if(nargin != 1) + usage("[a,b,c,d,tsam,n,nz,stname,inname,outname,yd] = sys2ss(sys)") + endif + + if (nargout > 11) + warning(["sys2ss: ",num2str(nargout)," out arguments exceeds max=11"]) + usage("[a,b,c,d,tsam,n,nz,stname,inname,outname,yd] = sys2ss(sys)") + endif + + if( ! is_struct(sys) ) + error("input argument must be a system data structure"); + endif + + sys = sysupdate(sys,"ss"); #make sure ss is up to date + + cont = sum(sys.yd == 0) + sys.n; + dig = sum(sys.yd != 0) + sys.nz + sys.tsam; + if(cont*dig) + warning("sys2ss: input system is mixed continuous/discrete"); + endif + + a = sys.a; + b = sys.b; + c = sys.c; + d = sys.d; + [n,nz,m,p] = sysdimensions(sys); + [stname,inname,outname,yd] = sysgetsignals(sys); + tsam = sysgettsam(sys); + +endfunction + diff --git a/scripts/control/sys2tf.m b/scripts/control/sys2tf.m new file mode 100644 --- /dev/null +++ b/scripts/control/sys2tf.m @@ -0,0 +1,58 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [num,den,tsam,inname,outname] = sys2tf(Asys) +# function [num,den,tsam,inname,outname] = sys2tf(Asys) +# Conversion from a system data structure format to a transfer function. The +# transfer function part of ASYS is returned to the user in the form: +# +# num(s) +# G(s)=------- +# den(s) +# +# The user can also have the sampling time (TSAM), the name of the input +# (INNAME), and the output name (OUTNAME) + +# Written by R. Bruce Tenison (June 24, 1994) btenison@eng.auburn.edu +# modified to make sys2tf by A. S. Hodel Aug 1995 +# modified again for updated system format by John Ingram July 1996 +# $Revision: 1.2 $ + + if(nargin != 1) + usage("[num,den,tsam,inname,outname] = sys2tf(Asys)"); + endif + + if( !is_struct(Asys)) + error("Asys must be a system data structure (see ss2sys, tf2sys, zp2sys)"); + elseif (! is_siso(Asys) ) + [n, nz, m, p] = sysdimensions(Asys); + error(["system is not SISO (",num2str(m)," inputs, ... + ", num2str(p)," outputs"]); + endif + + Asys = sysupdate(Asys,"tf"); # just in case + + num = Asys.num; + den = Asys.den; + + tsam = Asys.tsam; + inname = Asys.inname; + outname = Asys.outname; + +endfunction + diff --git a/scripts/control/sys2zp.m b/scripts/control/sys2zp.m new file mode 100644 --- /dev/null +++ b/scripts/control/sys2zp.m @@ -0,0 +1,56 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [zer,pol,k,tsam,inname,outname] = sys2zp(sys) +# [zer,pol,k,tsam,inname,outname] = sys2zp(sys) +# extract zero/pole/leading coefficient information from a system data +# structure +# inputs: sys: system data structure +# outputs: +# zer: vector of system zeros +# pol: vector of system poles +# k: scalar leading coefficient +# tsam: sampling period. default: 0 (continuous system) +# inname, outname: input/output signal names (strings) + +# Created by John Ingram July 15 1996 +# $Revision: 1.2 $ + + if(nargin != 1) + usage("[zer,pol,k,tsam,inname,outname] = sys2zp(sys)"); + elseif( !is_struct(sys)) + error("sysconnect: sys must be in system data structure form") + elseif (! is_siso(sys) ) + [n, nz, m, p] = sysdimensions(sys); + error(["system is not SISO (",num2str(m)," inputs, ... + ", num2str(p)," outputs"]); + endif + + # update zero-pole form + sys = sysupdate(sys,"zp"); + + zer = sys.zer; + pol = sys.pol; + k = sys.k; + tsam = sys.tsam; + inname = sys.inname; + outname = sys.outname; + +endfunction + + diff --git a/scripts/control/sysadd.m b/scripts/control/sysadd.m new file mode 100644 --- /dev/null +++ b/scripts/control/sysadd.m @@ -0,0 +1,97 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function sys = sysadd(Gsys,Hsys) +# +# [sys] = sysadd(Gsys,Hsys) +# +# +# returns transfer function sys = Gsys + Hsys +# +# Method: Gsys and Hsys are connected in parallel +# The vector are connected to both systems; the outputs will be +# added. The names given to the system will be the G systems names. +# +# ________ +# ----| Gsys |--- +# u | ---------- +| +# ----- (_)----> y +# | ________ +| +# ----| Hsys |--- +# -------- + +# Written by John Ingram July 1996 +# $Revision: 1.2 $ + + save_val = implicit_str_to_num_ok; # save for later + implicit_str_to_num_ok = 1; + + if(nargin != 2) + usage("sysadd: [sys] = sysysadd(Gsys,Hsys)"); + endif + + # check inputs + if(!is_struct(Gsys) | !is_struct(Hsys)) + error("Both Gsys and Hsys must be in system data structure form"); + endif + + # check for compatibility + [n,nz,mg,pg] = sysdimensions(Gsys); + [n,nz,mh,ph] = sysdimensions(Hsys); + if(mg != mh) + error(sprintf("Gsys inputs(%d) != Hsys inputs (%d)",mg,mh)); + elseif(pg != ph) + error(sprintf("Gsys outputs(%d) != Hsys outputs (%d)",pg,ph)); + endif + + [Gst, Gin, Gout, Gyd] = sysgetsignals(Gsys); + [Hst, Hin, Hout, Hyd] = sysgetsignals(Hsys); + + # check for digital to continuous addition + if (Gyd != Hyd) + error("can not add a discrete output to a continuous output"); + endif + + if( (Gsys.sys(1) == 0) | (Hsys.sys(1) == 0) ) + # see if adding transfer functions with identical denominators + Gsys = sysupdate(Gsys,"tf"); + Hsys = sysupdate(Hsys,"tf"); + if(Hsys.den == Gsys.den) + sys = Gsys; + sys.sys(1) = 0; + sys.num = sys.num + Hsys.num; + return + endif + endif + + # make sure in ss form + Gsys = sysupdate(Gsys,"ss"); + Hsys = sysupdate(Hsys,"ss"); + + sys = sysgroup(Gsys,Hsys); + + eyin = eye(mg); + eyout = eye(pg); + + + inname = Gin; + outname = Gout; + + sys = sysscale(sys,[eyout eyout],[eyin;eyin],outname,inname); + +endfunction diff --git a/scripts/control/sysappend.m b/scripts/control/sysappend.m new file mode 100644 --- /dev/null +++ b/scripts/control/sysappend.m @@ -0,0 +1,202 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function retsys = sysappend(sys,b,c,d,outname,inname,yd) + # + # function retsys = sysappend(sys,b[,c,d,outname,inname,yd]) + # + # This function appends new inputs and/or outputs to a system + # Inputs: + # sys: system data structure + # b: matrix to be appended to sys "B" matrix (empty if none) + # c: matrix to be appended to sys "C" matrix (empty if none) + # d: revised sys d matrix (can be passed as [] if the revised d is all + # zeros) + # outname: names for new outputs + # inname: names for new inputs + # yd: indicator for which new outputs are continuous/discrete + # (yd(i) = 0 or , respectively) + # result: + # sys.b := [sys.b , b] + # sys.c := [sys.c ] + # [ c ] + # sys.d := [sys.d | D12 ] + # [D21 | D22 ] + # where D12, D21, and D22 are the appropriate dimensioned blocks + # of the input parameter d. The leading block D11 of d is ignored. + # If inname and outname are not given as arguments, the new inputs and + # outputs are be assigned default names. + # yd is a vector of length rows(c), and indicates which new outputs are + # discrete (yd(ii) = 1) and which are continuous (yd(ii) = 0). + # Default value for yd is: + # sys = continuous or mixed: yd = zeros(1,rows(c)) + # sys = discrete: yd = ones(1,rows(c)) + + # written by John Ingram August 1996 + # $Revision: 1.2 $ + # $Log: sysappend.m,v $ + # Revision 1.2 1998/07/21 14:53:09 hodelas + # use isempty instead of size tests; use sys calls to reduce direct + # access to system structure elements + # + # Revision 1.1.1.1 1998/05/19 20:24:09 jwe + # + # Revision 1.2 1997/04/09 04:36:21 scotte + # Fixed to properly handle new names (syschnames does not let you + # change the number of names any more.) a.s.hodel@eng.auburn.edu + # + + save_val = implicit_str_to_num_ok; # save for later + implicit_str_to_num_ok = 1; + + # check input arguments + if ( (nargin < 2) | (nargin > 7) | (!is_struct(sys))) + usage("retsys = sysappend(sys,b,c[,d,outname,inname,yd]) "); + endif + + # update system + sys = sysupdate(sys,"ss"); + sys.sys = [2 0 0 1]; + + #default c + if(nargin < 3) + c = []; + endif + + #default d + if(nargin < 4) + make_d = 1; + elseif(isempty(d)) + make_d = 1; + else + make_d = 0; + endif + if(make_d) + d = zeros(rows(c)+rows(sys.c),columns(b) + rows(sys.inname)); + #disp("sysappend: default d=") + #disp(d) + #disp("/sysappend: default d=") + endif + + # add default input names for new inputs (if any) + old_m = rows(sys.inname); + new_m = max(columns(d),columns(b)+old_m); + old_inname = sys.inname; + if(new_m) + sys.inname = sysdefioname(new_m,"u"); + if(old_m) + sys = syschnames(sys,"in",1:old_m,old_inname); + endif + if(nargin >= 6) + # input names were specified, check dimensions + if(rows(inname) != new_m - old_m) + inname + new_m + old_m + b + d + error(["inname has ",num2str(rows(inname))," entries, should have ", ... + num2str( new_m - old_m )]); + endif + sys = syschnames(sys,"in",(old_m+1):new_m,inname); + endif + endif + + #add default output names for new outputs (if any) + old_p = rows(sys.outname); + new_p = max(rows(d),rows(c)+old_p); + + # default yd + if (nargin < 7) + # discrete if positive sampling time, no continuous states/outputs + yd = ones(1,new_p)*( ... + (sys.tsam > 0) & (sys.n == 0) & isempty(find(sys.yd == 0)) ) ; + elseif ( (rows(c) != length(yd)) & (rows(d)) != yd) + error(["rows(c)=",num2str(rows(c)),", length(yd)=",num2str(length(yd))]) + endif + + old_outname = sys.outname; + if(new_p) + sys.outname = sysdefioname(new_p,"y"); + if(old_p) + sys = syschnames(sys,"out",1:old_p,old_outname); + endif + if(nargin >= 5) + # output names were specified, check dimensions + if(rows(outname) != new_p - old_p) + outname + new_p + old_p + c + d + error(["outname has ",num2str(rows(outname)), ... + " entries, should have ", num2str(new_p-old_p)]); + endif + sys = syschnames(sys,"out",(old_p+1):new_p,outname); + endif + endif + + sys = syschnames(sys,"yd",(old_p+1):new_p,yd); + + # append new b matrix (if any) + if( max(size(b)) ) + if(rows(b) != sys.n + sys.nz) + error(["sys has ",num2str(sys.n + sys.nz)," states; b has ", ... + num2str(rows(b))," rows"]); + else + if(old_m) + sys.b = [sys.b,b]; + else + sys.b = b; + endif + endif + endif + + # append new c matrix (if any) + if(max(size(c))) + if(columns(c) != sys.n + sys.nz) + error(["sys has ",num2str(sys.n + sys.nz)," states; c has ", ... + num2str(columns(c))," columns"]); + else + if(old_p) + sys.c = [sys.c;c]; + else + sys.c = c; + endif + endif + endif + + if(max(size(d))) + if( (rows(d) != new_p) | (columns(d) != new_m) ) + error(["d = (",num2str(rows(d)),"x",num2str(columns(d)), ... + ") should be (",num2str(new_p),"x",num2str(new_m),")"]); + else + if(old_m & old_p) + d(1:old_p, 1:old_m) = sys.d; + endif + sys.d = d; + endif + endif + + # append new input names + + retsys = sys; + + implicit_str_to_num_ok = save_val; # restore value + +endfunction diff --git a/scripts/control/syschnames.m b/scripts/control/syschnames.m new file mode 100644 --- /dev/null +++ b/scripts/control/syschnames.m @@ -0,0 +1,141 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function retsys = syschnames(sys,opt,list,names) +# retsys = syschnames(sys,opt,list,names) +# change the names of selected inputs, outputs and states. +# inputs: +# sys: system data structure +# opt: []: change default name (output) +# "out": change selected output names +# "in": change selected input names +# "st": change selected state names +# "yd": change selected outputs from discrete to continuous or +# from continuous to discrete. +# +# list: vector of indices of outputs, yd, inputs, or +# states whose respective names should be changed +# +# names: strings or string arrays containing +# names corresponding to the lists above. To +# change yd, use a vector. Set the name to 0 for continuous, +# or 1 for discrete. +# outputs: +# retsys=sys with appropriate signal names changed +# (or yd values, where appropriate) + +# Written by John Ingram August 1996 +# $Revision: 1.3 $ +# $Log: syschnames.m,v $ +# Revision 1.3 1998/07/17 15:31:06 hodelas +# use is_empty instead of max(size(...)) +# + + save_val = implicit_str_to_num_ok; # save for later + implicit_str_to_num_ok = 1; + + if (nargin != 4) + usage("retsys=syschnames(sys,opt,list[,names])"); + elseif (!is_struct(sys)) + error("sys must be a system data structure"); + elseif (isempty(opt)) + opt = "out"; + elseif( ! isstr(opt) ) + error("opt must be a string"); + elseif( ! (strcmp(opt,"out") + strcmp(opt,"yd") + ... + strcmp(opt,"in") + strcmp(opt,"st") ) ) + error("opt must be one of [], ""out"", ""yd"", ""in"", or ""st"""); + elseif(min(size(list)) > 1) + disp("syschnames: list=") + disp(list); + error("list must be a vector") + endif + + if (strcmp(opt,"out")) + # update output names + sys.outname = syschnamesl(list,sys.outname,names,"outlist"); + elseif (strcmp(opt,"in")) + sys.inname = syschnamesl(list,sys.inname,names, "inlist"); + elseif (strcmp(opt,"st")) + sys.stname = syschnamesl(list,sys.stname,names,"stlist"); + else + # it's yd + ym = max(size(list)); + ys = min(size(list)); + maxn = rows(sys.outname); + + if(ym != 0) + if( (ym > maxn) | (ys != 1) ) + error(["system has ",num2str(maxn)," outputs, ", ... + "list=(",num2str(rows(list)),"x",num2str(columns(list)),")"]); + endif + + if( ym != length(names)) + error(["list has ",num2str(ym)," entries, and names has ",... + num2str(length(names))," entries."]); + endif + + if (min((names == 1) | (names == 0)) == 0) + error("yd must be either zero or one"); + endif + + if (max(list) > maxn) + error(["The largest entry in the list is ",num2str(max(list)),... + " exceeds number of outputs=",num2str(maxn)]) + endif + + if (max(names) && (sys.tsam == 0) ) + warning("syschnames: discrete outputs with tsam=0; setting tsam=1"); + disp(" effected outputs are:") + if(is_siso(sys)) + outlist(sys.outname," ",[],list); + else + outlist(sys.outname(list,:)," ",[],list); + endif + sys.tsam = 1; + endif + + # reshape everything as a column vector + sys.yd = reshape(sys.yd,length(sys.yd),1); + names = reshape(names,length(names),1); + + #disp("syschnames: list=") + #disp(list) + #disp("syschnames: names=") + #disp(names) + #disp("syschnames: sys.yd=") + #disp(sys.yd) + + sys.yd(list) = names; + + if ((min(sys.yd) == 0) && (max(sys.yd) == 0) && (sys.tsam > 0) ) + warning("discrete states but no discrete outputs selected"); + endif + + endif + + endif + + retsys = sys; + implicit_str_to_num_ok = save_val; # restore value + + #disp("syschnames: exiting with") + #retsys + #disp("/syschnames") + +endfunction diff --git a/scripts/control/syschnamesl.m b/scripts/control/syschnamesl.m new file mode 100644 --- /dev/null +++ b/scripts/control/syschnamesl.m @@ -0,0 +1,133 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function old_names = syschnamesl(olist,old_names,inames,listname) + # used internally in syschnames + # olist: index list + # old_names: original list names + # inames: new names + # listname: name of index list + # + # combines the two string lists old_names and inames + + # $Revision: 1.2 $ + # $Log: syschnamesl.m,v $ + # Revision 1.2 1998/07/01 16:23:39 hodelas + # Updated c2d, d2c to perform bilinear transforms. + # Updated several files per bug updates from users. + # + # Revision 1.5 1997/02/28 23:47:26 hodel + # fixed bug in checking parameters; (inames dimension not checked against olist) + # a.s.hodel@eng.auburn.edu + # + # Revision 1.4 1997/02/13 15:56:37 hodel + # added code to convert zeros in the name matrix to blanks + # a.s.hodel@eng.auburn.edu + # + # Revision 1.3 1997/02/13 15:11:17 hodel + # fixed bug when len_my < len_out a.s.hodel@eng.auburn.edu + # + # Revision 1.2 1997/02/12 22:54:50 hodel + # fixed string matrix <-> numerical matrix problem + # + + probstr = []; + if( max(olist) > rows(old_names) ) + probstr = ["index list value(s) exceed(s) number of signals (", ... + num2str(rows(old_names)),")"]; + + elseif( length(olist) > rows(inames) ) + probstr = ["index list dimension exceeds number of replacement names (", ... + num2str(rows(inames)),")"]; + + elseif(isempty(olist)) + probstr = []; # do nothing, no changes + + elseif(min(size(olist)) != 1 ) + probstr = "index list must be either a vector or an empty matrix"; + + elseif(max(olist) > rows(old_names)) + probstr = ["max(",listname,")=",num2str(max(olist))," > ", ... + num2str(rows(old_names)),", too big"]; + + elseif(min(olist) < 1) + probstr = ["min(",listname,")=",num2str(min(olist))," < 1, too small"]; + + else + if( length(olist) == 1) + len_in = columns(inames); + len_out = columns(old_names); + + if (len_in < len_out) + inames(1,(len_in+1):(len_out)) = zeros(1,(len_out - len_in)); + endif + + old_names(olist,1:length(inames)) = inames; + elseif(length(olist) > 1) + for ii=1:length(olist) + mystr = inames(ii,:); + len_my = columns(mystr); + len_out = columns(old_names); + + if (len_my < len_out) + mystr(1,(len_my+1):(len_out)) = " "*ones(1,(len_out - len_my)); + len_my = len_out; + endif + + old_names(olist(ii),1:len_my) = mystr; + endfor + endif + endif + if(!isempty(probstr)) + # the following lines are NOT debugging code! + disp("Problem in syschnames: old names are") + outlist(old_names," ") + disp("new names are") + outlist(inames," ") + disp("list indices are") + disp(olist) + error(sprintf("syschnames: \"%s\" dim=(%d x %d)--\n\t%s\n", ... + listname, rows(olist), columns(olist),probstr)); + endif + + # change zeros to blanks + if( find(old_names == 0) ) + #disp("syschnamesl: old_names contains zeros ") + #old_names + #disp("/syschnamesl"); + + [ii,jj] = find(old_names == 0); + for idx=1:length(ii) + old_names(ii(idx),jj(idx)) = " "; + endfor + + #disp("syschnamesl: old_names fixed zeros ") + #old_names + #disp("/syschnamesl"); + endif + + # just in case it's not a string anymore + if( !isstr(old_names) ) + old_names = setstr(old_names); + endif + + #disp("syschnamesl: exit, old_names=") + #old_names + #disp("/syschnamesl: exiting") + +endfunction diff --git a/scripts/control/syschtsam.m b/scripts/control/syschtsam.m new file mode 100644 --- /dev/null +++ b/scripts/control/syschtsam.m @@ -0,0 +1,45 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function retsys = syschtsam(sys,tsam) +# +# retsys = syschtsam(sys,tsam); +# +# This function changes the sampling time (tsam) of the system. + +# Written by John Ingram August 1996 +# $Revision: 1.1.1.1 $ + + if (nargin != 2) + usage("retsys = syschtsam(sys,tsam)"); + elseif (!is_struct(sys)) + error("sys must be in system data structure form"); + elseif(!is_scalar(tsam)) + disp("syschtsam:") + tsam + error("tsam must be a scalar") + elseif ( ! (is_sample(tsam) | (tsam == 0) ) ) + error("tsam must be real, scalar, and greater than zero"); + elseif (sys.tsam == 0) + error("The system is continuous, use c2d to make the system discrete"); + endif + + retsys = sys; + retsys.tsam = tsam; + +endfunction diff --git a/scripts/control/sysconnect.m b/scripts/control/sysconnect.m new file mode 100644 --- /dev/null +++ b/scripts/control/sysconnect.m @@ -0,0 +1,270 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function sys = sysconnect(sys,output_list,input_list,order,tol) +# function retsys = sysconnect(sys,output_list,input_list[,order,tol]) +# Close the loop from specified outputs to respective specified inputs +# +# inputs: +# sys: system data structure +# output_list,input_list: list of connections indices; y(output_list(ii)) +# is connected to u(input_list(ii)). +# order: logical flag (default = 0) +# 0: leave inputs and outputs in their original order +# 1: permute inputs and outputs to the order shown in the diagram below +# tol: tolerance for singularities in algebraic loops +# default: 200*eps +# output: sys: resulting closed loop system: +# +# Operation: sysconnect internally permutes selected inputs, outputs as shown +# below, closes the loop, and then permutes inputs and outputs back to their +# original order +# ____________________ +# | | +# u_1 ----->| |----> y_1 +# | sys | +# old u_2 | | +# u_2* ------>(+)--->| |----->y_2 +# (input_list) ^ | | | (output_list) +# | -------------------- | +# | | +# ------------------------------- +# +# The input that has the summing junction added to it has an * added to the end +# of the input name. + +# A. S. Hodel August 1995 +# modified by John Ingram July 1996 +# $Revision: 1.1.1.1 $ +# $Log: sysconnect.m,v $ +# Revision 1.1.1.1 1998/05/19 20:24:09 jwe +# +# Revision 1.3 1997/03/03 19:21:06 hodel +# removed calls to packsys: a.s.hodel@eng.auburn.edu +# +# Revision 1.2 1997/02/13 14:23:18 hodel +# fixed bug in continuous<->discrete loop connection check. +# a.s.hodel@eng.auburn.edu +# + + save_val = implicit_str_to_num_ok; # save for later + implicit_str_to_num_ok = 1; + + if( (nargin < 3) | (nargin > 5) ) + usage("retsys = sysconnect(sys,output_list,input_list[,order,tol])"); + endif + + # check order + if(nargin <= 3) + order = 0; + elseif( (order != 0) & (order != 1) ) + error("sysconnect: order must be either 0 or 1") + endif + + if (nargin <= 4) + tol = 200*eps; + elseif( !is_sample(tol) ) + error("sysconnect: tol must be a positive scalar"); + elseif(tol > 1e2*sqrt(eps)) + warning(["sysconnect: tol set to large value=",num2str(tol), ... + ", eps=",num2str(eps)]) + endif + + # verify sizes,format of input, output lists + if( min(size(output_list))*min(size(input_list)) != 1) + error("output_list and input_list must be vectors"); + else + lo = length(output_list); + li = length(input_list); + if(lo != li) + error("output_list and input_list must be of the same length") + endif + + if(is_duplicate_entry(output_list) | is_duplicate_entry(input_list) ) + error("duplicate entry in input_list and/or output_list"); + endif + endif + + mm = rows(sys.inname); + pp = rows(sys.outname); + nn = rows(sys.stname); + + if( !is_struct(sys)) + error("sys must be in structured system form") + elseif(pp < li) + error(["length(output_list)=",num2str(li),", sys has only ", ... + num2str(pp),"system outputs"]) + elseif(mm < li) + error(["length(input_list)=",num2str(li),", sys has only ", ... + num2str(mm),"system inputs"]) + endif + + # check that there are enough inputs/outputs in the system for the lists + if(max(input_list) > mm) + error("max(input_list) exceeds the number of inputs"); + elseif(max(output_list) > pp) + error("max(output_list) exceeds the number of outputs"); + endif + + output_list = reshape(output_list,1,length(output_list)); + + # make sure we're in state space form + sys = sysupdate(sys,'ss'); + + # permute rows and columns of B,C,D matrices into pseudo-dgkf form... + all_inputs = sysreorder(mm,input_list); + all_outputs = sysreorder(pp,output_list); + + sys.b = sys.b(:,all_inputs); + sys.c = sys.c(all_outputs,:); + sys.d = sys.d(all_outputs,all_inputs); + sys.yd = sys.yd(all_outputs); + + # m1, p1 = number of inputs, outputs that are not being connected + m1 = mm-li; + p1 = pp-li; + + # m2, p2: 1st column, row of B, C that is being connected + m2 = m1+1; + p2 = p1+1; + + # partition system into a DGKF-like form; the loop is closed around + # B2, C2 + if(m1 > 0) + B1 = sys.b(:,1:m1); + D21= sys.d(p2:pp,1:m1); + endif + B2 = sys.b(:,m2:mm); + if(p1 > 0) + C1 = sys.c(1:p1,:); + D12= sys.d(1:p1,m2:mm); + endif + C2 = sys.c(p2:pp,:); + if(m1*p1 > 0) + D11= sys.d(1:p1,1:m1); + endif + D22= sys.d(p2:pp,m2:mm); + + if(norm(D22)) + warning("sysconnect: possible algebraic loop, D22 non-zero"); + D22i = (eye(size(D22))-D22); + C2h = D22i\C2; + if(m1 > 0) + D21h = D22i\D21; + endif + D22h = D22i\D22; + else + C2h = C2; + if(m1 > 0) + D21h = D21; + endif + D22h = D22; + + endif + + # check cont state -> disc output -> cont state + dyi = find(sys.yd(p2:pp)); + + #disp("sysconnect: dyi=") + #dyi + #sys.n + #disp("/sysconnect"); + + if( (sys.n > 0) & find(dyi > 0) ) + B2con = B2(1:sys.n,dyi); # connection to cont states + C2hd = C2h(dyi,1:sys.n); # cont states -> outputs + else + B2con = C2hd = []; + endif + + if(max(size(B2con)) & max(size(C2hd)) ) + if(norm(B2con*C2hd)) + warning("sysconnect: cont-state -> disc output -> cont state derivative"); + warning(" connection made; resulting system may not be meaningful"); + endif + endif + + Ac = sys.a+B2*C2h; + if(m1 > 0) + B1c = B1 + B2*D21h; + endif + B2c = B2*(eye(size(D22h)) + D22h); + if(p1*m1 > 0) + D11c = D11 + D12*D21h; + endif + if(p1 > 0) + C1c = C1+D12*C2h; + D12c = D12*(eye(size(D22h))+D22h); + endif + + # construct system data structure + if(m1 > 0) + Bc = [B1c B2c]; + else + Bc = B2c; + endif + + if(p1 > 0) + Cc = [C1c;C2h]; + else + Cc = C2h; + endif + + if(m1*p1 > 0) + Dc = [D11c,D12c; D21h,D22h]; + elseif(m1 > 0) + Dc = [D21h, D22h]; + elseif(p1 > 0) + Dc = [D12c; D22h]; + else + Dc = D22h; + endif + + # permute rows and columns of Bc, Cc, Dc back into original order + Im = eye(mm,mm); + Pi = Im(:,all_inputs); + back_inputs = Pi*[1:mm]'; + + Ip = eye(pp,pp); + Po = Ip(:,all_outputs); + back_outputs = Po*[1:pp]'; + + Bc = Bc(:,back_inputs); + Cc = Cc(back_outputs,:); + Dc = Dc(back_outputs,back_inputs); + sys.yd = sys.yd(back_outputs); + + sys.a = Ac; + sys.b = Bc; + sys.c = Cc; + sys.d = Dc; + + for ii = 1:length(input_list) + strval = [dezero(sys.inname(input_list(ii),:)),"*"]; + sys.inname(input_list(ii),(1:length(strval))) = [strval]; + endfor + + if (sys.sys(1) == 0) + sysupdate(sys,'tf'); + elseif (sys.sys(1) == 1) + sysupdate(sys,'zp'); + endif + + implicit_str_to_num_ok = save_val; # restore value + +endfunction diff --git a/scripts/control/syscont.m b/scripts/control/syscont.m new file mode 100644 --- /dev/null +++ b/scripts/control/syscont.m @@ -0,0 +1,80 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [csys,Acd,Ccd] = syscont(sys) +# function [csys,Acd,Ccd] = syscont(sys) +# returns csys = sys with discrete states./utputs omitted. +# +# inputs: sys is a system data structure +# outputs: csys is the purely continuous input/output connections of +# sys +# Acd, Ccd: connections from discrete states to continuous states, +# discrete states to continuous outputs, respectively. +# +# returns csys empty if no continuous/continous path exists + +# Written by John Ingram August 1996 +# $Revision: 1.2 $ + + save_val = implicit_str_to_num_ok; # save for later + save_empty = empty_list_elements_ok; + empty_list_elements_ok = implicit_str_to_num_ok = 1; + + if (nargin != 1) + usage("[csys,Acd,Ccd,Dcd] = syscont(sys)"); + elseif (!is_struct(sys)) + error("sys must be in system data structure form"); + endif + + sys = sysupdate(sys,"ss"); + [n_tot,st_c,st_d,y_c,y_d] = syscont_disc(sys); # get ranges + + # assume there's nothing there; build partitions as appropriate + Acc = Acd = Bcc = Ccc = Ccd = Dcc = []; + + if(isempty(st_c) & isempty(y_c)) + error("syscont: expecting continous states and/or continous outputs"); + elseif (isempty(st_c)) + warning("syscont: no continuous states"); + elseif(isempty(y_c)) + warning("syscont: no continuous outputs"); + endif + + [sys_a, sys_b, sys_c, sys_d ] = sys2ss(sys); + [sys_stname, sys_inname, sys_outname] = sysgetsignals(sys); + [sys_n, sys_nz, sys_m, sys_p] = sysdimensions(sys); + if(!isempty(st_c)) + Acc = sys_a(st_c,st_c); + stname = sys_stname(st_c, :); + Bcc = sys_b(st_c,:); + Ccc = sys_c(y_c,st_c); + Acd = sys_a(st_c,st_d); + else + stname=[]; + endif + outname = sys_outname(y_c,:); + Dcc = sys_d(y_c,:); + Ccd = sys_c(y_c,st_d); + inname = sys_inname; + + csys = ss2sys(Acc,Bcc,Ccc,Dcc,0,sys_n,0,stname,inname,outname); + + implicit_str_to_num_ok = save_val; # restore value + empty_list_elements_ok = save_empty; + +endfunction diff --git a/scripts/control/syscont_disc.m b/scripts/control/syscont_disc.m new file mode 100644 --- /dev/null +++ b/scripts/control/syscont_disc.m @@ -0,0 +1,45 @@ +# Copyright (C) 1996,1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [n_tot,st_c,st_d,y_c,y_d] = syscont_disc(sys) +# function [n_tot,st_c,st_d,y_c,y_d] = syscont_disc(sys) +# Used internally in syscont and sysdisc. +# +# inputs: sys is a system data structure +# outputs: n_tot: total number of states +# st_c: vector of continuous state indices (empty if none) +# st_d: vector of discrete state indices (empty if none) +# y_c: vector of continuous output indices +# y_d: vector of discrete output indices + +# Written by A. S. Hodel (a.s.hodel@eng.auburn.edu) Feb 1997 +# $Log: syscont_disc.m,v $ +# Revision 1.2 1998/07/15 12:29:13 hodelas +# Updated to use sysdimensions. Removed extraneous if commands (find now +# returns empty matrix if none found) +# + + # get ranges for discrete/continuous states and outputs + [nn,nz,mm,pp,yd] = sysdimensions(sys); + n_tot = nn + nz; + st_c = 1:(nn); + st_d = nn + (1:nz); + y_c = find(yd == 0); # y_c, y_d will be empty if there are none. + y_d = find(yd == 1); + +endfunction diff --git a/scripts/control/sysdefioname.m b/scripts/control/sysdefioname.m new file mode 100644 --- /dev/null +++ b/scripts/control/sysdefioname.m @@ -0,0 +1,63 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function ioname = sysdefioname(n,str,m) +# function ioname = sysdefioname(n,str[,m]) +# return default input or output names given n, str, m +# n is the final value, str is the string prefix, and m is start value +# ex: ioname = sysdefioname(5,"u",3) +# +# returns: ioname = u_3 +# u_4 +# u_5 +# used internally, minimal argument checking + +# $Log: sysdefioname.m,v $ + + save_val = implicit_str_to_num_ok; # save for later + implicit_str_to_num_ok = 1; + + if (nargin < 2 | nargin > 3) + usage("ioname = sysdefioname(n,str[,m])"); + endif + + if (nargin == 2) + m = 1; + endif + + jj = 1; + + if(n > 0 & m > 0 & m <= n) + for ii = m:n + strval = [str,"_",num2str(ii)]; + ioname(jj,1:length(strval)) = strval; + jj = jj+1; + endfor + elseif(n == 0) + ioname = ""; + elseif(m > n) + error(["start value m=",num2str(m)," > final value n=",num2str(n),"; bad!"]) + endif + + if( !isstr(ioname) ) + ioname = setstr(ioname); + endif + + implicit_str_to_num_ok = save_val; # restore value + +endfunction diff --git a/scripts/control/sysdefstname.m b/scripts/control/sysdefstname.m new file mode 100644 --- /dev/null +++ b/scripts/control/sysdefstname.m @@ -0,0 +1,50 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function stname = sysdefstname(n,nz) +# function stname = sysdefstname(n,nz) +# return default state names given n, nz +# used internally, minimal argument checking + +# $Revision: 1.2 $ + + sav_val = implicit_str_to_num_ok; + implicit_str_to_num_ok = 1; + + stname = []; + if(n > 0) + for ii = 1:n + strval = ["x_",num2str(ii)]; + stname(ii,1:length(strval)) = strval; + endfor + endif + + # Set default names for discrete states + if(nz > 0) + for ii = (n+1):(n+nz) + strval = ["xd_",num2str(ii)]; + stname(ii,1:length(strval)) = strval; + endfor + endif + + if( !(isstr(stname) | (rows(stname) == 0) ) ) + stname = setstr(stname); + endif + + implicit_str_to_num_ok = sav_val; +endfunction diff --git a/scripts/control/sysdimensions.m b/scripts/control/sysdimensions.m new file mode 100644 --- /dev/null +++ b/scripts/control/sysdimensions.m @@ -0,0 +1,45 @@ +# Copyright (C) 1996,1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [n,nz,m,p,yd] = sysdimensions(sys) +# [n,nz,m,p[,yd]] = sysdimensions(sys) +# return the number of states, inputs, and outputs in the system sys. +# inputs: sys: system data structure +# outputs: +# n: number of continuous states +# nz: number of discrete states +# m: number of system inputs +# p: number of system outputs +# yd: is the discrete output vector: yd(ii) = 1 if output ii is sampled, +# yd(ii) = 0 if output ii is continous +# +# see also: sysgetsignals, sysgettsam + +if(nargout > 5 | nargin != 1) + usage("[n,nz,m,p[,yd]] = sysdimensions(sys)"); +elseif(!is_struct(sys)) + usage("[n,nz,m,p] = sysdimensions(sys)"); +endif + +n = sys.n; +nz = sys.nz; +m = rows(sys.inname); +p = rows(sys.outname); +yd = sys.yd; + +endfunction diff --git a/scripts/control/sysdisc.m b/scripts/control/sysdisc.m new file mode 100644 --- /dev/null +++ b/scripts/control/sysdisc.m @@ -0,0 +1,90 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [dsys,Adc,Cdc] = sysdisc(sys) +# function [dsys,Adc,Cdc] = sysdisc(sys) +# inputs: sys = system data structure +# outputs: +# dsys: purely discrete portion of sys (returned empty if there is +# no purely discrete path from inputs to outputs) +# Adc, Cdc: connections from continuous states to discrete states/discrete +# outputs, respectively. +# + +# $Revision: 1.1.1.1 $ + + save_val = implicit_str_to_num_ok; # save for later + save_empty = empty_list_elements_ok; + empty_list_elements_ok = implicit_str_to_num_ok = 1; + + + if (nargin != 1) + usage("[dsys,Adc,Cdc] = sysdisc(sys)"); + elseif (!is_struct(sys)) + error("sys must be in system data structure form"); + endif + + sys = sysupdate(sys,"ss"); + [n_tot,st_c,st_d,y_c,y_d] = syscont_disc(sys); # get ranges + + # assume there's nothing there; build partitions as appropriate + Add = Adc = Bdd = Cdd = Cdc = Ddd = []; + + if(isempty(st_d) & isempty(y_d)) + error("sysdisc: expecting discrete states and/or continous outputs"); + elseif (isempty(st_d)) + warning("sysdisc: no discrete states"); + elseif(isempty(y_d)) + warning("sysdisc: no discrete outputs"); + endif + + if(!isempty(st_d) ) + Add = sys.a( st_d , st_d); + stname = sys.stname(st_d , :); + Bdd = sys.b( st_d , :); + if(!isempty(st_c)) + Adc = sys.a( st_d , st_c); + endif + if(!isempty(y_d)) + Cdd = sys.c(y_d , st_d); + endif + else + stname = []; + endif + if(!isempty(y_d)) + Ddd = sys.d(y_d , :); + outname = sys.outname(y_d , :); + if(!isempty(st_c)) + Cdc = sys.c(y_d , st_c); + endif + else + outname=[]; + endif + inname = sys.inname; + outlist = 1:rows(outname); + + if(!isempty(outname)) + dsys = ss2sys(Add,Bdd,Cdd,Ddd,sys.tsam,0,sys.nz,stname, ... + inname,outname,outlist); + else + dsys=[]; + endif + implicit_str_to_num_ok = save_val; # restore value + empty_list_elements_ok = save_empty; + +endfunction diff --git a/scripts/control/sysdup.m b/scripts/control/sysdup.m new file mode 100644 --- /dev/null +++ b/scripts/control/sysdup.m @@ -0,0 +1,136 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function retsys = sysdup(Asys,output_list,input_list) +# function retsys = sysdup(Asys,output_list,input_list) +# Duplicate specified input/output connections of a system +# +# +# inputs: +# Asys: system data structure (see ss2sys) +# output_list,input_list: list of connections indices; +# duplicates are made of y(output_list(ii)) and u(input_list(ii)). +# output: retsys: resulting closed loop system: +# duplicated i/o names are appended with a "+" suffix. +# +# Operation: sysdup creates copies of selected inputs and outputs as +# shown below. u1/y1 is the set of original inputs/outputs, and +# u2,y2 is the set of duplicated inputs/outputs in the order specified +# in input_list,output_list, respectively +# ____________________ +# | | +# u1 ----->| |----> y1 +# | Asys | +# | | +# u2 ------------->| |----->y2 +# (input_list) | | (output_list) +# -------------------- + +# A. S. Hodel August 1995 +# modified by John Ingram July 1996 +# $Revision: 1.2 $ + + save_val = implicit_str_to_num_ok; # save for later + implicit_str_to_num_ok = 1; + + if( nargin != 3) + usage("retsys = sysdup(Asys,output_list,input_list)"); + endif + + if( !is_struct(Asys)) + error("Asys must be a system data structure (see ss2sys, tf2sys, or zp2sys)") + endif + + if (Asys.sys(4) != 1) + Asys = sysupdate(Asys,'ss'); + endif + + mm = rows(Asys.inname); + pp = rows(Asys.outname); + + # first duplicate inputs + if(is_vector(input_list)) + for ii=1:length(input_list); + Asys.b(:,mm+ii) = Asys.b(:,input_list(ii)); + Asys.d(:,mm+ii) = Asys.d(:,input_list(ii)); + end + elseif(!isempty(input_list)) + error("input_list must be a vector or empty"); + endif + + + # now duplicate outputs + osize = min(size(output_list)); + if(osize == 1) + for ii=1:length(output_list); + Asys.c(pp+ii,:) = Asys.c(output_list(ii),:); + Asys.d(pp+ii,:) = Asys.d(output_list(ii),:); + end + elseif(osize != 0) + error("output_list must be a vector or empty"); + endif + + yd = Asys.yd(output_list); + Asys.yd = [Asys.yd yd]; + + # give default names to the added inputs + for ii=(mm+1):(mm+length(input_list)) + orig_name = Asys.inname(input_list(ii-mm),:); + + #disp("sysdup: orig_name=") + #orig_name + #disp("/sysdup") + + strval = [dezero(orig_name),"(dup)"]; + + #disp("sysdup: strval=") + #strval + #disp("/sysdup") + + Asys.inname(ii,1:length(strval)) = [strval]; + + #disp("sysdup: resulting Asys.inname:") + #Asys.inname + #disp("/sysdup"); + + endfor + + # give default names to the added outputs + for jj=(pp+1):(pp+length(output_list)) + if(isstr(Asys.outname)) + orig_name =Asys.outname; + else + orig_name = Asys.outname(output_list(jj-pp),:); + endif + strval = [dezero(orig_name),"(dup)"]; + Asys.outname(jj,1:length(strval)) = [strval]; + + endfor + + + + if(max(size(Asys.d)) > 1 ) + Asys.sys = [2 0 0 1]; # change default form to state space + # tf and zp are no longer relevant + endif + + retsys = Asys; + + implicit_str_to_num_ok = save_val; # restore value + +endfunction diff --git a/scripts/control/sysgetsignals.m b/scripts/control/sysgetsignals.m new file mode 100644 --- /dev/null +++ b/scripts/control/sysgetsignals.m @@ -0,0 +1,45 @@ +# Copyright (C) 1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [stname,inname,outname,yd] = sysgetsignals(sys) + # function [stname,inname,outname,yd] = sysgetsignals(sys) + # Get signal names from a system + # inputs: + # sys: system data structure for the state space system + # + # outputs: + # stname, inname, outname: signal names (strings); names of states, + # inputs, and outputs, respectively + # yd: binary vector; yd(ii) is nonzero if output y is discrete. + # + + # Adapted from ss2sys + + if(nargin != 1 | nargout > 4) + usage("[stname,inname,outname,yd] = sysgetsignals(sys)") + elseif( ! is_struct(sys) ) + error("input argument must be a system data structure"); + endif + sys = sysupdate(sys,"ss"); #make sure ss is up to date + yd = sys.yd; + stname = sys.stname; + inname = sys.inname; + outname = sys.outname; + +endfunction + diff --git a/scripts/control/sysgettsam.m b/scripts/control/sysgettsam.m new file mode 100644 --- /dev/null +++ b/scripts/control/sysgettsam.m @@ -0,0 +1,20 @@ +function T = sysgettsam(sys) +# T = sysgettsam(sys) +# return the sampling time of the system + +# $Revision: 1.3 $ +# $Log: sysdimensions.m,v $ +# Revision 1.3 1997/03/10 21:35:13 scotte +# added debugging code (commented out) +# +# Revision 1.2 1997/03/10 20:42:27 scotte +# added warning message about nargout +# + +if(!is_struct(sys)) + usage("T = sysgettsam(sys)"); +endif + +T = sys.tsam; + +endfunction diff --git a/scripts/control/sysgettype.m b/scripts/control/sysgettype.m new file mode 100644 --- /dev/null +++ b/scripts/control/sysgettype.m @@ -0,0 +1,37 @@ +# Copyright (C) 1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function systype = sysgettype(sys) +# systype = sysgetype(sys) +# return the initial system type of the system +# inputs: +# sys: system data structure +# outputs: +# systype: string indicating how the structure was initially +# constructed: +# values: "ss", "zp", or "tf" +# +# $Log$ + + if(!is_struct(sys)) + error("sysgettype: input sys is not a structure"); + endif + + typestr = ["tf";"zp";"ss"]; + systype = typestr(sys.sys(1) + 1, :); +endfunction diff --git a/scripts/control/sysgroup.m b/scripts/control/sysgroup.m new file mode 100644 --- /dev/null +++ b/scripts/control/sysgroup.m @@ -0,0 +1,124 @@ +# Copyright (C) 1996,1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function sys = sysgroup(Asys,Bsys) +# function sys = sysgroup(Asys,Bsys) +# Combines two system data structures into a single system +# +# input: Asys, Bsys: system data structures +# output: sys: Asys and Bsys are combined into a single system: +# +# __________________ +# | ________ | +# u1 ----->|--> | Asys |--->|----> y1 +# | -------- | +# | ________ | +# u2 ----->|--> | Bsys |--->|----> y2 +# | -------- | +# ------------------ +# Ksys +# +# The function also rearranges the A,B,C matrices so that the +# continuous states come first and the discrete states come last. +# If there are duplicate names, the second name has a unique suffix appended +# on to the end of the name. + +# A. S. Hodel August 1995 +# modified by John Ingram July 1996 +# $Revision: 1.3 $ + + save_val = implicit_str_to_num_ok; # save for later + implicit_str_to_num_ok = 1; + + save_emp = empty_list_elements_ok; + empty_list_elements_ok = 1; + + if(nargin ~= 2) + usage("sys = sysgroup(Asys,Bsys)"); + elseif( !is_struct(Asys) | !is_struct(Bsys) ) + error("sysgroup: input arguments must both be structured systems"); + endif + + # extract information from Asys, Bsys to consruct sys + Asys = sysupdate(Asys,"ss"); + Bsys = sysupdate(Bsys,"ss"); + [n1,nz1,m1,p1] = sysdimensions(Asys); + [n2,nz2,m2,p2] = sysdimensions(Bsys); + [Aa,Ab,Ac,Ad,Atsam,An,Anz,Ast,Ain,Aout,Ayd] = sys2ss(Asys); + [Ba,Bb,Bc,Bd,Btsam,Bn,Bnz,Bst,Bin,Bout,Byd] = sys2ss(Bsys); + nA = An + Anz; + nB = Bn + Bnz; + + if(p1*m1*p2*m2 == 0) + error("sysgroup: argument lacks inputs and/or outputs"); + + elseif((Atsam + Btsam > 0) & (Atsam * Btsam == 0) ) + warning("sysgroup: creating combination of continuous and discrete systems") + + elseif(Atsam != Btsam) + error(["sysgroup: Asys.tsam=", ... + num2str(Atsam),", Bsys.tsam=",num2str(Btsam)]); + + endif + + A = [Aa,zeros(nA,nB); zeros(nB,nA),Ba]; + B = [Ab,zeros(nA,m2); zeros(nB,m1),Bb]; + C = [Ac,zeros(p1,nB); zeros(p2,nA),Bc]; + D = [Ad,zeros(p1,m2); zeros(p2,m1),Bd]; + tsam = max(Atsam,Btsam); + + # construct combined signal names; stnames must check for pure gain blocks + if(isempty(Ast)) + stname = Bst; + elseif(isempty(Bst)) + stname = Ast; + else + stname = str2mat(Ast, Bst); + endif + inname = str2mat(Ain, Bin); + outname = str2mat(Aout,Bout); + + # Sort states into continous first, then discrete + dstates = ones(1,(nA+nB)); + if(An) + dstates(1:(An)) = zeros(1,An); + endif + if(Bn) + dstates((nA+1):(nA+Bn)) = zeros(1,Bn); + endif + [tmp,pv] = sort(dstates); + A = A(pv,pv); + B = B(pv,:); + C = C(:,pv); + stname = stname(pv,:); + + # check for duplicate signal names + inname = sysgroupn(inname,"input"); + stname = sysgroupn(stname,"state"); + outname = sysgroupn(outname,"output"); + + # mark discrete outputs + outlist = find([Ayd, Byd]); + + # build new system + sys = ss2sys(A,B,C,D,tsam,An+Bn,Anz+Bnz,stname,inname,outname); + + implicit_str_to_num_ok = save_val; # restore value + empty_list_elements_ok = save_emp; + +endfunction diff --git a/scripts/control/sysgroupn.m b/scripts/control/sysgroupn.m new file mode 100644 --- /dev/null +++ b/scripts/control/sysgroupn.m @@ -0,0 +1,57 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function names = sysgroupn(names,kind) +# names = sysgroupn(names) +# locate and mark duplicate names +# +# used internally in sysgroup + +# $Revision: 1.1 $ + + #disp("sysgroupn: entry") + #names + #[lmatrws,lmatcls] = size(names) + #disp("/sysgroupn") + + # check for duplicate names + l = rows(names); + if(l > 1) + for ii = 1:(l-1); + #disp(["sysgroupn: ii=",num2str(ii)]) + #names + #[lmatrws,lmatcls] = size(names) + #disp("/sysgroupn") + st1 = dezero(names(ii,:)); + for jj = (ii+1):l + st2 = dezero(names(jj,:)); + if(strcmp(st1,st2)) + suffix = ["_",num2str(jj)]; + warning(["sysgroup: Appending ",suffix," to duplicate ",kind,... + " name '",st2,"'."]); + strval = [st2,suffix]; + + #disp(["sysgroupn: length(strval)=",num2str(length(strval))]); + #disp(["sysgroupn: length(st2)=",num2str(length(st2))]); + + names(jj,(1:length(strval))) = strval; + endif + endfor + endfor + endif +endfunction diff --git a/scripts/control/sysmult.m b/scripts/control/sysmult.m new file mode 100644 --- /dev/null +++ b/scripts/control/sysmult.m @@ -0,0 +1,92 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [sys] = sysmult(Asys,Bsys) +# +# [sys] = sysmult(Asys,Bsys) +# +# returns sys = Asys*Bsys +# +# This function takes two systems, Asys and Bsys, and multiplies them together. +# This has the effect of connecting the outputs of Bsys to the inputs of Asys. +# +# +# u ---------- ---------- +# --->| Bsys |---->| Asys |---> +# ---------- ---------- +# +# A warning occurs if there is direct feed-through +# from an input of Bsys or a continuous state of Bsys through a discrete +# output of Bsys to a continuous state or output in Asys (system data structure form +# does not recognize discrete inputs) + +# Written by John Ingram July 1996 +# $Revision: 1.2 $ + + save_val = implicit_str_to_num_ok; # save for later + implicit_str_to_num_ok = 1; + + if(nargin != 2) + usage("sysmult: [sys] = sysmult(Asys,Bsys)"); + endif + + # check inputs + if(!is_struct(Asys) | !is_struct(Bsys)) + error("Both Asys and Bsys must be in system data structure form") + endif + + # check for compatibility + [An,Anz,Am,Ap] = sysdimensions(Asys); + [Bn,Bnz,Bm,Bp] = sysdimensions(Bsys); + if(Bp != Am) + error(["Bsys has ",num2str(Bp)," outputs, Asys has ",num2str(Am), ... + " inputs; mismatch."]); + endif + + [Aa,Ab,Ac,Ad,Atsam,An,Anz,Astname,Ainname,Aoutname,Ayd] = sys2ss(Asys); + [Ba,Bb,Bc,Bd,Btsam,Bn,Bnz,Bstname,Binname,Boutname,Byd] = sys2ss(Bsys); + + if(Byd) + # check direct feed-through of inputs through discrete outputs + alist = find(Byd); + if(An) + bd = Ab(1:An)* Bd(alist,:); + if(norm(bd,1)) + warning("sysmult: inputs -> Bsys discrete outputs -> continous states of Asys"); + endif + endif + # check direct feed-through of continuous state through discrete outputs + if(Bn) + bc = Ab(1:An)* Bc(alist,1:(Bn)); + if( norm(bc,1) ) + warning("sysmult: Bsys states -> Bsys discrete outputs -> continuous states of Asys"); + endif + endif + endif + + sys = sysgroup(Asys,Bsys); + + # connect outputs of B to inputs of A + sys = sysconnect(sys,Ap+(1:Bp),1:Am); + + # now keep only outputs of A and inputs of B + sys = sysprune(sys,1:Ap,Am+(1:Bm)); + + implicit_str_to_num_ok = save_val; # restore value +endfunction + diff --git a/scripts/control/sysout.m b/scripts/control/sysout.m new file mode 100644 --- /dev/null +++ b/scripts/control/sysout.m @@ -0,0 +1,143 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function retsys = sysout(sys,opt) +# function sysout(sys[,opt]) +# print out a system data structure in desired format +# +# sys: system data structure +# opt: []: primary system form (default) +# "ss": state space form +# "tf": transfer function form +# "zp": zero-pole form +# "all": all of the above + +# Written by A S Hodel: 1995-1996 +# $Revision: 1.2 $ + +# save for restoring at end of routine +save_val = implicit_str_to_num_ok; +implicit_str_to_num_ok = 1; + +if( (nargin < 1) || (nargin > 2) ) + usage("sysout(sys[,opt])"); +endif + +if(isempty(sys)) + retsys = sys; + warning("sysout: empty system") + return; +endif + +if(! is_struct(sys)) + disp("sysout: input must be a system structure") +endif + +# set up output type array +farray = ["tf";"zp";"ss"]; + +if( nargin == 1 ) + opt = farray(sys.sys(1)+1,:); +else + if( ! (strcmp(opt,"ss") + strcmp(opt,"tf") + ... + strcmp(opt,"zp") + strcmp(opt,"all") ) ) + error("opt must be one of [], \"ss\", \"tf\", \"zp\", or \"all\""); + endif +endif + +# now check output for each form: +if( !isempty(sys.inname) ) + disp("Input(s)") + outlist(sys.inname," ") +else + disp("Input(s): none"); +endif +if ( ! isempty(sys.outname) ) + disp("Output(s):") + outlist(sys.outname," ",sys.yd) +else + disp("Output(s): none"); +endif +if(sys.tsam > 0) + disp(["Sampling interval: ",num2str(sys.tsam)]); + str = "z"; +else + str = "s"; +endif + +# transfer function form +if( strcmp(opt,"tf") + strcmp(opt,"all") ) + sys = sysupdate(sys,"tf"); #make sure tf is up to date + disp("transfer function form:") + tfout(sys.num,sys.den,str); +endif + +if( strcmp(opt,"zp") + strcmp(opt,"all") ) + sys = sysupdate(sys,"zp"); #make sure zp is up to date + disp("zero-pole form:") + zpout(sys.zer, sys.pol,sys.k,str) +endif + +if( strcmp(opt,"ss") + strcmp(opt,"all") ) + sys = sysupdate(sys,"ss"); + disp("state-space form:"); + disp([num2str(sys.n)," continuous states, ", ... + num2str(sys.nz)," discrete states"]); + if( !isempty(sys.stname) ) + disp("State(s):") + xi = (sys.n+1):(sys.n+sys.nz); + xd = zeros(1,rows(sys.a)); + if(!isempty(xi)) + xd(xi) = 1; + endif + outlist(sys.stname," ",xd); + else + disp("State(s): none"); + endif + + # display matrix values? + dmat = (max( [ size(sys.a) size(sys.b) size(sys.c) size(sys.d) ] ) <= 32); + + disp(sprintf("A matrix: %d x %d",rows(sys.a),columns(sys.a))) + if(dmat) + disp(sys.a) + endif + + disp(sprintf("B matrix: %d x %d",rows(sys.b),columns(sys.b))) + if(dmat) + disp(sys.b) + endif + + disp(sprintf("C matrix: %d x %d",rows(sys.c),columns(sys.c))) + if(dmat) + disp(sys.c) + endif + disp(sprintf("D matrix: %d x %d",rows(sys.d),columns(sys.d))) + if(dmat) + disp(sys.d) + endif +endif + +if(nargout >= 1) + retsys = sys; +endif + +# restore global variable +implicit_str_to_num_ok = save_val; + +endfunction diff --git a/scripts/control/sysprune.m b/scripts/control/sysprune.m new file mode 100644 --- /dev/null +++ b/scripts/control/sysprune.m @@ -0,0 +1,93 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function sys = sysprune(sys,output_list,input_list) +# function retsys = sysprune(Asys,output_list,input_list) +# Extract specified inputs/outputs from a system +# +# inputs: +# Asys: system data structure +# output_list,input_list: list of connections indices; the new +# system has outputs y(output_list(ii)) and inputs u(input_list(ii)). +# May select as [] (empty matrix) to specify all outputs/inputs. +# +# output: retsys: resulting system: +# ____________________ +# | | +# u1 ----->| |----> y1 +# (input_list) | Asys | (output_list) +# | | +# u2 (deleted) |---->| |----| y2 (deleted) +# | | +# -------------------- + +# A. S. Hodel August 1995 +# Updated by John Ingram 7-15-96 +# $Revision: 1.2 $ + + if( nargin != 3 ) + usage("retsys = sysprune(sys,output_list,input_list)"); + endif + + # default: no action + if(isempty(output_list)) + outputlist = 1:rows(sys.outname); + endif + if(isempty(input_list)) + input_list = 1:rows(sys.inname); + endif + + # check dimensions + if( !( + (is_vector(output_list) | isempty(output_list)) + & (is_vector(input_list) | isempty(input_list)) + )) + error("sysprune: output_list and input_list must be vectors"); + else + lo = length(output_list); + li = length(input_list); + + if(is_duplicate_entry(output_list) || is_duplicate_entry(input_list) ) + error("sysprune: duplicate entry in input and/or output list"); + endif + endif + + m = rows(sys.inname); + p = rows(sys.outname); + + if( !is_struct(sys)) + error("Asys must be a system data structure (see ss2sys, tf2sys, or zp2sys)") + elseif(p < lo) + error([num2str(lo)," output_list entries, system has only ", ... + num2str(p)," outputs"]); + elseif(m < li) + error([num2str(li)," input_list entries, system has only ", ... + num2str(m)," inputs"]); + endif + + sys = sysupdate(sys,"ss"); + + sys.b = sys.b(:,input_list); + sys.c = sys.c(output_list,:); + sys.d = sys.d(output_list,input_list); + + sys.inname = sys.inname(input_list,:); + sys.outname = sys.outname(output_list,:); + sys.yd = sys.yd(output_list); + +endfunction diff --git a/scripts/control/sysreorder.m b/scripts/control/sysreorder.m new file mode 100644 --- /dev/null +++ b/scripts/control/sysreorder.m @@ -0,0 +1,53 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function pv = sysreorder(vlen,list) +# function pv = sysreorder(vlen,list) +# +# inputs: vlen: vector length +# list: a subset of {1:vlen} +# pv: a permutation vector to order elements of [1:vlen] in -list- +# to the end of a vector +# used internally by sysconnect to permute vector elements to their +# desired locations. No user-serviceable parts inside; do not attempt +# to use this at home! + +# A. S. Hodel, Aug 1995 +# $Revision: 1.1.1.1 $ + + #disp('sysreorder: entry') + + pv = 1:vlen; + # make it a row vector + list = reshape(list,1,length(list)); + A = pv'*ones(size(list)); + B = ones(size(pv'))*list; + X = (A != B); + if(!is_vector(X)) + y = min(X'); + else + y = X'; + endif + z = find(y == 1); + if(!isempty(z)) + pv = [z, list]; + else + pv = list; + endif + +endfunction diff --git a/scripts/control/sysrepdemo.m b/scripts/control/sysrepdemo.m new file mode 100644 --- /dev/null +++ b/scripts/control/sysrepdemo.m @@ -0,0 +1,473 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function sysrepdemo() + +# Octave Controls toolbox demo: System representation + +# Written by A. S. Hodel June 1995 +# Revised Aug 1995 for system data structure format + +# $Revision: 1.1.1.1 $ +# $Log: sysrepdemo.m,v $ +# Revision 1.1.1.1 1998/05/19 20:24:09 jwe +# +# Revision 1.4 1997/02/13 15:38:26 hodel +# fixed misprint in zp2sys demo (needed empty zeros vector in option 3) +# +# Revision 1.3 1997/02/13 15:22:32 hodel +# fixed typo in menu option +# +# Revision 1.2 1997/02/12 11:53:22 hodel +# *** empty log message *** +# +# Revision 1.1 1997/02/12 11:35:14 hodel +# Initial revision +# + + save_val = page_screen_output; + page_screen_output = 1; + + disp('System representation demo:') + num = [5 -1]; + denom = [1 -2 6]; + a = b = c = []; + syschoice = -1; + ch_init = 2; + ch_extract = ch_init+1; + ch_update = ch_extract+1; + ch_view = ch_update+1; + ch_details = ch_view+1; + ch_quit = ch_details+1; + while(syschoice != ch_quit) + disp(" ") + syschoice = menu('Octave System Representation Menu', ... + "General overview of system representation (DO THIS FIRST)", ... + "Initialize a system (ss2sys, tf2sys, zp2sys)", ... + "Extract data from a system(sys2ss, sys2tf, sys2zp)", ... + "Update internal representation (sysupdate)", ... + "View the internal contents of a system (sysout)", ... + "Details of internal representation", ... + "Return to main menu"); + if(syschoice == 1) # general overview + disp("The Octave Control Systems Toolbox (OCST) was designed to") + disp("provide a simple user interface to a powerful set of tools.") + disp(' ') + disp(' ----------') + disp(' input(s) ---->| System | ---> output(s) ') + disp(' ----------') + disp(' ') + disp("Like other computer-aided control system design tools, the OCST") + disp("enables users to enter their descriptions of dynamic systems in ") + disp("their preferred form (state space, transfer function, or "); + disp("zero-pole format). "); + disp("The OCST stores system descriptions in a single variable data "); + disp("structure that allows for continuous time, discrete-time, or mixed "); + disp("(sampled-data) systems. "); + disp(" "); + disp("This single variable description of dynamic systems greatly simplifies "); + disp("both the code of the OCST as well as the user interface, since only") + disp("one variable is passed per system, regardless of the internal ") + disp("representation used in the data structure. As a result, the "); + disp("likelihood of user error is greatly reduced when calling OCST") + disp("functions. Further, all OCST functions have been written to") + disp("provide meaningful warning or error message to assist the user") + disp("in correcting their programming errors while using the OCST.") + disp("The details of the internal representation can be seen in "); + disp(["menu option ",num2str(ch_details)]); + disp("The data structure used in the OCST is called a \"system data structure.\""); + disp("A system data structure is contstructed with one of:") + disp(" fir2sys (FIR transfer function to system)") + disp(" ss2sys (state space matrices to system)") + disp(" tf2sys (SISO transfer function to system)") + disp(" zp2sys (SISO zero/pole/leading coefficient to system)") + disp(" ") + disp(["These functions are discussed in in menu option ",num2str(ch_init)]) + disp("The data in a system may be extracted using ") + disp(" sys2fir (FIR transfer function from system") + disp(" sys2ss (state space matrices from system)") + disp(" sys2tf (SISO transfer function from system)") + disp(" sys2zp (SISO zero/pole/leading coefficient from system)") + disp(" ") + disp(["These functions are discussed in menu option ", ... + num2str(ch_extract)]); + disp("Other options discussed under this menu are updating the internal") + disp("representation form of a system data structure with sysupdate and printing") + disp("the description of a dynamic system to the screen with sysout.") + disp(" ") + disp("Once the user is familiar with these commands, the rest of the ") + disp("OCST package will be quite easy to use.") + elseif(syschoice == ch_init) % Initialize + disp("Initialization of a system:"); + disp(' '); + formopt = 0; + while(formopt != 4) + disp("Three data formats may be used to initialize a system:") + formopt = menu("System data structure initialization menu", ... + "State space form (ss2sys)", ... + "Transfer function form (tf2sys)", ... + "zero-pole form (zp2sys)", ... + "Return to System representation menu"); + if(formopt == 1) + disp('State space representation of a system is based on the usual') + disp('multi-variable differential equations') + disp(' ') + disp(' . ') + disp(' x = A x + B u -or - x(k+1) = A x(k) + B u(k) ') + disp(' y = C x + D u y(k) = C x(k) + D u(k) ') + disp(' ') + disp('for matrices A, B, C, D of appropriate dimension.') + disp(' ') + ssopt = 0; + ssquit = 5; + while(ssopt < ssquit) + ssopt = menu("State space initialization examples", ... + "Double integrator example", ... + "Double delay (discrete-time) example", ... + "Summing junction (D-matrix only) example", ... + "ss2sys details (help ss2sys)", ... + "return to system initialization menu", ... + "return to system representation main menu"); + if(ssopt == 1) + disp("Example: construct a system representation of a") + disp("double integrator via state-space form") + cmd = "a = [0 1; 0 0];"; + run_cmd + cmd = "b = [0 ; 1];"; + run_cmd + cmd = "c = [1 0];"; + run_cmd + cmd = "sys = ss2sys(a,b,c);"; + run_cmd + disp("The state space form of the system is seen via sysout:") + cmd = "sysout(sys)"; + run_cmd + disp("Notice that the Octave controls toolbox automatically") + disp("assigns names to the states, inputs and outputs,") + disp("and that the D matrix was filled in automatically.") + disp("We verify that it's a double integrator via sysout:") + cmd = "sysout(sys,""tf"")"; + run_cmd + prompt + elseif(ssopt == 2) + disp("Example: discrete-time double-delay:") + disp("This example is identical to the double-integrator,") + disp("except that it is a discrete-time system, and so has") + disp("a sampling interval. We arbitrarily select T=1e-3."); + cmd = "a = [0 1; 0 0];"; + run_cmd + cmd = "b = [0 ; 1];"; + run_cmd + cmd = "c = [1 0];"; + run_cmd + cmd = "sys=ss2sys(a,b,c,[],1e-3);"; + run_cmd + cmd = "sysout(sys)"; + run_cmd + disp("Notice that the D matrix was filled in automatically.") + disp("This is done if D is input as the empty matrix.") + disp(" ") + disp("Notice also that the output y_1 is labelled as a discrete") + disp("output. The OCST data structure keeps track of states") + disp("and output signals that are produced by the discrete-time") + disp("portion of a system. Discrete states and outputs are ") + disp("implemented as shown in the block diagram below:") + disp(" ") + disp(" ") + disp(" _________ ________ x(kT) ________________") + disp("f(t)-->|sampler|-->| delay |----->|zero order hold| -->") + disp(" --------- -------- ----------------") + disp(" ") + disp(" ___________ _______________") + disp("f(t)-->| sampler |-->|zero-order hold| --> y(discrete)") + disp(" ----------- ---------------") + disp(" ") + disp("where f(t) is an input signal to either the output or the") + disp(" discrete state.") + disp(" ") + disp("The OCST does not implement samplers on inputs to continuous") + disp("time states (i.e., there are no samplers implicit in the B") + disp("or D matrices unless there are corresponding discrete") + disp("outputs or states. The OCST provides warning messages when") + disp("if this convention is violated.") + prompt + elseif(ssopt == 3) + disp("A summing junction that computes e(t) = r(t) - y(t) may be"); + disp("constructed as follows:"); + disp("First, we set the matrix D:") + cmd = "D = [1 -1];"; + run_cmd + disp("ss2sys allows the initialization of signal and state names") + disp("(see option 4), so we initialize these as follows:") + cmd = "inname = [\"r(t)\";\"y(t)\"];"; + run_cmd; + cmd = "outname = \"e(t)\";"; + run_cmd + disp("Since the system is continous time and without states,") + disp("the ss2sys inputs tsam, n, and nz are all zero:") + cmd = "sys = ss2sys([],[],[],D,0,0,0,[],inname,outname);"; + run_cmd + disp("The resulting system is:") + cmd = "sysout(sys)"; + run_cmd + disp("A discrete-time summing block can be implemented by setting") + disp("the sampling time positive:") + cmd = "sys = ss2sys([],[],[],D,1e-3,0,0,[],inname,outname);"; + run_cmd + disp("The resulting system is:") + cmd = "sysout(sys)"; + run_cmd + prompt + elseif(ssopt == 4) + help ss2sys + disp(" ") + disp(" ") + disp("Notice that state-space form allows a single system to have") + disp("both continuous and discrete-time states and to have both continuous") + disp("and discrete-time outputs. Since it's fairly easy to make an") + disp("error when mixing systems of this form, the Octave controls") + disp("toolbox attempts to print warning messages whenever something") + disp("questionable occurs.") + elseif(ssopt == 6) + formopt = 4; # return to main menu + endif + endwhile + elseif(formopt == 2) + tfopt = 0; + while(tfopt < 5) + tfopt = menu("Transfer function initialization menu", ... + "Continuous time initialization" , ... + "Discrete time initialization" , ... + "User specified signal names" , ... + "tf2sys details (help tf2sys)", ... + "Return to system initialization menu", ... + "Return to system representation main menu"); + if(tfopt == 1) # continuous time + disp("A transfer function is represented by vectors of the") + disp("coefficients of the numerator and denominator polynomials"); + disp(" ") + disp("For example: the transfer function"); + disp(" "); + num = [5 -1]; + denom = [1 -2 6]; + tfout(num,denom); + disp(" ") + disp("is generated by the following commands:") + cmd = "num = [5 -1]"; + run_cmd + cmd = "denom = [1 -2 6]"; + run_cmd + cmd = "sys = tf2sys(num,denom);"; + run_cmd + disp("alternatively, the system can be generated in a single command:"); + cmd = "sys = tf2sys([5 -1],[1 -2 6]);"; + run_cmd + disp("Notice the output of sys: it is an Octave data structure.") + disp("The details of its member variables are explained under") + disp("System Representation Menu option 5 (the details of system form)") + disp(" "); + disp("The data structure can be observed with the sysout command:") + cmd = "sysout(sys)"; + run_cmd + disp("Notice that Octave assigns names to inputs and outputs.") + disp("The user may manually select input and output names; see option 3"); + prompt + elseif(tfopt == 2) # discrete time + disp("A transfer function is represented by vectors of the") + disp("coefficients of the numerator and denominator polynomials"); + disp("Discrete-time transfer functions require ") + disp("the additional parameter of a sampling period:") + cmd = "sys=tf2sys([5 -1],[1 2 -6],1e-3);"; + run_cmd + cmd = "sysout(sys)"; + run_cmd + disp("The OCST recognizes discrete-time transfer functions and") + disp("accordingly prints them with the frequency domain variable z."); + disp("Notice that Octave assigns names to inputs and outputs.") + disp("The user may set input and output names; see option 3"); + elseif(tfopt == 3) # user specified names + disp("The OCST requires all signals to have names. The OCST assigned default"); + disp("names to the signals in the other examples. We may initialize a transfer"); + disp("function with user-specified names as follows: Consider a simple ") + disp("double-integrator model of aircraft roll dynamics with ") + disp("input \"aileron angle\" and output \"theta\". A ") + disp("system for this model is generated by the command") + cmd = "aircraft=tf2sys(1,[1 0 0],0,\"aileron angle\",\"theta\");"; run_cmd + disp("The sampling time parameter 0 indicates that the system") + disp("is continuous time. A positive sampling time indicates a") + disp("discrete-time system (or sampled data system).") + cmd = "sysout(aircraft)"; + run_cmd + disp("Notice that the user-selected signal names are listed.") + disp("These signal names are used in OCST plots and design functions."); + disp("(Run the frequency response demo to see an example of the use of "); + disp("signal names in plots.)") + prompt + elseif(tfopt == 4) # help + help tf2sys + prompt + elseif(tfopt == 6) # return to main menu + formopt = 4; + endif + endwhile + elseif (formopt == 3) + zpopt = 0; + while(zpopt < 5) + zpopt = menu("Zero-pole initialization menu", ... + "Continuous time initialization" , ... + "Discrete time initialization" , ... + "User specified signal names" , ... + "zp2sys details (help zp2sys)", ... + "Return to system initialization menu", ... + "Return to system representation main menu"); + if(zpopt == 1) # continuous time + disp("A zero-pole form representation of a system includes vectors") + disp("of the system poles and zeros and a scalar leading coefficient."); + disp(" ") + disp("For example: the transfer function"); + disp(" "); + k = 5; + num = [5 -1]; + denom = [1 -2 6]; + zpout(num,denom,k); + disp(" ") + disp("is generated by the following commands:") + cmd = "num = [5 -1]"; + run_cmd + cmd = "denom = [1 -2 6]"; + run_cmd + cmd = "k = 5"; + run_cmd + cmd = "sys = zp2sys(num,denom,k);"; + run_cmd + disp("alternatively, the system can be generated in a single command:"); + cmd = "sys = zp2sys([5 -1],[1 -2 6],5);"; + run_cmd + disp("Notice the output of sys: it is an Octave data structure.") + disp("The details of its member variables are explained under") + disp("System Representation Menu option 5 (the details of system form)") + disp(" "); + disp("The data structure can be observed with the sysout command:") + cmd = "sysout(sys)"; + run_cmd + disp("Notice that Octave assigns names to inputs and outputs.") + disp("The user may manually select input and output names; see option 3"); + prompt + elseif(zpopt == 2) # discrete time + disp("A zero-pole form representation of a system includes vectors") + disp("of the system poles and zeros and a scalar leading coefficient."); + disp(" ") + disp("Discrete-time systems require the additional parameter of a sampling period:") + cmd = "sys=zp2sys([5 -1],[1 2 -6],5,1e-3);"; + run_cmd + cmd = "sysout(sys)"; + run_cmd + disp("The OCST recognizes discrete-time transfer functions and") + disp("accordingly prints them with the frequency domain variable z."); + disp("Notice that Octave assigns names to inputs and outputs.") + disp("The user may set input and output names; see option 3"); + elseif(zpopt == 3) # user specified names + disp("The OCST requires all signals to have names. The OCST assigned default"); + disp("names to the signals in the other examples. We may initialize a transfer"); + disp("function with user-specified names as follows: Consider a simple ") + disp("double-integrator model of aircraft roll dynamics with ") + disp("input \"aileron angle\" and output \"theta\". A ") + disp("system for this model is generated by the command") + cmd = "aircraft=zp2sys([],[0 0],1,0,\"aileron angle\",\"theta\");"; run_cmd + disp("The sampling time parameter 0 indicates that the system") + disp("is continuous time. A positive sampling time indicates a") + disp("discrete-time system (or sampled data system).") + cmd = "sysout(aircraft)"; + run_cmd + disp("Notice that the user-selected signal names are listed.") + disp("These signal names are used in OCST plots and design functions."); + disp("(Run the frequency response demo to see an example of the use of "); + disp("signal names in plots.)") + prompt + elseif(zpopt == 4) # help + help zp2sys + prompt + elseif(zpopt == 6) # return to main menu + formopt = 4; + endif + endwhile + endif + endwhile + elseif(syschoice == ch_extract) # extract system information + disp("Extract information from a system data structure in a selected format:") + disp("The actions of operations ss2sys, tf2sys, and zp2sys are reversed by") + disp("respective functions sys2ss, sys2tf, and sys2zp. The latter two"); + disp("functions are applicable only to SISO systems.") + formopt = 0; + while(formopt != 4) + formopt = menu("Extract system information", ... + "in state space form (sys2ss)", ... + "in transfer function form (sys2tf)", ... + "in zero pole form (sys2zp)", ... + "Return to system representation menu"); + if(formopt == 1) + help sys2ss + elseif(formopt == 2) + help sys2tf + elseif(formopt == 3) + help sys2zp + endif + prompt + endwhile + elseif(syschoice== ch_update) + disp("The OCST system data structure format will store a system in the same format") + disp("as that with which it was initialized. For example, consider the following:") + cmd = "sys=zp2sys([1 2],[3 4 5],6)"; + run_cmd + disp(" ") + disp("Notice the internal variables in the structure include zer, pol, and k,") + disp("the required variables for zero-pole form. We can update the system") + disp("to include state-space form as follows:") + cmd = "sys = sysupdate(sys,\"ss\")"; + run_cmd + disp(" ") + disp("Now the sys data structure includes variables a, b, c, and d, as well") + disp("the default state names stname. sysupdate is usually used internally in") + disp("the OCST, but can be used manually if desired. A full description of") + disp("sysupdate is as follows:") + help sysupdate + prompt + elseif(syschoice == ch_view) + disp("The sysout command can be used to view a system in any desired format.") + disp("For example, consider the system created as follows:") + cmd = "aircraft=zp2sys(1,[0 0],1,0,\"aileron angle\",\"theta\");"; run_cmd + disp("The system may be viewed in its default format (zero-pole) as follows") + cmd = "sysout(aircraft)"; + run_cmd + disp(" ") + disp("The system may be viewed in state-space or transfer function form as well:") + cmd = "sysout(aircraft,\"ss\")"; + run_cmd + cmd = "sysout(aircraft,\"tf\")"; + run_cmd + disp("A complete description of sysout is below:") + help sysout + prompt + elseif(syschoice == ch_details) + packedform + endif + + endwhile + page_screen_output = save_val; +endfunction + diff --git a/scripts/control/sysscale.m b/scripts/control/sysscale.m new file mode 100644 --- /dev/null +++ b/scripts/control/sysscale.m @@ -0,0 +1,131 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function sys = sysscale(sys,outscale,inscale,outname,inname) +# +# function sys = sysscale(sys,outscale,inscale[,outname,inname]) +# scale inputs/outputs of a system. +# +# inputs: +# sys: structured system +# outscale, inscale: constant matrices of appropriate dimension +# output: sys: resulting open loop system: +# +# ----------- ------- ----------- +# u --->| inscale |--->| sys |--->| outscale |---> y +# ----------- ------- ----------- +# +# If the input names and output names are not given and the scaling matrices +# are not square, then default names will be given to the inputs and/or +# outputs. +# +# A warning message is printed if outscale attempts to add continuous +# system outputs to discrete system outputs; otherwise yd is set appropriately + +# A. S. Hodel August 1995 +# modified by John Ingram 7-15-96 +# $Revision: 1.1.1.1 $ + + if( (nargin < 3) || (nargin > 5) ) + usage("retsys = sysscale(Asys,output_list,input_list{,inname,outname})"); + elseif (!is_struct(sys)) + error("sys must be a structured system"); + endif + + # check for omitted scales + if(isempty(outscale)) + outscale = eye(rows(sys.outname)); + endif + if(isempty(inscale)) + inscale = eye(rows(sys.inname)); + endif + + # check dimensions of scaling matrices + if((columns(sys.b)!=rows(inscale)) & (columns(sys.d)!=rows(inscale))) + error('inscale is not compatible with the system inputs'); + elseif( (columns(outscale)!=rows(sys.c)) & ... + (columns(outscale)!=rows(sys.d))) + error("outscale is not compatible with the system outputs"); + endif + + outc = find(sys.yd==0); + outd = find(sys.yd==1); + + #disp("sysscale: outc,outd=") + #disp(outc) + #disp(outd) + #disp("sysscale") + + if(length(outc) & length(outd)) + for ii = 1:rows(outscale) + nci = norm(outscale(ii,outc)); + ndi = norm(outscale(ii,outd)); + + #disp(["sysscale: ii=",num2str(ii),", nci, ndi="]) + #disp(nci) + #disp(ndi) + #disp("syscale") + + if( nci & ndi) + warning(["sysscale: outscale(",num2str(ii), ... + ",:) sums continuous and discrete outputs; setting output to cont"]) + yd(ii) = 0; + else + yd(ii) = (ndi != 0); + endif + + #disp(["sysscale: yd(,",num2str(ii),"=",num2str(yd(ii)),": press a key"]); + #kbhit + endfor + else + yd = ones(1,rows(outscale))*( length(outd) > 0); + endif + sys.yd = yd; + + sys.b = (sys.b)*inscale; + sys.d = (sys.d)*inscale; + sys.c = outscale*(sys.c); + sys.d = outscale*(sys.d); + + if( !is_square(outscale) ) + # strip extra output names (if any) + sys.outname = sys.outname(1:min(rows(outscale),columns(outscale)),:); + if( nargin < 4) + warning("sysscale: outscale not square, outname not specified"); + warning("sysscale: using default output names"); + outname = sysdefioname(rows(sys.c),"y"); + endif + else + outname = sys.outname; + endif + if( !is_square(inscale) ) + # strip extra output names (if any) + sys.inname = sys.inname(1:min(rows(inscale),columns(inscale)),:); + if(nargin < 5) + warning("sysscale: inscale not square, inname not specified"); + warning("sysscale: using default input names"); + inname = sysdefioname(columns(sys.b),"u"); + endif + else + inname = sys.inname; + endif + + sys = syschnames(sys,"out",1:rows(outname),outname); + sys = syschnames(sys,"in",1:rows(inname),inname); + +endfunction diff --git a/scripts/control/syssub.m b/scripts/control/syssub.m new file mode 100644 --- /dev/null +++ b/scripts/control/syssub.m @@ -0,0 +1,104 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function sys = syssub(Gsys,Hsys) +# +# [sys] = syssub(Gsys,Hsys) +# +# +# returns transfer functin sys = Gsys - Hsys +# +# Method: Gsys and Hsys are connected in parallel +# The vector are connected to both systems; the outputs will be +# subtracted. The names given to the system will be the G systems names. +# +# ________ +# ----| Gsys |--- +# u | ---------- +| +# ----- (_)----> y +# | ________ -| +# ----| Hsys |--- +# -------- + +# Written by John Ingram July 1996 +# $Revision: 1.1.1.1 $ + + save_val = implicit_str_to_num_ok; # save for later + implicit_str_to_num_ok = 1; + + if(nargin != 2) + usage("syssub: [sys] = syssub(Gsys,Hsys)"); + endif + + # check inputs + if(!is_struct(Gsys) | !is_struct(Hsys)) + error("Both Gsys and Hsys must be a system data structure"); + endif + + # check for compatibility + if(rows(Gsys.inname) != rows(Hsys.inname)) + error("Gsys and Hsys must have the same number of inputs"); + elseif(rows(Gsys.outname) != rows(Hsys.outname)) + error("Gsys and Hsys must have the same number of outputs"); + endif + + # check for digital to continuous addition + if (Gsys.yd != Hsys.yd) + error("can not add a discrete output to a continuous output"); + endif + + if( (Gsys.sys(1) == 0) | (Hsys.sys(1) == 0) ) + # see if adding transfer functions with identical denominators + Gsys = sysupdate(Gsys,"tf"); + Hsys = sysupdate(Hsys,"tf"); + if(Hsys.den == Gsys.den) + sys = Gsys; + sys.sys(1) = 0; + sys.num = sys.num - Hsys.num; + return + endif + endif + + # make sure in ss form + Gsys = sysupdate(Gsys,"ss"); + Hsys = sysupdate(Hsys,"ss"); + + #disp("syssub: Gsys=") + #sysout(Gsys,"ss"); + #disp("syssub: Hsys=") + #sysout(Hsys,"ss") + + sys = sysgroup(Gsys,Hsys); + + eyin = eye(columns(Gsys.b)); + eyout = eye(rows(Gsys.c)); + + inname = sys.inname(1:rows(Gsys.inname) , :); + outname = sys.outname(1:rows(Gsys.outname) , :); + + #disp("syssub: before sysscale: sys.yd=") + #disp(sys.yd) + #disp("syssub:") + + sys = sysscale(sys,[eyout -eyout],[eyin;eyin],outname,inname); + + #disp("syssub: sys.yd=") + #disp(sys.yd) + #disp("syssub: exiting") + +endfunction diff --git a/scripts/control/sysupdate.m b/scripts/control/sysupdate.m new file mode 100644 --- /dev/null +++ b/scripts/control/sysupdate.m @@ -0,0 +1,107 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function sys = sysupdate(sys,opt) +# function retsys = sysupdate(sys,opt) +# Update the internal representation of a system. +# inputs: +# sys: system data structure +# opt: string: "tf" -> update transfer function +# "zp" -> update zero-pole form +# "ss" -> update state space form +# "all" -> all of the above +# outputs: retsys: contains union of data in sys and requested data. +# if requested data in sys is already up to date then retsys=sys. +# +# conversion to tf or zp exits with an error if the system is +# mixed continuous/digital +# +# see also: tf2sys, ss2sys, zp2sys, sysout, sys2ss, sys2tf, sys2zp + +# Written by John Ingram 7-9-96 +# $Revision: 1.1.1.1 $ + + # check for correct number of inputs + if (nargin != 2) + usage("newsys = sysupdate(sys,opt)"); + elseif(! is_struct(sys) ) + error("1st argument must be system data structure") + elseif(! (strcmp(opt,"tf") + strcmp(opt,"zp") + ... + strcmp(opt,"ss") + strcmp(opt,"all")) ) + error("2nd argument must be \"tf\", \"zp\", \"ss\", or \"all\""); + endif + + # check to make sure not trying to make a SISO system out of a MIMO sys + if ( (strcmp(opt,"tf") + strcmp(opt,"zp") + strcmp(opt,"all")) ... + & (sys.sys(1) == 2) & (! is_siso(sys) ) ) + error("MIMO -> SISO update requested"); + endif + + # update transfer function if desired + if ( (strcmp(opt, "tf") + strcmp(opt,"all"))&& (!sys.sys(2))) + # check to make sure the system is not discrete and continuous + is_digital(sys); + + # if original system zero-pole + if (sys.sys(1) == 1) + [sys.num,sys.den] = zp2tf(sys.zer,sys.pol,sys.k); + sys.sys(2) = 1; + # if original system is state-space + elseif(sys.sys(1) == 2) + [sys.num,sys.den] = ss2tf(sys.a,sys.b,sys.c,sys.d); + sys.sys(2) = 1; + endif + endif + + + # update zero-pole if desired + if ( (strcmp(opt, "zp") + strcmp(opt,"all")) && (! sys.sys(3)) ) + # check to make sure the system is not discrete and continuous + is_digital(sys); + + # original system is transfer function + if (sys.sys(1) == 0) + [sys.zer,sys.pol,sys.k] = tf2zp(sys.num,sys.den); + sys.sys(3) = 1; + # original system is state-space + + elseif(sys.sys(1) == 2) + [sys.zer,sys.pol,sys.k] = ss2zp(sys.a,sys.b,sys.c,sys.d); + sys.sys(3) = 1; + endif + + endif + + # update state-space if desired + if ( (strcmp(opt, "ss") + strcmp(opt,"all")) && (! sys.sys(4)) ) + # original system is transfer function + if (sys.sys(1) == 0) + [sys.a,sys.b,sys.c,sys.d] = tf2ss(sys.num,sys.den); + sys.sys(4) = 1; + # original system is zero-pole + elseif(sys.sys(1) == 1) + [sys.a,sys.b,sys.c,sys.d] = zp2ss(sys.zer,sys.pol,sys.k); + sys.sys(4) = 1; + endif + + # create new state names + sys.stname = sysdefstname(sys.n, sys.nz); + endif + + +endfunction diff --git a/scripts/control/tf2ss.m b/scripts/control/tf2ss.m new file mode 100644 --- /dev/null +++ b/scripts/control/tf2ss.m @@ -0,0 +1,92 @@ +# Copyright (C) 1996,1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [a,b,c,d] = tf2ss(num,den) + # Conversion from tranfer function to state-space. + # The state space system + # . + # x = Ax + Bu + # y = Cx + Du + # + # is obtained from a transfer function + # + # num(s) + # G(s)=------- + # den(s) + # + # via the function call [a,b,c,d] = tf2ss(num,den). + # The vector 'den' must contain only one row, whereas the vector 'num' + # may contain as many rows as there are outputs of the system 'y'. + # The state space system matrices obtained from this function will be + # in controllable canonical form as described in "Modern Control Theory", + # [Brogan, 1991]. + + + # Written by R. Bruce Tenison (June 22, 1994) btenison@eng.auburn.edu + # mod A S Hodel July, Aug 1995 + # $Revision: 1.1.1.1 $ + # $Log$ + + if(nargin != 2) error("tf2ss: wrong number of input arguments") + elseif(isempty(num)) error("tf2ss: empty numerator"); + elseif(isempty(den)) error("tf2ss: empy denominator"); + elseif(!is_vector(num)) + error(sprintf("num(%dx%d) must be a vector",rows(num),columns(num))); + elseif(!is_vector(den)) + error(sprintf("den(%dx%d) must be a vector",rows(den),columns(den))); + endif + + # strip leading zeros from num, den + nz = find(num != 0); + if(isempty(nz)) num = 0; + else num = num(nz(1):length(num)); endif + nz = find(den != 0); + if(isempty(nz)) error("denominator is 0."); + else den = den(nz(1):length(den)); endif + + # force num, den to be row vectors + num = vec(num)'; den = vec(den)'; + nn = length(num); nd = length(den); + if(nn > nd) error(sprintf("deg(num)=%d > deg(den)= %d",nn,nd)); endif + + # Check sizes + if (nd == 1) a = []; b = []; c = []; d = num(:,1) / den(1); + else + # Pad num so that length(num) = length(den) + if (nd-nn > 0) num = [zeros(1,nd-nn), num]; endif + + # Normalize the numerator and denominator vector w.r.t. the leading + # coefficient + d1 = den(1); num = num / d1; den = den(2:nd)/d1; + sw = nd-1:-1:1; + + # Form the A matrix + if(nd > 2) a = [zeros(nd-2,1),eye(nd-2,nd-2);-den(sw)]; + else a = -den(sw); endif + + # Form the B matrix + b = zeros(nd-1,1); b(nd-1,1) = 1; + + # Form the C matrix + c = num(:,2:nd)-num(:,1)*den; c = c(:,sw); + + # Form the D matrix + d = num(:,1); + endif + +endfunction diff --git a/scripts/control/tf2sys.m b/scripts/control/tf2sys.m new file mode 100644 --- /dev/null +++ b/scripts/control/tf2sys.m @@ -0,0 +1,126 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function outsys = tf2sys(num,den,tsam,inname,outname) + # + # sys = tf2sys(num,den{,tsam,inname,outname}) + # build system data structure from transfer function format data + # inputs: + # num, den: coefficients of numerator/denominator polynomials + # tsam: sampling interval. default: 0 (continuous time) + # inname, outname: input/output signal names (string variables) + # outputs: sys = system data structure + + # Written by R. Bruce Tenison July 29, 1994 + # Name changed to TF2SYS July 1995 + # updated for new system data structure format July 1996 + # $Revision: 1.4 $ + # $Log: tf2sys.m,v $ + # Revision 1.4 1998/08/24 15:50:30 hodelas + # updated documentation + # + # Revision 1.2 1998/07/01 16:23:39 hodelas + # Updated c2d, d2c to perform bilinear transforms. + # Updated several files per bug updates from users. + # + # Revision 1.2 1997/02/12 22:45:57 hodel + # added debugging code (commented out) + # + + save_val = implicit_str_to_num_ok; + implicit_str_to_num_ok = 1; + + # Test for the correct number of input arguments + if ((nargin < 2) || (nargin > 5)) + usage('outsys=tf2sys(num,den[,tsam,inname,outname])'); + return + endif + + # check input format + if( ! ( (is_vector(num) || is_scalar(num)) && ... + (is_vector(den) || is_scalar(den))) ) + error(['num (',num2str(rows(num)),'x',num2str(columns(num)), ... + ') and den (',num2str(rows(den)),'x',num2str(columns(den)), ... + ') must be vectors']) + endif + + # strip leading zero coefficients + num = tf2sysl(num); + den = tf2sysl(den); + + if (length(num) > length(den)) + error([ 'number of poles (', num2str(length(den)-1), ... + ') < number of zeros (', num2str(length(num)-1),')']); + endif + + # check sampling interval (if any) + if(nargin <= 2) + tsam = 0; # default + elseif (isempty(tsam)) + tsam = 0; + endif + if ( (! (is_scalar(tsam) && (imag(tsam) == 0) )) || (tsam < 0) ) + error('tsam must be a positive real scalar') + endif + + outsys.num = num; + outsys.den = den; + + # Set the system vector: active = 0(tf), updated = [1 0 0]; + outsys.sys = [0 1 0 0]; + + # Set defaults + outsys.tsam = tsam; + outsys.n = length(den)-1; + outsys.nz = 0; + outsys.yd = 0; # assume discrete-time + # check discrete time + if(tsam > 0) + [outsys.n,outsys.nz] = swap(outsys.n, outsys.nz); + outsys.yd = 1; + endif + + outsys.inname = sysdefioname(1,"u"); + outsys.outname = sysdefioname(1,"y"); + outsys.stname = sysdefstname(outsys.n,outsys.nz); + + # Set name of input + if (nargin > 3) + if (rows(inname) > 1) + warning(["tf2sys: ",num2str(rows(inname))," input names given, 1st used"]); + inname = inname(1,:); + endif + outsys.inname(1,1:length(inname)) = inname; + endif + + # Set name of output + if (nargin > 4) + if (rows(outname) > 1) + warning(["tf2sys: ",num2str(rows(outname)), ... + " output names given, 1st used"]); + outname = outname(1,:); + endif + outsys.outname(1,1:length(outname)) = outname; + endif + + #disp("tf2sys: returning") + #outsys + #disp("/tf2sys") + + implicit_str_to_num_ok = save_val; +endfunction diff --git a/scripts/control/tf2sysl.m b/scripts/control/tf2sysl.m new file mode 100644 --- /dev/null +++ b/scripts/control/tf2sysl.m @@ -0,0 +1,34 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function vec = tf2sysl(vec) +# vec = tf2sysl(vec) +# +# used internally in tf2sys +# strip leading zero coefficients to get the true polynomial length + +# $Revision: 1.1 $ + +while( (length(vec) > 1) & (vec(1) == 0) ) + vec = vec(2:length(vec)); +endwhile +if(vec(1) == 0) + warning("tf2sys: polynomial has no nonzero coefficients!") +endif + +endfunction diff --git a/scripts/control/tf2zp.m b/scripts/control/tf2zp.m new file mode 100644 --- /dev/null +++ b/scripts/control/tf2zp.m @@ -0,0 +1,39 @@ +# Copyright (C) 1996,1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [zer,pol,k] = tf2zp(num,den) +# Converts transfer functions to poles / zeros. +# +# [zer,pol,k] = tf2zp(num,den) returns the zeros and poles of the SISO system +# defined by num/den. K is a gain associated with the system zeros. + +# Written by A. S. Hodel, etc. +# $Revision: 1.2 $ +# $Log: tf2zp.m,v $ + + if(nargin == 2) + if(length(den) > 1) pol = roots(den); + else pol=[]; endif + if(length(num) > 1) zer = roots(num); + else zer=[]; endif + else error("Incorrect number of input arguments"); + endif + + [a,b,c,d] = tf2ss(num,den); + [dum,k] = tzero(a,b,c,d); +endfunction diff --git a/scripts/control/tfout.m b/scripts/control/tfout.m new file mode 100644 --- /dev/null +++ b/scripts/control/tfout.m @@ -0,0 +1,65 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function tfout(num,denom,x) +# +# usage: tfout(num,denom[,x]) +# +# print formatted transfer function num(s)/d(s) +# to the screen +# x defaults to the string "s" +# +# SEE ALSO: polyval, polyvalm, poly, roots, conv, deconv, residue, +# filter, polyderiv, polyinteg, polyout + +# Written by A. Scottedward Hodel (scotte@eng.auburn.edu) June 1995) +# $Revision: 1.2 $ + + save_val = implicit_str_to_num_ok; + save_empty = empty_list_elements_ok; + empty_list_elements_ok = implicit_str_to_num_ok = 1; + + if (nargin < 2 ) | (nargin > 3) | (nargout != 0 ) + usage("tfout(num,denom[,x])"); + endif + + if ( (!is_vector(num)) | (!is_vector(denom)) ) + error("tfout: first two argument must be vectors"); + endif + + if (nargin == 2) + x = 's'; + elseif( ! isstr(x) ) + error("tfout: third argument must be a string"); + endif + + numstring = polyout(num,x); + denomstring = polyout(denom,x); + len = max(length(numstring),length(denomstring)); + if(len > 0) + y = strrep(blanks(len)," ","-"); + disp(numstring) + disp(y) + disp(denomstring) + else + error('tfout: empty transfer function') + end + + implicit_str_to_num_ok = save_val; + empty_list_elements_ok = save_empty; +endfunction diff --git a/scripts/control/tzero.m b/scripts/control/tzero.m --- a/scripts/control/tzero.m +++ b/scripts/control/tzero.m @@ -1,66 +1,130 @@ -## Copyright (C) 1996, 1997 John W. Eaton -## -## 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-1307, USA. +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [zer, gain] = tzero(A,B,C,D) + # [zer{,gain}] = tzero(A,B,C,D) -or- + # [zer{,gain}] = tzero(Asys) + # Compute transmission zeros of a continuous + # . + # x = Ax + Bu + # y = Cx + Du + # + # or discrete + # x(k+1) = A x(k) + B u(k) + # y(k) = C x(k) + D u(k) + # + # system. + # + # outputs: + # zer: transmission zeros of the system + # gain: leading coefficient (pole-zero form) of SISO transfer function + # returns gain=0 if system is multivariable + # References: + # Hodel, "Computation of Zeros with Balancing," 1992 Lin. Alg. Appl. + + # R. Bruce Tenison July 4, 1994 + # A. S. Hodel Aug 1995: allow for MIMO and system data structures + # $Revision: 1.16 $ + # $Log: tzero.m,v $ + # Revision 1.16 1998-11-06 16:15:37 jwe + # *** empty log message *** + # + # Revision 1.7 1998/08/24 15:50:30 hodelas + # updated documentation + # + # Revision 1.4 1998/08/12 20:34:36 hodelas + # Updated to use system access calls instead of direct structure access + # + # Revision 1.3 1998/07/21 14:53:11 hodelas + # use isempty instead of size tests; use sys calls to reduce direct + # access to system structure elements + # + # Revision 1.2 1997/02/13 11:58:05 hodel + # tracked down error in zgfslv; added Log message -## Usage: zr = tzero (a, b, c, d, bal) -## -## Compute the transmission zeros of a, b, c, d. -## -## bal = balancing option (see balance); default is "B". -## -## Needs to incorporate mvzero algorithm to isolate finite zeros. - -## Author: A. S. Hodel -## Created: August 1993 -## Adapted-By: jwe - -function zr = tzero (a, b, c, d, bal) - - if (nargin == 4) - bal = "B"; - elseif (nargin != 5) - error ("tzero: invalid number of arguments"); + # get A,B,C,D and Asys variables, regardless of initial form + if(nargin == 4) + Asys = ss2sys(A,B,C,D); + elseif( (nargin == 1) && (! is_struct(A))) + usage("[zer,gain] = tzero(A,B,C,D) or zer = tzero(Asys)"); + elseif(nargin != 1) + usage("[zer,gain] = tzero(A,B,C,D) or zer = tzero(Asys)"); + else + Asys = A; + [A,B,C,D] = sys2ss(Asys); endif - [n, m, p] = abcddim (a, b, c, d); + Ao = Asys; # save for leading coefficient + siso = is_siso(Asys); + digital = is_digital(Asys); # check if it's mixed or not + + # see if it's a gain block + if(isempty(A)) + zer = []; + gain = D; + return; + endif - if (n > 0 && m > 0 && p > 0) - if (m != p) - warning ("tzero: number of inputs,outputs differ. squaring up"); - if (p > m) - warning (" by padding b and d with zeros."); - b = [b, (zeros (n, p-m))]; - d = [d, (zeros (p, p-m))]; - m = p; - else - warning (" by padding c and d with zeros."); - c = [c; (zeros (m-p, n))]; - d = [d; (zeros (m-p, m))]; - p = m; - endif - warning ("This is a kludge. Try again with SISO system."); - endif - ab = [-a, -b; c, d]; - bb = [(eye (n)), (zeros (n, m)); (zeros (p, n)), (zeros (p, m))]; - [ab, bb] = balance (ab, bb); - zr = -qzval (ab, bb); - else - error ("tzero: a, b, c, d not compatible"); + # First, balance the system via the zero computation generalized eigenvalue + # problem balancing method (Hodel and Tiller, Linear Alg. Appl., 1992) + + Asys = zgpbal(Asys); [A,B,C,D] = sys2ss(Asys); # balance coefficients + meps = 2*eps*norm([A B; C D],'fro'); + Asys = zgreduce(Asys,meps); [A, B, C, D] = sys2ss(Asys); # ENVD algorithm + if(!isempty(A)) + # repeat with dual system + Asys = ss2sys(A', C', B', D'); Asys = zgreduce(Asys,meps); + + # transform back + [A,B,C,D] = sys2ss(Asys); Asys = ss2sys(A', C', B', D'); endif + zer = []; # assume none + [A,B,C,D] = sys2ss(Asys); + if( !isempty(C) ) + [W,r,Pi] = qr([C D]'); + [nonz,ztmp] = zgrownorm(r,meps); + if(nonz) + # We can now solve the generalized eigenvalue problem. + [pp,mm] = size(D); + nn = rows(A); + Afm = [A , B ; C D] * W'; + Bfm = [eye(nn), zeros(nn,mm); zeros(pp,nn+mm)]*W'; + + jdx = (mm+1):(mm+nn); + Af = Afm(1:nn,jdx); + Bf = Bfm(1:nn,jdx); + zer = qz(Af,Bf); + endif + endif + + mz = length(zer); + [A,B,C,D] = sys2ss(Ao); # recover original system + #compute leading coefficient + if ( (nargout == 2) && siso) + n = rows(A); + if ( mz == n) + gain = D; + elseif ( mz < n ) + gain = C*(A^(n-1-mz))*B; + endif + else + gain = []; + endif endfunction + diff --git a/scripts/control/tzero2.m b/scripts/control/tzero2.m new file mode 100644 --- /dev/null +++ b/scripts/control/tzero2.m @@ -0,0 +1,65 @@ +# Copyright (C) 1993 John W. Eaton +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function zr = tzero2 (a, b, c, d, bal) + +# Usage: zr = tzero2 (a, b, c, d, bal) +# +# Compute the transmission zeros of a, b, c, d. +# +# bal = balancing option (see balance); default is "B". +# +# Needs to incorporate mvzero algorithm to isolate finite zeros. + +# Written by A. S. Hodel (scotte@eng.auburn.edu) August 1993. +# $Revision: 1.1.1.1 $ +# $Log$ + + if (nargin == 4) + bal = "B"; + elseif (nargin != 5) + error ("tzero: illegal number of arguments"); + endif + + [n, m, p] = abcddim (a, b, c, d); + + if (n > 0 && m > 0 && p > 0) + if (m != p) + fprintf (stderr, "tzero: number of inputs,outputs differ. squaring up"); + if (p > m) + fprintf (stderr, " by padding b and d with zeros."); + b = [b, zeros (n, p-m)]; + d = [d, zeros (p, p-m)]; + m = p; + else + fprintf (stderr, " by padding c and d with zeros."); + c = [c; zeros (m-p, n)]; + d = [d; zeros (m-p, m)]; + p = m; + endif + fprintf (stderr, "This is a kludge. Try again with SISO system."); + endif + ab = [-a, -b; c, d]; + bb = [eye (n), zeros (n, m); zeros (p, n), zeros (p, m)]; + [ab,bb] = balance (ab, bb); + zr = -qz (ab, bb); + else + error ("tzero: a, b, c, d not compatible. exiting"); + endif + +endfunction diff --git a/scripts/control/ugain.m b/scripts/control/ugain.m new file mode 100644 --- /dev/null +++ b/scripts/control/ugain.m @@ -0,0 +1,49 @@ +# Copyright (C) 1997 Kai P. Mueller +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function outsys = ugain(n) + # function outsys = ugain(n) + # Creates a system with unity gain, no states. + # This trivial system is sometimes needed to create arbitrary + # complex systems from simple systems with buildssic. + # Watch out if you are forming sampled systems since "ugain" + # does not contain a sampling period. + # + # See also: hinfdemo (MIMO H_infinty example, Boeing 707-321 aircraft model) + + # Written by Kai P. Mueller April, 1998 + # Updates + # $Revision: 1.1.1.1 $ + # $Log: ugain.m,v $ + # Revision 1.1.1.1 1998/05/19 20:24:10 jwe + # + # Revision 1.1 1998/05/05 17:04:45 scotte + # Initial revision + # +# Revision 1.1 1998/05/04 15:15:37 mueller +# Initial revision +# + # +# Initial revision +# + + if((nargin != 1) || (nargout > 1)) + usage("outsys = ugain(n)") + endif + outsys = ss2sys([],[],[],eye(n)); +endfunction diff --git a/scripts/control/unpacksys.m b/scripts/control/unpacksys.m new file mode 100644 --- /dev/null +++ b/scripts/control/unpacksys.m @@ -0,0 +1,30 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [a,b,c,d] = unpacksys(syst) + # [a,b,c,d] = unpacksys(sys) + # Obsolete. Use sys2ss instead. + + # Written by David Clem August 19, 1994 + # $Revision: 1.1.1.1 $ + + warning("unpacksys obsolete; calling sys2ss"); + [a,b,c,d] = sys2ss(syst); + +endfunction + diff --git a/scripts/control/wgt1o.m b/scripts/control/wgt1o.m new file mode 100644 --- /dev/null +++ b/scripts/control/wgt1o.m @@ -0,0 +1,68 @@ +# Copyright (C) 1998 Kai P. Mueller +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function wsys = wgt1o(vl, vh, fc) +# wgt10 State space description of a first order weighting function. +# +# wsys = wgt1o(vl, vh, fc) +# +# Weighting function are needed by the H2/H_infinity design procedure. +# These function are part of thye augmented plant P (see hinfdemo +# for an applicattion example). +# +# vl = Gain @ low frequencies +# vh = Gain @ high frequencies +# fc = Corner frequency (in Hz, *not* in rad/sec) + +# Written by Kai P. Mueller September 30, 1997 +# $Revision: 1.1.1.1 $ +# $Log: wgt1o.m,v $ +# Revision 1.1.1.1 1998/05/19 20:24:10 jwe +# +# Revision 1.1 1998/05/05 17:04:56 scotte +# Initial revision +# +# Revision 1.2 1998/05/05 09:01:22 mueller +# comments added +# +# Revision 1.1 1998/05/04 15:08:46 mueller +# Initial revision +# +# + + if (nargin != 3) + usage("wsys = wgt1o(vl, vh, fc)"); + endif + + if(nargout > 1) + usage("wsys = wgt1o(vl, vh, fc)"); + endif + + if (vl == vh) + a = []; + b = []; + c = []; + else + a = [-2*pi*fc]; + b = [-2*pi*fc]; + c = [vh-vl]; + endif + d=[vh]; + + wsys = ss2sys(a,b,c,d); +endfunction diff --git a/scripts/control/zgfmul.m b/scripts/control/zgfmul.m new file mode 100644 --- /dev/null +++ b/scripts/control/zgfmul.m @@ -0,0 +1,84 @@ +# Copyright (C) 1996,1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function y = zgfmul(a,b,c,d,x) + # y = zgfmul(a,b,c,d,x) + # + # Compute product of zgep incidence matrix F with vector x. + # Used by zgepbal (in zgscal) as part of generalized conjugate gradient + # iteration. + # + # References: + # ZGEP: Hodel, "Computation of Zeros with Balancing," Linear algebra and + # its Applications, 1993 + # Generalized CG: Golub and Van Loan, "Matrix Computations, 2nd ed" 1989 + + # A. S. Hodel July 24 1992 + # Conversion to Octave July 3, 1994 + # $Revision: 1.1 $ + # $Log: zgfmul.m,v $ +# Revision 1.1 1998/11/04 14:35:42 hodel +# Initial revision +# + + [n,m] = size(b); + [p,m1] = size(c); + nm = n+m; + y = zeros(nm+p,1); + + # construct F column by column + for jj=1:n + Fj = zeros(nm+p,1); + + #rows 1:n: F1 + aridx = complement(jj,find(a(jj,:) != 0)); + acidx = complement(jj,find(a(:,jj) != 0)); + bidx = find(b(jj,:) != 0); + cidx = find(c(:,jj) != 0); + + Fj(aridx) = Fj(aridx) - 1; # off diagonal entries of F1 + Fj(acidx) = Fj(acidx) - 1; + # diagonal entry of F1 + Fj(jj) = length(aridx)+length(acidx) + length(bidx) + length(cidx); + + if(!isempty(bidx)) Fj(n+bidx) = 1; endif # B' incidence + if(!isempty(cidx)) Fj(n+m+cidx) = -1; endif # -C incidence + y = y + x(jj)*Fj; # multiply by corresponding entry of x + endfor + + for jj=1:m + Fj = zeros(nm+p,1); + bidx = find(b(:,jj) != 0); + if(!isempty(bidx)) Fj(bidx) = 1; endif # B incidence + didx = find(d(:,jj) != 0); + if(!isempty(didx)) Fj(n+m+didx) = 1; endif # D incidence + Fj(n+jj) = length(bidx) + length(didx); # F2 is diagonal + y = y + x(n+jj)*Fj; # multiply by corresponding entry of x + endfor + + for jj=1:p + Fj = zeros(nm+p,1); + cidx = find(c(jj,:) != 0); + if(!isempty(cidx)) Fj(cidx) = -1; endif # -C' incidence + didx = find(d(jj,:) != 0); + if(!isempty(didx)) Fj(n+didx) = 1; endif # D' incidence + Fj(n+m+jj) = length(cidx) + length(didx); # F2 is diagonal + y = y + x(n+m+jj)*Fj; # multiply by corresponding entry of x + endfor + +endfunction diff --git a/scripts/control/zgfslv.m b/scripts/control/zgfslv.m new file mode 100644 --- /dev/null +++ b/scripts/control/zgfslv.m @@ -0,0 +1,66 @@ +# Copyright (C) 1996,1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function x = zgfslv(n,m,p,b) + # x = zgfslv(n,m,p,b) + # solve system of equations for dense zgep problem + + # Written by A. Scotte Hodel + # Converted to Octave by R Bruce Tenison, July 3, 1994 + # $Revision: 1.2 $ + # $Log: zgfslv.m,v $ + + nmp = n+m+p; + gam1 = (2*n)+m+p; gam2 = n+p; gam3 = n+m; + + G1 = givens(sqrt(m),-sqrt(p))'; + G2 = givens(m+p,sqrt(n*(m+p)))'; + + x = b; + + # 1) U1 e^n = sqrt(n)e_1^n + # 2) U2 e^m = sqrt(m)e_1^m + # 3) U3 e^p = sqrt(p)e_1^p + xdx1 = 1:n; xdx2 = n+(1:m); xdx3 = n+m+(1:p); + x(xdx1,1) = zgshsr(x(xdx1,1)); + x(xdx2,1) = zgshsr(x(xdx2,1)); + x(xdx3,1) = zgshsr(x(xdx3,1)); + + # 4) Givens rotations to reduce stray non-zero elements + idx1 = [n+1,n+m+1]; idx2 = [1,n+1]; + x(idx1) = G1'*x(idx1); + x(idx2) = G2'*x(idx2); + + # 6) Scale x, then back-transform to get x + en = ones(n,1); em = ones(m,1); ep = ones(p,1); + lam = [gam1*en;gam2*em;gam3*ep]; + lam(1) = n+m+p; + lam(n+1) = 1; # dummy value to avoid divide by zero + lam(n+m+1)=n+m+p; + + x = x ./ lam; x(n+1) = 0; # minimum norm solution + + # back transform now. + x(idx2) = G2*x(idx2); + x(idx1) = G1*x(idx1); + x(xdx3,1) = zgshsr(x(xdx3,1)); + x(xdx2,1) = zgshsr(x(xdx2,1)); + x(xdx1,1) = zgshsr(x(xdx1,1)); + +endfunction + diff --git a/scripts/control/zginit.m b/scripts/control/zginit.m new file mode 100644 --- /dev/null +++ b/scripts/control/zginit.m @@ -0,0 +1,85 @@ +# Copyright (C) 1996,1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function zz = zginit(a,b,c,d) + # zz = zginit(a,b,c,d) + # construct right hand side vector zz + # for the zero-computation generalized eigenvalue problem + # balancing procedure + # called by zgepbal + # References: + # ZGEP: Hodel, "Computation of Zeros with Balancing," Linear Algebra and + # its Applications, 1993 + # Generalized CG: Golub and Van Loan, "Matrix Computations, 2nd ed" 1989 + + # A. S. Hodel July 24 1992 + # Conversion to Octave by R. Bruce Tenison, July 3, 1994 + # $Revision: 1.1 $ + # $Log: zginit.m,v $ + + [nn,mm] = size(b); + [pp,mm] = size(d); + + nmp = nn+mm+pp; + + # set up log vector zz + zz = zeros(nmp,1); + + # zz part 1: + for i=1:nn + # nonzero off diagonal entries of a + nidx = complement(i,1:nn); + a_row_i = a(i,nidx); a_col_i = a(nidx,i); + arnz = a_row_i(find(a_row_i != 0)); acnz = a_col_i(find(a_col_i != 0)); + + # row of b + bidx = find(b(i,:) != 0); + b_row_i = b(i,bidx); + + # column of c + cidx = find(c(:,i) != 0); + c_col_i = c(cidx,i); + + # sum the entries + zz(i) = sum(log(abs(acnz))) - sum(log(abs(arnz))) ... + - sum(log(abs(b_row_i))) + sum(log(abs(c_col_i))); + endfor + + # zz part 2: + bd = [b;d]; + for i=1:mm + i1 = i+nn; + + # column of [b;d] + bdidx = find(bd(:,i) != 0); + bd_col_i = bd(bdidx,i); + zz(i1) = sum(log(abs(bd_col_i))); + endfor + + # zz part 3: + cd = [c d]; + for i=1:pp + i1 = i+nn+mm; + cdidx = find(cd(i,:) != 0); + cd_row_i = cd(i,cdidx); + zz(i1) = -sum(log(abs(cd_row_i))); + endfor + + # now set zz as log base 2 + zz = zz*(1/log(2)); +endfunction diff --git a/scripts/control/zgpbal.m b/scripts/control/zgpbal.m new file mode 100644 --- /dev/null +++ b/scripts/control/zgpbal.m @@ -0,0 +1,118 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [retsys] = zgpbal(Asys) + # function [retsys] = zgpbal(Asys) + # + # used internally in tzero; minimal argument checking performed + # + # implementation of zero computation generalized eigenvalue problem + # balancing method (Hodel and Tiller, Allerton Conference, 1991) + # Based on Ward's balancing algorithm (SIAM J. Sci Stat. Comput., 1981) + # + # zgpbal computes a state/input/output weighting that attempts to + # reduced the range of the magnitudes of the nonzero elements of [a,b,c,d] + # The weighting uses scalar multiplication by powers of 2, so no roundoff + # will occur. + # + # zgpbal should be followed by zgpred + # References: + # ZGEP: Hodel, "Computation of Zeros with Balancing," 1992, Linear Algebra + # and its Applications + # Generalized CG: Golub and Van Loan, "Matrix Computations, 2nd ed" 1989 + + # A. S. Hodel July 24 1992 + # Conversion to Octave by R. Bruce Tenison July 3, 1994 + # $Revision: 1.1 $ + # $Log: zgpbal.m,v $ +# Revision 1.1 1998/11/04 14:35:42 hodel +# Initial revision +# + # Revision 1.2 1998/08/24 15:50:31 hodelas + # updated documentation + # + # Revision 1.1.1.1 1998/05/19 20:24:10 jwe + # + # Revision 1.2 1997/02/13 11:54:59 hodel + # added debugging code (commented out). + # + + if( (nargin != 1) | (!is_struct(Asys))) + usage("retsys = zgpbal(Asys)"); + endif + + Asys = sysupdate(Asys,"ss"); + [a,b,c,d] = sys2ss(Asys); + + [nn,mm,pp] = abcddim(a,b,c,d); + + np1 = nn+1; + nmp = nn+mm+pp; + + # set up log vector zz, incidence matrix ff + zz = zginit(a,b,c,d); + + #disp("zgpbal: zginit returns") + #zz + #disp("/zgpbal") + + if (norm(zz)) + # generalized conjugate gradient approach + xx = zgscal(a,b,c,d,zz,nn,mm,pp); + + for i=1:nmp + xx(i) = floor(xx(i)+0.5); + xx(i) = 2.0^xx(i); + endfor + + # now scale a + # block 1: a = sigma a inv(sigma) + for i=1:nn + a(i,1:nn) = a(i,1:nn)*xx(i); + a(1:nn,i) = a(1:nn,i)/xx(i); + endfor + # block 2: b= sigma a phi + for j=1:mm + j1 = j+nn; + b(1:nn,j) = b(1:nn,j)*xx(j1); + endfor + for i=1:nn + b(i,1:mm) = b(i,1:mm)*xx(i); + endfor + for i=1:pp + i1 = i+nn+mm; + # block 3: c = psi C inv(sigma) + c(i,1:nn) = c(i,1:nn)*xx(i1); + endfor + for j=1:nn + c(1:pp,j) = c(1:pp,j)/xx(j); + endfor + # block 4: d = psi D phi + for j=1:mm + j1 = j+nn; + d(1:pp,j) = d(1:pp,j)*xx(j1); + endfor + for i=1:pp + i1 = i + nn + mm; + d(i,1:mm) = d(i,1:mm)*xx(i1); + endfor + endif + + retsys = ss2sys(a,b,c,d); +endfunction + diff --git a/scripts/control/zgreduce.m b/scripts/control/zgreduce.m new file mode 100644 --- /dev/null +++ b/scripts/control/zgreduce.m @@ -0,0 +1,142 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function retsys = zgreduce(Asys,meps) +# function retsys = zgreduce(Asys,meps) +# implementation of procedure REDUCE in (Emami-Naeini and Van Dooren, +# Automatica, # 1982). +# +# used internally in tzero; minimal argument checking performed + +#$Revision: 1.1.1.1 $ +# SYS_INTERNAL accesses members of system data structure + +is_digital(Asys); # make sure it's pure digital/continuous + +exit_1 = 0; # exit_1 = 1 or 2 on exit of loop + +if(Asys.n + Asys.nz == 0) + exit_1 = 2; # there are no finite zeros +endif + +while (! exit_1) + [Q,R,Pi] = qr(Asys.d); # compress rows of D + Asys.d = Q'*Asys.d; + Asys.c = Q'*Asys.c; + + # check row norms of Asys.d + [sig,tau] = zgrownorm(Asys.d,meps); + + #disp("=======================================") + #disp(["zgreduce: meps=",num2str(meps), ", sig=",num2str(sig), ... + # ", tau=",num2str(tau)]) + #sysout(Asys) + + if(tau == 0) + exit_1 = 1; # exit_1 - reduction complete and correct + else + Cb = Db = []; + if(sig) + Cb = Asys.c(1:sig,:); + Db = Asys.d(1:sig,:); + endif + Ct =Asys.c(sig+(1:tau),:); + + # compress columns of Ct + [pp,nn] = size(Ct); + rvec = nn:-1:1; + [V,Sj,Pi] = qr(Ct'); + V = V(:,rvec); + [rho,gnu] = zgrownorm(Sj,meps); + + #disp(["zgreduce: rho=",num2str(rho),", gnu=",num2str(gnu)]) + #Cb + #Db + #Ct + #Sj' + + if(rho == 0) + exit_1 = 1; # exit_1 - reduction complete and correct + elseif(gnu == 0) + exit_1 = 2; # there are no zeros at all + else + mu = rho + sig; + + # update system with Q + M = [Asys.a , Asys.b ]; + [nn,mm] = size(Asys.b); + + pp = rows(Asys.d); + Vm =[V,zeros(nn,mm) ; zeros(mm,nn), eye(mm)]; + if(sig) + M = [M; Cb, Db]; + Vs =[V',zeros(nn,sig) ; zeros(sig,nn), eye(sig)]; + else + Vs = V'; + endif + #disp("zgreduce: before transform: M="); + #M + #Vs + #Vm + + M = Vs*M*Vm; + + #disp("zgreduce: after transform: M="); + #M + + #disp("debugging code:") + #Mtmp = [Asys.a Asys.b; Asys.c Asys.d] + #Vl = [V', zeros(nn,mm); zeros(mm,nn),Q] + #Vr =[V,zeros(nn,mm) ; zeros(mm,nn), eye(mm)]; + #Mtmpf = Vl*Mtmp*Vr + + idx = 1:gnu; + jdx = nn + (1:mm); + sdx = gnu + (1:mu); + + Asys.a = M(idx,idx); + Asys.b = M(idx,jdx); + Asys.c = M(sdx,idx); + Asys.d = M(sdx,jdx); + + #disp(["zgreduce: resulting system: nn =",num2str(nn)," mu=",num2str(mu)]) + #sysout(Asys) + #idx + #jdx + #sdx + endif + endif +endwhile + +#disp(["zgreduce: while loop done: exit_1=",num2str(exit_1)]); + +if(exit_1 == 2) + # there are no zeros at all! + Asys.a = Asys.b = Asys.c = []; +endif + +# update dimensions +if(is_digital(Asys)) + Asys.nz = rows(Asys.a); +else + Asys.n = rows(Asys.a); +endif + +retsys = Asys; + +endfunction diff --git a/scripts/control/zgrownorm.m b/scripts/control/zgrownorm.m new file mode 100644 --- /dev/null +++ b/scripts/control/zgrownorm.m @@ -0,0 +1,35 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [sig, tau] = zgrownorm(mat,meps) +# function [nonz, zer] = zgrownorm(mat,meps) +# used internally in tzero +# returns nonz = number of rows of mat whose two norm exceeds meps +# zer = number of rows of mat whose two norm is less than meps + +# $Revision: 1.1 $ + + rownorm = []; + for ii=1:rows(mat) + rownorm(ii) = norm(mat(ii,:)); + endfor + sig = sum(rownorm > meps); + tau = sum(rownorm <= meps); + +endfunction + diff --git a/scripts/control/zgscal.m b/scripts/control/zgscal.m new file mode 100644 --- /dev/null +++ b/scripts/control/zgscal.m @@ -0,0 +1,107 @@ +# Copyright (C) 1996,1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function x = zgscal(a,b,c,d,z,n,m,p) + # x = zgscal(f,z,n,m,p) generalized conjugate gradient iteration to + # solve zero-computation generalized eigenvalue problem balancing equation + # fx=z + # called by zgepbal + # + # References: + # ZGEP: Hodel, "Computation of Zeros with Balancing," 1992, submitted to LAA + # Generalized CG: Golub and Van Loan, "Matrix Computations, 2nd ed" 1989 + + # A. S. Hodel July 24 1992 + # Conversion to Octave R. Bruce Tenison July 3, 1994 + # $Revision: 1.2 $ + + #************************************************************************** + #initialize parameters: + # Givens rotations, diagonalized 2x2 block of F, gcg vector initialization + #************************************************************************** + nmp = n+m+p; + + #x_0 = x_{-1} = 0, r_0 = z + x = zeros(nmp,1); + xk1 = x; + xk2 = x; + rk1 = z; + k = 0; + + #initialize residual error norm + rnorm = norm(rk1,1); + + xnorm = 0; + fnorm = 1e-12 * norm([a,b;c,d],1); + + # dummy defines for MATHTOOLS compiler + gamk2 = 0; omega1 = 0; ztmz2 = 0; + + #do until small changes to x + len_x = length(x); + while ((k < 2*len_x) & (xnorm> 0.5) & (rnorm>fnorm))|(k == 0) + k = k+1; + + # solve F_d z_{k-1} = r_{k-1} + zk1= zgfslv(n,m,p,rk1); + + # Generalized CG iteration + # gamk1 = (zk1'*F_d*zk1)/(zk1'*F*zk1); + ztMz1 = zk1'*rk1; + gamk1 = ztMz1/(zk1'*zgfmul(a,b,c,d,zk1)); + + if(rem(k,len_x) == 1) omega = 1; + else omega = 1/(1-gamk1*ztMz1/(gamk2*omega1*ztmz2)); + endif + + # store x in xk2 to save space + xk2 = xk2 + omega*(gamk1*zk1 + xk1 - xk2); + + # compute new residual error: rk = z - F xk, check end conditions + rk1 = z - zgfmul(a,b,c,d,xk2); + rnorm = norm(rk1); + xnorm = max(abs(xk1 - xk2)); + + #printf("zgscal: k=%d, gamk1=%e, gamk2=%e, \nztMz1=%e ztmz2=%e\n", ... + # k,gamk1, gamk2, ztMz1, ztmz2); + # xk2_1_zk1 = [xk2 xk1 zk1] + # ABCD = [a,b;c,d] + # prompt + + # get ready for next iteration + gamk2 = gamk1; + omega1 = omega; + ztmz2 = ztMz1; + [xk1,xk2] = swap(xk1,xk2); + endwhile + x = xk2; + + # check convergence + if (xnorm> 0.5 & rnorm>fnorm) + warning("zgscal(tzero): GCG iteration failed; solving with pinv"); + + # perform brute force least squares; construct F + Am = eye(nmp); + for ii=1:nmp + Am(:,ii) = zgfmul(a,b,c,d,Am(:,ii)); + endfor + + # now solve with qr factorization + x = pinv(Am)*z; + endif +endfunction diff --git a/scripts/control/zgsgiv.m b/scripts/control/zgsgiv.m new file mode 100644 --- /dev/null +++ b/scripts/control/zgsgiv.m @@ -0,0 +1,32 @@ +# Copyright (C) 1996,1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [a,b] = zgsgiv(c,s,a,b) + # [a,b] = zgsgiv(c,s,a,b) + # apply givens rotation c,s to row vectors a,b + # No longer used in zero-balancing (zgpbal); kept for backward compatibility + + # A. S. Hodel July 29, 1992 + # Convertion to Octave by R. Bruce Tenison July 3, 1994 + # $Revision: 1.1.1.1 $ + + t1 = c*a + s*b; + t2 = -s*a + c*b; + a = t1; + b = t2; +endfunction diff --git a/scripts/control/zgshsr.m b/scripts/control/zgshsr.m new file mode 100644 --- /dev/null +++ b/scripts/control/zgshsr.m @@ -0,0 +1,42 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function x = zgshsr(y) + # x = zgshsr(y) + # apply householder vector based on e^(m) to + # (column vector) y. + # Called by zgfslv + + # A. S. Hodel July 24, 1992 + # Conversion to Octave by R. Bruce Tenison July 3, 1994 + # $Revision: 1.1.1.1 $ + + if(!is_vector(y)) + error(sprintf("y(%dx%d) must be a vector",rows(y),columns(y))); + endif + x = vec(y); + m = length(x); + if (m>1) + beta = (1 + sqrt(m))*x(1) + sum(x(2:m)); + beta = beta/(m+sqrt(m)); + x(1) = x(1) - beta*(1.0d0+sqrt(m)); + x(2:m) = x(2:m) - beta*ones(m-1,1); + else + x = -x; + endif +endfunction diff --git a/scripts/control/zp2ss.m b/scripts/control/zp2ss.m new file mode 100644 --- /dev/null +++ b/scripts/control/zp2ss.m @@ -0,0 +1,140 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [a,b,c,d] = zp2ss(zer,pol,k) +# Conversion from zero / pole to state space. +# The state space system +# . +# x = Ax + Bu +# y = Cx + Du +# +# is obtained from a vector of zeros and a vector of poles via the +# function call [a,b,c,d] = zp2ss(zer,pol,k). The vectors 'zer' and +# 'pol' may either be row or column vectors. Each zero and pole that +# has an imaginary part must have a conjugate in the list. +# The number of poles must at least equal the number of zeros. +# k is a gain that is associated with the zero vector. + +# Written by David Clem August 15, 1994 +# $Revision: 1.4 $ +# $Log: zp2ss.m,v $ +# Revision 1.4 1998/07/10 17:51:29 hodelas +# Fixed bug in zp2ss system construction; overhauled zp2ssg2 +# +# +# calls: tf2sys, sysmult + + sav_val = empty_list_elements_ok; + empty_list_elements_ok = 1; + + if(nargin != 3) + error("Incorrect number of input arguments"); + endif + + if(! (is_vector(zer) | isempty(zer)) ) + error(["zer(",num2str(rows(zer)),",",num2str(columns(zer)), ... + ") should be a vector"]); + elseif(! (is_vector(pol) | isempty(pol) ) ) + error(["pol(",num2str(rows(pol)),",",num2str(columns(pol)), ... + ") should be a vector"]); + elseif(! is_scalar(k)) + error(["k(",num2str(rows(k)),",",num2str(columns(k)), ... + ") should be a scalar"]); + elseif( k != real(k)) + warning("zp2ss: k is complex") + endif + + zpsys = ss2sys([],[],[],k); + + # Find the number of zeros and the number of poles + nzer=length(zer); + npol =length(pol); + + if(nzer > npol) + error([num2str(nzer)," zeros, exceeds number of poles=",num2str(npol)]); + endif + + # Sort to place complex conjugate pairs together + zer=sortcom(zer); + pol=sortcom(pol); + + # construct the system as a series connection of poles and zeros + # problem: poles and zeros may come in conjugate pairs, and not + # matched up! + + # approach: remove poles/zeros from the list as they are included in + # the ss system + + while(length(pol)) + + # search for complex poles, zeros + cpol=[]; czer = []; + if(!isempty(pol)) + cpol = find(imag(pol) != 0); + endif + if(!isempty(zer)) + czer = find(imag(zer) != 0); + endif + + if(isempty(cpol) & isempty(czer)) + pcnt = 1; + else + pcnt = 2; + endif + + num=1; # assume no zeros left. + switch(pcnt) + case(1) + # real pole/zero combination + if(length(zer)) + num = [1 -zer(1)]; + zer = zer(2:length(zer)); + endif + den = [1 -pol(1)]; + pol = pol(2:length(pol)); + case(2) + # got a complex pole or zero, need two roots (if available) + if(length(zer) > 1) + [num,zer] = zp2ssg2(zer); # get two zeros + elseif(length(zer) == 1) + num = [1 -zer]; # use last zero (better be real!) + zer = []; + endif + [den,pol] = zp2ssg2(pol); # get two poles + otherwise + error(["pcnt = ",num2str(pcnt)]) + endswitch + + # pack tf into system form and put in series with earlier realization + zpsys1 = tf2sys(num,den,0,"u","yy"); + + # change names to avoid warning messages from sysgroup + zpsys = syschnames(zpsys,"in",1,"u1"); + zpsys1 = sysupdate(zpsys1,"ss"); + zpsys = syschnames(zpsys,"st",(1:zpsys.n),sysdefioname(zpsys.n,"x")); + zpsys1 = syschnames(zpsys1,"st",(1:zpsys1.n),sysdefioname(zpsys1.n,"xx")); + + zpsys = sysmult(zpsys,zpsys1); + + endwhile + + [a,b,c,d] = sys2ss(zpsys); + + empty_list_elements_ok = sav_val; +endfunction + diff --git a/scripts/control/zp2ssg2.m b/scripts/control/zp2ssg2.m new file mode 100644 --- /dev/null +++ b/scripts/control/zp2ssg2.m @@ -0,0 +1,66 @@ +# Copyright (C) 1996,1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [poly,rvals] = zp2ssg2(rvals) +# [poly,rvals] = zp2ssg2(rvals) +# +# used internally in zp2ss +# extract 2 values from rvals (if possible) and construct +# a polynomial with those roots. + +# $Revision: 1.2 $ +# A. S. Hodel Aug 1996 + +# locate imaginary roots (if any) +cidx = find(imag(rvals)); + +if(!isempty(cidx)) + # select first complex root, omit from cidx + r1i = cidx(1); r1 = rvals(r1i); cidx = complement(r1i,cidx); + + # locate conjugate root (must be in cidx list, just in case there's + # roundoff) + err = abs(rvals(cidx) - r1'); + minerr = min(err); + c2i = find(err == minerr); + r2i = cidx(c2i); + r2 = rvals(r2i); + cidx = complement(r2i,cidx); + + # don't check for divide by zero, since 0 is not complex. + if(abs(r2 - r1')/abs(r1) > 1e-12) + error(sprintf("r1=(%f,%f); r2=(%f,%f), not conjugates.", ... + real(r1),imag(r1),real(r2),imag(r2))); + endif + + # complex conjugate pair + poly = [1, -2*real(r1), real(r1)^2+imag(r1)^2]; +else + # select two roots (they're all real) + r1 = rvals(1); + r2 = rvals(2); + poly = [1, -(r1+r2), (r1*r2)]; + r1i = 1; r2i = 2; +endif + +# remove roots used +idx = complement([r1i r2i],1:length(rvals)); +rvals = rvals(idx); + +endfunction + diff --git a/scripts/control/zp2sys.m b/scripts/control/zp2sys.m new file mode 100644 --- /dev/null +++ b/scripts/control/zp2sys.m @@ -0,0 +1,116 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function outsys = zp2sys (zer,pol,k,tsam,inname,outname) + # sys = zp2sys (zer,pol,k{,tsam,inname,outname}) + # Create system data structure from zero-pole data + # inputs: + # zer: vector of system zeros + # pol: vector of system poles + # k: scalar leading coefficient + # tsam: sampling period. default: 0 (continuous system) + # inname, outname: input/output signal names (strings) + # outputs: sys: system data structure + + # Modified by John Ingram July 20, 1996 + # $Revision: 1.2 $ + + save_val = implicit_str_to_num_ok; # save for restoring later + implicit_str_to_num_ok = 1; + + # Test for the correct number of input arguments + if ((nargin < 3) || (nargin > 6)) + usage("outsys = zp2sys(zer,pol,k[,tsam,inname,outname])"); + endif + + # check input format + if( ! (is_vector(zer) | isempty(zer) ) ) + error("zer must be a vector or empty"); + endif + zer = reshape(zer,1,length(zer)); # make it a row vector + + if( ! (is_vector(pol) | isempty(pol))) + error("pol must be a vector"); + endif + pol = reshape(pol,1,length(pol)); + + if (! is_scalar(k)) + error('k must be a scalar'); + endif + + # Test proper numbers of poles and zeros. The number of poles must be + # greater than or equal to the number of zeros. + if (length(zer) > length(pol)) + error(["number of poles (", num2str(length(pol)), ... + ") < number of zeros (", num2str(length(zer)),")"]); + endif + + # Set the system transfer function + outsys.zer = zer; + outsys.pol = pol; + outsys.k = k; + + # Set the system vector: active = 1, updated = [0 1 0]; + outsys.sys = [1 0 1 0]; + + # Set defaults + outsys.tsam = 0; + outsys.n = length(pol); + outsys.nz = 0; + outsys.yd = 0; # assume (for now) continuous time outputs + + # Set the type of system + if (nargin > 3) + if( !is_scalar(tsam) ) + error("tsam must be a nonnegative scalar"); + endif + if (tsam < 0) + error("sampling time must be positve") + elseif (tsam > 0) + [outsys.n,outsys.nz] = swap(outsys.n, outsys.nz); + outsys.yd = 1; # discrete-time output + endif + + outsys.tsam = tsam; + endif + + outsys.inname = sysdefioname(1,"u"); + outsys.outname = sysdefioname(1,"y"); + outsys.stname = sysdefstname(outsys.n,outsys.nz); + + # Set name of input + if (nargin > 4) + if (rows(inname) > 1) + warning("zp2sys: ",num2str(rows(inname))," input names given, 1st used"); + inname = inname(1,:); + endif + outsys.inname(1,1:length(inname)) = inname; + endif + + # Set name of output + if (nargin > 5) + if (rows(outname) > 1) + warning("zp2sys: ",num2str(rows(outname)), ... + " output names given, 1st used"); + outname = outname(1,:); + endif + outsys.outname(1,1:length(outname)) = outname; + endif + + implicit_str_to_num_ok = save_val; +endfunction diff --git a/scripts/control/zp2tf.m b/scripts/control/zp2tf.m new file mode 100644 --- /dev/null +++ b/scripts/control/zp2tf.m @@ -0,0 +1,67 @@ +# Copyright (C) 1996,1998 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function [num,den] = zp2tf(zer,pol,k) +# [num,den] = zp2tf(zer,pol,k) +# Converts zeros / poles to a transfer function. +# +# Inputs: +# zer, pol: vectors of (possibly complex) poles and zeros of a transfer +# function. Complex values should appear in conjugate pairs +# k: real scalar (leading coefficient) +# Forms the transfer function num/den from +# the vectors of poles and zeros. K is a scalar gain associated with the +# zeros. + +# Find out whether data was entered as a row or a column vector and +# convert to a column vector if necessary +# Written by A. S. Hodel with help from students Ingram, McGowan. +# a.s.hodel@eng.auburn.edu +# $Revision: 1.2 $ +# + + [rp,cp] = size(pol); + [rz,cz] = size(zer); + + if(!(is_vector(zer) | isempty(zer)) ) + error(sprintf("zer(%dx%d) must be a vector",rz,cz)); + elseif(!(is_vector(pol) | isempty(pol)) ) + error(sprintf("pol(%dx%d) must be a vector",rp,cp)); + elseif(length(zer) > length(pol)) + error(sprintf("zer(%dx%d) longer than pol(%dx%d)",rz,cz,rp,cp)); + endif + + num = k; den = 1; # initialize converted polynomials + + # call zp2ssg2 if there are complex conjugate pairs left, otherwise + # construct real zeros one by one. Repeat for poles. + while(!isempty(zer)) + if( max(abs(imag(zer))) ) [poly,zer] = zp2ssg2(zer); + else poly = [1 -zer(1)]; + zer = zer(2:length(zer)); endif + num = conv(num,poly); + endwhile + + while(!isempty(pol)) + if( max(abs(imag(pol))) ) [poly,pol] = zp2ssg2(pol); + else poly = [1 -pol(1)]; + pol = pol(2:length(pol)); endif + den = conv(den,poly); + endwhile + +endfunction diff --git a/scripts/control/zpout.m b/scripts/control/zpout.m new file mode 100644 --- /dev/null +++ b/scripts/control/zpout.m @@ -0,0 +1,112 @@ +# Copyright (C) 1996 A. Scottedward Hodel +# +# 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +function zpout(zer,pol,k,x) +# +# usage: zpout(zer,pol,k,[,x]) +# +# print formatted zero-pole form +# to the screen +# x defaults to the string "s" +# +# SEE ALSO: polyval, polyvalm, poly, roots, conv, deconv, residue, +# filter, polyderiv, polyinteg, polyout + +# Written by A. Scottedward Hodel (scotte@eng.auburn.edu) June 1995) +# $Log$ + + save_val = implicit_str_to_num_ok; + save_empty = empty_list_elements_ok; + empty_list_elements_ok = 1; + + implicit_str_to_num_ok = 1; + + if (nargin < 3 ) | (nargin > 4) | (nargout != 0 ) + usage("zpout(zer,pol,k[,x])"); + endif + + if( !(is_vector(zer) | isempty(zer)) | !(is_vector(pol) | isempty(pol)) ) + error("zer, pol must be vectors or empty"); + endif + + if(!is_scalar(k)) + error("zpout: argument k must be a scalar.") + endif + + if (nargin == 3) + x = 's'; + elseif( ! isstr(x) ) + error("zpout: third argument must be a string"); + endif + + numstring = num2str(k); + + if(length(zer)) + # find roots at z,s = 0 + nzr = sum(zer == 0); + if(nzr) + if(nzr > 1) + numstring = [numstring,sprintf(" %s^%d",x,nzr)]; + else + numstring = [numstring,sprintf(" %s",x)]; + endif + endif + zer = sortcom(-zer); + for ii=1:length(zer) + if(zer(ii) != 0) + numstring = [numstring,sprintf(" (%s %s)",x,com2str(zer(ii),1) ) ]; + endif + endfor + endif + + if(length(pol)) + # find roots at z,s = 0 + nzr = sum(pol == 0); + if(nzr) + if(nzr > 1) + denomstring = [sprintf("%s^%d",x,nzr)]; + else + denomstring = [sprintf("%s",x)]; + endif + else + denomstring = " "; + endif + pol = sortcom(-pol); + for ii=1:length(pol) + if(pol(ii) != 0) + denomstring = [denomstring,sprintf(" (%s %s)",x,com2str(pol(ii),1))]; + endif + endfor + endif + + len = max(length(numstring),length(denomstring)); + if(len > 0) + y = strrep(blanks(len)," ","-"); + disp(numstring) + if(length(denomstring)) + disp(y) + disp(denomstring) + endif + else + error('zpout: empty transfer function') + end + + implicit_str_to_num_ok = save_val; + empty_list_elements_ok = save_empty; + +endfunction