10
10
import numpy as np
11
11
import control as ctrl
12
12
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
14
15
from control .exception import slycot_check
15
16
import matplotlib .pyplot as plt
16
17
@@ -45,7 +46,7 @@ def test_superimpose(self):
45
46
ctrl .bode_plot (ctrl .tf ([5 ], [1 , 1 ]))
46
47
47
48
# Check to make sure there are two axes and that each axes has two lines
48
- assert len (plt .gcf ().axes ) == 2
49
+ self . assertEqual ( len (plt .gcf ().axes ), 2 )
49
50
for ax in plt .gcf ().axes :
50
51
# Make sure there are 2 lines in each subplot
51
52
assert len (ax .get_lines ()) == 2
@@ -55,7 +56,7 @@ def test_superimpose(self):
55
56
ctrl .bode_plot ([ctrl .tf ([1 ], [1 ,2 ,1 ]), ctrl .tf ([5 ], [1 , 1 ])])
56
57
57
58
# Check to make sure there are two axes and that each axes has two lines
58
- assert len (plt .gcf ().axes ) == 2
59
+ self . assertEqual ( len (plt .gcf ().axes ), 2 )
59
60
for ax in plt .gcf ().axes :
60
61
# Make sure there are 2 lines in each subplot
61
62
assert len (ax .get_lines ()) == 2
@@ -67,7 +68,7 @@ def test_superimpose(self):
67
68
ctrl .bode_plot (ctrl .tf ([5 ], [1 , 1 ]))
68
69
69
70
# Check to make sure there are two axes and that each axes has one line
70
- assert len (plt .gcf ().axes ) == 2
71
+ self . assertEqual ( len (plt .gcf ().axes ), 2 )
71
72
for ax in plt .gcf ().axes :
72
73
# Make sure there is only 1 line in the subplot
73
74
assert len (ax .get_lines ()) == 1
@@ -77,7 +78,7 @@ def test_superimpose(self):
77
78
if ax .get_label () == 'control-bode-magnitude' :
78
79
break
79
80
ax .semilogx ([1e-2 , 1e1 ], 20 * np .log10 ([1 , 1 ]), 'k-' )
80
- assert len (ax .get_lines ()) == 2
81
+ self . assertEqual ( len (ax .get_lines ()), 2 )
81
82
82
83
def test_doubleint (self ):
83
84
# 30 May 2016, RMM: added to replicate typecast bug in freqresp.py
@@ -107,6 +108,62 @@ def test_mimo(self):
107
108
#plt.figure(4)
108
109
#bode(sysMIMO,self.omega)
109
110
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
+ warnings .simplefilter ('always' , UserWarning ) # don't supress
143
+ with warnings .catch_warnings (record = True ) as w :
144
+ omega_bad = np .linspace (10e-4 ,1.1 ,10 ) * np .pi / sys .dt
145
+ ret = sys .freqresp (omega_bad )
146
+ print ("len(w) =" , len (w ))
147
+ self .assertEqual (len (w ), 1 )
148
+ self .assertIn ("above" , str (w [- 1 ].message ))
149
+ self .assertIn ("Nyquist" , str (w [- 1 ].message ))
150
+
151
+ # Test bode plots (currently only implemented for SISO)
152
+ if (sys .inputs == 1 and sys .outputs == 1 ):
153
+ # Generic call (frequency range calculated automatically)
154
+ ret_ss = bode (sys )
155
+
156
+ # Convert to transfer function and test bode again
157
+ systf = tf (sys );
158
+ ret_tf = bode (systf )
159
+
160
+ # Make sure we can pass a frequency range
161
+ bode (sys , omega_ok )
162
+
163
+ else :
164
+ # Calling bode should generate a not implemented error
165
+ self .assertRaises (NotImplementedError , bode , (sys ,))
166
+
110
167
def suite ():
111
168
return unittest .TestLoader ().loadTestsFromTestCase (TestTimeresp )
112
169
0 commit comments