@@ -209,7 +209,9 @@ def forced_response(sys, T=None, U=0., X0=0., transpose=False,
209209 LTI system to simulate
210210
211211 T : array_like, optional for discrete LTI `sys`
212- Time steps at which the input is defined; values must be evenly spaced.
212+ Time steps at which the input is defined; values must be evenly spaced
213+ and start with 0.
214+
213215 If None, `U` must be given and `len(U)` time steps of sys.dt are
214216 simulated. If sys.dt is None or True (undetermined time step), a time
215217 step of 1.0 is assumed.
@@ -355,13 +357,14 @@ def forced_response(sys, T=None, U=0., X0=0., transpose=False,
355357 'Parameter ``T``: ' , squeeze = True ,
356358 transpose = transpose )
357359
358- # equally spaced also implies strictly monotonic increase
359- dt = T [1 ] - T [0 ]
360- if not np .allclose (np .diff (T ), dt ):
361- raise ValueError ("Parameter ``T``: time values must be "
362- "equally spaced." )
363360 n_steps = T .shape [0 ] # number of simulation steps
364361
362+ # equally spaced also implies strictly monotonic increase,
363+ dt = T [- 1 ] / (n_steps - 1 )
364+ if not np .allclose (np .diff (T ), dt ):
365+ raise ValueError ("Parameter ``T`` must start with 0 and time values "
366+ "must be equally spaced." )
367+
365368 # create X0 if not given, test if X0 has correct shape
366369 X0 = _check_convert_array (X0 , [(n_states ,), (n_states , 1 )],
367370 'Parameter ``X0``: ' , squeeze = True )
@@ -432,12 +435,21 @@ def forced_response(sys, T=None, U=0., X0=0., transpose=False,
432435
433436 # Now check to make sure it is a multiple (with check against
434437 # sys.dt because floating point mod can have small errors
435- elif not (np .isclose (dt % sys .dt , 0 ) or
436- np .isclose (dt % sys .dt , sys .dt )):
438+ if not (np .isclose (dt % sys .dt , 0 ) or
439+ np .isclose (dt % sys .dt , sys .dt )):
437440 raise ValueError ("Time steps ``T`` must be multiples of "
438441 "sampling time" )
439442 sys_dt = sys .dt
440443
444+ # sp.signal.dlsim returns not enough samples if
445+ # T[-1] - T[0] < sys_dt * decimation * (n_steps - 1)
446+ # due to rounding errors.
447+ # https://github.com/scipyscipy/blob/v1.6.1/scipy/signal/ltisys.py#L3462
448+ scipy_out_samples = int (np .floor (T [- 1 ] / sys_dt )) + 1
449+ if scipy_out_samples < n_steps :
450+ # parantheses: order of evaluation is important
451+ T [- 1 ] = T [- 1 ] * (n_steps / (T [- 1 ] / sys_dt + 1 ))
452+
441453 else :
442454 sys_dt = dt # For unspecified sampling time, use time incr
443455
@@ -459,7 +471,8 @@ def forced_response(sys, T=None, U=0., X0=0., transpose=False,
459471 xout = np .transpose (xout )
460472 yout = np .transpose (yout )
461473
462- return _process_time_response (sys , tout , yout , xout , transpose = transpose ,
474+ return _process_time_response (sys , tout [:n_steps ], yout [:, :n_steps ],
475+ xout [:, :n_steps ], transpose = transpose ,
463476 return_x = return_x , squeeze = squeeze )
464477
465478
0 commit comments