diff --git a/control/frdata.py b/control/frdata.py index 1bdf28528..ac032d3f7 100644 --- a/control/frdata.py +++ b/control/frdata.py @@ -261,6 +261,11 @@ def __init__(self, *args, **kwargs): # create interpolation functions if smooth: + # Set the order of the fit + if self.omega.size < 2: + raise ValueError("can't smooth with only 1 frequency") + degree = 3 if self.omega.size > 3 else self.omega.size - 1 + self.ifunc = empty((self.fresp.shape[0], self.fresp.shape[1]), dtype=tuple) for i in range(self.fresp.shape[0]): @@ -268,7 +273,8 @@ def __init__(self, *args, **kwargs): self.ifunc[i, j], u = splprep( u=self.omega, x=[real(self.fresp[i, j, :]), imag(self.fresp[i, j, :])], - w=1.0/(absolute(self.fresp[i, j, :]) + 0.001), s=0.0) + w=1.0/(absolute(self.fresp[i, j, :]) + 0.001), + s=0.0, k=degree) else: self.ifunc = None @@ -392,7 +398,12 @@ def __add__(self, other): # Convert the second argument to a frequency response function. # or re-base the frd to the current omega (if needed) - other = _convert_to_frd(other, omega=self.omega) + if isinstance(other, (int, float, complex, np.number)): + other = _convert_to_frd( + other, omega=self.omega, + inputs=self.ninputs, outputs=self.noutputs) + else: + other = _convert_to_frd(other, omega=self.omega) # Check that the input-output sizes are consistent. if self.ninputs != other.ninputs: diff --git a/control/tests/lti_test.py b/control/tests/lti_test.py index 3f001c17b..5359ceea3 100644 --- a/control/tests/lti_test.py +++ b/control/tests/lti_test.py @@ -350,3 +350,21 @@ def test_subsys_indexing(fcn, outdx, inpdx, key): np.testing.assert_almost_equal( subsys_fcn.frequency_response(omega).response, subsys_chk.frequency_response(omega).response) + + +@slycotonly +@pytest.mark.parametrize("op", [ + '__mul__', '__rmul__', '__add__', '__radd__', '__sub__', '__rsub__']) +@pytest.mark.parametrize("fcn", [ct.ss, ct.tf, ct.frd]) +def test_scalar_algebra(op, fcn): + sys_ss = ct.rss(4, 2, 2) + match fcn: + case ct.ss: + sys = sys_ss + case ct.tf: + sys = ct.tf(sys_ss) + case ct.frd: + sys = ct.frd(sys_ss, [0.1, 1, 10]) + + scaled = getattr(sys, op)(2) + np.testing.assert_almost_equal(getattr(sys(1j), op)(2), scaled(1j)) diff --git a/control/xferfcn.py b/control/xferfcn.py index 56ec7395f..5304ea636 100644 --- a/control/xferfcn.py +++ b/control/xferfcn.py @@ -634,11 +634,11 @@ def __mul__(self, other): from .statesp import StateSpace # Convert the second argument to a transfer function. - if isinstance(other, StateSpace): + if isinstance(other, (StateSpace, np.ndarray)): other = _convert_to_transfer_function(other) - elif isinstance(other, (int, float, complex, np.number, np.ndarray)): - other = _convert_to_transfer_function(other, inputs=self.ninputs, - outputs=self.noutputs) + elif isinstance(other, (int, float, complex, np.number)): + # Multiply by a scaled identity matrix (transfer function) + other = _convert_to_transfer_function(np.eye(self.ninputs) * other) if not isinstance(other, TransferFunction): return NotImplemented @@ -681,8 +681,8 @@ def __rmul__(self, other): # Convert the second argument to a transfer function. if isinstance(other, (int, float, complex, np.number)): - other = _convert_to_transfer_function(other, inputs=self.ninputs, - outputs=self.ninputs) + # Multiply by a scaled identity matrix (transfer function) + other = _convert_to_transfer_function(np.eye(self.noutputs) * other) else: other = _convert_to_transfer_function(other) @@ -723,9 +723,8 @@ def __truediv__(self, other): """Divide two LTI objects.""" if isinstance(other, (int, float, complex, np.number)): - other = _convert_to_transfer_function( - other, inputs=self.ninputs, - outputs=self.ninputs) + # Multiply by a scaled identity matrix (transfer function) + other = _convert_to_transfer_function(np.eye(self.ninputs) * other) else: other = _convert_to_transfer_function(other)