1010
1111import unittest
1212import numpy as np
13- # import scipy as sp
1413from control .timeresp import *
1514from control .statesp import *
1615from control .xferfcn import TransferFunction , _convert_to_transfer_function
@@ -309,7 +308,7 @@ def check(u, x0, xtrue):
309308 def test_discrete_initial (self ):
310309 h1 = TransferFunction ([1. ], [1. , 0. ], 1. )
311310 t , yout = impulse_response (h1 , np .arange (4 ))
312- np .testing .assert_array_equal (yout [ 0 ] , [0. , 1. , 0. , 0. ])
311+ np .testing .assert_array_equal (yout , [0. , 1. , 0. , 0. ])
313312
314313 @unittest .skipIf (not slycot_check (), "slycot not installed" )
315314 def test_step_robustness (self ):
@@ -345,27 +344,32 @@ def test_time_vector(self):
345344 # No timebase in system => output should match input
346345 #
347346 # Initial response
348- tout , yout = initial_response (self .siso_dtf1 , Tin2 , siso_x0 )
347+ tout , yout = initial_response (self .siso_dtf1 , Tin2 , siso_x0 ,
348+ squeeze = False )
349349 self .assertEqual (np .shape (tout ), np .shape (yout [0 ,:]))
350350 np .testing .assert_array_equal (tout , Tin2 )
351351
352352 # Impulse response
353- tout , yout = impulse_response (self .siso_dtf1 , Tin2 )
353+ tout , yout = impulse_response (self .siso_dtf1 , Tin2 ,
354+ squeeze = False )
354355 self .assertEqual (np .shape (tout ), np .shape (yout [0 ,:]))
355356 np .testing .assert_array_equal (tout , Tin2 )
356357
357358 # Step response
358- tout , yout = step_response (self .siso_dtf1 , Tin2 )
359+ tout , yout = step_response (self .siso_dtf1 , Tin2 ,
360+ squeeze = False )
359361 self .assertEqual (np .shape (tout ), np .shape (yout [0 ,:]))
360362 np .testing .assert_array_equal (tout , Tin2 )
361363
362364 # Forced response with specified time vector
363- tout , yout , xout = forced_response (self .siso_dtf1 , Tin2 , np .sin (Tin2 ))
365+ tout , yout , xout = forced_response (self .siso_dtf1 , Tin2 , np .sin (Tin2 ),
366+ squeeze = False )
364367 self .assertEqual (np .shape (tout ), np .shape (yout [0 ,:]))
365368 np .testing .assert_array_equal (tout , Tin2 )
366369
367370 # Forced response with no time vector, no sample time (should use 1)
368- tout , yout , xout = forced_response (self .siso_dtf1 , None , np .sin (Tin1 ))
371+ tout , yout , xout = forced_response (self .siso_dtf1 , None , np .sin (Tin1 ),
372+ squeeze = False )
369373 self .assertEqual (np .shape (tout ), np .shape (yout [0 ,:]))
370374 np .testing .assert_array_equal (tout , Tin1 )
371375
@@ -380,49 +384,58 @@ def test_time_vector(self):
380384 # Matching timebase in system => output should match input
381385 #
382386 # Initial response
383- tout , yout = initial_response (self .siso_dtf2 , Tin2 , siso_x0 )
387+ tout , yout = initial_response (self .siso_dtf2 , Tin2 , siso_x0 ,
388+ squeeze = False )
384389 self .assertEqual (np .shape (tout ), np .shape (yout [0 ,:]))
385390 np .testing .assert_array_equal (tout , Tin2 )
386391
387392 # Impulse response
388- tout , yout = impulse_response (self .siso_dtf2 , Tin2 )
393+ tout , yout = impulse_response (self .siso_dtf2 , Tin2 ,
394+ squeeze = False )
389395 self .assertEqual (np .shape (tout ), np .shape (yout [0 ,:]))
390396 np .testing .assert_array_equal (tout , Tin2 )
391397
392398 # Step response
393- tout , yout = step_response (self .siso_dtf2 , Tin2 )
399+ tout , yout = step_response (self .siso_dtf2 , Tin2 ,
400+ squeeze = False )
394401 self .assertEqual (np .shape (tout ), np .shape (yout [0 ,:]))
395402 np .testing .assert_array_equal (tout , Tin2 )
396403
397404 # Forced response
398- tout , yout , xout = forced_response (self .siso_dtf2 , Tin2 , np .sin (Tin2 ))
405+ tout , yout , xout = forced_response (self .siso_dtf2 , Tin2 , np .sin (Tin2 ),
406+ squeeze = False )
399407 self .assertEqual (np .shape (tout ), np .shape (yout [0 ,:]))
400408 np .testing .assert_array_equal (tout , Tin2 )
401409
402410 # Forced response with no time vector, use sample time
403- tout , yout , xout = forced_response (self .siso_dtf2 , None , np .sin (Tin2 ))
411+ tout , yout , xout = forced_response (self .siso_dtf2 , None , np .sin (Tin2 ),
412+ squeeze = False )
404413 self .assertEqual (np .shape (tout ), np .shape (yout [0 ,:]))
405414 np .testing .assert_array_equal (tout , Tin2 )
406415
407416 # Compatible timebase in system => output should match input
408417 #
409418 # Initial response
410- tout , yout = initial_response (self .siso_dtf2 , Tin1 , siso_x0 )
419+ tout , yout = initial_response (self .siso_dtf2 , Tin1 , siso_x0 ,
420+ squeeze = False )
411421 self .assertEqual (np .shape (tout ), np .shape (yout [0 ,:]))
412422 np .testing .assert_array_equal (tout , Tin1 )
413423
414424 # Impulse response
415- tout , yout = impulse_response (self .siso_dtf2 , Tin1 )
425+ tout , yout = impulse_response (self .siso_dtf2 , Tin1 ,
426+ squeeze = False )
416427 self .assertEqual (np .shape (tout ), np .shape (yout [0 ,:]))
417428 np .testing .assert_array_equal (tout , Tin1 )
418429
419430 # Step response
420- tout , yout = step_response (self .siso_dtf2 , Tin1 )
431+ tout , yout = step_response (self .siso_dtf2 , Tin1 ,
432+ squeeze = False )
421433 self .assertEqual (np .shape (tout ), np .shape (yout [0 ,:]))
422434 np .testing .assert_array_equal (tout , Tin1 )
423435
424436 # Forced response
425- tout , yout , xout = forced_response (self .siso_dtf2 , Tin1 , np .sin (Tin1 ))
437+ tout , yout , xout = forced_response (self .siso_dtf2 , Tin1 , np .sin (Tin1 ),
438+ squeeze = False )
426439 self .assertEqual (np .shape (tout ), np .shape (yout [0 ,:]))
427440 np .testing .assert_array_equal (tout , Tin1 )
428441
@@ -431,7 +444,8 @@ def test_time_vector(self):
431444 #
432445 # Initial response
433446 tout , yout , xout = forced_response (self .siso_dtf2 , Tin1 ,
434- np .sin (Tin1 ), interpolate = True )
447+ np .sin (Tin1 ), interpolate = True ,
448+ squeeze = False )
435449 self .assertEqual (np .shape (tout ), np .shape (yout [0 ,:]))
436450 self .assertTrue (np .allclose (tout [1 :] - tout [:- 1 ], self .siso_dtf2 .dt ))
437451
@@ -442,9 +456,62 @@ def test_time_vector(self):
442456 #
443457 # Initial response
444458 with self .assertRaises (Exception ) as context :
445- tout , yout = initial_response (self .siso_dtf2 , Tin3 , siso_x0 )
459+ tout , yout = initial_response (self .siso_dtf2 , Tin3 , siso_x0 ,
460+ squeeze = False )
446461 self .assertTrue (isinstance (context .exception , ValueError ))
447462
463+ def test_time_series_data_convention (self ):
464+ """Make sure time series data matches documentation conventions"""
465+ # SISO continuous time
466+ t , y = step_response (self .siso_ss1 )
467+ self .assertTrue (isinstance (t , np .ndarray )
468+ and not isinstance (t , np .matrix ))
469+ self .assertTrue (len (t .shape ) == 1 )
470+ self .assertTrue (len (y .shape ) == 1 ) # SISO returns "scalar" output
471+ self .assertTrue (len (t ) == len (y )) # Allows direct plotting of output
472+
473+ # SISO discrete time
474+ t , y = step_response (self .siso_dss1 )
475+ self .assertTrue (isinstance (t , np .ndarray )
476+ and not isinstance (t , np .matrix ))
477+ self .assertTrue (len (t .shape ) == 1 )
478+ self .assertTrue (len (y .shape ) == 1 ) # SISO returns "scalar" output
479+ self .assertTrue (len (t ) == len (y )) # Allows direct plotting of output
480+
481+ # MIMO continuous time
482+ tin = np .linspace (0 , 10 , 100 )
483+ uin = [np .sin (tin ), np .cos (tin )]
484+ t , y , x = forced_response (self .mimo_ss1 , tin , uin )
485+ self .assertTrue (isinstance (t , np .ndarray )
486+ and not isinstance (t , np .matrix ))
487+ self .assertTrue (len (t .shape ) == 1 )
488+ self .assertTrue (len (y [0 ].shape ) == 1 )
489+ self .assertTrue (len (y [1 ].shape ) == 1 )
490+ self .assertTrue (len (t ) == len (y [0 ]))
491+ self .assertTrue (len (t ) == len (y [1 ]))
492+
493+ # MIMO discrete time
494+ tin = np .linspace (0 , 10 , 100 )
495+ uin = [np .sin (tin ), np .cos (tin )]
496+ t , y , x = forced_response (self .mimo_dss1 , tin , uin )
497+ self .assertTrue (isinstance (t , np .ndarray )
498+ and not isinstance (t , np .matrix ))
499+ self .assertTrue (len (t .shape ) == 1 )
500+ self .assertTrue (len (y [0 ].shape ) == 1 )
501+ self .assertTrue (len (y [1 ].shape ) == 1 )
502+ self .assertTrue (len (t ) == len (y [0 ]))
503+ self .assertTrue (len (t ) == len (y [1 ]))
504+
505+ # Allow input time as 2D array (output should be 1D)
506+ tin = np .array (np .linspace (0 , 10 , 100 ), ndmin = 2 )
507+ t , y = step_response (self .siso_ss1 , tin )
508+ self .assertTrue (isinstance (t , np .ndarray )
509+ and not isinstance (t , np .matrix ))
510+ self .assertTrue (len (t .shape ) == 1 )
511+ self .assertTrue (len (y .shape ) == 1 ) # SISO returns "scalar" output
512+ self .assertTrue (len (t ) == len (y )) # Allows direct plotting of output
513+
514+
448515def suite ():
449516 return unittest .TestLoader ().loadTestsFromTestCase (TestTimeresp )
450517
0 commit comments