Problem
ResampleSettings.max_chunk_delay documents: "Maximum delay between outputs in seconds. If the delay exceeds this value, the transformer will extrapolate." In practice this extrapolation can never fire once input stops, because ResampleProcessor.__next__ returns early before the extrapolation condition (b_project) is ever evaluated.
The sequence:
- On each productive
__next__, the source buffer is seeked to keep only ~2 samples (one sample before the last resampled value onward):
seek_ix = np.where(x >= xnew[-1])[0]
if len(seek_ix) > 0:
self.state.src_buffer.seek(max(0, src_start_ix + seek_ix[0] - 1))
- The next call hits the guard at the top of
__next__ and returns the empty template:
if ref.is_empty() or src.available() < 3:
return ... # empty
- The
b_project check (resample_rate is not None and time.monotonic() > last_write_time + max_chunk_delay) sits below that guard, so it is never reached.
This is independent of how the unit polls or wakes — it was equally unreachable under the previous busy-wait publisher (which called next() continuously), and remains so under the event-driven publisher from #178, whose timed wake fires at the right moment but finds the same early return.
Reproduction
import time
import numpy as np
from ezmsg.util.messages.axisarray import AxisArray
from ezmsg.sigproc.resample import ResampleProcessor, ResampleSettings
def mk(fs, offset, n):
t = offset + np.arange(n) / fs
return AxisArray(data=t[:, None], dims=["time", "ch"],
axes={"time": AxisArray.LinearAxis(gain=1/fs, offset=offset, unit="s")}, key="sig")
proc = ResampleProcessor(settings=ResampleSettings(
axis="time", resample_rate=90.0, buffer_duration=4.0, max_chunk_delay=0.05))
for i in range(5):
proc(mk(100.0, i * 0.3, 30))
next(proc)
print(proc.state.src_buffer.available()) # 2 -> below the < 3 guard
time.sleep(0.06) # exceed max_chunk_delay
r = next(proc)
print(r.data.shape[0]) # 0 -- expected: extrapolated samples past input end
Possible fix
Linear scipy.interpolate.interp1d needs only 2 points, so the src.available() < 3 guard could be relaxed (to < 2, or bypassed specifically when b_project is true) allowing the extrapolation path to run from the retained tail of the source buffer. Care needed:
- repeated timeouts with no input should not emit unbounded/duplicate extrapolations — the high-water mark (
last_ref_ax_val) already appears to bound this to one max_chunk_delay window past the reference buffer end, but that should be pinned down with a test;
fill_value="last" vs "extrapolate" behave differently out-of-bounds and both should be covered.
Test hook
tests/integration/ezmsg/test_resample_system.py::test_resample_system_prescribed_rate (added in #178) documents this limitation in its docstring and is the natural place to assert output beyond the input end once the guard is reworked. The unit-side timed wake is already in place, so no unit changes should be needed.
Problem
ResampleSettings.max_chunk_delaydocuments: "Maximum delay between outputs in seconds. If the delay exceeds this value, the transformer will extrapolate." In practice this extrapolation can never fire once input stops, becauseResampleProcessor.__next__returns early before the extrapolation condition (b_project) is ever evaluated.The sequence:
__next__, the source buffer is seeked to keep only ~2 samples (one sample before the last resampled value onward):__next__and returns the empty template:b_projectcheck (resample_rate is not None and time.monotonic() > last_write_time + max_chunk_delay) sits below that guard, so it is never reached.This is independent of how the unit polls or wakes — it was equally unreachable under the previous busy-wait publisher (which called
next()continuously), and remains so under the event-driven publisher from #178, whose timed wake fires at the right moment but finds the same early return.Reproduction
Possible fix
Linear
scipy.interpolate.interp1dneeds only 2 points, so thesrc.available() < 3guard could be relaxed (to< 2, or bypassed specifically whenb_projectis true) allowing the extrapolation path to run from the retained tail of the source buffer. Care needed:last_ref_ax_val) already appears to bound this to onemax_chunk_delaywindow past the reference buffer end, but that should be pinned down with a test;fill_value="last"vs"extrapolate"behave differently out-of-bounds and both should be covered.Test hook
tests/integration/ezmsg/test_resample_system.py::test_resample_system_prescribed_rate(added in #178) documents this limitation in its docstring and is the natural place to assert output beyond the input end once the guard is reworked. The unit-side timed wake is already in place, so no unit changes should be needed.