# 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, 59 Temple Place, Suite 330, Boston, MA 02111 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

  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) < p)
    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
