Mercurial > forge
changeset 10927:e108354f2e31 octave-forge
control: finish multiplot lsim
author | paramaniac |
---|---|
date | Tue, 25 Sep 2012 16:51:35 +0000 |
parents | d84dda882a95 |
children | 7aa602e6aa5c |
files | main/control/devel/lsim2.m main/control/inst/lsim.m |
diffstat | 2 files changed, 158 insertions(+), 379 deletions(-) [+] |
line wrap: on
line diff
--- a/main/control/devel/lsim2.m Tue Sep 25 12:41:30 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,298 +0,0 @@ -## Copyright (C) 2009, 2010, 2011, 2012 Lukas F. Reichlin -## -## This file is part of LTI Syncope. -## -## LTI Syncope 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 3 of the License, or -## (at your option) any later version. -## -## LTI Syncope 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 LTI Syncope. If not, see <http://www.gnu.org/licenses/>. - -## -*- texinfo -*- -## @deftypefn{Function File} lsim (@var{sys}, @var{u}) -## @deftypefnx{Function File} lsim (@var{sys1}, @var{sys2}, @dots{}, @var{sysN}, @var{u}) -## @deftypefnx{Function File} lsim (@var{sys1}, @var{'style1'}, @dots{}, @var{sysN}, @var{'styleN'}, @var{u}) -## @deftypefnx{Function File} lsim (@var{sys1}, @dots{}, @var{u}, @var{t}) -## @deftypefnx{Function File} lsim (@var{sys1}, @dots{}, @var{u}, @var{t}, @var{x0}) -## @deftypefnx{Function File} lsim (@var{sys1}, @dots{}, @var{u}, @var{t}, @var{[]}, @var{method}) -## @deftypefnx{Function File} lsim (@var{sys1}, @dots{}, @var{u}, @var{t}, @var{x0}, @var{method}) -## @deftypefnx{Function File} {[@var{y}, @var{t}, @var{x}] =} lsim (@var{sys}, @var{u}) -## @deftypefnx{Function File} {[@var{y}, @var{t}, @var{x}] =} lsim (@var{sys}, @var{u}, @var{t}) -## @deftypefnx{Function File} {[@var{y}, @var{t}, @var{x}] =} lsim (@var{sys}, @var{u}, @var{t}, @var{x0}) -## @deftypefnx{Function File} {[@var{y}, @var{t}, @var{x}] =} lsim (@var{sys}, @var{u}, @var{t}, @var{[]}, @var{method}) -## @deftypefnx{Function File} {[@var{y}, @var{t}, @var{x}] =} lsim (@var{sys}, @var{u}, @var{t}, @var{x0}, @var{method}) -## Simulate LTI model response to arbitrary inputs. If no output arguments are given, -## the system response is plotted on the screen. -## -## @strong{Inputs} -## @table @var -## @item sys -## LTI model. System must be proper, i.e. it must not have more zeros than poles. -## @item u -## Vector or array of input signal. Needs @code{length(t)} rows and as many columns -## as there are inputs. If @var{sys} is a single-input system, row vectors @var{u} -## of length @code{length(t)} are accepted as well. -## @item t -## Time vector. Should be evenly spaced. If @var{sys} is a continuous-time system -## and @var{t} is a real scalar, @var{sys} is discretized with sampling time -## @code{tsam = t/(rows(u)-1)}. If @var{sys} is a discrete-time system and @var{t} -## is not specified, vector @var{t} is assumed to be @code{0 : tsam : tsam*(rows(u)-1)}. -## @item x0 -## Vector of initial conditions for each state. If not specified, a zero vector is assumed. -## @item method -## Discretization method for continuous-time models. Default value is zoh -## (zero-order hold). All methods from @code{c2d} are supported. -## @end table -## -## @strong{Outputs} -## @table @var -## @item y -## Output response array. Has as many rows as time samples (length of t) -## and as many columns as outputs. -## @item t -## Time row vector. It is always evenly spaced. -## @item x -## State trajectories array. Has @code{length (t)} rows and as many columns as states. -## @end table -## -## @seealso{impulse, initial, step} -## @end deftypefn - -## Author: Lukas Reichlin <lukas.reichlin@gmail.com> -## Created: October 2009 -## Version: 0.4 - -% function [y_r, t_r, x_r] = lsim (sys, u, t = [], x0 = [], method = "zoh") -function [y_r, t_r, x_r] = lsim (varargin) - - if (nargin < 2) - print_usage (); - endif - - sys_idx = find (cellfun (@isa, varargin, {"lti"})); # look for LTI models, 'find' needed for plot styles - sys_cell = cellfun (@ss, varargin(sys_idx), "uniformoutput", false); # convert to state-space - - if (! size_equal (sys_cell{:})) - error ("lsim: models must have equal sizes"); - endif - - mat_idx = find (cellfun (@is_real_matrix, varargin)); # indices of matrix arguments - n_mat = length (mat_idx); # number of vector arguments - n_sys = length (sys_cell); # number of LTI systems - - t = []; - x0 = []; - method = "zoh"; - - if (n_mat < 1) - error ("lsim: require input signal 'u'"); - else - arg = varargin{mat_idx(1)}; - if (is_real_vector (arg)) - u = reshape (u, [], 1); # allow row vectors for single-input systems - elseif (is_real_matrix (u)); - u = arg; - else - error ("lsim: input signal 'u' must be an array of real numbers"); - endif - if (n_mat > 1) # time vector t - arg = varargin{mat_idx(2)}; - if (is_real_vector (arg) || isempty (arg)) - t = arg; - else - error ("lsim: time vector 't' must be real-valued or empty"); - endif - if (n_mat > 2) # initial state vector x0 - arg = varargin{mat_idx(3)}; - if (is_real_vector (arg)) - x0 = arg; - else - error ("lsim: initial state vector 'x0' must be a real-valued vector"); - endif - endif - endif - endif - - - - ## function [y, x_arr] = __linear_simulation__ (sys_dt, u, t, x0) - - [y, x] = cellfun (@__linear_simulation__, sys_dt_cell, {u}, t, {x0}, "uniformoutput", false); - - -%{ - if (! isa (sys, "ss")) - sys = ss (sys); # sys must be proper - endif - - if (is_real_vector (u)) - u = reshape (u, [], 1); # allow row vectors for single-input systems - elseif (! is_real_matrix (u)) - error ("lsim: input signal u must be an array of real numbers"); - endif - - if (! is_real_vector (t) && ! isempty (t)) - error ("lsim: time vector t must be real or empty"); - endif - - discrete = ! isct (sys); # static gains are treated as continuous-time systems - tsam = abs (get (sys, "tsam")); # use 1 second as default if tsam is unspecified (-1) - urows = rows (u); - ucols = columns (u); - - if (discrete) # discrete system - if (isempty (t)) # lsim (sys, u) - dt = tsam; - tinitial = 0; - tfinal = tsam * (urows - 1); - elseif (length (t) == 1) # lsim (sys, u, tfinal) - dt = tsam; - tinitial = 0; - tfinal = t; - else # lsim (sys, u, t, ...) - warning ("lsim: spacing of time vector has no effect on sampling time of discrete system"); - dt = tsam; - tinitial = t(1); - tfinal = t(end); - endif - else # continuous system - if (isempty (t)) # lsim (sys, u, [], ...) - error ("lsim: invalid time vector"); - elseif (length (t) == 1) # lsim (sys, u, tfinal, ...) - dt = t / (urows - 1); - tinitial = 0; - tfinal = t; - else # lsim (sys, u, t, ...) - dt = t(2) - t(1); # assume that t is regularly spaced - tinitial = t(1); - tfinal = t(end); - endif - sys = c2d (sys, dt, method); # convert to discrete-time model - endif - - [A, B, C, D] = ssdata (sys); - - n = rows (A); # number of states - m = columns (B); # number of inputs - p = rows (C); # number of outputs - - t = reshape (tinitial : dt : tfinal, [], 1); # time vector - trows = length (t); - - if (urows != trows) - error ("lsim: input vector u must have %d rows", trows); - endif - - if (ucols != m) - error ("lsim: input vector u must have %d columns", m); - endif - -%} - - if (nargout == 0) # plot information - [p, m] = size (sys_cell{1}); - style_idx = find (cellfun (@ischar, varargin)); - str = "Linear Simulation Results"; - outname = get (sys_cell{end}, "outname"); - outname = __labels__ (outname, "y"); - colororder = get (gca, "colororder"); - rc = rows (colororder); - - sysname = cell (n_sys, 1); - - for k = 1 : n_sys # for every system - if (k == n_sys) - lim = numel (args); - else - lim = sys_idx(k+1); - endif - style = args(style_idx(style_idx > sys_idx(k) & style_idx <= lim)); - if (isempty (style)) - color = colororder(1+rem (k-1, rc), :); - style = {"color", color}; - endif - try - sysname{k} = inputname(sys_idx(k)); - catch - sysname{k} = ""; - end_try_catch - if (ct_idx(k)) # continuous-time system - for i = 1 : p # for every output - subplot (p, 1, i); - plot (t{k}, y{k}(:, i), style{:}); - hold on; - grid on; - if (k == n_sys) - axis tight - ylim (__axis_margin__ (ylim)) - ylabel (outname{i}); - if (i == 1) - title (str); - endif - endif - endfor - else # discrete-time system - for i = 1 : p # for every output - subplot (p, 1, i); - stairs (t{k}, y{k}(:, i), style{:}); - hold on; - grid on; - if (k == n_sys) - axis tight; - ylim (__axis_margin__ (ylim)) - ylabel (outname{i}); - if (i == 1) - title (str); - endif - endif - endfor - endif - endfor - xlabel ("Time [s]"); - if (p == 1 && m == 1) - legend (sysname) - endif - hold off; - endif - -endfunction - - -function [y, x_arr] = __linear_simulation__ (sys_dt, u, t, x0) - - [A, B, C, D] = ssdata (sys_dt); - [p, m] = size (D); # number of outputs and inputs - n = rows (A); # number of states - len_t = length (t); - - ## preallocate memory - y = zeros (len_t, p); - x_arr = zeros (len_t, n); - - ## initial conditions - if (isempty (x0)) - x0 = zeros (n, 1); - elseif (n != length (x0) || ! is_real_vector (x0)) - error ("lsim: x0 must be a vector with %d elements", n); - endif - - x = reshape (x0, [], 1); # make sure that x is a column vector - - ## simulation - for k = 1 : len_t - y(k, :) = C * x + D * u(k, :).'; - x_arr(k, :) = x; - x = A * x + B * u(k, :).'; - endfor - -endfunction - - -## TODO: add test cases
--- a/main/control/inst/lsim.m Tue Sep 25 12:41:30 2012 +0000 +++ b/main/control/inst/lsim.m Tue Sep 25 16:51:35 2012 +0000 @@ -1,4 +1,4 @@ -## Copyright (C) 2009, 2010, 2011 Lukas F. Reichlin +## Copyright (C) 2009, 2010, 2011, 2012 Lukas F. Reichlin ## ## This file is part of LTI Syncope. ## @@ -16,7 +16,14 @@ ## along with LTI Syncope. If not, see <http://www.gnu.org/licenses/>. ## -*- texinfo -*- -## @deftypefn{Function File} {[@var{y}, @var{t}, @var{x}] =} lsim (@var{sys}, @var{u}) +## @deftypefn{Function File} lsim (@var{sys}, @var{u}) +## @deftypefnx{Function File} lsim (@var{sys1}, @var{sys2}, @dots{}, @var{sysN}, @var{u}) +## @deftypefnx{Function File} lsim (@var{sys1}, @var{'style1'}, @dots{}, @var{sysN}, @var{'styleN'}, @var{u}) +## @deftypefnx{Function File} lsim (@var{sys1}, @dots{}, @var{u}, @var{t}) +## @deftypefnx{Function File} lsim (@var{sys1}, @dots{}, @var{u}, @var{t}, @var{x0}) +## @deftypefnx{Function File} lsim (@var{sys1}, @dots{}, @var{u}, @var{t}, @var{[]}, @var{method}) +## @deftypefnx{Function File} lsim (@var{sys1}, @dots{}, @var{u}, @var{t}, @var{x0}, @var{method}) +## @deftypefnx{Function File} {[@var{y}, @var{t}, @var{x}] =} lsim (@var{sys}, @var{u}) ## @deftypefnx{Function File} {[@var{y}, @var{t}, @var{x}] =} lsim (@var{sys}, @var{u}, @var{t}) ## @deftypefnx{Function File} {[@var{y}, @var{t}, @var{x}] =} lsim (@var{sys}, @var{u}, @var{t}, @var{x0}) ## @deftypefnx{Function File} {[@var{y}, @var{t}, @var{x}] =} lsim (@var{sys}, @var{u}, @var{t}, @var{[]}, @var{method}) @@ -60,51 +67,141 @@ ## Author: Lukas Reichlin <lukas.reichlin@gmail.com> ## Created: October 2009 -## Version: 0.3 - -function [y_r, t_r, x_r] = lsim (sys, u, t = [], x0 = [], method = "zoh") +## Version: 0.4 - ## TODO: multiplot feature: lsim (sys1, "b", sys2, "r", ..., u, t) +function [y_r, t_r, x_r] = lsim (varargin) - if (nargin < 2 || nargin > 5) + if (nargin < 2) print_usage (); endif - if (! isa (sys, "ss")) - sys = ss (sys); # sys must be proper + sys_idx = find (cellfun (@isa, varargin, {"lti"})); # look for LTI models, 'find' needed for plot styles + sys_cell = cellfun (@ss, varargin(sys_idx), "uniformoutput", false); # convert to state-space + + if (! size_equal (sys_cell{:})) + error ("lsim: models must have equal sizes"); + endif + + mat_idx = find (cellfun (@is_real_matrix, varargin)); # indices of matrix arguments + n_mat = length (mat_idx); # number of vector arguments + n_sys = length (sys_cell); # number of LTI systems + + t = []; + x0 = []; + % method = "zoh"; + + if (n_mat < 1) + error ("lsim: require input signal 'u'"); + else + arg = varargin{mat_idx(1)}; + if (is_real_vector (arg)) + u = reshape (u, [], 1); # allow row vectors for single-input systems + elseif (is_real_matrix (u)); + u = arg; + else + error ("lsim: input signal 'u' must be an array of real numbers"); + endif + if (n_mat > 1) # time vector t + arg = varargin{mat_idx(2)}; + if (is_real_vector (arg) || isempty (arg)) + t = arg; + else + error ("lsim: time vector 't' must be real-valued or empty"); + endif + if (n_mat > 2) # initial state vector x0 + arg = varargin{mat_idx(3)}; + if (is_real_vector (arg)) + x0 = arg; + else + error ("lsim: initial state vector 'x0' must be a real-valued vector"); + endif + if (n_mat > 3) + warning ("lsim: ignored"); + endif + endif + endif endif - if (is_real_vector (u)) - u = reshape (u, [], 1); # allow row vectors for single-input systems - elseif (! is_real_matrix (u)) - error ("lsim: input signal u must be an array of real numbers"); - endif + ## function [y, t, x_arr] = __linear_simulation__ (sys, u, t, x0) - if (! is_real_vector (t) && ! isempty (t)) - error ("lsim: time vector t must be real or empty"); + [y, t, x] = cellfun (@__linear_simulation__, sys_cell, {u}, {t}, {x0}, "uniformoutput", false); + + + if (nargout == 0) # plot information + [p, m] = size (sys_cell{1}); + style_idx = find (cellfun (@ischar, varargin)); + str = "Linear Simulation Results"; + outname = get (sys_cell{end}, "outname"); + outname = __labels__ (outname, "y"); + colororder = get (gca, "colororder"); + rc = rows (colororder); + + sysname = cell (n_sys, 1); + + for k = 1 : n_sys # for every system + if (k == n_sys) + lim = numel (args); + else + lim = sys_idx(k+1); + endif + style = args(style_idx(style_idx > sys_idx(k) & style_idx <= lim)); + if (isempty (style)) + color = colororder(1+rem (k-1, rc), :); + style = {"color", color}; + endif + try + sysname{k} = inputname(sys_idx(k)); + catch + sysname{k} = ""; + end_try_catch + if (ct_idx(k)) # continuous-time system + for i = 1 : p # for every output + subplot (p, 1, i); + plot (t{k}, y{k}(:, i), style{:}); + hold on; + grid on; + if (k == n_sys) + axis tight + ylim (__axis_margin__ (ylim)) + ylabel (outname{i}); + if (i == 1) + title (str); + endif + endif + endfor + else # discrete-time system + for i = 1 : p # for every output + subplot (p, 1, i); + stairs (t{k}, y{k}(:, i), style{:}); + hold on; + grid on; + if (k == n_sys) + axis tight; + ylim (__axis_margin__ (ylim)) + ylabel (outname{i}); + if (i == 1) + title (str); + endif + endif + endfor + endif + endfor + xlabel ("Time [s]"); + if (p == 1 && m == 1) + legend (sysname) + endif + hold off; endif - discrete = ! isct (sys); # static gains are treated as continuous-time systems - tsam = abs (get (sys, "tsam")); # use 1 second as default if tsam is unspecified (-1) - urows = rows (u); - ucols = columns (u); +endfunction + + +function [y, t, x_arr] = __linear_simulation__ (sys, u, t, x0) - if (discrete) # discrete system - if (isempty (t)) # lsim (sys, u) - dt = tsam; - tinitial = 0; - tfinal = tsam * (urows - 1); - elseif (length (t) == 1) # lsim (sys, u, tfinal) - dt = tsam; - tinitial = 0; - tfinal = t; - else # lsim (sys, u, t, ...) - warning ("lsim: spacing of time vector has no effect on sampling time of discrete system"); - dt = tsam; - tinitial = t(1); - tfinal = t(end); - endif - else # continuous system + method = "zoh"; + [urows, ucols] = size (u); + + if (isct (sys)) # continuous-time system if (isempty (t)) # lsim (sys, u, [], ...) error ("lsim: invalid time vector"); elseif (length (t) == 1) # lsim (sys, u, tfinal, ...) @@ -117,28 +214,41 @@ tfinal = t(end); endif sys = c2d (sys, dt, method); # convert to discrete-time model + else # discrete-time system + dt = abs (get (sys, "tsam")); # use 1 second as default if tsam is unspecified (-1) + if (isempty (t)) # lsim (sys, u) + tinitial = 0; + tfinal = dt * (urows - 1); + elseif (length (t) == 1) # lsim (sys, u, tfinal) + tinitial = 0; + tfinal = t; + else # lsim (sys, u, t, ...) + warning ("lsim: spacing of time vector has no effect on sampling time of discrete-time system"); + tinitial = t(1); + tfinal = t(end); + endif endif [A, B, C, D] = ssdata (sys); - + [p, m] = size (D); # number of outputs and inputs n = rows (A); # number of states - m = columns (B); # number of inputs - p = rows (C); # number of outputs + % len_t = length (t); - t = reshape (tinitial : dt : tfinal, [], 1); # time vector - trows = length (t); + ## time vector + t = reshape (tinitial : dt : tfinal, [], 1); + len_t = length (t); - if (urows != trows) - error ("lsim: input vector u must have %d rows", trows); + if (urows != len_t) + error ("lsim: input vector u must have %d rows", len_t); endif - + if (ucols != m) error ("lsim: input vector u must have %d columns", m); endif ## preallocate memory - y = zeros (trows, p); - x_arr = zeros (trows, n); + y = zeros (len_t, p); + x_arr = zeros (len_t, n); ## initial conditions if (isempty (x0)) @@ -150,46 +260,13 @@ x = reshape (x0, [], 1); # make sure that x is a column vector ## simulation - for k = 1 : trows + for k = 1 : len_t y(k, :) = C * x + D * u(k, :).'; x_arr(k, :) = x; x = A * x + B * u(k, :).'; endfor - if (nargout == 0) # plot information - str = ["Linear Simulation Results of ", inputname(1)]; - outname = get (sys, "outname"); - outname = __labels__ (outname, "y_"); - if (discrete) # discrete system - for k = 1 : p - subplot (p, 1, k); - stairs (t, y(:, k)); - grid ("on"); - if (k == 1) - title (str); - endif - ylabel (sprintf ("Amplitude %s", outname{k})); - endfor - xlabel ("Time [s]"); - else # continuous system - for k = 1 : p - subplot (p, 1, k); - plot (t, y(:, k)); - grid ("on"); - if (k == 1) - title (str); - endif - ylabel (sprintf ("Amplitude %s", outname{k})); - endfor - xlabel ("Time [s]"); - endif - else # return values - y_r = y; - t_r = t; - x_r = x_arr; - endif - endfunction -## TODO: add test cases \ No newline at end of file +## TODO: add test cases