Skip to content

File boxcar_core.h

FileList > boxcar > boxcar_core.h

Go to the source code of this file

Boxcar (rectangular) moving-average filter — cf32, fixed window. More...

  • #include "doppler/clib_common.h"
  • #include "doppler/dp_state.h"
  • #include "doppler/jm_perf.h"

Classes

Type Name
struct dp_boxcar_state_t
Boxcar moving-average state (cf32).

Public Functions

Type Name
dp_boxcar_state_t * dp_boxcar_create (size_t len, double gain)
Create a boxcar instance.
void dp_boxcar_destroy (dp_boxcar_state_t * s)
Destroy a boxcar instance.
JM_FORCEINLINE double dp_boxcar_get_gain (const dp_boxcar_state_t * s)
Current output gain.
void dp_boxcar_get_state (const dp_boxcar_state_t * s, void * blob)
Serialize the full state into blob .
void dp_boxcar_init (dp_boxcar_state_t * s, size_t len, double gain)
Initialise a boxcar in place (no allocation).
void dp_boxcar_reset (dp_boxcar_state_t * s)
Clear the window (zero the ring and the running sum); keep the configured length and gain.
JM_FORCEINLINE void dp_boxcar_set_gain (dp_boxcar_state_t * s, double gain)
Set the output gain; refresh the cached scale.
int dp_boxcar_set_state (dp_boxcar_state_t * s, const void * blob)
Restore state; DP_OK, or DP_ERR_INVALID if the envelope rejects.
size_t dp_boxcar_state_bytes (const dp_boxcar_state_t * s)
Serialized-state byte size.
JM_FORCEINLINE JM_HOT float _Complex dp_boxcar_step (dp_boxcar_state_t * s, float _Complex x)
Slide the window by one sample; return the gained moving average.
void dp_boxcar_steps (dp_boxcar_state_t * s, const float _Complex * x, float _Complex * out, size_t n)
Filter a block: write the gained moving average of each sample.

Macros

