8000 Fix namespace and logic errrors in statesp.freqresp + unit tests · MaxGaukler/python-control@057c5ca · GitHub
[go: up one dir, main page]

Skip to content

Commit 057c5ca

Browse files
committed
Fix namespace and logic errrors in statesp.freqresp + unit tests
As pointed out in PR python-control#192, there were some namespace errors in the statesp module related to checking whether the frequency range as valid for discrete time systems. I added a unit test that covered this code (and exposed the error) and fixed up the namespace problems (as in PR python-control#192). There was also a logic error in the way that frequencies were checked and the warning message referred to the wrong function.
1 parent 601b581 commit 057c5ca

File tree

2 files changed

+64
-5
lines changed

2 files changed

+64
-5
lines changed

control/statesp.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -460,10 +460,8 @@ def freqresp(self, omega):
460460
if isdtime(self, strict=True):
461461
dt = timebase(self)
462462
cmplx_freqs = exp(1.j * omega * dt)
463-
if ((omega * dt).any() > pi):
464-
warn_message = ("evalfr: frequency evaluation"
465-
" above Nyquist frequency")
466-
warnings.warn(warn_message)
463+
if (max(omega) * dt > math.pi):
464+
warn("freqresp: frequency evaluation above Nyquist frequency")
467465
else:
468466
cmplx_freqs = omega * 1.j
469467

control/tests/freqresp_test.py

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
import numpy as np
1111
import control as ctrl
1212
from control.statesp import StateSpace
13-
from control.matlab import ss, tf, bode
13+
from control.xferfcn import TransferFunction
14+
from control.matlab import ss, tf, bode, rss
1415
from control.exception import slycot_check
1516
import matplotlib.pyplot as plt
1617

@@ -107,6 +108,66 @@ def test_mimo(self):
107108
#plt.figure(4)
108109
#bode(sysMIMO,self.omega)
109110

111+
def test_discrete(self):
112+
# Test discrete time frequency response
113+
114+
# SISO state space systems with either fixed or unspecified sampling times
115+
sys = rss(3, 1, 1)
116+
siso_ss1d = StateSpace(sys.A, sys.B, sys.C, sys.D, 0.1)
117+
siso_ss2d = StateSpace(sys.A, sys.B, sys.C, sys.D, True)
118+
119+
# MIMO state space systems with either fixed or unspecified sampling times
120+
A = [[-3., 4., 2.], [-1., -3., 0.], [2., 5., 3.]]
121+
B = [[1., 4.], [-3., -3.], [-2., 1.]]
122+
C = [[4., 2., -3.], [1., 4., 3.]]
123+
D = [[-2., 4.], [0., 1.]]
124+
mimo_ss1d = StateSpace(A, B, C, D, 0.1)
125+
mimo_ss2d = StateSpace(A, B, C, D, True)
126+
127+
# SISO transfer functions
128+
siso_tf1d = TransferFunction([1, 1], [1, 2, 1], 0.1)
129+
siso_tf2d = TransferFunction([1, 1], [1, 2, 1], True)
130+
131+
# Go through each system and call the code, checking return types
132+
for sys in (siso_ss1d, siso_ss2d, mimo_ss1d, mimo_ss2d,
133+
siso_tf1d, siso_tf2d):
134+
# Set frequency range to just below Nyquist freq (for Bode)
135+
omega_ok = np.linspace(10e-4,0.99,100) * np.pi/sys.dt
136+
137+
# Test frequency response
138+
ret = sys.freqresp(omega_ok)
139+
140+
# Check for warning if frequency is out of range
141+
import warnings
142+
with warnings.catch_warnings(record=True) as w:
143+
omega_bad = np.linspace(10e-4,1.1,10) * np.pi/sys.dt
144+
ret = sys.freqresp(omega_bad)
145+
print("len(w) =", len(w))
146+
assert len(w) == 1
147+
assert "above" in str(w[-1].message)
148+
assert "Nyquist" in str(w[-1].message)
149+
150+
# Test bode plots (currently only implemented for SISO)
151+
if (sys.inputs == 1 and sys.outputs == 1):
152+
# Generic call (frequency range calculated automatically)
153+
ret_ss = bode(sys)
154+
155+
# Convert to transfer function and test bode again
156+
systf = tf(sys);
157+
ret_tf = bode(systf)
158+
159+
# Make sure we can pass a frequency range
160+
bode(sys, omega_ok)
161+
162+
else:
163+
# Calling bode should generate a not implemented error
164+
try:
165+
ret_ss = bode(sys)
166+
raise RuntimeError("MIMO bode seems to be implemented?")
167+
except NotImplementedError:
168+
# This is where we should end up (so do nothing)
169+
continue
170+
110171
def suite():
111172
return unittest.TestLoader().loadTestsFromTestCase(TestTimeresp)
112173

0 commit comments

Comments
 (0)
0