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} =} ss (@var{sys})
20## @deftypefnx {Function File} {@var{sys} =} ss (@var{d}, @dots{})
21## @deftypefnx {Function File} {@var{sys} =} ss (@var{a}, @var{b}, @dots{})
22## @deftypefnx {Function File} {@var{sys} =} ss (@var{a}, @var{b}, @var{c}, @dots{})
23## @deftypefnx {Function File} {@var{sys} =} ss (@var{a}, @var{b}, @var{c}, @var{d}, @dots{})
24## @deftypefnx {Function File} {@var{sys} =} ss (@var{a}, @var{b}, @var{c}, @var{d}, @var{tsam}, @dots{})
25## Create or convert to state-space model.
26##
27## @strong{Inputs}
28## @table @var
29## @item sys
30## @acronym{LTI} model to be converted to state-space.
31## @item a
32## State matrix (n-by-n).
33## @item b
34## Input matrix (n-by-m).
35## @item c
36## Output matrix (p-by-n).
37## If @var{c} is empty @code{[]} or not specified, an identity matrix is assumed.
38## @item d
39## Feedthrough matrix (p-by-m).
40## If @var{d} is empty @code{[]} or not specified, a zero matrix is assumed.
41## @item tsam
42## Sampling time in seconds.  If @var{tsam} is not specified, a continuous-time model is assumed.
43## @item @dots{}
44## Optional pairs of properties and values.
45## Type @command{set (ss)} for more information.
46## @end table
47##
48## @strong{Outputs}
49## @table @var
50## @item sys
51## State-space model.
52## @end table
53##
54## @strong{Option Keys and Values}
55## @table @var
56## @item 'a', 'b', 'c', 'd', 'e'
57## State-space matrices.  See 'Inputs' for details.
58##
59## @item 'stname'
60## The name of the states in @var{sys}.
61## Cell vector containing strings for each state.
62## Default names are @code{@{'x1', 'x2', ...@}}
63##
64## @item 'scaled'
65## Logical.  If set to true, no automatic scaling is used,
66## e.g. for frequency response plots.
67##
68## @item 'tsam'
69## Sampling time.  See 'Inputs' for details.
70##
71## @item 'inname'
72## The name of the input channels in @var{sys}.
73## Cell vector of length m containing strings.
74## Default names are @code{@{'u1', 'u2', ...@}}
75##
76## @item 'outname'
77## The name of the output channels in @var{sys}.
78## Cell vector of length p containing strings.
79## Default names are @code{@{'y1', 'y2', ...@}}
80##
81## @item 'ingroup'
82## Struct with input group names as field names and
83## vectors of input indices as field values.
84## Default is an empty struct.
85##
86## @item 'outgroup'
87## Struct with output group names as field names and
88## vectors of output indices as field values.
89## Default is an empty struct.
90##
91## @item 'name'
92## String containing the name of the model.
93##
94## @item 'notes'
95## String or cell of string containing comments.
96##
97## @item 'userdata'
98## Any data type.
99## @end table
100##
101## @strong{Equations}
102## @example
103## @group
104## .
105## x = A x + B u
106## y = C x + D u
107## @end group
108## @end example
109##
110## @strong{Example}
111## @example
112## @group
113## octave:1> a = [1 2 3; 4 5 6; 7 8 9];
114## octave:2> b = [10; 11; 12];
115## octave:3> stname = @{'V', 'A', 'kJ'@};
116## octave:4> sys = ss (a, b, 'stname', stname)
117## @end group
118## @end example
119##
120## @example
121## @group
122## sys.a =
123##         V   A  kJ
124##    V    1   2   3
125##    A    4   5   6
126##    kJ   7   8   9
127## @end group
128## @end example
129##
130## @example
131## @group
132## sys.b =
133##        u1
134##    V   10
135##    A   11
136##    kJ  12
137## @end group
138## @end example
139##
140## @example
141## @group
142## sys.c =
143##         V   A  kJ
144##    y1   1   0   0
145##    y2   0   1   0
146##    y3   0   0   1
147## @end group
148## @end example
149##
150## @example
151## @group
152## sys.d =
153##        u1
154##    y1   0
155##    y2   0
156##    y3   0
157##
158## Continuous-time model.
159## octave:5>
160## @end group
161## @end example
162##
163## @strong{Note on compatibility}
164##
165## If the state-space model @var{sys} is converted from a transfer
166## function, the resulting state-space model can be transformed into
167## the form computed by Matlab (a controllable canonical form with
168## flipped state variables order) by using the following
169## similarity transformation:
170##
171## @example
172## @group
173## n = size (sys.a, 1)
174## QSi = inv (ctrb (sys))
175## T(n,:) = QSi(n,:)
176## for i=n-1:-1:1, T(i,:) = T(i+1,:)*sys.a, endfor
177## sys_ml = ss2ss (sys, T)
178## @end group
179## @end example
180##
181## @seealso{tf, dss}
182## @end deftypefn
183
184## Author: Lukas Reichlin <lukas.reichlin@gmail.com>
185## Created: September 2009
186## Version: 0.4
187
188function sys = ss (varargin)
189
190  ## model precedence: frd > ss > zpk > tf > double
191  ## inferiorto ("frd");
192  superiorto ("zpk", "tf", "double");
193
194  if (nargin == 1)                      # shortcut for lti objects
195    if (isa (varargin{1}, "ss"))        # already in ss form  sys = ss (sssys)
196      sys = varargin{1};
197      return;
198    elseif (isa (varargin{1}, "lti"))   # another lti object  sys = ss (sys)
199      [sys, lti] = __sys2ss__ (varargin{1});
200      sys.lti = lti;                    # preserve lti properties
201      return;
202    endif
203  elseif (nargin == 2)                  # shortcut for lti objects plus
204    if (ischar (varargin{2}) && isa (varargin{1}, "lti"))
205      candidates = {"minimal", "explicit"};
206      key = __match_key__ (varargin{2}, candidates, "ss");
207      switch (key)
208        case "minimal"                  # ss (sys, 'minimal')
209          sys = minreal (ss (varargin{1}));
210          return;
211        case "explicit"                 # ss (sys, 'explicit')
212          sys = dss2ss (ss (varargin{1}));
213          return;
214        otherwise                       # this would be a silly bug
215      endswitch
216    endif
217  endif
218
219  a = []; b = []; c = []; d = [];       # default state-space matrices
220  tsam = 0;                             # default sampling time
221
222  [mat_idx, opt_idx, obj_flg] = __lti_input_idx__ (varargin);
223
224  switch (numel (mat_idx))
225    case 1
226      d = varargin{mat_idx};
227    case 2
228      [a, b] = varargin{mat_idx};
229    case 3
230      [a, b, c] = varargin{mat_idx};
231    case 4
232      [a, b, c, d] = varargin{mat_idx};
233    case 5
234      [a, b, c, d, tsam] = varargin{mat_idx};
235      if (isempty (tsam) && is_real_matrix (tsam))
236        tsam = -1;
237      elseif (! issample (tsam, -10))
238        error ("ss: invalid sampling time");
239      endif
240    case 0
241      ## nothing to do here, just prevent case 'otherwise'
242    otherwise
243      print_usage ();
244  endswitch
245
246  varargin = varargin(opt_idx);
247  if (obj_flg)
248    varargin = horzcat ({"lti"}, varargin);
249  endif
250
251  [a, b, c, d, tsam] = __adjust_ss_data__ (a, b, c, d, tsam);
252  [p, m, n] = __ss_dim__ (a, b, c, d);  # determine number of outputs, inputs and states
253
254  stname = repmat ({""}, n, 1);         # cell with empty state names
255
256  ssdata = struct ("a", a, "b", b,
257                   "c", c, "d", d,
258                   "e", [],
259                   "stname", {stname},
260                   "scaled", false);    # struct for ss-specific data
261
262  ltisys = lti (p, m, tsam);            # parent class for general lti data
263
264  sys = class (ssdata, "ss", ltisys);   # create ss object
265
266  if (numel (varargin) > 0)             # if there are any properties and values, ...
267    sys = set (sys, varargin{:});       # use the general set function
268  endif
269
270endfunction
271
272
273## TODO: create a separate function @lti/dss2ss.m
274function G = dss2ss (G)
275
276  [G.a, G.b, G.c, G.d, G.e] = __dss2ss__ (G.a, G.b, G.c, G.d, G.e);
277
278endfunction
279