Skip to content

Commit 9cac9fa

Browse files
committed
ensure dlsim output length
1 parent f0d764f commit 9cac9fa

1 file changed

Lines changed: 22 additions & 9 deletions

File tree

control/timeresp.py

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)