1010import numpy as np
1111import control as ctrl
1212from 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
1415from control .exception import slycot_check
1516import matplotlib .pyplot as plt
1617
@@ -45,7 +46,7 @@ def test_superimpose(self):
4546 ctrl .bode_plot (ctrl .tf ([5 ], [1 , 1 ]))
4647
4748 # 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 )
4950 for ax in plt .gcf ().axes :
5051 # Make sure there are 2 lines in each subplot
5152 assert len (ax .get_lines ()) == 2
@@ -55,7 +56,7 @@ def test_superimpose(self):
5556 ctrl .bode_plot ([ctrl .tf ([1 ], [1 ,2 ,1 ]), ctrl .tf ([5 ], [1 , 1 ])])
5657
5758 # 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 )
5960 for ax in plt .gcf ().axes :
6061 # Make sure there are 2 lines in each subplot
6162 assert len (ax .get_lines ()) == 2
@@ -67,7 +68,7 @@ def test_superimpose(self):
6768 ctrl .bode_plot (ctrl .tf ([5 ], [1 , 1 ]))
6869
6970 # 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 )
7172 for ax in plt .gcf ().axes :
7273 # Make sure there is only 1 line in the subplot
7374 assert len (ax .get_lines ()) == 1
@@ -77,7 +78,7 @@ def test_superimpose(self):
7778 if ax .get_label () == 'control-bode-magnitude' :
7879 break
7980 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 )
8182
8283 def test_doubleint (self ):
8384 # 30 May 2016, RMM: added to replicate typecast bug in freqresp.py
@@ -107,6 +108,62 @@ 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+ 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+
110167def suite ():
111168 return unittest .TestLoader ().loadTestsFromTestCase (TestTimeresp )
112169
0 commit comments