Python Resample API
Continuously-variable polyphase resampler — three implementations all backed by the native C library, accepting and returning complex64 NumPy arrays with state preserved across calls.
Source:
python/dsp/doppler/resample/__init__.py
resample
doppler.resample — continuously-variable polyphase resampler.
Three implementations are provided:
-
:class:
Resampler— Kaiser polyphase table resampler backed by the C library (dp_resamp_cf32). Default choice: fast, cache-resident for small banks. -
:class:
ResamplerDpmfs— Dual Phase Modified Farrow Structure resampler (dp_resamp_dpmfs). 608-byte coefficient footprint; ideal for multi-channel pipelines. -
:class:
HalfbandDecimator— dedicated 2:1 halfband decimator (dp_hbdecim_cf32). Exploits halfband symmetry to halve the multiply count vs a general 2-phase polyphase decimator.
All classes accept complex64 NumPy arrays and return complex64 NumPy arrays. Internal state is preserved across calls — suitable for streaming pipelines.
Typical usage
import numpy as np from doppler.resample import Resampler from doppler.polyphase import kaiser_prototype _, bank = kaiser_prototype() r = Resampler(bank, rate=2.0) x = np.ones(64, dtype=np.complex64) y = r.execute(x) y.dtype dtype('complex64') len(y) > 0 True
The reference Python implementation lives in
:mod:doppler.resample.reference.
Resampler
Kaiser polyphase table resampler for cf32 IQ samples.
Wraps dp_resamp_cf32_t from the C library. Supports
continuously-variable interpolation and decimation at any
rational or irrational rate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bank
|
np.ndarray, shape (L, N), dtype=float32
|
Polyphase coefficient bank. Row-major: |
required |
rate
|
float
|
Output-to-input sample-rate ratio |
required |
Examples:
>>> import numpy as np
>>> from doppler.polyphase import kaiser_prototype
>>> from doppler.resample import Resampler
>>> _, bank = kaiser_prototype(passband=0.4, stopband=0.6)
>>> r = Resampler(bank, rate=1.5)
>>> x = np.zeros(256, dtype=np.complex64)
>>> x[0] = 1.0 # impulse
>>> y = r.execute(x)
>>> y.dtype
dtype('complex64')
>>> len(y) >= 384 - 20 # approx ceil(256 * 1.5)
True
rate
property
rate: float
Output-to-input sample-rate ratio.
num_phases
property
num_phases: int
Number of polyphase branches (L).
num_taps
property
num_taps: int
Taps per polyphase branch (N).
execute
execute(x: ndarray) -> np.ndarray
Resample a block of cf32 samples.
State is preserved across calls — suitable for streaming.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
np.ndarray, dtype=complex64
|
Input samples. |
required |
Returns:
| Type | Description |
|---|---|
np.ndarray, dtype=complex64
|
Output samples. Length ≈ |
Examples:
>>> import numpy as np
>>> from doppler.polyphase import kaiser_prototype
>>> from doppler.resample import Resampler
>>> _, bank = kaiser_prototype()
>>> r = Resampler(bank, rate=0.5)
>>> y = r.execute(np.ones(128, dtype=np.complex64))
>>> len(y) <= 70 # roughly half the input
True
reset
reset() -> None
Zero the sample history and reset the phase accumulator.
ResamplerDpmfs
DPMFS polyphase resampler for cf32 IQ samples.
Wraps dp_resamp_dpmfs_t from the C library. Uses a compact
polynomial coefficient bank (608 bytes for M=3, N=19) that fits
in L1 cache, eliminating table-lookup cache misses in multi-channel
pipelines.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
coeffs
|
DPMFSCoeffs
|
Polynomial coefficients from
:func: |
required |
rate
|
float
|
Output-to-input sample-rate ratio |
required |
Examples:
>>> import numpy as np
>>> from doppler.polyphase import kaiser_prototype, fit_dpmfs
>>> from doppler.resample import ResamplerDpmfs
>>> _, bank = kaiser_prototype()
>>> coeffs = fit_dpmfs(bank, M=3)
>>> r = ResamplerDpmfs(coeffs, rate=1.5)
>>> x = np.zeros(256, dtype=np.complex64)
>>> x[0] = 1.0
>>> y = r.execute(x)
>>> y.dtype
dtype('complex64')
>>> len(y) >= 384 - 20
True
rate
property
rate: float
Output-to-input sample-rate ratio.
num_taps
property
num_taps: int
Taps per branch (N).
poly_order
property
poly_order: int
Polynomial order (M).
execute
execute(x: ndarray) -> np.ndarray
Resample a block of cf32 samples.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
np.ndarray, dtype=complex64
|
Input samples. |
required |
Returns:
| Type | Description |
|---|---|
np.ndarray, dtype=complex64
|
Output samples. |
Examples:
>>> import numpy as np
>>> from doppler.polyphase import kaiser_prototype, fit_dpmfs
>>> from doppler.resample import ResamplerDpmfs
>>> _, bank = kaiser_prototype()
>>> r = ResamplerDpmfs(fit_dpmfs(bank, M=3), rate=0.5)
>>> y = r.execute(np.ones(128, dtype=np.complex64))
>>> len(y) <= 70
True
reset
reset() -> None
Zero the sample history and reset the phase accumulator.
HalfbandDecimator
Halfband 2:1 decimator for cf32 IQ samples.
Wraps dp_hbdecim_cf32_t from the C library. Exploits halfband
filter symmetry to reduce the multiply count to N/2 + 2 per output
sample (vs N for a general 2-phase polyphase decimator).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bank
|
np.ndarray, shape (2, N), dtype=float32
|
Polyphase bank from :func: |
required |
Examples:
>>> import numpy as np
>>> from doppler.polyphase import kaiser_prototype
>>> from doppler.resample import HalfbandDecimator
>>> _, bank = kaiser_prototype(phases=2)
>>> r = HalfbandDecimator(bank)
>>> x = np.zeros(256, dtype=np.complex64)
>>> x[0] = 1.0 # impulse
>>> y = r.execute(x)
>>> y.dtype
dtype('complex64')
>>> len(y) == 128
True
rate
property
rate: float
Decimation rate (always 0.5).
num_taps
property
num_taps: int
FIR branch length (N).
execute
execute(x: ndarray) -> np.ndarray
Decimate a block of cf32 samples by 2.
State is preserved across calls — suitable for streaming. Odd-length blocks are handled transparently: the dangling even sample is buffered and consumed on the next call.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
np.ndarray, dtype=complex64
|
Input samples. |
required |
Returns:
| Type | Description |
|---|---|
np.ndarray, dtype=complex64
|
Output samples. Length == |
Examples:
>>> import numpy as np
>>> from doppler.polyphase import kaiser_prototype
>>> from doppler.resample import HalfbandDecimator
>>> _, bank = kaiser_prototype(phases=2)
>>> r = HalfbandDecimator(bank)
>>> y = r.execute(np.ones(128, dtype=np.complex64))
>>> len(y) == 64
True
reset
reset() -> None
Zero the sample history and clear any pending sample.