Skip to content

Commit 4404169

Browse files
committed
reallow non zero timevector start
1 parent ce51d34 commit 4404169

2 files changed

Lines changed: 13 additions & 12 deletions

File tree

control/tests/timeresp_test.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -685,8 +685,6 @@ def test_forced_response_invalid_c(self, tsystem):
685685
with pytest.raises(ValueError, match="time values must be equally "
686686
"spaced"):
687687
forced_response(tsystem.sys, [0, 0.1, 0.12, 0.4])
688-
with pytest.raises(ValueError, match="must start with 0"):
689-
forced_response(tsystem.sys, [1, 1.1, 1.2, 1.3])
690688

691689
@pytest.mark.parametrize("tsystem", ["siso_dss2"], indirect=True)
692690
def test_forced_response_invalid_d(self, tsystem):

control/timeresp.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,7 @@ 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
213-
and start with 0.
212+
Time steps at which the input is defined; values must be evenly spaced.
214213
215214
If None, `U` must be given and `len(U)` time steps of sys.dt are
216215
simulated. If sys.dt is None or True (undetermined time step), a time
@@ -360,10 +359,10 @@ def forced_response(sys, T=None, U=0., X0=0., transpose=False,
360359
n_steps = T.shape[0] # number of simulation steps
361360

362361
# equally spaced also implies strictly monotonic increase,
363-
dt = T[-1] / (n_steps - 1)
362+
dt = (T[-1] - T[0]) / (n_steps - 1)
364363
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.")
364+
raise ValueError("Parameter ``T``: time values must be equally "
365+
"spaced.")
367366

368367
# create X0 if not given, test if X0 has correct shape
369368
X0 = _check_convert_array(X0, [(n_states,), (n_states, 1)],
@@ -426,6 +425,10 @@ def forced_response(sys, T=None, U=0., X0=0., transpose=False,
426425

427426
else:
428427
# Discrete type system => use SciPy signal processing toolbox
428+
429+
# sp.signal.dlsim assumes T[0] == 0
430+
spT = T - T[0]
431+
429432
if sys.dt is not True and sys.dt is not None:
430433
# Make sure that the time increment is a multiple of sampling time
431434

@@ -446,10 +449,10 @@ def forced_response(sys, T=None, U=0., X0=0., transpose=False,
446449
# T[-1] - T[0] < sys_dt * decimation * (n_steps - 1)
447450
# due to rounding errors.
448451
# https://github.com/scipyscipy/blob/v1.6.1/scipy/signal/ltisys.py#L3462
449-
scipy_out_samples = int(np.floor(T[-1] / sys_dt)) + 1
452+
scipy_out_samples = int(np.floor(spT[-1] / sys_dt)) + 1
450453
if scipy_out_samples < n_steps:
451454
# parantheses: order of evaluation is important
452-
T[-1] = T[-1] * (n_steps / (T[-1] / sys_dt + 1))
455+
spT[-1] = spT[-1] * (n_steps / (spT[-1] / sys_dt + 1))
453456

454457
else:
455458
sys_dt = dt # For unspecified sampling time, use time incr
@@ -459,7 +462,8 @@ def forced_response(sys, T=None, U=0., X0=0., transpose=False,
459462

460463
# Use signal processing toolbox for the discrete time simulation
461464
# Transpose the input to match toolbox convention
462-
tout, yout, xout = sp.signal.dlsim(dsys, np.transpose(U), T, X0)
465+
tout, yout, xout = sp.signal.dlsim(dsys, np.transpose(U), spT, X0)
466+
tout = tout + T[0]
463467

464468
if not interpolate:
465469
# If dt is different from sys.dt, resample the output
@@ -472,8 +476,7 @@ def forced_response(sys, T=None, U=0., X0=0., transpose=False,
472476
xout = np.transpose(xout)
473477
yout = np.transpose(yout)
474478

475-
return _process_time_response(sys, tout[:n_steps], yout[:, :n_steps],
476-
xout[:, :n_steps], transpose=transpose,
479+
return _process_time_response(sys, tout, yout, xout, transpose=transpose,
477480
return_x=return_x, squeeze=squeeze)
478481

479482

0 commit comments

Comments
 (0)