1## Copyright (C) 2009-2016   Lukas F. Reichlin
2##
3## This file is part of LTI Syncope.
4##
5## LTI Syncope is free software: you can redistribute it and/or modify
6## it under the terms of the GNU General Public License as published by
7## the Free Software Foundation, either version 3 of the License, or
8## (at your option) any later version.
9##
10## LTI Syncope is distributed in the hope that it will be useful,
11## but WITHOUT ANY WARRANTY; without even the implied warranty of
12## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13## GNU General Public License for more details.
14##
15## You should have received a copy of the GNU General Public License
16## along with LTI Syncope.  If not, see <http://www.gnu.org/licenses/>.
17
18## -*- texinfo -*-
19## @deftypefn {Function File} {@var{sys} =} lft (@var{sys1}, @var{sys2})
20## @deftypefnx {Function File} {@var{sys} =} lft (@var{sys1}, @var{sys2}, @var{nu}, @var{ny})
21## Linear fractional tranformation, also known as Redheffer star product.
22##
23## @strong{Inputs}
24## @table @var
25## @item sys1
26## Upper @acronym{LTI} model.
27## @item sys2
28## Lower @acronym{LTI} model.
29## @item nu
30## The last nu inputs of @var{sys1} are connected with the first nu outputs of @var{sys2}.
31## If not specified, @code{min (m1, p2)} is taken.
32## @item ny
33## The last ny outputs of @var{sys1} are connected with the first ny inputs of @var{sys2}.
34## If not specified, @code{min (p1, m2)} is taken.
35## @end table
36##
37## @strong{Outputs}
38## @table @var
39## @item sys
40## Resulting @acronym{LTI} model.
41## @end table
42##
43## @strong{Block Diagram}
44## @example
45## @group
46##       .............sys..............
47##       :         +--------+         :
48## w1 ------------>|        |------------> z1
49##       :         |  sys1  |         :
50##       : u +---->|        |-----+ y :
51##       :   |     +--------+     |   :          Lower LFT
52##       :   |                    |   :
53##       :   |     +--------+     |   :          lft (sys1, sys2)
54##       :   +-----|  sys2  |<----+   :
55##       :         +--------+         :
56##       :............................:
57## @end group
58## @end example
59## @example
60## @group
61##       .............sys..............
62##       :         +--------+         :
63##       : u +---->|  sys1  |-----+ y :
64##       :   |     +--------+     |   :          Upper LFT
65##       :   |                    |   :
66##       :   |     +--------+     |   :          lft (sys1, sys2)
67##       :   +-----|        |<----+   :
68##       :         |  sys2  |         :
69## z2 <------------|        |<------------ w2
70##       :         +--------+         :
71##       :............................:
72## @end group
73## @end example
74## @example
75## @group
76##       .............sys..............
77##       :         +--------+         :
78## w1 ------------>|        |------------> z1
79##       :         |  sys1  |         :
80##       : u +---->|        |-----+ y :
81##       :   |     +--------+     |   :
82##       :   |                    |   :          lft (sys1, sys2, nu, ny)
83##       :   |     +--------+     |   :
84##       :   +-----|        |<----+   :
85##       :         |  sys2  |         :
86## z2 <------------|        |<------------ w2
87##       :         +--------+         :
88##       :............................:
89## @end group
90## @end example
91## @end deftypefn
92
93## Author: Lukas Reichlin <lukas.reichlin@gmail.com>
94## Created: October 2009
95## Version: 0.2
96
97function sys = lft (sys1, sys2, nu, ny)
98
99  if (nargin != 2 && nargin != 4)
100    print_usage ();
101  endif
102
103  ## object conversion done by sys_group if necessary
104
105  [p1, m1] = size (sys1);
106  [p2, m2] = size (sys2);
107
108  nu_max = min (m1, p2);
109  ny_max = min (m2, p1);
110
111  if (nargin == 2)  # sys = lft (sys1, sys2)
112    nu = nu_max;
113    ny = ny_max;
114  else              # sys = lft (sys1, sys2, nu, ny)
115    if (! is_real_scalar (nu) || nu < 0)
116      error ("lft: argument 'nu' must be a positive integer");
117    endif
118
119    if (! is_real_scalar (ny) || ny < 0)
120      error ("lft: argument 'ny' must be a positive integer");
121    endif
122
123    if (nu > nu_max)
124      error ("lft: argument 'nu' (%d) must be at most %d", nu, nu_max);
125    endif
126
127    if (ny > ny_max)
128      error ("lft: argument 'ny' (%d) must be at most %d", ny, ny_max);
129    endif
130  endif
131
132  M11 = zeros (m1, p1);
133  M12 = [zeros(m1-nu, p2); eye(nu), zeros(nu, p2-nu)];
134  M21 = [zeros(ny, p1-ny), eye(ny); zeros(m2-ny, p1)];
135  M22 = zeros (m2, p2);
136
137  M = [M11, M12; M21, M22];
138
139  in_idx = [1 : (m1-nu), m1 + (ny+1 : m2)];
140  out_idx = [1 : (p1-ny), p1 + (nu+1 : p2)];
141
142  sys = __sys_group__ (sys1, sys2);
143  sys = __sys_connect__ (sys, M);
144  sys = __sys_prune__ (sys, out_idx, in_idx);
145
146  [p, m] = size (sys);
147
148  if (m == 0)
149    warning ("lft: resulting system has no inputs");
150  endif
151
152  if (p == 0)
153    warning ("lft: resulting system has no outputs");
154  endif
155
156endfunction
157