Mercurial > forge
changeset 10888:256b88bc1861 octave-forge
control: move multiplot time response into place
author | paramaniac |
---|---|
date | Sat, 22 Sep 2012 10:23:23 +0000 |
parents | 5af195699f20 |
children | 63e17eb83383 |
files | main/control/inst/__time_response__.m main/control/inst/step.m |
diffstat | 2 files changed, 334 insertions(+), 212 deletions(-) [+] |
line wrap: on
line diff
--- a/main/control/inst/__time_response__.m Sat Sep 22 10:14:12 2012 +0000 +++ b/main/control/inst/__time_response__.m Sat Sep 22 10:23:23 2012 +0000 @@ -1,4 +1,4 @@ -## Copyright (C) 2009, 2010 Lukas F. Reichlin +## Copyright (C) 2009, 2010, 2012 Lukas F. Reichlin ## ## This file is part of LTI Syncope. ## @@ -20,241 +20,350 @@ ## Author: Lukas Reichlin <lukas.reichlin@gmail.com> ## Created: October 2009 -## Version: 0.2 +## Version: 0.3 -function [y, t, x_arr] = __time_response__ (sys, resptype, plotflag, tfinal, dt, x0, sysname) +function [y, t, x] = __time_response__ (response, args, sysname, plotflag) - if (! isa (sys, "ss")) - sys = ss (sys); # sys must be proper - endif + sys_idx = find (cellfun (@isa, args, {"lti"})); # look for LTI models, 'find' needed for plot styles + sys_cell = cellfun (@ss, args(sys_idx), "uniformoutput", false); # convert to state-space - if (is_real_vector (tfinal) && length (tfinal) > 1) # time vector t passed - dt = tfinal(2) - tfinal(1); # assume that t is regularly spaced - tfinal = tfinal(end); + if (! size_equal (sys_cell{:})) + error ("%s: models must have equal sizes", response); endif - [A, B, C, D, tsam] = ssdata (sys); + vec_idx = find (cellfun (@is_real_matrix, args)); # indices of vector arguments + n_vec = length (vec_idx); # number of vector arguments + n_sys = length (sys_cell); # number of LTI systems - discrete = ! isct (sys); # static gains are treated as analog systems - tsam = abs (tsam); # use 1 second if tsam is unspecified (-1) + tfinal = []; + dt = []; + x0 = []; - if (discrete) - if (! isempty (dt)) - warning ("time_response: argument dt has no effect on sampling time of discrete system"); + ## extract tfinal/t, dt, x0 from args + if (strcmpi (response, "initial")) + if (n_vec < 1) + error ("initial: require initial state vector 'x0'"); + else # initial state vector x0 specified + arg = args{vec_idx(1)}; + if (is_real_vector (arg)) + x0 = arg; + else + error ("initial: initial state vector 'x0' must be a vector of real values"); + endif + if (n_vec > 1) # tfinal or time vector t specified + arg = args{vec_idx(2)}; + if (issample (arg)) + tfinal = arg; + elseif (isempty (arg)) + ## tfinal = []; # nothing to do here + elseif (is_real_vector (arg)) + dt = abs (arg(2) - arg(1)); # assume that t is regularly spaced + tfinal = arg(end); + else + warning ("initial: argument number %d ignored", vec_idx(2)); + endif + if (n_vec > 2) # sampling time dt specified + arg = args{vec_idx(3)}; + if (issample (arg)) + dt = arg; + else + warning ("initial: argument number %d ignored", vec_idx(3)); + endif + if (n_vec > 3) + warning ("initial: ignored"); + endif + endif + endif + endif + else # step or impulse response + if (n_vec > 0) # tfinal or time vector t specified + arg = args{vec_idx(1)}; + if (issample (arg)) + tfinal = arg; + elseif (isempty (arg)) + ## tfinal = []; # nothing to do here + elseif (is_real_vector (arg)) + dt = abs (arg(2) - arg(1)); # assume that t is regularly spaced + tfinal = arg(end); + else + warning ("%s: argument number %d ignored", response, vec_idx(1)); + endif + if (n_vec > 1) # sampling time dt specified + arg = args{vec_idx(2)}; + if (issample (arg)) + dt = arg; + else + warning ("%s: argument number %d ignored", response, vec_idx(2)); + endif + if (n_vec > 2) + warning ("%s: ignored", response); + endif + endif endif - - dt = tsam; endif + ## TODO: share common code between initial and step/impulse - [tfinal, dt] = __sim_horizon__ (A, discrete, tfinal, dt); + [tfinal, dt] = cellfun (@__sim_horizon__, sys_cell, {tfinal}, {dt}, "uniformoutput", false); + tfinal = max ([tfinal{:}]); - if (! discrete) - sys = c2d (sys, dt, "zoh"); - endif - - [F, G] = ssdata (sys); # matrices C and D don't change - - n = rows (F); # number of states - m = columns (G); # number of inputs - p = rows (C); # number of outputs + ct_idx = cellfun (@isct, sys_cell); + sys_dt_cell = sys_cell; + tmp = cellfun (@c2d, sys_cell(ct_idx), dt(ct_idx), {"zoh"}, "uniformoutput", false); + sys_dt_cell(ct_idx) = tmp; ## time vector - t = reshape (0 : dt : tfinal, [], 1); - l_t = length (t); - - switch (resptype) - case "initial" - str = ["Response of ", sysname, " to Initial Conditions"]; - yfinal = zeros (p, 1); - - ## preallocate memory - y = zeros (l_t, p); - x_arr = zeros (l_t, n); - - ## initial conditions - x = reshape (x0, [], 1); # make sure that x is a column vector - - if (n != length (x0) || ! is_real_vector (x0)) - error ("initial: x0 must be a real vector with %d elements", n); - endif + t = @cellfun (@(dt) reshape (0 : dt : tfinal, [], 1), dt, "uniformoutput", false); - ## simulation - for k = 1 : l_t - y(k, :) = C * x; - x_arr(k, :) = x; - x = F * x; - endfor - - case "step" - str = ["Step Response of ", sysname]; - yfinal = dcgain (sys); - - ## preallocate memory - y = zeros (l_t, p, m); - x_arr = zeros (l_t, n, m); - - for j = 1 : m # for every input channel - ## initial conditions - x = zeros (n, 1); - u = zeros (m, 1); - u(j) = 1; + ## function [y, x_arr] = __initial_response__ (sys, sys_dt, t, x0) + ## function [y, x_arr] = __step_response__ (sys_dt, t) + ## function [y, x_arr] = __impulse_response__ (sys, sys_dt, t) - ## simulation - for k = 1 : l_t - y(k, :, j) = C * x + D * u; - x_arr(k, :, j) = x; - x = F * x + G * u; - endfor - endfor - + switch (response) + case "initial" + [y, x] = cellfun (@__initial_response__, sys_dt_cell, t, {x0}, "uniformoutput", false); + case "step" + [y, x] = cellfun (@__step_response__, sys_dt_cell, t, "uniformoutput", false); case "impulse" - str = ["Impulse Response of ", sysname]; - yfinal = zeros (p, m); - - ## preallocate memory - y = zeros (l_t, p, m); - x_arr = zeros (l_t, n, m); - - for j = 1 : m # for every input channel - ## initial conditions - u = zeros (m, 1); - u(j) = 1; - - if (discrete) - x = zeros (n, 1); # zero by definition - y(1, :, j) = D * u / dt; - x_arr(1, :, j) = x; - x = G * u / dt; - else - x = B * u; # B, not G! - y(1, :, j) = C * x; - x_arr(1, :, j) = x; - x = F * x; - endif - - ## simulation - for k = 2 : l_t - y (k, :, j) = C * x; - x_arr(k, :, j) = x; - x = F * x; - endfor - endfor - - if (discrete) - y *= dt; - x_arr *= dt; - endif - + [y, x] = cellfun (@__impulse_response__, sys_cell, sys_dt_cell, t, "uniformoutput", false); otherwise error ("time_response: invalid response type"); - endswitch + + if (plotflag) # display plot + [p, m] = size (sys_cell{1}); + switch (response) + case "initial" + str = "Response to Initial Conditions"; + cols = 1; + ## yfinal = zeros (p, 1); + case "step" + str = "Step Response"; + cols = m; + ## yfinal = dcgain (sys_cell{1}); + case "impulse" + str = "Impulse Response"; + cols = m; + ## yfinal = zeros (p, m); + otherwise + error ("time_response: invalid response type"); + endswitch + + style_idx = find (cellfun (@ischar, args)); + outname = get (sys_cell{end}, "outname"); + outname = __labels__ (outname, "y"); + colororder = get (gca, "colororder"); + rc = rows (colororder); - if (plotflag) # display plot - - ## TODO: Set correct titles, especially for multi-input systems - - stable = isstable (sys); - outname = get (sys, "outname"); - outname = __labels__ (outname, "y_"); - - if (strcmp (resptype, "initial")) - cols = 1; - else - cols = m; + 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 + discrete = ! ct_idx(k); + if (discrete) # discrete-time system + for i = 1 : p # for every output + for j = 1 : cols # for every input (except for initial where cols=1) + subplot (p, cols, (i-1)*cols+j); + stairs (t{k}, y{k}(:, i, j), style{:}); + hold on; + grid on; + if (k == n_sys) + axis tight; + ylim (__axis_margin__ (ylim)) + if (j == 1) + ylabel (outname{i}); + if (i == 1) + title (str); + endif + endif + endif + endfor + endfor + else # continuous-time system + for i = 1 : p # for every output + for j = 1 : cols # for every input (except for initial where cols=1) + subplot (p, cols, (i-1)*cols+j); + ##if (n_sys == 1 && isstable (sys_cell{1})) + ## plot (t{k}, y{k}(:, i, j), style{:}, [t{k}(1), t{k}(end)], repmat (yfinal(i,j), 1, 2)); + ## ## TODO: plot final value first such that its line doesn't overprint the response + ##else + plot (t{k}, y{k}(:, i, j), style{:}); + ##endif + hold on; + grid on; + if (k == n_sys) + axis tight + ylim (__axis_margin__ (ylim)) + if (j == 1) + ylabel (outname{i}); + if (i == 1) + title (str); + endif + endif + endif + endfor + endfor + endif + endfor + xlabel ("Time [s]"); + if (p == 1 && m == 1) + legend (sysname) endif - - if (discrete) # discrete system - for k = 1 : p - for j = 1 : cols - - subplot (p, cols, (k-1)*cols+j); - - if (stable) - stairs (t, [y(:, k, j), yfinal(k, j) * ones(l_t, 1)]); - else - stairs (t, y(:, k, j)); - endif - - grid ("on"); - - if (k == 1 && j == 1) - title (str); - endif - - if (j == 1) - ylabel (sprintf ("Amplitude %s", outname{k})); - endif - - endfor - endfor - - xlabel ("Time [s]"); - - else # continuous system - for k = 1 : p - for j = 1 : cols - - subplot (p, cols, (k-1)*cols+j); - - if (stable) - plot (t, [y(:, k, j), yfinal(k, j) * ones(l_t, 1)]); - else - plot (t, y(:, k, j)); - endif - - grid ("on"); - - if (k == 1 && j == 1) - title (str); - endif - - if (j == 1) - ylabel (sprintf ("Amplitude %s", outname{k})); - endif - - endfor - endfor - - xlabel ("Time [s]"); - - endif + hold off; endif endfunction -function [tfinal, dt] = __sim_horizon__ (A, discrete, tfinal, Ts) +function [y, x_arr] = __initial_response__ (sys_dt, t, x0) + + [F, G, C, D] = ssdata (sys_dt); # system must be proper + + n = rows (F); # number of states + m = columns (G); # number of inputs + p = rows (C); # number of outputs + l_t = length (t); + + ## preallocate memory + y = zeros (l_t, p); + x_arr = zeros (l_t, n); + + ## initial conditions + x = reshape (x0, [], 1); # make sure that x is a column vector + + if (n != length (x0) || ! is_real_vector (x0)) + error ("initial: x0 must be a real vector with %d elements", n); + endif + + ## simulation + for k = 1 : l_t + y(k, :) = C * x; + x_arr(k, :) = x; + x = F * x; + endfor + +endfunction + + +function [y, x_arr] = __step_response__ (sys_dt, t) + + [F, G, C, D] = ssdata (sys_dt); # system must be proper + + n = rows (F); # number of states + m = columns (G); # number of inputs + p = rows (C); # number of outputs + l_t = length (t); + + ## preallocate memory + y = zeros (l_t, p, m); + x_arr = zeros (l_t, n, m); + + for j = 1 : m # for every input channel + ## initial conditions + x = zeros (n, 1); + u = zeros (m, 1); + u(j) = 1; + + ## simulation + for k = 1 : l_t + y(k, :, j) = C * x + D * u; + x_arr(k, :, j) = x; + x = F * x + G * u; + endfor + endfor + +endfunction + + +function [y, x_arr] = __impulse_response__ (sys, sys_dt, t) + + [~, B] = ssdata (sys); + [F, G, C, D, dt] = ssdata (sys_dt); # system must be proper + dt = abs (dt); # use 1 second if tsam is unspecified (-1) + discrete = ! isct (sys); + + n = rows (F); # number of states + m = columns (G); # number of inputs + p = rows (C); # number of outputs + l_t = length (t); + + ## preallocate memory + y = zeros (l_t, p, m); + x_arr = zeros (l_t, n, m); + + for j = 1 : m # for every input channel + ## initial conditions + u = zeros (m, 1); + u(j) = 1; + + if (discrete) + x = zeros (n, 1); # zero by definition + y(1, :, j) = D * u / dt; + x_arr(1, :, j) = x; + x = G * u / dt; + else + x = B * u; # B, not G! + y(1, :, j) = C * x; + x_arr(1, :, j) = x; + x = F * x; + endif + + ## simulation + for k = 2 : l_t + y (k, :, j) = C * x; + x_arr(k, :, j) = x; + x = F * x; + endfor + endfor + + if (discrete) + y *= dt; + x_arr *= dt; + endif + +endfunction + + +function [tfinal, dt] = __sim_horizon__ (sys, tfinal, Ts) ## code based on __stepimp__.m of Kai P. Mueller and A. Scottedward Hodel - TOL = 1.0e-10; # values below TOL are assumed to be zero - N_MIN = 50; # min number of points - N_MAX = 2000; # max number of points - N_DEF = 1000; # default number of points - T_DEF = 10; # default simulation time + TOL = 1.0e-10; # values below TOL are assumed to be zero + N_MIN = 50; # min number of points + N_MAX = 2000; # max number of points + N_DEF = 1000; # default number of points + T_DEF = 10; # default simulation time - n = rows (A); - eigw = eig (A); + ev = pole (sys); + n = length (ev); # number of states/poles + continuous = isct (sys); + discrete = ! continuous; if (discrete) + dt = Ts = abs (get (sys, "tsam")); ## perform bilinear transformation on poles in z for k = 1 : n - pol = eigw(k); + pol = ev(k); if (abs (pol + 1) < TOL) - eigw(k) = 0; + ev(k) = 0; else - eigw(k) = 2 / Ts * (pol - 1) / (pol + 1); + ev(k) = 2 / Ts * (pol - 1) / (pol + 1); endif endfor endif - ## remove poles near zero from eigenvalue array eigw + ## remove poles near zero from eigenvalue array ev nk = n; for k = 1 : n - if (abs (real (eigw(k))) < TOL) - eigw(k) = 0; + if (abs (real (ev(k))) < TOL) + ev(k) = 0; nk -= 1; endif endfor @@ -264,27 +373,27 @@ tfinal = T_DEF; endif - if (! discrete) + if (continuous) dt = tfinal / N_DEF; endif else - eigw = eigw(find (eigw)); - eigw_max = max (abs (eigw)); + ev = ev(find (ev)); + ev_max = max (abs (ev)); - if (! discrete) - dt = 0.2 * pi / eigw_max; + if (continuous) + dt = 0.2 * pi / ev_max; endif if (isempty (tfinal)) - eigw_min = min (abs (real (eigw))); - tfinal = 5.0 / eigw_min; + ev_min = min (abs (real (ev))); + tfinal = 5.0 / ev_min; ## round up yy = 10^(ceil (log10 (tfinal)) - 1); tfinal = yy * ceil (tfinal / yy); endif - if (! discrete) + if (continuous) N = tfinal / dt; if (N < N_MIN) @@ -297,7 +406,7 @@ endif endif - if (! isempty (Ts)) # catch case cont. system with dt specified + if (continuous && ! isempty (Ts)) # catch case cont. system with dt specified dt = Ts; endif
--- a/main/control/inst/step.m Sat Sep 22 10:14:12 2012 +0000 +++ b/main/control/inst/step.m Sat Sep 22 10:23:23 2012 +0000 @@ -1,4 +1,4 @@ -## Copyright (C) 2009 Lukas F. Reichlin +## Copyright (C) 2009, 2012 Lukas F. Reichlin ## ## This file is part of LTI Syncope. ## @@ -54,22 +54,35 @@ ## Author: Lukas Reichlin <lukas.reichlin@gmail.com> ## Created: October 2009 -## Version: 0.1 - -function [y_r, t_r, x_r] = step (sys, tfinal = [], dt = []) +## Version: 0.2 - ## TODO: multiplot feature: step (sys1, "b", sys2, "r", ...) +function [y_r, t_r, x_r] = step2 (varargin) - if (nargin == 0 || nargin > 3) + if (nargin == 0) print_usage (); endif - [y, t, x] = __time_response__ (sys, "step", ! nargout, tfinal, dt, [], inputname (1)); + if (nargout) + sysname = {}; + else + sys_idx = find (cellfun (@isa, varargin, {"lti"})); + len = length (sys_idx); + sysname = cell (len, 1); + for k = 1 : len + try + sysname{k} = inputname(sys_idx(k)); + catch + sysname{k} = ""; + end_try_catch + endfor + endif + + [y, t, x] = __time_response_2__ ("step", varargin, sysname, ! nargout); if (nargout) - y_r = y; - t_r = t; - x_r = x; + y_r = y{1}; + t_r = t{1}; + x_r = x{1}; endif endfunction