From a9b1599a37dfe8af953fac2cd8a84a5991bdb54b Mon Sep 17 00:00:00 2001 From: Torsten Lilge Date: Sun, 7 Jun 2026 21:57:40 +0200 Subject: [PATCH 1/5] updated version number to dev development version --- DESCRIPTION | 2 +- NEWS | 6 ++++++ src/configure.ac | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 414b3254..078986e2 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,5 +1,5 @@ Name: control -Version: 4.2.2+ +Version: 4.2.99+ Date: XXXX-XX-XX Author: Lukas Reichlin Maintainer: Doug Stewart , Torsten Lilge diff --git a/NEWS b/NEWS index e10d5638..dfcad4c8 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,12 @@ Summary of important user-visible changes for releases of the control package +=============================================================================== +control-4.2.99+ Upcoming 4.3.0, not yet released +=============================================================================== + +** + =============================================================================== control-4.2.2+ Upcoming 4.2.3, not yet released =============================================================================== diff --git a/src/configure.ac b/src/configure.ac index 5b711bf9..5677ae19 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.71]) -AC_INIT([control],[4.2.2+]) +AC_INIT([control],[4.2.99+]) AC_CONFIG_SRCDIR([sl_ab01od.cc]) AC_CONFIG_HEADERS([config.h]) From 611a573c29a9cbf97b45524a1c68c3b4ec639c66 Mon Sep 17 00:00:00 2001 From: Mitchell Thompkins Date: Thu, 2 Jul 2026 20:35:07 +0000 Subject: [PATCH 2/5] implement native @zpk class, adds a few tests, ignore vim files --- .gitignore | 2 +- inst/@tf/__c2d__.m | 6 +- inst/@tf/__d2c__.m | 4 +- inst/@zpk/__c2d__.m | 100 +++++++++++++++++++ inst/@zpk/__ctranspose__.m | 27 +++++ inst/@zpk/__d2c__.m | 75 ++++++++++++++ inst/@zpk/__freqresp__.m | 27 +++++ inst/@zpk/__get__.m | 38 ++++++++ inst/@zpk/__minreal__.m | 27 +++++ inst/@zpk/__pole__.m | 35 +++++++ inst/@zpk/__set__.m | 59 +++++++++++ inst/@zpk/__sys2frd__.m | 29 ++++++ inst/@zpk/__sys2ss__.m | 31 ++++++ inst/@zpk/__sys2tf__.m | 38 ++++++++ inst/@zpk/__sys_connect__.m | 27 +++++ inst/@zpk/__sys_data__.m | 31 ++++++ inst/@zpk/__sys_group__.m | 33 +++++++ inst/@zpk/__sys_inverse__.m | 27 +++++ inst/@zpk/__sys_keys__.m | 40 ++++++++ inst/@zpk/__sys_prune__.m | 33 +++++++ inst/@zpk/__times__.m | 33 +++++++ inst/@zpk/__transpose__.m | 27 +++++ inst/@zpk/__zero__.m | 37 +++++++ inst/@zpk/display.m | 77 +++++++++++++++ inst/@zpk/isstaticgain.m | 30 ++++++ inst/@zpk/zpk.m | 190 ++++++++++++++++++++++++++++++++++++ inst/@zpk/zpkdata.m | 55 +++++++++++ inst/zpk.m | 140 -------------------------- 28 files changed, 1132 insertions(+), 146 deletions(-) create mode 100644 inst/@zpk/__c2d__.m create mode 100644 inst/@zpk/__ctranspose__.m create mode 100644 inst/@zpk/__d2c__.m create mode 100644 inst/@zpk/__freqresp__.m create mode 100644 inst/@zpk/__get__.m create mode 100644 inst/@zpk/__minreal__.m create mode 100644 inst/@zpk/__pole__.m create mode 100644 inst/@zpk/__set__.m create mode 100644 inst/@zpk/__sys2frd__.m create mode 100644 inst/@zpk/__sys2ss__.m create mode 100644 inst/@zpk/__sys2tf__.m create mode 100644 inst/@zpk/__sys_connect__.m create mode 100644 inst/@zpk/__sys_data__.m create mode 100644 inst/@zpk/__sys_group__.m create mode 100644 inst/@zpk/__sys_inverse__.m create mode 100644 inst/@zpk/__sys_keys__.m create mode 100644 inst/@zpk/__sys_prune__.m create mode 100644 inst/@zpk/__times__.m create mode 100644 inst/@zpk/__transpose__.m create mode 100644 inst/@zpk/__zero__.m create mode 100644 inst/@zpk/display.m create mode 100644 inst/@zpk/isstaticgain.m create mode 100644 inst/@zpk/zpk.m create mode 100644 inst/@zpk/zpkdata.m delete mode 100644 inst/zpk.m diff --git a/.gitignore b/.gitignore index 699dd9f5..1952b07e 100644 --- a/.gitignore +++ b/.gitignore @@ -54,4 +54,4 @@ TAGS$ semantic.cache # Other text editors often create these ~.* - +*.swp diff --git a/inst/@tf/__c2d__.m b/inst/@tf/__c2d__.m index b493b5c9..f5b4b484 100644 --- a/inst/@tf/__c2d__.m +++ b/inst/@tf/__c2d__.m @@ -1,4 +1,5 @@ ## Copyright (C) 2009-2016 Lukas F. Reichlin +## Copyright (C) 2026 Mitchell Thompkins ## ## This file is part of LTI Syncope. ## @@ -35,8 +36,7 @@ sys=imp_invar(sys,1/tsam); elseif (strncmpi (method, "m", 1)) # "matched" - ## TODO: move this code to @zpk/__c2d__.m once ZPK models are implemented - + if (! issiso (sys)) error ("tf: c2d: require SISO system for matched pole/zero method"); endif @@ -68,7 +68,7 @@ w_d = exp (1j * w_c * tsam); k_d = real (k_c * prod (1j*w_c - z_c) / prod (1j*w_c - p_c) * prod (w_d - p_d) / prod (w_d - z_d)); - tmp = zpk (z_d, p_d, k_d, tsam); + tmp = tf (zpk (z_d, p_d, k_d, tsam)); sys.num = tmp.num; sys.den = tmp.den; diff --git a/inst/@tf/__d2c__.m b/inst/@tf/__d2c__.m index fffd796c..7b1d6d48 100644 --- a/inst/@tf/__d2c__.m +++ b/inst/@tf/__d2c__.m @@ -1,4 +1,5 @@ ## Copyright (C) 2009-2016 Lukas F. Reichlin +## Copyright (C) 2026 Mitchell Thompkins ## ## This file is part of LTI Syncope. ## @@ -25,7 +26,6 @@ function sys = __d2c__ (sys, tsam, method = "zoh", w0 = 0) if (strncmpi (method, "m", 1)) # "matched" - ## TODO: move this code to @zpk/__d2c__.m once ZPK models are implemented if (! issiso (sys)) error ("tf: d2c: require SISO system for matched pole/zero method"); @@ -52,7 +52,7 @@ w_d = exp (w_c * tsam); k_c = real (k_d * prod (w_d - z_d_orig) / prod (w_d - p_d) * prod (w_c - p_c) / prod (w_c - z_c)); - tmp = zpk (z_c, p_c, k_c); + tmp = tf (zpk (z_c, p_c, k_c)); sys.num = tmp.num; sys.den = tmp.den; diff --git a/inst/@zpk/__c2d__.m b/inst/@zpk/__c2d__.m new file mode 100644 index 00000000..40a35566 --- /dev/null +++ b/inst/@zpk/__c2d__.m @@ -0,0 +1,100 @@ +## Copyright (C) 2026 Mitchell Thompkins +## +## 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 . + +## -*- texinfo -*- +## Convert the continuous ZPK model into its discrete-time equivalent. +## The matched method maps each stored pole and zero directly via exp (s*tsam), +## avoiding the ill-conditioned polynomial round-trip of the TF representation. + +## Author: Mitchell Thompkins +## Created: June 2026 +## Version: 0.1 + +function sys = __c2d__ (sys, tsam, method = "zoh", w0 = 0) + + if (strncmpi (method, "m", 1)) # "matched" + + if (! issiso (sys)) + error ("zpk: c2d: require SISO system for matched pole/zero method"); + endif + + z_c = sys.z{1}; + p_c = sys.p{1}; + k_c = sys.k; + + p_d = exp (p_c * tsam); + z_d = exp (z_c * tsam); + + if (any (! isfinite (p_d)) || any (! isfinite (z_d))) + error ("zpk: c2d: discrete-time poles and zeros are not finite"); + endif + + ## continuous-time zeros at infinity are mapped to -1 in discrete-time + ## except for one. for non-proper transfer functions, no zeros at -1 are added. + np = length (p_c); # number of poles + nz = length (z_c); # number of finite zeros, np-nz number of infinite zeros + z_d = vertcat (z_d, repmat (-1, np-nz-1, 1)); + + ## the discrete-time gain k_d is matched at frequency w_c to continuous-time + ## gain k_c. dc gain is taken (w_c=0) unless there are continuous-time + ## poles/zeros near the imaginary axis at j*w_c. gain is evaluated on the + ## imaginary axis (s=j*w_c) and unit circle (z=exp(j*w_c*tsam)) so that + ## |H_d(exp(j*w_c*tsam))| = |H_c(j*w_c)| holds in the frequency domain. + w_c = 0; # start at dc + tol = sqrt (eps); # poles/zeros within tol of j*w_c are avoided + while (any (abs ([p_c; z_c] - 1j*w_c) < tol)) + w_c += 0.1 / tsam; + endwhile + w_d = exp (1j * w_c * tsam); + k_d = real (k_c * prod (1j*w_c - z_c) / prod (1j*w_c - p_c) * prod (w_d - p_d) / prod (w_d - z_d)); + + sys.z{1} = z_d; + sys.p{1} = p_d; + sys.k = k_d; + + else + ## zoh/foh/tustin/prewarp/impulse are not per-root maps. their natural + ## representation is state-space. convert back to zpk for type consistency. + sys = zpk (__c2d__ (ss (sys), tsam, method, w0)); + endif + +endfunction + + +%!test +%! ## single pole: p_d = exp(p_c * Ts) +%! sys = zpk ([], [-1], 1); +%! sys_d = c2d (sys, 0.1, 'matched'); +%! assert (isa (sys_d, 'zpk')); +%! [~, p_d] = zpkdata (sys_d, 'v'); +%! assert (p_d, exp (-0.1), 1e-14); + +%!test +%! ## 25 poles clustered near the imaginary axis: matched c2d keeps them +%! ## stable and maps each one exactly to exp(p*Ts) with no rounding error +%! N = 25; Ts = 1/1000; +%! p_s = (-0.001 + 1i * linspace (1, 25, N))' * 2*pi*4; +%! sys_d = c2d (zpk ([], p_s, 1), Ts, 'matched'); +%! [~, p_d] = zpkdata (sys_d, 'v'); +%! assert (all (abs (p_d) < 1)); +%! assert (max (abs (p_d - exp (p_s * Ts))), 0, 1e-12); + +%!test +%! ## non-matched methods still work on zpk and return zpk +%! sys_d = c2d (zpk ([], [-1], 1), 0.1, 'zoh'); +%! assert (isa (sys_d, 'zpk')); +%! assert (get (sys_d, 'tsam'), 0.1); diff --git a/inst/@zpk/__ctranspose__.m b/inst/@zpk/__ctranspose__.m new file mode 100644 index 00000000..f28f8a98 --- /dev/null +++ b/inst/@zpk/__ctranspose__.m @@ -0,0 +1,27 @@ +## Copyright (C) 2026 Mitchell Thompkins +## +## 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 . + +## -*- texinfo -*- +## Complex-conjugate transpose ZPK model by delegating to TF. + +## Author: Mitchell Thompkins +## Created: June 2026 +## Version: 0.1 + +function sys = __ctranspose__ (sys, ct) + sys = __ctranspose__ (tf (sys), ct); +endfunction diff --git a/inst/@zpk/__d2c__.m b/inst/@zpk/__d2c__.m new file mode 100644 index 00000000..6e01ad78 --- /dev/null +++ b/inst/@zpk/__d2c__.m @@ -0,0 +1,75 @@ +## Copyright (C) 2026 Mitchell Thompkins +## +## 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 . + +## -*- texinfo -*- +## Convert the discrete ZPK model into its continuous-time equivalent. + +## Author: Mitchell Thompkins +## Created: June 2026 +## Version: 0.1 + +function sys = __d2c__ (sys, tsam, method = "zoh", w0 = 0) + + if (strncmpi (method, "m", 1)) # "matched" + + if (! issiso (sys)) + error ("zpk: d2c: require SISO system for matched pole/zero method"); + endif + + z_d = sys.z{1}; + p_d = sys.p{1}; + k_d = sys.k; + + if (any (abs (p_d) < eps) || any (abs (z_d) < eps)) + error ("zpk: d2c: discrete-time poles and zeros at 0 not supported because log(0) is -Inf"); + endif + + z_d_orig = z_d; + z_d(abs (z_d+1) < sqrt (eps)) = []; # remove zeros added at -1 by c2d + + p_c = log (p_d) / tsam; + z_c = log (z_d) / tsam; + + w_c = 0; + w_d = 1; + tol = sqrt (eps); + while (any (abs ([p_d; z_d_orig] - w_d) < tol)) + w_c += 0.1 / tsam; + w_d = exp (w_c * tsam); + endwhile + k_c = real (k_d * prod (w_d - z_d_orig) / prod (w_d - p_d) * prod (w_c - p_c) / prod (w_c - z_c)); + + sys.z{1} = z_c; + sys.p{1} = p_c; + sys.k = k_c; + + else + sys = zpk (__d2c__ (ss (sys), tsam, method, w0)); + endif + +endfunction + + +%!test +%! ## matched round trip is the identity on poles/zeros +%! sys = zpk ([-3], [-1; -2], 4); +%! sysd = c2d (sys, 0.1, 'matched'); +%! sysc = d2c (sysd, 'matched'); +%! [z, p, k] = zpkdata (sysc, 'v'); +%! assert (sort (real (p)), [-2; -1], 1e-10); +%! assert (real (z), -3, 1e-10); +%! assert (k, 4, 1e-10); diff --git a/inst/@zpk/__freqresp__.m b/inst/@zpk/__freqresp__.m new file mode 100644 index 00000000..0bd4b9b6 --- /dev/null +++ b/inst/@zpk/__freqresp__.m @@ -0,0 +1,27 @@ +## Copyright (C) 2026 Mitchell Thompkins +## +## 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 . + +## -*- texinfo -*- +## Compute frequency response of ZPK model by delegating to TF. + +## Author: Mitchell Thompkins +## Created: June 2026 +## Version: 0.1 + +function H = __freqresp__ (sys, w, cellflag = false) + H = __freqresp__ (tf (sys), w, cellflag); +endfunction diff --git a/inst/@zpk/__get__.m b/inst/@zpk/__get__.m new file mode 100644 index 00000000..0560c8ad --- /dev/null +++ b/inst/@zpk/__get__.m @@ -0,0 +1,38 @@ +## Copyright (C) 2026 Mitchell Thompkins +## +## 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 . + +## -*- texinfo -*- +## Access key values of ZPK objects. + +## Author: Mitchell Thompkins +## Created: June 2026 +## Version: 0.1 + +function val = __get__ (sys, key) + + switch (key) + case {"z", "zeros"} + val = sys.z; + case {"p", "poles"} + val = sys.p; + case {"k", "gain"} + val = sys.k; + otherwise + error ("zpk: get: invalid key name '%s'", key); + endswitch + +endfunction diff --git a/inst/@zpk/__minreal__.m b/inst/@zpk/__minreal__.m new file mode 100644 index 00000000..21137a94 --- /dev/null +++ b/inst/@zpk/__minreal__.m @@ -0,0 +1,27 @@ +## Copyright (C) 2026 Mitchell Thompkins +## +## 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 . + +## -*- texinfo -*- +## Return minimal realization of ZPK model by delegating to TF. + +## Author: Mitchell Thompkins +## Created: June 2026 +## Version: 0.1 + +function sys = __minreal__ (sys, tol) + sys = __minreal__ (tf (sys), tol); +endfunction diff --git a/inst/@zpk/__pole__.m b/inst/@zpk/__pole__.m new file mode 100644 index 00000000..8cec4645 --- /dev/null +++ b/inst/@zpk/__pole__.m @@ -0,0 +1,35 @@ +## Copyright (C) 2026 Mitchell Thompkins +## +## 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 . + +## -*- texinfo -*- +## Return poles of ZPK model directly from stored data (no polynomial round-trip). + +## Author: Mitchell Thompkins +## Created: June 2026 +## Version: 0.1 + +function pol = __pole__ (sys) + + if (issiso (sys)) + pol = sys.p{1}; + else + warning ("Control:convert-to-state-space", + "zpk: pole: converting to minimal state-space for pole computation of mimo zpk\n"); + pol = pole (ss (sys)); + endif + +endfunction diff --git a/inst/@zpk/__set__.m b/inst/@zpk/__set__.m new file mode 100644 index 00000000..ff5fceb8 --- /dev/null +++ b/inst/@zpk/__set__.m @@ -0,0 +1,59 @@ +## Copyright (C) 2026 Mitchell Thompkins +## +## 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 . + +## -*- texinfo -*- +## Set or modify keys of ZPK objects. + +## Author: Mitchell Thompkins +## Created: June 2026 +## Version: 0.1 + +function sys = __set__ (sys, key, val) + + switch (key) + case {"z", "zeros"} + if (! iscell (val)) + val = {val}; + endif + if (! size_equal (val, sys.p, sys.k)) + error ("zpk: set: 'z' dimensions must match 'p' and 'k'"); + endif + sys.z = cellfun (@(v) v(:), val, "uniformoutput", false); + + case {"p", "poles"} + if (! iscell (val)) + val = {val}; + endif + if (! size_equal (sys.z, val, sys.k)) + error ("zpk: set: 'p' dimensions must match 'z' and 'k'"); + endif + sys.p = cellfun (@(v) v(:), val, "uniformoutput", false); + + case {"k", "gain"} + if (! is_real_matrix (val)) + error ("zpk: set: 'k' must be a real-valued matrix"); + endif + if (! size_equal (sys.z, sys.p, val)) + error ("zpk: set: 'k' dimensions must match 'z' and 'p'"); + endif + sys.k = val; + + otherwise + error ("zpk: set: invalid key name '%s'", key); + endswitch + +endfunction diff --git a/inst/@zpk/__sys2frd__.m b/inst/@zpk/__sys2frd__.m new file mode 100644 index 00000000..54b152be --- /dev/null +++ b/inst/@zpk/__sys2frd__.m @@ -0,0 +1,29 @@ +## Copyright (C) 2026 Mitchell Thompkins +## +## 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 . + +## -*- texinfo -*- +## Convert ZPK model to frequency-response data representation. + +## Author: Mitchell Thompkins +## Created: June 2026 +## Version: 0.1 + +function [retsys, retlti] = __sys2frd__ (sys, w = []) + + [retsys, retlti] = __sys2frd__ (tf (sys), w); + +endfunction diff --git a/inst/@zpk/__sys2ss__.m b/inst/@zpk/__sys2ss__.m new file mode 100644 index 00000000..bb78033b --- /dev/null +++ b/inst/@zpk/__sys2ss__.m @@ -0,0 +1,31 @@ +## Copyright (C) 2026 Mitchell Thompkins +## +## 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 . + +## -*- texinfo -*- +## Convert ZPK model to state-space representation. + +## Author: Mitchell Thompkins +## Created: June 2026 +## Version: 0.1 + +function [retsys, retlti] = __sys2ss__ (sys) + + ## explicit representation change. goes via the (lossy) polynomial form, + ## same numerics as the former zpk-returns-tf wrapper + [retsys, retlti] = __sys2ss__ (tf (sys)); + +endfunction diff --git a/inst/@zpk/__sys2tf__.m b/inst/@zpk/__sys2tf__.m new file mode 100644 index 00000000..bfa813f9 --- /dev/null +++ b/inst/@zpk/__sys2tf__.m @@ -0,0 +1,38 @@ +## Copyright (C) 2026 Mitchell Thompkins +## +## 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 . + +## -*- texinfo -*- +## ZPK to TF conversion via poly(). Lossy for high-order systems; only used +## for explicit conversion (e.g. tf(zpk_sys)), not for c2d matched method. + +## Author: Mitchell Thompkins +## Created: June 2026 +## Version: 0.1 + +function [retsys, retlti] = __sys2tf__ (sys) + + [z, p, k] = __sys_data__ (sys); + tsam = get (sys, "tsam"); + + num = cellfun (@(zi, ki) real (ki * poly (zi)), z, num2cell (k), ... + "uniformoutput", false); + den = cellfun (@(pi) real (poly (pi)), p, "uniformoutput", false); + + retsys = tf (num, den, tsam); + retlti = sys.lti; + +endfunction diff --git a/inst/@zpk/__sys_connect__.m b/inst/@zpk/__sys_connect__.m new file mode 100644 index 00000000..e6176bd5 --- /dev/null +++ b/inst/@zpk/__sys_connect__.m @@ -0,0 +1,27 @@ +## Copyright (C) 2026 Mitchell Thompkins +## +## 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 . + +## -*- texinfo -*- +## Connect ZPK model in a block diagram by delegating to TF. + +## Author: Mitchell Thompkins +## Created: June 2026 +## Version: 0.1 + +function sys = __sys_connect__ (sys, m) + sys = __sys_connect__ (tf (sys), m); +endfunction diff --git a/inst/@zpk/__sys_data__.m b/inst/@zpk/__sys_data__.m new file mode 100644 index 00000000..1672761a --- /dev/null +++ b/inst/@zpk/__sys_data__.m @@ -0,0 +1,31 @@ +## Copyright (C) 2026 Mitchell Thompkins +## +## 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 . + +## -*- texinfo -*- +## Used by zpkdata instead of multiple get calls. + +## Author: Mitchell Thompkins +## Created: June 2026 +## Version: 0.1 + +function [z, p, k] = __sys_data__ (sys) + + z = sys.z; + p = sys.p; + k = sys.k; + +endfunction diff --git a/inst/@zpk/__sys_group__.m b/inst/@zpk/__sys_group__.m new file mode 100644 index 00000000..537d9b33 --- /dev/null +++ b/inst/@zpk/__sys_group__.m @@ -0,0 +1,33 @@ +## Copyright (C) 2026 Mitchell Thompkins +## +## 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 . + +## -*- texinfo -*- +## Group (stack) two ZPK models by delegating to TF. + +## Author: Mitchell Thompkins +## Created: June 2026 +## Version: 0.1 + +function retsys = __sys_group__ (sys1, sys2) + if (isa (sys1, "zpk")) + sys1 = tf (sys1); + endif + if (isa (sys2, "zpk")) + sys2 = tf (sys2); + endif + retsys = __sys_group__ (sys1, sys2); +endfunction diff --git a/inst/@zpk/__sys_inverse__.m b/inst/@zpk/__sys_inverse__.m new file mode 100644 index 00000000..3c9efbf1 --- /dev/null +++ b/inst/@zpk/__sys_inverse__.m @@ -0,0 +1,27 @@ +## Copyright (C) 2026 Mitchell Thompkins +## +## 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 . + +## -*- texinfo -*- +## Invert ZPK model by delegating to TF. + +## Author: Mitchell Thompkins +## Created: June 2026 +## Version: 0.1 + +function sys = __sys_inverse__ (sys) + sys = __sys_inverse__ (tf (sys)); +endfunction diff --git a/inst/@zpk/__sys_keys__.m b/inst/@zpk/__sys_keys__.m new file mode 100644 index 00000000..36c6ea22 --- /dev/null +++ b/inst/@zpk/__sys_keys__.m @@ -0,0 +1,40 @@ +## Copyright (C) 2026 Mitchell Thompkins +## +## 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 . + +## -*- texinfo -*- +## Return the list of keys for a zpk object. + +## Author: Mitchell Thompkins +## Created: June 2026 +## Version: 0.1 + +function [keys, vals] = __sys_keys__ (sys, aliases = false) + + keys = {"z"; + "p"; + "k"}; + + vals = {"p-by-m cell array of zero vectors (m = number of inputs)"; + "p-by-m cell array of pole vectors (p = number of outputs)"; + "p-by-m real-valued gain matrix"}; + + if (aliases) + ka = {"zeros"; "poles"; "gain"}; + keys = [keys; ka]; + endif + +endfunction diff --git a/inst/@zpk/__sys_prune__.m b/inst/@zpk/__sys_prune__.m new file mode 100644 index 00000000..be48d440 --- /dev/null +++ b/inst/@zpk/__sys_prune__.m @@ -0,0 +1,33 @@ +## Copyright (C) 2026 Mitchell Thompkins +## +## 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 . + +## -*- texinfo -*- +## Select a subset of inputs/outputs from a ZPK model (lossless indexing). + +## Author: Mitchell Thompkins +## Created: June 2026 +## Version: 0.1 + +function sys = __sys_prune__ (sys, out_idx, in_idx) + + [sys.lti, out_idx, in_idx] = __lti_prune__ (sys.lti, out_idx, in_idx); + + sys.z = sys.z(out_idx, in_idx); + sys.p = sys.p(out_idx, in_idx); + sys.k = sys.k(out_idx, in_idx); + +endfunction diff --git a/inst/@zpk/__times__.m b/inst/@zpk/__times__.m new file mode 100644 index 00000000..9d8aeced --- /dev/null +++ b/inst/@zpk/__times__.m @@ -0,0 +1,33 @@ +## Copyright (C) 2026 Mitchell Thompkins +## +## 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 . + +## -*- texinfo -*- +## Multiply two ZPK models by delegating to TF. + +## Author: Mitchell Thompkins +## Created: June 2026 +## Version: 0.1 + +function sys = __times__ (sys1, sys2) + if (isa (sys1, "zpk")) + sys1 = tf (sys1); + endif + if (isa (sys2, "zpk")) + sys2 = tf (sys2); + endif + sys = __times__ (sys1, sys2); +endfunction diff --git a/inst/@zpk/__transpose__.m b/inst/@zpk/__transpose__.m new file mode 100644 index 00000000..992ee073 --- /dev/null +++ b/inst/@zpk/__transpose__.m @@ -0,0 +1,27 @@ +## Copyright (C) 2026 Mitchell Thompkins +## +## 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 . + +## -*- texinfo -*- +## Transpose ZPK model by delegating to TF. + +## Author: Mitchell Thompkins +## Created: June 2026 +## Version: 0.1 + +function sys = __transpose__ (sys) + sys = __transpose__ (tf (sys)); +endfunction diff --git a/inst/@zpk/__zero__.m b/inst/@zpk/__zero__.m new file mode 100644 index 00000000..90db1b7d --- /dev/null +++ b/inst/@zpk/__zero__.m @@ -0,0 +1,37 @@ +## Copyright (C) 2026 Mitchell Thompkins +## +## 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 . + +## -*- texinfo -*- +## Return zeros of ZPK model directly from stored data (no polynomial round-trip). + +## Author: Mitchell Thompkins +## Created: June 2026 +## Version: 0.1 + +function [zer, gain, info] = __zero__ (sys, ~) + + if (issiso (sys)) + zer = sys.z{1}; + gain = sys.k; + info = []; + else + warning ("Control:convert-to-state-space", + "zpk: zero: converting to minimal state-space for zero computation of mimo zpk\n"); + [zer, gain, info] = zero (ss (sys)); + endif + +endfunction diff --git a/inst/@zpk/display.m b/inst/@zpk/display.m new file mode 100644 index 00000000..731c127b --- /dev/null +++ b/inst/@zpk/display.m @@ -0,0 +1,77 @@ +## Copyright (C) 2026 Mitchell Thompkins +## +## 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 . + +## -*- texinfo -*- +## Display routine for ZPK objects. + +## Author: Mitchell Thompkins +## Created: June 2026 +## Version: 0.1 + +function display (sys) + + sysname = inputname (1); + [inname, outname, tsam] = __lti_data__ (sys.lti); + + [inname, m] = __labels__ (inname, "u"); + [outname, p] = __labels__ (outname, "y"); + + disp (""); + disp (["Zero/pole/gain model '", sysname, "':"]); + + for nu = 1 : m + for ny = 1 : p + z_ch = sys.z{ny, nu}; + p_ch = sys.p{ny, nu}; + k_ch = sys.k(ny, nu); + + disp (""); + disp ([outname{ny}, " <- ", inname{nu}, ":"]); + disp (["gain: ", num2str(k_ch)]); + + if (isempty (z_ch)) + disp ("zeros: (none)"); + else + disp ("zeros:"); + for i = 1 : length (z_ch) + disp ([" ", num2str(z_ch(i), 6)]); + endfor + endif + + if (isempty (p_ch)) + disp ("poles: (none)"); + else + disp ("poles:"); + for i = 1 : length (p_ch) + disp ([" ", num2str(p_ch(i), 6)]); + endfor + endif + endfor + endfor + + disp (""); + display (sys.lti); + + if (tsam == -1) + disp ("Static gain."); + elseif (tsam == 0) + disp ("Continuous-time model."); + else + disp ("Discrete-time model."); + endif + +endfunction diff --git a/inst/@zpk/isstaticgain.m b/inst/@zpk/isstaticgain.m new file mode 100644 index 00000000..137d254d --- /dev/null +++ b/inst/@zpk/isstaticgain.m @@ -0,0 +1,30 @@ +## Copyright (C) 2026 Mitchell Thompkins +## +## 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 . + +## -*- texinfo -*- +## Return true if ZPK model has no poles or zeros (is a static gain). + +## Author: Mitchell Thompkins +## Created: June 2026 +## Version: 0.1 + +function static_gain = isstaticgain (sys) + + static_gain = all (cellfun (@isempty, sys.z(:))) ... + && all (cellfun (@isempty, sys.p(:))); + +endfunction diff --git a/inst/@zpk/zpk.m b/inst/@zpk/zpk.m new file mode 100644 index 00000000..9ce4dadd --- /dev/null +++ b/inst/@zpk/zpk.m @@ -0,0 +1,190 @@ +## Copyright (C) 2009-2016 Lukas F. Reichlin +## Copyright (C) 2026 Mitchell Thompkins +## +## 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 . + +## -*- texinfo -*- +## @deftypefn {Function File} {@var{s} =} zpk (@var{'s'}) +## @deftypefnx {Function File} {@var{z} =} zpk (@var{'z'}, @var{tsam}) +## @deftypefnx {Function File} {@var{sys} =} zpk (@var{sys}) +## @deftypefnx {Function File} {@var{sys} =} zpk (@var{k}, @dots{}) +## @deftypefnx {Function File} {@var{sys} =} zpk (@var{z}, @var{p}, @var{k}, @dots{}) +## @deftypefnx {Function File} {@var{sys} =} zpk (@var{z}, @var{p}, @var{k}, @var{tsam}, @dots{}) +## Create zero-pole-gain model. +## +## @strong{Inputs} +## @table @var +## @item sys +## @acronym{LTI} model to be converted to zero-pole-gain form. +## @item z +## Cell of vectors containing the zeros for each channel. +## z@{i,j@} contains the zeros from input j to output i. +## In the SISO case, a single vector is accepted as well. +## @item p +## Cell of vectors containing the poles for each channel. +## p@{i,j@} contains the poles from input j to output i. +## In the SISO case, a single vector is accepted as well. +## @item k +## Matrix of gains. k(i,j) contains the gain from input j to output i. +## @item tsam +## Sampling time in seconds. If @var{tsam} is not specified, a +## continuous-time model is assumed. +## @end table +## @strong{Outputs} +## @table @var +## @item sys +## Zero-pole-gain model. +## @end table +## @strong{Option Keys and Values} +## @table @var +## @item 'InputName' +## String or cell array of strings specifying the names of the inputs. +## @item 'OutputName' +## String or cell array of strings specifying the names of the outputs. +## @end table +## Type @code{set (zpk)} for further options. +## @end deftypefn + +## Author: Lukas Reichlin +## Created: September 2011 +## Version: 0.2 + +function sys = zpk (varargin) + + ## model precedence: zpk > tf > double + superiorto ("tf", "double"); + + if (nargin >= 1 && ischar (varargin{1})) # zpk ('s'), zpk ('z', tsam) + ## transfer-function variable shorthand. still returns a TF model + sys = tf (varargin{:}); + return; + endif + + if (nargin == 1 && isa (varargin{1}, "zpk")) # zpk (zpksys) + sys = varargin{1}; + return; + endif + + if (nargin == 1 && isa (varargin{1}, "lti")) # zpk (ltisys) + ## conversion from tf/ss/frd; root-finding happens in zpkdata, + ## which is legitimate here because the user asked for the conversion + [z, p, k, tsam] = zpkdata (varargin{1}); + varargin = {}; + else + z = {}; p = {}; k = []; # default values + tsam = 0; # default sampling time + + [mat_idx, opt_idx] = __lti_input_idx__ (varargin); + + switch (numel (mat_idx)) + case 0 + tsam = -1; # empty model + case 1 # zpk (k) -> static gain + k = varargin{mat_idx}; + tsam = -1; + case 3 # zpk (z, p, k) + [z, p, k] = varargin{mat_idx}; + case 4 # zpk (z, p, k, tsam) + [z, p, k, tsam] = varargin{mat_idx}; + if (isempty (tsam) && is_real_matrix (tsam)) + tsam = -1; + elseif (! issample (tsam, -10)) + error ("zpk: invalid sampling time"); + endif + otherwise + print_usage (); + endswitch + + varargin = varargin(opt_idx); + + if (! iscell (z)) + z = {z}; + endif + + if (! iscell (p)) + p = {p}; + endif + + ## static gain: one empty zero/pole vector per channel of k + if (isempty (z) && isempty (p) && ! isempty (k)) + z = repmat ({[]}, size (k)); + p = repmat ({[]}, size (k)); + endif + + if (! size_equal (z, p, k)) + error ("zpk: arguments 'z', 'p' and 'k' must have equal dimensions"); + endif + + if (! is_zp_vector (z{:}, 1)) # last argument 1 needed if z is empty cell + error ("zpk: first argument 'z' must be a vector or a cell of vectors"); + endif + + if (! is_zp_vector (p{:}, 1)) + error ("zpk: second argument 'p' must be a vector or a cell of vectors"); + endif + + if (! is_real_matrix (k)) + error ("zpk: third argument 'k' must be a real-valued gain matrix"); + endif + endif + + ## ensure column vectors; store data verbatim, no polynomial conversion + z = cellfun (@(v) v(:), z, "uniformoutput", false); + p = cellfun (@(v) v(:), p, "uniformoutput", false); + + [p_out, m_in] = size (k); + + zdata = struct ("z", {z}, "p", {p}, "k", k); + ltisys = lti (p_out, m_in, tsam); + sys = class (zdata, "zpk", ltisys); + + if (numel (varargin) > 0) + sys = set (sys, varargin{:}); + endif + +endfunction + + +%!test +%! sys = zpk ([], [-1], 1); +%! assert (isa (sys, 'zpk')); +%! assert (get (sys, 'tsam'), 0); + +%!test +%! sys = zpk ([], [-0.5], 2, 0.1); +%! assert (isa (sys, 'zpk')); +%! assert (get (sys, 'tsam'), 0.1); + +%!test +%! ze = {[1]; [-2; 0]}; +%! pe = {[-1; 0]; [-4; -3; -1]}; +%! ke = [5; 10]; +%! sys = zpk (ze, pe, ke); +%! assert (isa (sys, 'zpk')); +%! assert (size (sys), [2, 1]); + +%!test +%! ## static gain +%! sys = zpk (5); +%! assert (isa (sys, 'zpk')); +%! assert (isstaticgain (sys)); + +%!test +%! ## conversion from tf keeps class +%! sys = zpk (tf (1, [1 3 2])); +%! assert (isa (sys, 'zpk')); +%! [~, p] = zpkdata (sys, 'v'); +%! assert (sort (real (p)), [-2; -1], 1e-10); diff --git a/inst/@zpk/zpkdata.m b/inst/@zpk/zpkdata.m new file mode 100644 index 00000000..1caff8f4 --- /dev/null +++ b/inst/@zpk/zpkdata.m @@ -0,0 +1,55 @@ +## Copyright (C) 2009-2016 Lukas F. Reichlin +## Copyright (C) 2026 Mitchell Thompkins +## +## 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 . + +## -*- texinfo -*- +## @deftypefn {Function File} {[@var{z}, @var{p}, @var{k}, @var{tsam}] =} zpkdata (@var{sys}) +## @deftypefnx {Function File} {[@var{z}, @var{p}, @var{k}, @var{tsam}] =} zpkdata (@var{sys}, @var{"v"}) +## Access zero-pole-gain data of a ZPK model directly, without polynomial +## conversion. +## @end deftypefn + +function [z, p, k, tsam] = zpkdata (sys, rtype = "cell") + + [z, p, k] = __sys_data__ (sys); + tsam = get (sys, "tsam"); + + if (strncmpi (rtype, "v", 1) && issiso (sys)) + z = z{1}; + p = p{1}; + endif + +endfunction + + +%!test +%! ze = {[1]; [-2; 0]}; +%! pe = {[-1; 0]; [-4; -3; -1]}; +%! ke = [5; 10]; +%! sys = zpk (ze, pe, ke); +%! [zo, po, ko] = zpkdata (sys); +%! assert (zo, ze); +%! assert (po, pe); +%! assert (ko, ke); + +%!test +%! sys = zpk ([], [-1; -2], 3); +%! [z, p, k, tsam] = zpkdata (sys, 'v'); +%! assert (isempty (z)); +%! assert (sort (p), [-2; -1]); +%! assert (k, 3); +%! assert (tsam, 0); diff --git a/inst/zpk.m b/inst/zpk.m deleted file mode 100644 index fa3a7309..00000000 --- a/inst/zpk.m +++ /dev/null @@ -1,140 +0,0 @@ -## Copyright (C) 2009-2016 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 . - -## -*- texinfo -*- -## @deftypefn {Function File} {@var{s} =} zpk (@var{'s'}) -## @deftypefnx {Function File} {@var{z} =} zpk (@var{'z'}, @var{tsam}) -## @deftypefnx {Function File} {@var{sys} =} zpk (@var{sys}) -## @deftypefnx {Function File} {@var{sys} =} zpk (@var{k}, @dots{}) -## @deftypefnx {Function File} {@var{sys} =} zpk (@var{z}, @var{p}, @var{k}, @dots{}) -## @deftypefnx {Function File} {@var{sys} =} zpk (@var{z}, @var{p}, @var{k}, @var{tsam}, @dots{}) -## @deftypefnx {Function File} {@var{sys} =} zpk (@var{z}, @var{p}, @var{k}, @var{tsam}, @dots{}) -## Create transfer function model from zero-pole-gain data. -## This is just a stop-gap compatibility wrapper since zpk -## models are not yet implemented. -## -## @strong{Inputs} -## @table @var -## @item sys -## @acronym{LTI} model to be converted to transfer function. -## @item z -## Cell of vectors containing the zeros for each channel. -## z@{i,j@} contains the zeros from input j to output i. -## In the SISO case, a single vector is accepted as well. -## @item p -## Cell of vectors containing the poles for each channel. -## p@{i,j@} contains the poles from input j to output i. -## In the SISO case, a single vector is accepted as well. -## @item k -## Matrix containing the gains for each channel. -## k(i,j) contains the gain from input j to output i. -## @item tsam -## Sampling time in seconds. If @var{tsam} is not specified, -## a continuous-time model is assumed. -## @item @dots{} -## Optional pairs of properties and values. -## Type @command{set (tf)} for more information. -## @end table -## -## @strong{Outputs} -## @table @var -## @item sys -## Transfer function model. -## @end table -## -## @seealso{@@tf/tf, @@ss/ss, dss, @@frd/frd} -## @end deftypefn - -## Author: Lukas Reichlin -## Created: September 2011 -## Version: 0.2 - -function sys = zpk (varargin) - - if (nargin <= 1) # zpk (), zpk (sys), zpk (k), zpk ('s') - sys = tf (varargin{:}); - return; - elseif (nargin == 2 ... - && ischar (varargin{1})) # zpk ('z', tsam) - sys = tf (varargin{:}); - return; - endif - - z = {}; p = {}; k = []; # default values - tsam = 0; # default sampling time - - [mat_idx, opt_idx] = __lti_input_idx__ (varargin); - - switch (numel (mat_idx)) - case 1 - k = varargin{mat_idx}; - case 3 - [z, p, k] = varargin{mat_idx}; - case 4 - [z, p, k, tsam] = varargin{mat_idx}; - if (isempty (tsam) && is_real_matrix (tsam)) - tsam = -1; - elseif (! issample (tsam, -10)) - error ("zpk: invalid sampling time"); - endif - case 0 - ## nothing to do here, just prevent case 'otherwise' - otherwise - print_usage (); - endswitch - - varargin = varargin(opt_idx); - - if (isempty (z) && isempty (p) && is_real_matrix (k)) - sys = tf (k, varargin{:}); - return; - endif - - if (! iscell (z)) - z = {z}; - endif - - if (! iscell (p)) - p = {p}; - endif - - if (! size_equal (z, p, k)) - error ("zpk: arguments 'z', 'p' and 'k' must have equal dimensions"); - endif - - ## NOTE: accept [], scalars and vectors but not matrices as 'z' and 'p' - ## because poly (matrix) returns the characteristic polynomial - ## if the matrix is square! - - if (! is_zp_vector (z{:}, 1)) # last argument 1 needed if z is empty cell - error ("zpk: first argument 'z' must be a vector or a cell of vectors"); - endif - - if (! is_zp_vector (p{:}, 1)) - error ("zpk: second argument 'p' must be a vector or a cell of vectors") - endif - - if (! is_real_matrix (k)) - error ("zpk: third argument 'k' must be a real-valued gain matrix"); - endif - - num = cellfun (@(zer, gain) real (gain * poly (zer)), z, num2cell (k), "uniformoutput", false); - den = cellfun (@(pol) real (poly (pol)), p, "uniformoutput", false); - - sys = tf (num, den, tsam, varargin{:}); - -endfunction From be7cdd5da284765cedebf01b519c41f717cb6ca0 Mon Sep 17 00:00:00 2001 From: Mitchell Thompkins Date: Sat, 4 Jul 2026 23:56:29 +0000 Subject: [PATCH 3/5] Apply same fix as https://github.com/gnu-octave/pkg-control/pull/29 to d2c paths --- inst/@tf/__d2c__.m | 4 ++-- inst/@zpk/__d2c__.m | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/inst/@tf/__d2c__.m b/inst/@tf/__d2c__.m index 7b1d6d48..f47e66d9 100644 --- a/inst/@tf/__d2c__.m +++ b/inst/@tf/__d2c__.m @@ -48,9 +48,9 @@ tol = sqrt (eps); while (any (abs ([p_d; z_d_orig] - w_d) < tol)) w_c += 0.1 / tsam; + w_d = exp (1j * w_c * tsam); endwhile - w_d = exp (w_c * tsam); - k_c = real (k_d * prod (w_d - z_d_orig) / prod (w_d - p_d) * prod (w_c - p_c) / prod (w_c - z_c)); + k_c = real (k_d * prod (w_d - z_d_orig) / prod (w_d - p_d) * prod (1j*w_c - p_c) / prod (1j*w_c - z_c)); tmp = tf (zpk (z_c, p_c, k_c)); sys.num = tmp.num; diff --git a/inst/@zpk/__d2c__.m b/inst/@zpk/__d2c__.m index 6e01ad78..6f8528a9 100644 --- a/inst/@zpk/__d2c__.m +++ b/inst/@zpk/__d2c__.m @@ -49,9 +49,9 @@ tol = sqrt (eps); while (any (abs ([p_d; z_d_orig] - w_d) < tol)) w_c += 0.1 / tsam; - w_d = exp (w_c * tsam); + w_d = exp (1j * w_c * tsam); endwhile - k_c = real (k_d * prod (w_d - z_d_orig) / prod (w_d - p_d) * prod (w_c - p_c) / prod (w_c - z_c)); + k_c = real (k_d * prod (w_d - z_d_orig) / prod (w_d - p_d) * prod (1j*w_c - p_c) / prod (1j*w_c - z_c)); sys.z{1} = z_c; sys.p{1} = p_c; From 6c1c05e8f984533b2f42c0a4eb732a25a3daa4b2 Mon Sep 17 00:00:00 2001 From: Mitchell Thompkins Date: Wed, 8 Jul 2026 05:30:05 +0000 Subject: [PATCH 4/5] Modify GNU lic and copyright --- inst/@tf/__c2d__.m | 1 - inst/@tf/__d2c__.m | 1 - inst/@zpk/__c2d__.m | 17 +++++++++-------- inst/@zpk/__ctranspose__.m | 17 +++++++++-------- inst/@zpk/__d2c__.m | 17 +++++++++-------- inst/@zpk/__freqresp__.m | 17 +++++++++-------- inst/@zpk/__get__.m | 17 +++++++++-------- inst/@zpk/__minreal__.m | 17 +++++++++-------- inst/@zpk/__pole__.m | 17 +++++++++-------- inst/@zpk/__set__.m | 17 +++++++++-------- inst/@zpk/__sys2frd__.m | 17 +++++++++-------- inst/@zpk/__sys2ss__.m | 17 +++++++++-------- inst/@zpk/__sys2tf__.m | 17 +++++++++-------- inst/@zpk/__sys_connect__.m | 17 +++++++++-------- inst/@zpk/__sys_data__.m | 17 +++++++++-------- inst/@zpk/__sys_group__.m | 17 +++++++++-------- inst/@zpk/__sys_inverse__.m | 17 +++++++++-------- inst/@zpk/__sys_keys__.m | 17 +++++++++-------- inst/@zpk/__sys_prune__.m | 17 +++++++++-------- inst/@zpk/__times__.m | 17 +++++++++-------- inst/@zpk/__transpose__.m | 17 +++++++++-------- inst/@zpk/__zero__.m | 17 +++++++++-------- inst/@zpk/display.m | 17 +++++++++-------- inst/@zpk/isstaticgain.m | 17 +++++++++-------- inst/@zpk/zpk.m | 1 - inst/@zpk/zpkdata.m | 1 - 26 files changed, 198 insertions(+), 180 deletions(-) diff --git a/inst/@tf/__c2d__.m b/inst/@tf/__c2d__.m index f5b4b484..11d31bb6 100644 --- a/inst/@tf/__c2d__.m +++ b/inst/@tf/__c2d__.m @@ -1,5 +1,4 @@ ## Copyright (C) 2009-2016 Lukas F. Reichlin -## Copyright (C) 2026 Mitchell Thompkins ## ## This file is part of LTI Syncope. ## diff --git a/inst/@tf/__d2c__.m b/inst/@tf/__d2c__.m index f47e66d9..274dbf41 100644 --- a/inst/@tf/__d2c__.m +++ b/inst/@tf/__d2c__.m @@ -1,5 +1,4 @@ ## Copyright (C) 2009-2016 Lukas F. Reichlin -## Copyright (C) 2026 Mitchell Thompkins ## ## This file is part of LTI Syncope. ## diff --git a/inst/@zpk/__c2d__.m b/inst/@zpk/__c2d__.m index 40a35566..7cac6856 100644 --- a/inst/@zpk/__c2d__.m +++ b/inst/@zpk/__c2d__.m @@ -1,19 +1,20 @@ -## Copyright (C) 2026 Mitchell Thompkins +## Copyright (C) 2026 Mitchell Thompkins ## -## This file is part of LTI Syncope. +## This file is part of the control package for GNU Octave. ## -## 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 +## Octave 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 +## Octave 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 . +## along with Octave; see the file COPYING. If not, +## see . ## -*- texinfo -*- ## Convert the continuous ZPK model into its discrete-time equivalent. diff --git a/inst/@zpk/__ctranspose__.m b/inst/@zpk/__ctranspose__.m index f28f8a98..d39732b6 100644 --- a/inst/@zpk/__ctranspose__.m +++ b/inst/@zpk/__ctranspose__.m @@ -1,19 +1,20 @@ -## Copyright (C) 2026 Mitchell Thompkins +## Copyright (C) 2026 Mitchell Thompkins ## -## This file is part of LTI Syncope. +## This file is part of the control package for GNU Octave. ## -## 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 +## Octave 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 +## Octave 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 . +## along with Octave; see the file COPYING. If not, +## see . ## -*- texinfo -*- ## Complex-conjugate transpose ZPK model by delegating to TF. diff --git a/inst/@zpk/__d2c__.m b/inst/@zpk/__d2c__.m index 6f8528a9..aa2bbabf 100644 --- a/inst/@zpk/__d2c__.m +++ b/inst/@zpk/__d2c__.m @@ -1,19 +1,20 @@ -## Copyright (C) 2026 Mitchell Thompkins +## Copyright (C) 2026 Mitchell Thompkins ## -## This file is part of LTI Syncope. +## This file is part of the control package for GNU Octave. ## -## 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 +## Octave 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 +## Octave 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 . +## along with Octave; see the file COPYING. If not, +## see . ## -*- texinfo -*- ## Convert the discrete ZPK model into its continuous-time equivalent. diff --git a/inst/@zpk/__freqresp__.m b/inst/@zpk/__freqresp__.m index 0bd4b9b6..714e93fe 100644 --- a/inst/@zpk/__freqresp__.m +++ b/inst/@zpk/__freqresp__.m @@ -1,19 +1,20 @@ -## Copyright (C) 2026 Mitchell Thompkins +## Copyright (C) 2026 Mitchell Thompkins ## -## This file is part of LTI Syncope. +## This file is part of the control package for GNU Octave. ## -## 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 +## Octave 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 +## Octave 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 . +## along with Octave; see the file COPYING. If not, +## see . ## -*- texinfo -*- ## Compute frequency response of ZPK model by delegating to TF. diff --git a/inst/@zpk/__get__.m b/inst/@zpk/__get__.m index 0560c8ad..1af63fb2 100644 --- a/inst/@zpk/__get__.m +++ b/inst/@zpk/__get__.m @@ -1,19 +1,20 @@ -## Copyright (C) 2026 Mitchell Thompkins +## Copyright (C) 2026 Mitchell Thompkins ## -## This file is part of LTI Syncope. +## This file is part of the control package for GNU Octave. ## -## 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 +## Octave 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 +## Octave 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 . +## along with Octave; see the file COPYING. If not, +## see . ## -*- texinfo -*- ## Access key values of ZPK objects. diff --git a/inst/@zpk/__minreal__.m b/inst/@zpk/__minreal__.m index 21137a94..56d8555a 100644 --- a/inst/@zpk/__minreal__.m +++ b/inst/@zpk/__minreal__.m @@ -1,19 +1,20 @@ -## Copyright (C) 2026 Mitchell Thompkins +## Copyright (C) 2026 Mitchell Thompkins ## -## This file is part of LTI Syncope. +## This file is part of the control package for GNU Octave. ## -## 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 +## Octave 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 +## Octave 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 . +## along with Octave; see the file COPYING. If not, +## see . ## -*- texinfo -*- ## Return minimal realization of ZPK model by delegating to TF. diff --git a/inst/@zpk/__pole__.m b/inst/@zpk/__pole__.m index 8cec4645..5850b44c 100644 --- a/inst/@zpk/__pole__.m +++ b/inst/@zpk/__pole__.m @@ -1,19 +1,20 @@ -## Copyright (C) 2026 Mitchell Thompkins +## Copyright (C) 2026 Mitchell Thompkins ## -## This file is part of LTI Syncope. +## This file is part of the control package for GNU Octave. ## -## 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 +## Octave 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 +## Octave 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 . +## along with Octave; see the file COPYING. If not, +## see . ## -*- texinfo -*- ## Return poles of ZPK model directly from stored data (no polynomial round-trip). diff --git a/inst/@zpk/__set__.m b/inst/@zpk/__set__.m index ff5fceb8..ced75212 100644 --- a/inst/@zpk/__set__.m +++ b/inst/@zpk/__set__.m @@ -1,19 +1,20 @@ -## Copyright (C) 2026 Mitchell Thompkins +## Copyright (C) 2026 Mitchell Thompkins ## -## This file is part of LTI Syncope. +## This file is part of the control package for GNU Octave. ## -## 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 +## Octave 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 +## Octave 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 . +## along with Octave; see the file COPYING. If not, +## see . ## -*- texinfo -*- ## Set or modify keys of ZPK objects. diff --git a/inst/@zpk/__sys2frd__.m b/inst/@zpk/__sys2frd__.m index 54b152be..ae593302 100644 --- a/inst/@zpk/__sys2frd__.m +++ b/inst/@zpk/__sys2frd__.m @@ -1,19 +1,20 @@ -## Copyright (C) 2026 Mitchell Thompkins +## Copyright (C) 2026 Mitchell Thompkins ## -## This file is part of LTI Syncope. +## This file is part of the control package for GNU Octave. ## -## 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 +## Octave 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 +## Octave 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 . +## along with Octave; see the file COPYING. If not, +## see . ## -*- texinfo -*- ## Convert ZPK model to frequency-response data representation. diff --git a/inst/@zpk/__sys2ss__.m b/inst/@zpk/__sys2ss__.m index bb78033b..265e5020 100644 --- a/inst/@zpk/__sys2ss__.m +++ b/inst/@zpk/__sys2ss__.m @@ -1,19 +1,20 @@ -## Copyright (C) 2026 Mitchell Thompkins +## Copyright (C) 2026 Mitchell Thompkins ## -## This file is part of LTI Syncope. +## This file is part of the control package for GNU Octave. ## -## 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 +## Octave 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 +## Octave 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 . +## along with Octave; see the file COPYING. If not, +## see . ## -*- texinfo -*- ## Convert ZPK model to state-space representation. diff --git a/inst/@zpk/__sys2tf__.m b/inst/@zpk/__sys2tf__.m index bfa813f9..3d342d95 100644 --- a/inst/@zpk/__sys2tf__.m +++ b/inst/@zpk/__sys2tf__.m @@ -1,19 +1,20 @@ -## Copyright (C) 2026 Mitchell Thompkins +## Copyright (C) 2026 Mitchell Thompkins ## -## This file is part of LTI Syncope. +## This file is part of the control package for GNU Octave. ## -## 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 +## Octave 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 +## Octave 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 . +## along with Octave; see the file COPYING. If not, +## see . ## -*- texinfo -*- ## ZPK to TF conversion via poly(). Lossy for high-order systems; only used diff --git a/inst/@zpk/__sys_connect__.m b/inst/@zpk/__sys_connect__.m index e6176bd5..6f771ebe 100644 --- a/inst/@zpk/__sys_connect__.m +++ b/inst/@zpk/__sys_connect__.m @@ -1,19 +1,20 @@ -## Copyright (C) 2026 Mitchell Thompkins +## Copyright (C) 2026 Mitchell Thompkins ## -## This file is part of LTI Syncope. +## This file is part of the control package for GNU Octave. ## -## 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 +## Octave 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 +## Octave 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 . +## along with Octave; see the file COPYING. If not, +## see . ## -*- texinfo -*- ## Connect ZPK model in a block diagram by delegating to TF. diff --git a/inst/@zpk/__sys_data__.m b/inst/@zpk/__sys_data__.m index 1672761a..206599a9 100644 --- a/inst/@zpk/__sys_data__.m +++ b/inst/@zpk/__sys_data__.m @@ -1,19 +1,20 @@ -## Copyright (C) 2026 Mitchell Thompkins +## Copyright (C) 2026 Mitchell Thompkins ## -## This file is part of LTI Syncope. +## This file is part of the control package for GNU Octave. ## -## 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 +## Octave 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 +## Octave 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 . +## along with Octave; see the file COPYING. If not, +## see . ## -*- texinfo -*- ## Used by zpkdata instead of multiple get calls. diff --git a/inst/@zpk/__sys_group__.m b/inst/@zpk/__sys_group__.m index 537d9b33..73f69aca 100644 --- a/inst/@zpk/__sys_group__.m +++ b/inst/@zpk/__sys_group__.m @@ -1,19 +1,20 @@ -## Copyright (C) 2026 Mitchell Thompkins +## Copyright (C) 2026 Mitchell Thompkins ## -## This file is part of LTI Syncope. +## This file is part of the control package for GNU Octave. ## -## 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 +## Octave 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 +## Octave 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 . +## along with Octave; see the file COPYING. If not, +## see . ## -*- texinfo -*- ## Group (stack) two ZPK models by delegating to TF. diff --git a/inst/@zpk/__sys_inverse__.m b/inst/@zpk/__sys_inverse__.m index 3c9efbf1..8d29e1a2 100644 --- a/inst/@zpk/__sys_inverse__.m +++ b/inst/@zpk/__sys_inverse__.m @@ -1,19 +1,20 @@ -## Copyright (C) 2026 Mitchell Thompkins +## Copyright (C) 2026 Mitchell Thompkins ## -## This file is part of LTI Syncope. +## This file is part of the control package for GNU Octave. ## -## 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 +## Octave 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 +## Octave 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 . +## along with Octave; see the file COPYING. If not, +## see . ## -*- texinfo -*- ## Invert ZPK model by delegating to TF. diff --git a/inst/@zpk/__sys_keys__.m b/inst/@zpk/__sys_keys__.m index 36c6ea22..c9717f39 100644 --- a/inst/@zpk/__sys_keys__.m +++ b/inst/@zpk/__sys_keys__.m @@ -1,19 +1,20 @@ -## Copyright (C) 2026 Mitchell Thompkins +## Copyright (C) 2026 Mitchell Thompkins ## -## This file is part of LTI Syncope. +## This file is part of the control package for GNU Octave. ## -## 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 +## Octave 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 +## Octave 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 . +## along with Octave; see the file COPYING. If not, +## see . ## -*- texinfo -*- ## Return the list of keys for a zpk object. diff --git a/inst/@zpk/__sys_prune__.m b/inst/@zpk/__sys_prune__.m index be48d440..db6c33e1 100644 --- a/inst/@zpk/__sys_prune__.m +++ b/inst/@zpk/__sys_prune__.m @@ -1,19 +1,20 @@ -## Copyright (C) 2026 Mitchell Thompkins +## Copyright (C) 2026 Mitchell Thompkins ## -## This file is part of LTI Syncope. +## This file is part of the control package for GNU Octave. ## -## 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 +## Octave 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 +## Octave 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 . +## along with Octave; see the file COPYING. If not, +## see . ## -*- texinfo -*- ## Select a subset of inputs/outputs from a ZPK model (lossless indexing). diff --git a/inst/@zpk/__times__.m b/inst/@zpk/__times__.m index 9d8aeced..63915360 100644 --- a/inst/@zpk/__times__.m +++ b/inst/@zpk/__times__.m @@ -1,19 +1,20 @@ -## Copyright (C) 2026 Mitchell Thompkins +## Copyright (C) 2026 Mitchell Thompkins ## -## This file is part of LTI Syncope. +## This file is part of the control package for GNU Octave. ## -## 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 +## Octave 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 +## Octave 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 . +## along with Octave; see the file COPYING. If not, +## see . ## -*- texinfo -*- ## Multiply two ZPK models by delegating to TF. diff --git a/inst/@zpk/__transpose__.m b/inst/@zpk/__transpose__.m index 992ee073..b1df05a7 100644 --- a/inst/@zpk/__transpose__.m +++ b/inst/@zpk/__transpose__.m @@ -1,19 +1,20 @@ -## Copyright (C) 2026 Mitchell Thompkins +## Copyright (C) 2026 Mitchell Thompkins ## -## This file is part of LTI Syncope. +## This file is part of the control package for GNU Octave. ## -## 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 +## Octave 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 +## Octave 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 . +## along with Octave; see the file COPYING. If not, +## see . ## -*- texinfo -*- ## Transpose ZPK model by delegating to TF. diff --git a/inst/@zpk/__zero__.m b/inst/@zpk/__zero__.m index 90db1b7d..f126dda0 100644 --- a/inst/@zpk/__zero__.m +++ b/inst/@zpk/__zero__.m @@ -1,19 +1,20 @@ -## Copyright (C) 2026 Mitchell Thompkins +## Copyright (C) 2026 Mitchell Thompkins ## -## This file is part of LTI Syncope. +## This file is part of the control package for GNU Octave. ## -## 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 +## Octave 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 +## Octave 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 . +## along with Octave; see the file COPYING. If not, +## see . ## -*- texinfo -*- ## Return zeros of ZPK model directly from stored data (no polynomial round-trip). diff --git a/inst/@zpk/display.m b/inst/@zpk/display.m index 731c127b..3d1b559a 100644 --- a/inst/@zpk/display.m +++ b/inst/@zpk/display.m @@ -1,19 +1,20 @@ -## Copyright (C) 2026 Mitchell Thompkins +## Copyright (C) 2026 Mitchell Thompkins ## -## This file is part of LTI Syncope. +## This file is part of the control package for GNU Octave. ## -## 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 +## Octave 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 +## Octave 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 . +## along with Octave; see the file COPYING. If not, +## see . ## -*- texinfo -*- ## Display routine for ZPK objects. diff --git a/inst/@zpk/isstaticgain.m b/inst/@zpk/isstaticgain.m index 137d254d..32b04dc2 100644 --- a/inst/@zpk/isstaticgain.m +++ b/inst/@zpk/isstaticgain.m @@ -1,19 +1,20 @@ -## Copyright (C) 2026 Mitchell Thompkins +## Copyright (C) 2026 Mitchell Thompkins ## -## This file is part of LTI Syncope. +## This file is part of the control package for GNU Octave. ## -## 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 +## Octave 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 +## Octave 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 . +## along with Octave; see the file COPYING. If not, +## see . ## -*- texinfo -*- ## Return true if ZPK model has no poles or zeros (is a static gain). diff --git a/inst/@zpk/zpk.m b/inst/@zpk/zpk.m index 9ce4dadd..421c6226 100644 --- a/inst/@zpk/zpk.m +++ b/inst/@zpk/zpk.m @@ -1,5 +1,4 @@ ## Copyright (C) 2009-2016 Lukas F. Reichlin -## Copyright (C) 2026 Mitchell Thompkins ## ## This file is part of LTI Syncope. ## diff --git a/inst/@zpk/zpkdata.m b/inst/@zpk/zpkdata.m index 1caff8f4..9e85f18c 100644 --- a/inst/@zpk/zpkdata.m +++ b/inst/@zpk/zpkdata.m @@ -1,5 +1,4 @@ ## Copyright (C) 2009-2016 Lukas F. Reichlin -## Copyright (C) 2026 Mitchell Thompkins ## ## This file is part of LTI Syncope. ## From 9c227d15896471156edce6ee1170ce984f79883d Mon Sep 17 00:00:00 2001 From: Mitchell Thompkins Date: Sat, 11 Jul 2026 22:41:36 +0000 Subject: [PATCH 5/5] Add tests for zpk functions that combine systems --- inst/@zpk/__ctranspose__.m | 6 ++-- inst/@zpk/__minreal__.m | 6 ++-- inst/@zpk/__sys_connect__.m | 8 ++--- inst/@zpk/__sys_group__.m | 28 +++++++++++----- inst/@zpk/__sys_inverse__.m | 6 ++-- inst/@zpk/__times__.m | 23 ++++++++----- inst/@zpk/__transpose__.m | 6 ++-- inst/@zpk/zpk.m | 65 +++++++++++++++++++++++++++++++++++++ 8 files changed, 116 insertions(+), 32 deletions(-) diff --git a/inst/@zpk/__ctranspose__.m b/inst/@zpk/__ctranspose__.m index d39732b6..0eddb90c 100644 --- a/inst/@zpk/__ctranspose__.m +++ b/inst/@zpk/__ctranspose__.m @@ -20,9 +20,9 @@ ## Complex-conjugate transpose ZPK model by delegating to TF. ## Author: Mitchell Thompkins -## Created: June 2026 -## Version: 0.1 +## Created: July 2026 +## Version: 0.2 function sys = __ctranspose__ (sys, ct) - sys = __ctranspose__ (tf (sys), ct); + sys = zpk (__ctranspose__ (tf (sys), ct)); endfunction diff --git a/inst/@zpk/__minreal__.m b/inst/@zpk/__minreal__.m index 56d8555a..00df745c 100644 --- a/inst/@zpk/__minreal__.m +++ b/inst/@zpk/__minreal__.m @@ -20,9 +20,9 @@ ## Return minimal realization of ZPK model by delegating to TF. ## Author: Mitchell Thompkins -## Created: June 2026 -## Version: 0.1 +## Created: July 2026 +## Version: 0.2 function sys = __minreal__ (sys, tol) - sys = __minreal__ (tf (sys), tol); + sys = zpk (__minreal__ (tf (sys), tol)); endfunction diff --git a/inst/@zpk/__sys_connect__.m b/inst/@zpk/__sys_connect__.m index 6f771ebe..2765e70f 100644 --- a/inst/@zpk/__sys_connect__.m +++ b/inst/@zpk/__sys_connect__.m @@ -20,9 +20,9 @@ ## Connect ZPK model in a block diagram by delegating to TF. ## Author: Mitchell Thompkins -## Created: June 2026 -## Version: 0.1 +## Created: July 2026 +## Version: 0.2 -function sys = __sys_connect__ (sys, m) - sys = __sys_connect__ (tf (sys), m); +function sys = __sys_connect__ (sys, M) + sys = zpk (__sys_connect__ (tf (sys), M)); endfunction diff --git a/inst/@zpk/__sys_group__.m b/inst/@zpk/__sys_group__.m index 73f69aca..fb2725e5 100644 --- a/inst/@zpk/__sys_group__.m +++ b/inst/@zpk/__sys_group__.m @@ -17,18 +17,30 @@ ## see . ## -*- texinfo -*- -## Group (stack) two ZPK models by delegating to TF. +## Block diagonal concatenation of two ZPK models. ## Author: Mitchell Thompkins -## Created: June 2026 -## Version: 0.1 +## Created: July 2026 +## Version: 0.2 function retsys = __sys_group__ (sys1, sys2) - if (isa (sys1, "zpk")) - sys1 = tf (sys1); + [sys1, sys2] = __numeric_to_lti__ (sys1, sys2); + + if (! isa (sys1, "zpk")) + sys1 = zpk (sys1); endif - if (isa (sys2, "zpk")) - sys2 = tf (sys2); + if (! isa (sys2, "zpk")) + sys2 = zpk (sys2); endif - retsys = __sys_group__ (sys1, sys2); + + [p1, m1] = size (sys1); + [p2, m2] = size (sys2); + + z = [sys1.z, cell(p1, m2); cell(p2, m1), sys2.z]; + p = [sys1.p, cell(p1, m2); cell(p2, m1), sys2.p]; + k = [sys1.k, zeros(p1, m2); zeros(p2, m1), sys2.k]; + + ltisys = __lti_group__ (sys1.lti, sys2.lti); + + retsys = class (struct ("z", {z}, "p", {p}, "k", k), "zpk", ltisys); endfunction diff --git a/inst/@zpk/__sys_inverse__.m b/inst/@zpk/__sys_inverse__.m index 8d29e1a2..9e4c1dd0 100644 --- a/inst/@zpk/__sys_inverse__.m +++ b/inst/@zpk/__sys_inverse__.m @@ -20,9 +20,9 @@ ## Invert ZPK model by delegating to TF. ## Author: Mitchell Thompkins -## Created: June 2026 -## Version: 0.1 +## Created: July 2026 +## Version: 0.2 function sys = __sys_inverse__ (sys) - sys = __sys_inverse__ (tf (sys)); + sys = zpk (__sys_inverse__ (tf (sys))); endfunction diff --git a/inst/@zpk/__times__.m b/inst/@zpk/__times__.m index 63915360..5ff6610e 100644 --- a/inst/@zpk/__times__.m +++ b/inst/@zpk/__times__.m @@ -17,18 +17,25 @@ ## see . ## -*- texinfo -*- -## Multiply two ZPK models by delegating to TF. +## Multiply two ZPK models element-wise. ## Author: Mitchell Thompkins -## Created: June 2026 -## Version: 0.1 +## Created: July 2026 +## Version: 0.2 function sys = __times__ (sys1, sys2) - if (isa (sys1, "zpk")) - sys1 = tf (sys1); + if (! isa (sys1, "zpk")) + sys1 = zpk (sys1); endif - if (isa (sys2, "zpk")) - sys2 = tf (sys2); + if (! isa (sys2, "zpk")) + sys2 = zpk (sys2); endif - sys = __times__ (sys1, sys2); + + z = cellfun (@(a, b) [a; b], sys1.z, sys2.z, "uniformoutput", false); + p = cellfun (@(a, b) [a; b], sys1.p, sys2.p, "uniformoutput", false); + k = sys1.k .* sys2.k; + + ltisys = __lti_group__ (sys1.lti, sys2.lti, "times"); + + sys = class (struct ("z", {z}, "p", {p}, "k", k), "zpk", ltisys); endfunction diff --git a/inst/@zpk/__transpose__.m b/inst/@zpk/__transpose__.m index b1df05a7..7dabb17b 100644 --- a/inst/@zpk/__transpose__.m +++ b/inst/@zpk/__transpose__.m @@ -20,9 +20,9 @@ ## Transpose ZPK model by delegating to TF. ## Author: Mitchell Thompkins -## Created: June 2026 -## Version: 0.1 +## Created: July 2026 +## Version: 0.2 function sys = __transpose__ (sys) - sys = __transpose__ (tf (sys)); + sys = zpk (__transpose__ (tf (sys))); endfunction diff --git a/inst/@zpk/zpk.m b/inst/@zpk/zpk.m index 421c6226..9dfcf440 100644 --- a/inst/@zpk/zpk.m +++ b/inst/@zpk/zpk.m @@ -187,3 +187,68 @@ %! assert (isa (sys, 'zpk')); %! [~, p] = zpkdata (sys, 'v'); %! assert (sort (real (p)), [-2; -1], 1e-10); + +%!test +%! ## series of two zpk systems stays zpk with correct z, p, k +%! sys1 = zpk ([-1], [-2, -3], 5); +%! sys2 = zpk ([-4], [-5], 2); +%! sys = series (sys1, sys2); +%! assert (isa (sys, 'zpk')); +%! [z, p, k] = zpkdata (sys, 'v'); +%! assert (sort (real (z)), [-4; -1], 1e-10); +%! assert (sort (real (p)), [-5; -3; -2], 1e-10); +%! assert (k, 10, 1e-10); + +%!test +%! ## feedback of two zpk systems stays zpk +%! sys1 = zpk ([-1], [-2, -3], 5); +%! sys2 = zpk ([-4], [-5], 2); +%! sys = feedback (sys1, sys2); +%! assert (isa (sys, 'zpk')); +%! [z, p, k] = zpkdata (sys, 'v'); +%! sys_tf = feedback (tf (sys1), tf (sys2)); +%! [z_ref, p_ref, k_ref] = zpkdata (zpk (sys_tf), 'v'); +%! assert (sort (real (z)), sort (real (z_ref)), 1e-6); +%! assert (sort (real (p)), sort (real (p_ref)), 1e-6); +%! assert (k, k_ref, 1e-6); + +%!test +%! ## parallel of two zpk systems stays zpk +%! sys1 = zpk ([-1], [-2], 3); +%! sys2 = zpk ([-3], [-4], 2); +%! sys = sys1 + sys2; +%! assert (isa (sys, 'zpk')); + +%!test +%! ## transpose of a zpk system stays zpk +%! sys = zpk ({[-1]; [-2]}, {[-3]; [-4]}, [5; 6]); +%! sys_t = sys.'; +%! assert (isa (sys_t, 'zpk')); +%! assert (size (sys_t), [1, 2]); + +%!test +%! ## ctranspose of a zpk system stays zpk +%! sys = zpk ({[-1]; [-2]}, {[-3]; [-4]}, [5; 6]); +%! sys_ct = sys'; +%! assert (isa (sys_ct, 'zpk')); +%! assert (size (sys_ct), [1, 2]); + +%!test +%! ## minreal of a zpk system cancels the common pole/zero and stays zpk +%! sys = zpk ([-1], [-1, -2], 3); +%! sys_mr = minreal (sys); +%! assert (isa (sys_mr, 'zpk')); +%! [z, p, k] = zpkdata (sys_mr, 'v'); +%! assert (isempty (z)); +%! assert (p, -2, 1e-6); +%! assert (k, 3, 1e-6); + +%!test +%! ## inverse of a zpk system stays zpk +%! sys = zpk ([-1], [-2], 3); +%! sys_inv = inv (sys); +%! assert (isa (sys_inv, 'zpk')); +%! [z, p, k] = zpkdata (sys_inv, 'v'); +%! assert (z, -2, 1e-6); +%! assert (p, -1, 1e-6); +%! assert (k, 1/3, 1e-6);