Interpolator

class gpype.backend.timing.interpolator.Interpolator[source]

Bases: IONode

Raises the data rate of a stream by an integer factor L.

The mirror image of Decimator, and the replacement for the former Hold node: it raises the rate for real, so every downstream node still sees a sampling_rate and a frame_size that mean what they say. The rate rises because L frames are emitted per input frame, not because frames grow – frame_size is passed through unchanged, which is what lets the output be joined with a stream already running at the higher rate. The cost is burstiness: the L frames arrive together, once per input frame, so a joined result lags by up to one input period.

method chooses how the missing samples are filled. "hold" (the default) repeats the last computed value, which is exactly what a windowed estimate means; "filter" zero-stuffs and low-passes for a smooth reconstruction, at the price of overshoot and about two input samples of group delay – some 400 ms at a 5 Hz feature rate. Use “hold” for a feature stream, “filter” for a genuine signal.

Trigger and auxiliary channels are always held, in both methods: filtering a trigger turns a step into a ramp, which can cross a Trigger threshold on a row where nothing happened.

A master-timeline index or a quality channel on the input is refused at setup – both are consumed by Sync, so seeing one means this node sits upstream of Sync, where it must not sit. Only integer-factor upsampling is in scope.

A marker in the input context moves to the first of the L output samples its input sample expands into. A gap covers every output sample whose window holds a missing input sample. With "hold" the window is the input sample an output expands, so a gap covers all L of every missing one. With "filter", it is the input samples that carry 99 % of the output’s filter weight: 14 at L=2, 11 at L=4.

CUTOFF_RATIO = 0.8

Cutoff as a fraction of the input Nyquist frequency. Below one so that the transition band fits below that Nyquist rather than straddling it, which is where the zero-stuffed spectral images this filter exists to remove would leak back in.

FILTER_ORDER = 8

Order of the reconstruction low-pass.

RESPONSE_PERIODS = 64

Input periods of impulse response measured for a gap’s window. The windows are 11 to 14 of them for factors 2 to 16, and 512 gives the same windows.

METHOD_HOLD = 'hold'

Repeat the last computed value across the L output rows.

METHOD_FILTER = 'filter'

Zero-stuff and low-pass to reconstruct a smooth trajectory.

class Configuration[source]

Bases: Configuration

class Keys[source]

Bases: Keys

class OptionalKeys[source]

Bases: OptionalKeys

Optional configuration keys.

INTERPOLATION_FACTOR = 'interpolation_factor'

Integer factor L by which the data rate is raised.

METHOD = 'method'

Which of METHOD_HOLD / METHOD_FILTER to apply to signal channels.

__init__(interpolation_factor=1, method='hold', **kwargs)[source]

Initialize interpolator with interpolation factor and method.

Parameters:
  • interpolation_factor (int) – Factor L by which to raise the data rate. Must be a positive integer. Value of 1 means no interpolation (a pure pass-through).

  • method (str) – METHOD_HOLD or METHOD_FILTER, applied to signal channels only – see the class docstring for the trade-off. Trigger and auxiliary channels are always held, regardless of this setting.

  • **kwargs – Additional arguments for parent IONode.

Raises:

ValueError – If interpolation_factor is not a positive integer, or method is not one of METHOD_HOLD or METHOD_FILTER.

setup(data, port_context_in)[source]

Setup output context with a raised sampling rate.

Parameters:
  • data (dict[str, ndarray]) – Input data arrays.

  • port_context_in (dict[str, dict]) – Input port contexts containing frame size, sampling rate, and per-channel role information.

Return type:

dict[str, dict]

Returns:

Output port contexts with the interpolated sampling rate. Frame size is unchanged: the rate rises because L frames are emitted per input frame. Channel count, labels, roles and montage are carried through unchanged – this node changes rows, not columns.

Raises:

ValueError – If frame_size is not provided in the input context, or if the input carries a master-timeline index or a quality channel.

step(data)[source]

Expand one input frame into L frames at L times the rate.

L == 1 is a pure pass-through: the frame is forwarded unchanged, with no filter ever built or run. Otherwise the expanded rows go out as L frames of the input’s own frame size, so the frame shape is preserved and only the emission rate rises.

Parameters:

data (dict) – Input data dictionary containing one frame to expand.

Return type:

dict | None

Returns:

The frame itself when L == 1, otherwise None – the L frames have already been emitted by then.