@@ -193,6 +193,35 @@ def pole_cancellation(self):
193193 def no_pole_cancellation (self ):
194194 return TransferFunction ([1.881e+06 ],
195195 [188.1 , 1.881e+06 ])
196+
197+ @pytest .fixture
198+ def siso_tf_type1 (self ):
199+ # System Type 1 - Step response not stationary: G(s)=1/s(s+1)
200+ return TransferFunction (1 , [1 , 1 , 0 ])
201+
202+ @pytest .fixture
203+ def siso_tf_kpos (self ):
204+ # SISO under shoot response and positive final value G(s)=(-s+1)/(s²+s+1)
205+ return TransferFunction ([- 1 , 1 ], [1 , 1 , 1 ])
206+
207+ @pytest .fixture
208+ def siso_tf_kneg (self ):
209+ # SISO under shoot response and negative final value k=-1 G(s)=-(-s+1)/(s²+s+1)
210+ return TransferFunction ([1 , - 1 ], [1 , 1 , 1 ])
211+
212+ @pytest .fixture
213+ def tf1_matlab_help (self ):
214+ # example from matlab online help https://www.mathworks.com/help/control/ref/stepinfo.html
215+ return TransferFunction ([1 , 5 , 5 ], [1 , 1.65 , 5 , 6.5 , 2 ])
216+
217+ @pytest .fixture
218+ def tf2_matlab_help (self ):
219+ A = [[0.68 , - 0.34 ], [0.34 , 0.68 ]]
220+ B = [[0.18 ], [0.04 ]]
221+ C = [- 1.12 , - 1.10 ]
222+ D = [0.06 ]
223+ sys = StateSpace (A , B , C , D , 0.2 )
224+ return sys
196225
197226 @pytest .fixture
198227 def tsystem (self ,
@@ -202,7 +231,9 @@ def tsystem(self,
202231 siso_dtf0 , siso_dtf1 , siso_dtf2 ,
203232 siso_dss1 , siso_dss2 ,
204233 mimo_dss1 , mimo_dss2 , mimo_dtf1 ,
205- pole_cancellation , no_pole_cancellation ):
234+ pole_cancellation , no_pole_cancellation , siso_tf_type1 ,
235+ siso_tf_kpos , siso_tf_kneg , tf1_matlab_help ,
236+ tf2_matlab_help ):
206237 systems = {"siso_ss1" : siso_ss1 ,
207238 "siso_ss2" : siso_ss2 ,
208239 "siso_tf1" : siso_tf1 ,
@@ -220,6 +251,11 @@ def tsystem(self,
220251 "mimo_dtf1" : mimo_dtf1 ,
221252 "pole_cancellation" : pole_cancellation ,
222253 "no_pole_cancellation" : no_pole_cancellation ,
254+ "siso_tf_type1" : siso_tf_type1 ,
255+ "siso_tf_kpos" : siso_tf_kpos ,
256+ "siso_tf_kneg" : siso_tf_kneg ,
257+ "tf1_matlab_help" : tf1_matlab_help ,
258+ "tf2_matlab_help" : tf2_matlab_help ,
223259 }
224260 return systems [request .param ]
225261
@@ -303,6 +339,73 @@ def test_step_info(self):
303339 [Strue [k ] for k in Sktrue ],
304340 rtol = rtol )
305341
342+ # tolerance for all parameters could be wrong for some systems
343+ # discrete systems time parameters tolerance could be +/-dt
344+ @pytest .mark .parametrize (
345+ "tsystem, info_true, tolerance" ,
346+ [("tf1_matlab_help" , {
347+ 'RiseTime' : 3.8456 ,
348+ 'SettlingTime' : 27.9762 ,
349+ 'SettlingMin' : 2.0689 ,
350+ 'SettlingMax' : 2.6873 ,
351+ 'Overshoot' : 7.4915 ,
352+ 'Undershoot' : 0 ,
353+ 'Peak' : 2.6873 ,
354+ 'PeakTime' : 8.0530 ,
355+ 'SteadyStateValue' : 2.5 }, 2e-2 ),
356+ ("tf2_matlab_help" , {
357+ 'RiseTime' : 0.4000 ,
358+ 'SettlingTime' : 2.8000 ,
359+ 'SettlingMin' : - 0.6724 ,
360+ 'SettlingMax' : - 0.5188 ,
361+ 'Overshoot' : 24.6476 ,
362+ 'Undershoot' : 11.1224 ,
363+ 'Peak' : 0.6724 ,
364+ 'PeakTime' : 1 ,
365+ 'SteadyStateValue' : - 0.5394 }, .2 ),
366+ ("siso_tf_kpos" , {
367+ 'RiseTime' : 1.242 ,
368+ 'SettlingTime' : 9.110 ,
369+ 'SettlingMin' : 0.950 ,
370+ 'SettlingMax' : 1.208 ,
371+ 'Overshoot' : 20.840 ,
372+ 'Undershoot' : 27.840 ,
373+ 'Peak' : 1.208 ,
374+ 'PeakTime' : 4.282 ,
375+ 'SteadyStateValue' : 1.0 }, 2e-2 ),
376+ ("siso_tf_kneg" , {
377+ 'RiseTime' : 1.242 ,
378+ 'SettlingTime' : 9.110 ,
379+ 'SettlingMin' : - 1.208 ,
380+ 'SettlingMax' : - 0.950 ,
381+ 'Overshoot' : 20.840 ,
382+ 'Undershoot' : 27.840 ,
383+ 'Peak' : 1.208 ,
384+ 'PeakTime' : 4.282 ,
385+ 'SteadyStateValue' : - 1.0 }, 2e-2 ),
386+ ("siso_tf_type1" , {'RiseTime' : np .NaN ,
387+ 'SettlingTime' : np .NaN ,
388+ 'SettlingMin' : np .NaN ,
389+ 'SettlingMax' : np .NaN ,
390+ 'Overshoot' : np .NaN ,
391+ 'Undershoot' : np .NaN ,
392+ 'Peak' : np .Inf ,
393+ 'PeakTime' : np .Inf ,
394+ 'SteadyStateValue' : np .NaN }, 2e-2 )],
395+ indirect = ["tsystem" ])
396+ def test_step_info (self , tsystem , info_true , tolerance ):
397+ """ """
398+ info = step_info (tsystem )
399+
400+ info_true_sorted = sorted (info_true .keys ())
401+ info_sorted = sorted (info .keys ())
402+
403+ assert info_sorted == info_true_sorted
404+
405+ np .testing .assert_allclose ([info_true [k ] for k in info_true_sorted ],
406+ [info [k ] for k in info_sorted ],
407+ rtol = tolerance )
408+
306409 def test_step_pole_cancellation (self , pole_cancellation ,
307410 no_pole_cancellation ):
308411 # confirm that pole-zero cancellation doesn't perturb results
0 commit comments