returnScipySignalLTI for discrete systems and add unit tests · python-control/python-control@a8aa41e
@@ -57,7 +57,8 @@
5757polyadd, polymul, polyval, roots, sqrt, zeros, squeeze, exp, pi, \
5858where, delete, real, poly, nonzero
5959import scipy as sp
60-from scipy.signal import lti, tf2zpk, zpk2tf, cont2discrete
60+from scipy.signal import tf2zpk, zpk2tf, cont2discrete
61+from scipy.signal import TransferFunction as signalTransferFunction
6162from copy import deepcopy
6263from warnings import warn
6364from itertools import chain
@@ -93,8 +94,8 @@ class TransferFunction(LTI):
9394 instance variable and setting it to something other than 'None'. If 'dt'
9495 has a non-zero value, then it must match whenever two transfer functions
9596 are combined. If 'dt' is set to True, the system will be treated as a
96- discrete time system with unspecified sampling time. The default value of
97- 'dt' is None and can be changed by changing the value of
97+ discrete time system with unspecified sampling time. The default value of
98+ 'dt' is None and can be changed by changing the value of
9899 ``control.config.defaults['xferfcn.default_dt']``.
99100100101 The TransferFunction class defines two constants ``s`` and ``z`` that
@@ -801,7 +802,7 @@ def minreal(self, tol=None):
801802# end result
802803return TransferFunction(num, den, self.dt)
803804804-def returnScipySignalLTI(self):
805+def returnScipySignalLTI(self, strict=True):
805806"""Return a list of a list of scipy.signal.lti objects.
806807807808 For instance,
@@ -812,19 +813,38 @@ def returnScipySignalLTI(self):
812813 is a signal.scipy.lti object corresponding to the
813814 transfer function from the 6th input to the 4th output.
814815816+ Parameters
817+ ----------
818+ strict : bool, optional
819+ True (default):
820+ `tfobject` must be continuous or discrete.
821+ `tfobject.dt`cannot be None.
822+ False:
823+ if `tfobject.dt` is None, continuous time signal.TransferFunction
824+ objects are is returned
825+826+ Returns
827+ -------
828+ out : list of list of scipy.signal.TransferFunction
815829 """
830+if strict and self.dt is None:
831+raise ValueError("with strict=True, dt cannot be None")
816832817-# TODO: implement for discrete time systems
818-if self.dt != 0 and self.dt is not None:
819-raise NotImplementedError("Function not \
820- implemented in discrete time")
833+if self.dt:
834+kwdt = {'dt': self.dt}
835+else:
836+# scipy convention for continuous time lti systems: call without
837+# dt keyword argument
838+kwdt = {}
821839822840# Preallocate the output.
823841out = [[[] for j in range(self.inputs)] for i in range(self.outputs)]
824842825843for i in range(self.outputs):
826844for j in range(self.inputs):
827-out[i][j] = lti(self.num[i][j], self.den[i][j])
845+out[i][j] = signalTransferFunction(self.num[i][j],
846+self.den[i][j],
847+**kwdt)
828848829849return out
830850@@ -1016,11 +1036,11 @@ def sample(self, Ts, method='zoh', alpha=None, prewarp_frequency=None):
10161036 The generalized bilinear transformation weighting parameter, which
10171037 should only be specified with method="gbt", and is ignored
10181038 otherwise.
1019-1039+10201040 prewarp_frequency : float within [0, infinity)
10211041 The frequency [rad/s] at which to match with the input continuous-
1022- time system's magnitude and phase (the gain=1 crossover frequency,
1023- for example). Should only be specified with method='bilinear' or
1042+ time system's magnitude and phase (the gain=1 crossover frequency,
1043+ for example). Should only be specified with method='bilinear' or
10241044 'gbt' with alpha=0.5 and ignored otherwise.
1025104510261046 Returns
@@ -1050,7 +1070,7 @@ def sample(self, Ts, method='zoh', alpha=None, prewarp_frequency=None):
10501070if (method=='bilinear' or (method=='gbt' and alpha==0.5)) and \
10511071prewarp_frequency is not None:
10521072Twarp = 2*np.tan(prewarp_frequency*Ts/2)/prewarp_frequency
1053-else:
1073+else:
10541074Twarp = Ts
10551075numd, dend, _ = cont2discrete(sys, Twarp, method, alpha)
10561076return TransferFunction(numd[0, :], dend, Ts)