Skip to content

Python Accumulator API

Running-sum accumulators backed by dp_acc_f32_t and dp_acc_cf64_t. Used as the integrate-and-dump register in polyphase resamplers.

Source: src/doppler/accumulator/__init__.py


Classes

Class Accumulator type Coefficient type Use when
AccF32 float32 scalar float32 real-valued sums, power estimation
AccCf64 complex128 scalar float32 polyphase resampler I&D path
AccTrace float64 per-bin trace averaging (mean / EMA / hold)

AccTrace differs from the scalar accumulators above: it keeps one running value per bin over a fixed-length frame rather than reducing the frame to a single sum. Choose the reduction with mode"mean" (linear average), "exp" (exponential moving average using alpha), "maxhold", or "minhold". It is the averaging engine behind doppler.spectral.PSD, and is reusable for waterfall / spectrogram and video-averaged displays.


Examples

AccF32 — running sum

from doppler.accumulator import AccF32

acc = AccF32()
acc.step(1.0)
acc.step(2.5)
print(acc.get())    # 3.5 — read without clearing
print(acc.dump())   # 3.5 — read and zero
print(acc.dump())   # 0.0 — cleared by previous dump

AccF32 — multiply-accumulate (dot product)

madd(x, h) computes acc += sum(x[k] * h[k]) in C — the inner loop of a polyphase FIR branch.

from doppler.accumulator import AccF32
import numpy as np

x = np.array([1, 2, 3, 4], dtype=np.float32)
h = np.array([0.25, 0.25, 0.25, 0.25], dtype=np.float32)

acc = AccF32()
acc.madd(x, h)
print(acc.dump())   # 2.5 = mean([1,2,3,4])

AccCf64 — complex accumulator for resampler I&D

from doppler.accumulator import AccCf64
import numpy as np

x = np.array([1+2j, 3+4j], dtype=np.complex128)
h = np.array([0.5, 0.5], dtype=np.float32)

acc = AccCf64()
acc.madd(x, h)
print(acc.dump())   # (2+3j) = mean([1+2j, 3+4j])

steps vs step

  • step(v) — add a scalar to the accumulator.
  • steps(x) — add all elements of a NumPy array to the accumulator.
acc = AccF32()
acc.step(1.0)           # acc = 1.0
acc.steps(np.array([2.0, 3.0], dtype=np.float32))  # acc = 6.0

AccTrace — per-bin trace averaging

from doppler.accumulator import AccTrace
import numpy as np

# Linear (PSD (PSD-method)) average of two power frames, per bin.
acc = AccTrace(n=4, mode="mean")
acc.accumulate(np.array([1, 3, 5, 7], dtype=np.float32))
acc.accumulate(np.array([3, 5, 7, 9], dtype=np.float32))
print(acc.value())   # [2. 4. 6. 8.]
print(acc.count)     # 2

# Max-hold catches per-bin transients across frames.
mh = AccTrace(n=3, mode="maxhold")
mh.accumulate(np.array([1, 5, 2], dtype=np.float32))
mh.accumulate(np.array([4, 3, 6], dtype=np.float32))
print(mh.value())    # [4. 5. 6.]

value() returns None until the first frame is accumulated.


AccF32

Single-precision floating-point scalar accumulator. Maintains one running sum (acc) that persists across calls to step, steps, madd, add2d, and madd2d. Use get to read without side-effects or dump to read and atomically zero in a single call.

Parameters:

Name Type Description Default
acc float

acc state variable.

0.0

Examples:

Create with defaults:

>>> from doppler.accumulator import AccF32
>>> obj = AccF32(0.0)
>>> obj.get_acc()
0.0

Reset restores defaults:

>>> obj.set_acc(1.0)
>>> obj.reset()
>>> obj.get_acc()
0.0

reset

reset() -> None

Zero the accumulator, restoring the same state as a fresh AccF32(0.0) — regardless of the value supplied to acc_f32_create. Subsequent get / dump calls return 0.0 until new samples are processed.

Examples:

>>> from doppler.accumulator import AccF32
>>> obj = AccF32(0.0)
>>> obj.step(7.0)
>>> obj.reset()
>>> obj.get_acc()
0.0

step

step(x: float) -> None

Add one sample to the running sum (acc += x). This is the hot-path entry point for sample-by-sample processing. For block inputs prefer acc_f32_steps to amortise call overhead and allow auto-vectorisation.

Parameters:

Name Type Description Default
x float

Input sample (float).

required

Examples:

