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{s} =} tf (@var{'s'})
20## @deftypefnx {Function File} {@var{z} =} tf (@var{'z'}, @var{tsam})
21## @deftypefnx {Function File} {@var{sys} =} tf (@var{sys})
22## @deftypefnx {Function File} {@var{sys} =} tf (@var{mat}, @dots{})
23## @deftypefnx {Function File} {@var{sys} =} tf (@var{num}, @var{den}, @dots{})
24## @deftypefnx {Function File} {@var{sys} =} tf (@var{num}, @var{den}, @var{tsam}, @dots{})
25## Create or convert to transfer function model.
26##
27## @strong{Inputs}
28## @table @var
29## @item sys
30## @acronym{LTI} model to be converted to transfer function.
31## @item mat
32## Gain matrix to be converted to static transfer function.
33## @item num
34## Numerator or cell of numerators.  Each numerator must be a row vector
35## containing the coefficients of the polynomial in descending powers of
36## the transfer function variable.
37## num@{i,j@} contains the numerator polynomial from input j to output i.
38## In the SISO case, a single vector is accepted as well.
39## @item den
40## Denominator or cell of denominators.  Each denominator must be a row vector
41## containing the coefficients of the polynomial in descending powers of
42## the transfer function variable.
43## den@{i,j@} contains the denominator polynomial from input j to output i.
44## In the SISO case, a single vector is accepted as well.
45## @item tsam
46## Sampling time in seconds.  If @var{tsam} is not specified, a continuous-time
47## model is assumed.
48## @item @dots{}
49## Optional pairs of properties and values.
50## Type @command{set (tf)} for more information.
51## @end table
52##
53## @strong{Outputs}
54## @table @var
55## @item sys
56## Transfer function model.
57## @end table
58##
59## @strong{Option Keys and Values}
60## @table @var
61## @item 'num'
62## Numerator.  See 'Inputs' for details.
63##
64## @item 'den'
65## Denominator.  See 'Inputs' for details.
66##
67## @item 'tfvar'
68## String containing the transfer function variable.
69##
70## @item 'inv'
71## Logical.  True for negative powers of the transfer function variable.
72##
73## @item 'tsam'
74## Sampling time.  See 'Inputs' for details.
75##
76## @item 'inname'
77## The name of the input channels in @var{sys}.
78## Cell vector of length m containing strings.
79## Default names are @code{@{'u1', 'u2', ...@}}
80##
81## @item 'outname'
82## The name of the output channels in @var{sys}.
83## Cell vector of length p containing strings.
84## Default names are @code{@{'y1', 'y2', ...@}}
85##
86## @item 'ingroup'
87## Struct with input group names as field names and
88## vectors of input indices as field values.
89## Default is an empty struct.
90##
91## @item 'outgroup'
92## Struct with output group names as field names and
93## vectors of output indices as field values.
94## Default is an empty struct.
95##
96## @item 'name'
97## String containing the name of the model.
98##
99## @item 'notes'
100## String or cell of string containing comments.
101##
102## @item 'userdata'
103## Any data type.
104## @end table
105##
106## @strong{Example}
107## @example
108## @group
109## octave:1> s = tf ('s');
110## octave:2> G = 1/(s+1)
111##
112## Transfer function 'G' from input 'u1' to output ...
113##
114##         1
115##  y1:  -----
116##       s + 1
117##
118## Continuous-time model.
119## @end group
120## @end example
121## @example
122## @group
123## octave:3> z = tf ('z', 0.2);
124## octave:4> H = 0.095/(z-0.9)
125##
126## Transfer function 'H' from input 'u1' to output ...
127##
128##        0.095
129##  y1:  -------
130##       z - 0.9
131##
132## Sampling time: 0.2 s
133## Discrete-time model.
134## @end group
135## @end example
136## @example
137## @group
138## octave:5> num = @{[1, 5, 7], [1]; [1, 7], [1, 5, 5]@};
139## octave:6> den = @{[1, 5, 6], [1, 2]; [1, 8, 6], [1, 3, 2]@};
140## octave:7> sys = tf (num, den)
141## @end group
142## @end example
143##
144## @example
145## @group
146## Transfer function 'sys' from input 'u1' to output ...
147##
148##       s^2 + 5 s + 7
149##  y1:  -------------
150##       s^2 + 5 s + 6
151##
152##           s + 7
153##  y2:  -------------
154##       s^2 + 8 s + 6
155## @end group
156## @end example
157##
158## @example
159## @group
160## Transfer function 'sys' from input 'u2' to output ...
161##
162##         1
163##  y1:  -----
164##       s + 2
165##
166##       s^2 + 5 s + 5
167##  y2:  -------------
168##       s^2 + 3 s + 2
169##
170## Continuous-time model.
171## octave:8>
172## @end group
173## @end example
174##
175## @seealso{filt, ss, dss}
176## @end deftypefn
177
178## Author: Lukas Reichlin <lukas.reichlin@gmail.com>
179## Created: September 2009
180## Version: 0.4
181
182function sys = tf (varargin)
183
184  ## model precedence: frd > ss > zpk > tf > double
185  ## inferiorto ("frd", "ss", "zpk");           # error if de-commented. bug in octave?
186  superiorto ("double");
187
188  if (nargin == 1)
189    if (isa (varargin{1}, "tf"))                # tf (tfsys)
190      sys = varargin{1};
191      return;
192    elseif (isa (varargin{1}, "lti"))           # tf (ltisys)
193      [sys, lti] = __sys2tf__ (varargin{1});
194      sys.lti = lti;
195      return;
196    elseif (ischar (varargin{1}))               # s = tf ('s')
197      sys = tf ([1, 0], 1, "tfvar", varargin{:});
198      return;
199    endif
200  elseif (nargin == 2 ...
201          && ischar (varargin{1}) ...
202          && is_zp_vector (varargin{2}) ...
203          && length (varargin{2}) <= 1)         # z = tf ('z', tsam)
204    sys = tf ([1, 0], 1, varargin{2}, "tfvar", varargin{[1,3:end]});
205    return;
206  endif
207
208  num = {}; den = {};                           # default transfer matrix
209  tsam = -2;                                    # default sampling time
210
211  [mat_idx, opt_idx, obj_flg] = __lti_input_idx__ (varargin);
212
213  switch (numel (mat_idx))
214    case 1
215      num = varargin{mat_idx};
216    case 2
217      [num, den] = varargin{mat_idx};
218      tsam = 0;
219    case 3
220      [num, den, tsam] = varargin{mat_idx};
221      if (isempty (tsam) && is_real_matrix (tsam))
222        tsam = -1;
223      elseif (! issample (tsam, -10))
224        error ("tf: invalid sampling time");
225      endif
226    case 0
227      ## nothing to do here, just prevent case 'otherwise'
228    otherwise
229      print_usage ();
230  endswitch
231
232  varargin = varargin(opt_idx);
233  if (obj_flg)
234    varargin = horzcat ({"lti"}, varargin);
235  endif
236
237  [num, den, tsam, tfvar] = __adjust_tf_data__ (num, den, tsam);
238  [p, m] = __tf_dim__ (num, den);              # determine number of outputs and inputs
239
240  tfdata = struct ("num", {num},
241                   "den", {den},
242                   "tfvar", tfvar,
243                   "inv", false);              # struct for tf-specific data
244
245  ltisys = lti (p, m, tsam);                   # parent class for general lti data
246
247  sys = class (tfdata, "tf", ltisys);          # create tf object
248
249  if (numel (varargin) > 0)                    # if there are any properties and values, ...
250    sys = set (sys, varargin{:});              # use the general set function
251  endif
252
253endfunction
254