diff --git a/control/statesp.py b/control/statesp.py index fe2dc6643..13e662f03 100644 --- a/control/statesp.py +++ b/control/statesp.py @@ -10,6 +10,7 @@ # Python 3 compatibility (needs to go here) from __future__ import print_function +from __future__ import division # for _convertToStateSpace """Copyright (c) 2010 by California Institute of Technology All rights reserved. @@ -647,6 +648,7 @@ def _convertToStateSpace(sys, **kw): """ from .xferfcn import TransferFunction + import itertools if isinstance(sys, StateSpace): if len(kw): raise TypeError("If sys is a StateSpace, _convertToStateSpace \ @@ -679,16 +681,27 @@ def _convertToStateSpace(sys, **kw): ssout[3][:sys.outputs, :states], ssout[4], sys.dt) except ImportError: - # If slycot is not available, use signal.lti (SISO only) - if (sys.inputs != 1 or sys.outputs != 1): - raise TypeError("No support for MIMO without slycot") - - # TODO: do we want to squeeze first and check dimenations? - # I think this will fail if num and den aren't 1-D after - # the squeeze - lti_sys = lti(squeeze(sys.num), squeeze(sys.den)) - return StateSpace(lti_sys.A, lti_sys.B, lti_sys.C, lti_sys.D, - sys.dt) + # No Slycot. Scipy tf->ss can't handle MIMO, but static + # MIMO is an easy special case we can check for here + maxn = max(max(len(n) for n in nrow) + for nrow in sys.num) + maxd = max(max(len(d) for d in drow) + for drow in sys.den) + if 1==maxn and 1==maxd: + D = empty((sys.outputs,sys.inputs),dtype=float) + for i,j in itertools.product(range(sys.outputs),range(sys.inputs)): + D[i,j] = sys.num[i][j][0] / sys.den[i][j][0] + return StateSpace([], [], [], D, sys.dt) + else: + if (sys.inputs != 1 or sys.outputs != 1): + raise TypeError("No support for MIMO without slycot") + + # TODO: do we want to squeeze first and check dimenations? + # I think this will fail if num and den aren't 1-D after + # the squeeze + lti_sys = lti(squeeze(sys.num), squeeze(sys.den)) + return StateSpace(lti_sys.A, lti_sys.B, lti_sys.C, lti_sys.D, + sys.dt) elif isinstance(sys, (int, float, complex)): if "inputs" in kw: diff --git a/control/tests/convert_test.py b/control/tests/convert_test.py index 775118909..b395996e0 100644 --- a/control/tests/convert_test.py +++ b/control/tests/convert_test.py @@ -184,6 +184,28 @@ def testConvertMIMO(self): if (not slycot_check()): self.assertRaises(TypeError, control.tf2ss, tfcn) + def testTf2ssStaticSiso(self): + """Regression: tf2ss for SISO static gain""" + import control + gsiso = control.tf2ss(control.tf(23, 46)) + self.assertEqual(0, gsiso.states) + self.assertEqual(1, gsiso.inputs) + self.assertEqual(1, gsiso.outputs) + # in all cases ratios are exactly representable, so assert_array_equal is fine + np.testing.assert_array_equal([[0.5]], gsiso.D) + + def testTf2ssStaticMimo(self): + """Regression: tf2ss for MIMO static gain""" + import control + # 2x3 TFM + gmimo = control.tf2ss(control.tf([[ [23], [3], [5] ], [ [-1], [0.125], [101.3] ]], + [[ [46], [0.1], [80] ], [ [2], [-0.1], [1] ]])) + self.assertEqual(0, gmimo.states) + self.assertEqual(3, gmimo.inputs) + self.assertEqual(2, gmimo.outputs) + d = np.matrix([[0.5, 30, 0.0625], [-0.5, -1.25, 101.3]]) + np.testing.assert_array_equal(d, gmimo.D) + def suite(): return unittest.TestLoader().loadTestsFromTestCase(TestConvert)