>>> from doppler.accumulator import AccF32
>>> obj = AccF32(0.0)
>>> obj.step(3.0)
>>> obj.get()
3.0

steps

steps(x: NDArray[float32]) -> None

Add all samples in input to the running sum. Equivalent to calling acc_f32_step for each element, but SIMD-vectorised on platforms that provide it (AVX-512 / AVX2 / SSE2). The loop uses JM_RESTRICT so the compiler can assume no aliasing between state and input.

Parameters:

Name Type Description Default
x NDArray[float32]

Input.

required

Examples:

>>> import numpy as np
>>> from doppler.accumulator import AccF32
>>> obj = AccF32(0.0)
>>> obj.steps(np.array([1.0, 2.0, 3.0], dtype=np.float32))
>>> obj.get()
6.0

get

get() -> float

Return the current accumulated sum without resetting state. Identical to reading the acc property directly; retained as an explicit method so call sites that need the value can be uniform with dump without a conditional.

Returns:

Type Description
float

Current value of acc (float).

Examples:

>>> from doppler.accumulator import AccF32
>>> obj = AccF32(0.0)
>>> obj.step(2.0)
>>> obj.step(3.0)
>>> obj.get()
5.0

dump

dump() -> float

Return the accumulated sum and atomically reset it to zero. This is the canonical "drain" primitive: read the period total, then start a fresh accumulation interval without a separate reset call. The zero-reset is unconditional and always writes 0.0f.

Returns:

Type Description
float

Value of acc just before the reset (float).

Examples:

>>> from doppler.accumulator import AccF32
>>> obj = AccF32(0.0)
>>> obj.step(3.0)
>>> obj.step(4.0)
>>> obj.dump()
7.0
>>> obj.get()
0.0

madd

madd(x: NDArray[float32], h: NDArray[float32]) -> None

Dot-product accumulate: acc += sum(x[i] * h[i]) for i in 0 .. min(x_len, h_len) - 1. The shorter of the two arrays limits the iteration count; no out-of-bounds access occurs. Typical use: apply a short FIR weight vector to one block of signal samples and fold the result into a running total.

Parameters:

Name Type Description Default
x NDArray[float32]

Signal samples (float32 array).

required
h NDArray[float32]

Coefficient / weight array (float32 array).

required

Examples:

>>> import numpy as np
>>> from doppler.accumulator import AccF32
>>> obj = AccF32(0.0)
>>> x = np.array([1.0, 2.0, 3.0, 4.0], dtype=np.float32)
>>> h = np.array([0.5, 0.5, 0.5, 0.5], dtype=np.float32)
>>> obj.madd(x, h)
>>> obj.get()
5.0

add2d

add2d(x: NDArray[float32]) -> None

Sum all elements of a (logically) 2-D float array into the accumulator. The array is treated as a flat C-order buffer of x_len floats regardless of the original shape; the caller is responsible for passing the total element count.

Parameters:

Name Type Description Default
x NDArray[float32]

Input array (float32, any shape — passed as flat buffer).

required

Examples:

>>> import numpy as np
>>> from doppler.accumulator import AccF32
>>> obj = AccF32(0.0)
>>> grid = np.array([[1.0, 2.0], [3.0, 4.0]], dtype=np.float32)
>>> obj.add2d(grid)
>>> obj.get()
10.0

madd2d

madd2d(x: NDArray[float32], h: NDArray[float32]) -> None

Dot-product accumulate over a flat 2-D buffer: acc += sum(x[i] * h[i]) for i in 0 .. min(x_len, h_len) - 1. Combines add2d and madd semantics — a 2-D signal array is weighted element-wise by a coefficient buffer and the scalar total is folded into the running sum.

Parameters:

Name Type Description Default
x NDArray[float32]

Signal samples (float32, flat buffer of the 2-D array).

required
h NDArray[float32]

Coefficient / weight array (float32).

required

Examples:

>>> import numpy as np
>>> from doppler.accumulator import AccF32
>>> obj = AccF32(0.0)
>>> x = np.array([1.0, 2.0, 3.0, 4.0], dtype=np.float32)
>>> h = np.array([0.5, 0.5, 0.5, 0.5], dtype=np.float32)
>>> obj.madd2d(x, h)
>>> obj.get()
5.0

state_bytes

state_bytes() -> int

Serialized state size in bytes.

get_state

get_state() -> bytes

Serialize the engine's mutable state to bytes.

set_state

set_state(blob: bytes) -> None

Restore mutable state from a get_state() blob.

destroy

destroy() -> None