Type Name
define BOXCAR_MAX_LEN 64
define BOXCAR_STATE_MAGIC [**DP\_FOURCC**](dp__state_8h.md#define-dp_fourcc) ('B', 'O', 'X', 'C')
define BOXCAR_STATE_VERSION 1u

Detailed Description

A sliding-window moving average over the last len complex samples: one output per input sample (no rate change). Each step adds the new sample and subtracts the sample leaving the window, so it is O(1) per sample regardless of window length (a running window sum, not a re-summed convolution). The output is the window mean times an optional output gain: gain·(Σ window)/ len. Because the step must multiply by 1/len anyway, the gain is folded into a single cached scale = gain/len, so applying it is free — a composing loop (e.g. a carrier arm with an AGC) can push its gain into the boxcar and avoid a second multiply.

The delay ring is a fixed in-struct array (BOXCAR_MAX_LEN), so the state is pointer-free POD: it embeds by value into a composing object (a carrier loop's I/Q arm, a smoother ahead of a detector) and serializes as a whole-struct snapshot. A window longer than BOXCAR_MAX_LEN is rejected at create/init time. (A bounded window sum also stays numerically clean — unlike a never-reset CIC integrator-comb, whose integrator drifts in float.)

Until the ring fills (the first len-1 samples after a reset) the ring holds zeros, so the average is taken over a partial window and the output ramps in.

>>> import numpy as np
>>> from doppler.filter import MovingAverage
>>> ma = MovingAverage(2)              # 2-sample window, unit gain
>>> ma.steps(np.ones(3, np.complex64)).real.tolist()
[0.5, 1.0, 1.0]
>>> ma2 = MovingAverage(2, gain=2.0)   # gain folded into the mean
>>> ma2.steps(np.ones(3, np.complex64)).real.tolist()
[1.0, 2.0, 2.0]

Public Functions Documentation

function dp_boxcar_create

Create a boxcar instance.

dp_boxcar_state_t * dp_boxcar_create (
    size_t len,
    double gain
) 

Parameters:

  • len Window length (1 .. BOXCAR_MAX_LEN; default 4).
  • gain Output gain (default 1.0).

Returns:

Heap state, or NULL on invalid length / allocation failure.

Note:

Caller must call dp_boxcar_destroy() when done.


function dp_boxcar_destroy

Destroy a boxcar instance.

void dp_boxcar_destroy (
    dp_boxcar_state_t * s
) 

Parameters:

  • s May be NULL.

function dp_boxcar_get_gain

Current output gain.

JM_FORCEINLINE double dp_boxcar_get_gain (
    const dp_boxcar_state_t * s
) 


function dp_boxcar_get_state

Serialize the full state into blob .

void dp_boxcar_get_state (
    const dp_boxcar_state_t * s,
    void * blob
) 


function dp_boxcar_init

Initialise a boxcar in place (no allocation).

void dp_boxcar_init (
    dp_boxcar_state_t * s,
    size_t len,
    double gain
) 

Parameters:

  • s State to initialise. Must be non-NULL.
  • len Window length; clamped to [1, BOXCAR_MAX_LEN].
  • gain Output gain (folded into the averaging scale).

function dp_boxcar_reset

Clear the window (zero the ring and the running sum); keep the configured length and gain.

void dp_boxcar_reset (
    dp_boxcar_state_t * s
) 

Returns the filter to its just-constructed state: the delay ring and the running window sum are zeroed while len and gain are preserved, so the next len-1 outputs ramp in over a partial window exactly as they did on a fresh instance. Call it at a segment boundary so samples from one capture do not average into an unrelated next one.

Parameters:

  • s Boxcar state. Must be non-NULL.
    >>> import numpy as np
    >>> from doppler.filter import MovingAverage
    >>> ma = MovingAverage(2)                         # 2-sample window
    >>> _ = ma.steps(np.ones(4, np.complex64))        # fill the window
    >>> ma.reset()                                    # clear it
    >>> round(ma.step(1 + 0j).real, 4)                # ramps in from empty
    0.5
    

function dp_boxcar_set_gain

Set the output gain; refresh the cached scale.

JM_FORCEINLINE void dp_boxcar_set_gain (
    dp_boxcar_state_t * s,
    double gain
) 

Parameters:

  • s Boxcar state. Must be non-NULL.
  • gain New output gain (folded into scale = gain / len).

function dp_boxcar_set_state

Restore state; DP_OK, or DP_ERR_INVALID if the envelope rejects.

int dp_boxcar_set_state (
    dp_boxcar_state_t * s,
    const void * blob
) 


function dp_boxcar_state_bytes

Serialized-state byte size.

size_t dp_boxcar_state_bytes (
    const dp_boxcar_state_t * s
) 


function dp_boxcar_step

Slide the window by one sample; return the gained moving average.

JM_FORCEINLINE  JM_HOT float _Complex dp_boxcar_step (
    dp_boxcar_state_t * s,
    float _Complex x
) 

O(1): add x, drop the sample leaving the window, return acc · scale (= gain · acc / len) — one multiply.

Parameters:

  • s Boxcar state. Must be non-NULL.
  • x One input sample.

Returns:

The gained window mean after admitting x.

>>> from doppler.filter import MovingAverage
>>> ma = MovingAverage(2)   # 2-sample sliding window, unit gain
>>> [round(ma.step(v).real, 4) for v in (1 + 0j, 3 + 0j, 3 + 0j)]
[0.5, 2.0, 3.0]


function dp_boxcar_steps

Filter a block: write the gained moving average of each sample.

void dp_boxcar_steps (
    dp_boxcar_state_t * s,
    const float _Complex * x,
    float _Complex * out,
    size_t n
) 

Applies dp_boxcar_step() to each input sample in turn, so the window sum and ring carry across the block exactly as they would sample by sample — a stream can be processed in frames of any size with no seam. Immediately after a reset the first len-1 outputs average over a partial (still filling) window and ramp in.

Parameters:

  • s Boxcar state. Must be non-NULL.
  • x Input samples.
  • out Output (gained window means); may alias x.
  • n Number of samples.
    >>> import numpy as np
    >>> from doppler.filter import MovingAverage
    >>> ma = MovingAverage(3)                          # 3-sample window
    >>> x = np.ones(5, np.complex64)                   # unit step input
    >>> [round(v, 4) for v in ma.steps(x).real.tolist()]
    [0.3333, 0.6667, 1.0, 1.0, 1.0]
    

Macro Definition Documentation

define BOXCAR_MAX_LEN

#define BOXCAR_MAX_LEN `64`

define BOXCAR_STATE_MAGIC

#define BOXCAR_STATE_MAGIC `DP_FOURCC ('B', 'O', 'X', 'C')`

define BOXCAR_STATE_VERSION

#define BOXCAR_STATE_VERSION `1u`


The documentation for this class was generated from the following file native/inc/doppler/boxcar/boxcar_core.h