Release C resources immediately.


AccCf64

Double-precision complex scalar accumulator. Maintains one running complex sum (acc) across calls to step, steps, madd, add2d, and madd2d. The signal path is double-precision complex (128-bit per sample); coefficient arrays for madd/madd2d are single-precision float to match typical FIR weight storage. Use get to read without side-effects or dump to read and zero atomically.

Parameters:

Name Type Description Default
acc complex

acc state variable.

0j

Examples:

Create with defaults:

>>> from doppler.accumulator import AccCf64
>>> obj = AccCf64(0j)
>>> obj.get_acc()
0j

Reset restores defaults:

>>> obj.set_acc(0.0)
>>> obj.reset()
>>> obj.get_acc()
0j

reset

reset() -> None

Zero the accumulator, restoring the same state as a fresh AccCf64(0j) — regardless of the value supplied to acc_cf64_create. Both the real and imaginary parts are set to 0.0. Subsequent get / dump calls return 0j until new samples are processed.

Examples:

>>> from doppler.accumulator import AccCf64
>>> obj = AccCf64(0j)
>>> obj.step(3+2j)
>>> obj.reset()
>>> obj.get_acc()
0j

step

step(x: complex) -> None

Add one complex sample to the running sum (acc += x). This is the hot-path entry for sample-by-sample processing. For block inputs prefer acc_cf64_steps to amortise call overhead.

Parameters:

Name Type Description Default
x complex

Input sample (complex).

required

Examples:

>>> from doppler.accumulator import AccCf64
>>> obj = AccCf64(0j)
>>> obj.step(3+2j)
>>> obj.get()
(3+2j)

steps

steps(x: NDArray[complex128]) -> None

Add all samples in input to the running sum. Equivalent to calling acc_cf64_step for each element; iterates element-by-element over double-precision complex samples.

Parameters:

Name Type Description Default
x NDArray[complex128]

Input.

required

Examples:

>>> import numpy as np
>>> from doppler.accumulator import AccCf64
>>> obj = AccCf64(0j)
>>> obj.steps(np.array([1+0j, 2+1j, 3+2j], dtype=np.complex128))
>>> obj.get()
(6+3j)

get

get() -> complex

Return the current accumulated sum without resetting state. Identical to reading the acc property directly; retained as an explicit method so call sites that need the value can be uniform with dump without a conditional.

Returns:

Type Description
complex

Current value of acc (complex).

Examples:

>>> from doppler.accumulator import AccCf64
>>> obj = AccCf64(0j)
>>> obj.step(2+0j)
>>> obj.step(0+3j)
>>> obj.get()
(2+3j)

dump

dump() -> complex

Return the accumulated sum and atomically reset it to zero. This is the canonical "drain" primitive: read the period total, then start a fresh accumulation interval without a separate reset call. Both real and imaginary parts are zeroed unconditionally.

Returns:

Type Description
complex

Value of acc just before the reset (complex).

Examples:

>>> from doppler.accumulator import AccCf64
>>> obj = AccCf64(0j)
>>> obj.step(3+2j)
>>> obj.step(1+1j)
>>> obj.dump()
(4+3j)
>>> obj.get()
0j

madd

madd(x: NDArray[complex128], h: NDArray[float32]) -> None

Dot-product accumulate with complex signal and float weights: acc += sum(x[i] * h[i]) for i in 0 .. min(x_len, h_len) - 1. The signal array x is double-precision complex; the coefficient array h is single-precision float (widened to double before multiplication). The shorter of the two arrays limits iteration.

Parameters:

Name Type Description Default
x NDArray[complex128]

Complex signal samples (complex128 array).

required
h NDArray[float32]

Real coefficient / weight array (float32 array).

required

Examples:

>>> import numpy as np
>>> from doppler.accumulator import AccCf64
>>> obj = AccCf64(0j)
>>> x = np.array([1+0j, 2+0j, 3+0j, 4+0j], dtype=np.complex128)
>>> h = np.array([0.5, 0.5, 0.5, 0.5], dtype=np.float32)
>>> obj.madd(x, h)
>>> obj.get()
(5+0j)

add2d

add2d(x: NDArray[complex128]) -> None

Sum all elements of a (logically) 2-D complex array into the accumulator. The array is treated as a flat C-order buffer of x_len complex128 samples regardless of the original shape; the caller is responsible for passing the total element count.

Parameters:

Name Type Description Default
x NDArray[complex128]

Input array (complex128, any shape — passed as flat buffer).

required

Examples:

>>> import numpy as np
>>> from doppler.accumulator import AccCf64
>>> obj = AccCf64(0j)
>>> grid = np.array([[1+0j, 2+0j], [3+0j, 4+0j]], dtype=np.complex128)
>>> obj.add2d(grid)
>>> obj.get()
(10+0j)

madd2d

madd2d(x: NDArray[complex128], h: NDArray[float32]) -> None

Dot-product accumulate over a flat 2-D complex buffer: acc += sum(x[i] * h[i]) for i in 0 .. min(x_len, h_len) - 1. Combines add2d and madd semantics for 2-D data — a complex signal grid is weighted element-wise by a real coefficient buffer and folded into the running sum.

Parameters:

Name Type Description Default
x NDArray[complex128]

Complex signal samples (complex128, flat buffer).

required
h NDArray[float32]

Real coefficient / weight array (float32).

required

Examples:

>>> import numpy as np
>>> from doppler.accumulator import AccCf64
>>> obj = AccCf64(0j)
>>> x = np.array([1+0j, 2+0j, 3+0j, 4+0j], dtype=np.complex128)
>>> h = np.array([0.5, 0.5, 0.5, 0.5], dtype=np.float32)
>>> obj.madd2d(x, h)
>>> obj.get()
(5+0j)

state_bytes

state_bytes() -> int

Serialized state size in bytes.

get_state

get_state() -> bytes

Serialize the engine's mutable state to bytes.

set_state

set_state(blob: bytes) -> None

Restore mutable state from a get_state() blob.

destroy

destroy() -> None

Release C resources immediately.


AccTrace

Create a length-n trace accumulator.

Parameters:

Name Type Description Default
n int

Trace length in bins. Must be > 0; returns NULL otherwise.

1024
mode Literal['mean', 'exp', 'maxhold', 'minhold']

Reduction mode index (0=mean, 1=exp, 2=maxhold, 3=minhold).

"mean"
alpha float

EMA smoothing factor used only by exp mode (0 < alpha <= 1).

0.1

Examples:

Create with defaults:

>>> from doppler.accumulator import AccTrace
>>> obj = AccTrace(n=1024, mode="mean", alpha=0.1)

n property

n: int

N.

alpha property writable

alpha: float

Alpha.

count property

count: int

Count.

mode property

mode: int

Mode.

accumulate

accumulate(p: NDArray[float32]) -> None

Fold one length-n frame into the running trace.

Parameters:

Name Type Description Default
p NDArray[float32]

Input frame (float32).

required

Examples:

>>> import numpy as np
>>> from doppler.accumulator import AccTrace
>>> acc = AccTrace(n=4, mode="mean")
>>> acc.accumulate(np.array([1, 3, 5, 7], dtype=np.float32))
>>> acc.accumulate(np.array([3, 5, 7, 9], dtype=np.float32))
>>> acc.value().tolist()
[2.0, 4.0, 6.0, 8.0]

reset

reset() -> None

Discard the running trace; the next accumulate re-seeds it.

Examples:

>>> import numpy as np
>>> from doppler.accumulator import AccTrace
>>> acc = AccTrace(n=4, mode="mean")
>>> acc.accumulate(np.ones(4, dtype=np.float32))
>>> acc.reset()
>>> acc.count
0

value

value(out: NDArray[float32] | None = ...) -> NDArray[np.float32]

Copy the current averaged trace (None before any accumulate).

Without out=, the returned array is a view into a buffer reused on the next call (see value_max_out() to size an out= buffer for an independent, alias-free result).

Parameters:

Name Type Description Default
out NDArray[float32]

Caller-provided output buffer.

...

Returns:

Type Description
NDArray[float32]

Number of samples written (n, or 0 if empty).

Examples:

>>> import numpy as np
>>> from doppler.accumulator import AccTrace
>>> acc = AccTrace(n=3, mode="maxhold")
>>> acc.accumulate(np.array([1, 5, 2], dtype=np.float32))
>>> acc.accumulate(np.array([4, 3, 6], dtype=np.float32))
>>> acc.value().tolist()
[4.0, 5.0, 6.0]

value_max_out

value_max_out() -> int

Max output length value() can produce for the current state. Use to size the out= buffer.

state_bytes

state_bytes() -> int

Serialized state size in bytes.

get_state

get_state() -> bytes

Serialize the engine's mutable state to bytes.

set_state

set_state(blob: bytes) -> None

Restore mutable state from a get_state() blob.

destroy

destroy() -> None

Release C resources immediately.