Skip to content

File cic_core.h

FileList > cic > cic_core.h

Go to the source code of this file

CIC decimation filter — 4-stage, M=1, UQ16 integer pipeline. More...

  • #include "clib_common.h"
  • #include "jm_perf.h"

Classes

Type Name
struct cic_state_t
CIC filter state.

Public Functions

Type Name
cic_state_t * cic_create (uint32_t R)
Create a 4-stage, M=1 CIC decimation filter. Allocates the state struct on the heap and pre-computes the normalisation right-shift (CIC_N * log2(R) bits). All integrator and comb accumulators are zeroed; the first output arrives after R input samples. Returns NULL for invalid R or OOM.
JM_FORCEINLINE JM_HOT size_t cic_decimate (cic_state_t * state, const float complex * in, size_t n_in, float complex * out)
Decimate a block of CF32 samples through the CIC pipeline. Each sample is converted to offset-binary UQ16, pushed through CIC_N integrators (unsigned wrapping), and when the phase counter reaches R the integrated value is passed through CIC_N M=1 comb stages and converted back to CF32. State persists between calls. Feeding blocks that are multiples of R gives predictable output counts (exactly n_in/R samples per block).
size_t cic_decimate_max_out (cic_state_t * state)
Upper bound on decimate output — returns 0 (lazy-alloc signal).
void cic_destroy (cic_state_t * state)
void cic_reconfigure (cic_state_t * state, uint32_t R)
Change the decimation ratio in place and reset all filter state. Recomputes the normalisation shift (CIC_N * log2(R)) and zeros all accumulators so the filter behaves exactly like a freshly created one with the new R. Silently ignores R values that are not a power-of-two in [2, 4096] — the state is left unchanged in that case.
void cic_reset (cic_state_t * state)
Zero all integrator and comb accumulators; preserve R and shift. The first output sample after reset arrives after R more input samples, matching post-create behaviour. Use between signal bursts to eliminate transient artefacts caused by residual pipeline state.

Macros

Type Name
define CIC_N 4

Detailed Description

Fixed design parameters: N = 4 stages (~77 dB alias rejection at f_p = 0.1 * f_out) M = 1 (differential delay — one-sample comb) R = power-of-two decimation ratio (enforced at create time)

Input/output boundary: CF32 (float _Complex), matching the doppler default signal type. Internally, each sample is converted to UQ16 — offset-binary: v_q15 + 32768 → [0, 65535] in a uint64_t — giving 48 bits of headroom for the pipeline gain of N * log2(R) bits. For R <= 4096 (log2 = 12) the gain is 48 bits; max accumulation = 65535 * R^N = (2^16 - 1) * 2^48 = 2^64 - 2^48 < 2^64, so no overflow occurs.

All arithmetic is unsigned: inputs are non-negative [0, 65535], wrapping is defined (mod 2^64), and the output decode subtracts the offset in floating-point — no signed integer casts anywhere in the hot path.

The unsigned modular-arithmetic CIC property guarantees exact outputs: every intermediate overflow in the integrators cancels in the comb stages, provided the true result fits in 64 bits. No saturation, no range checks, no floating-point in the inner loop.

With M=1 and N fixed, the entire comb state is four uint64_t values per channel — no heap allocation beyond the state struct itself.

Alias rejection : ~77 dB at f_p = 0.1 * f_out (independent of R) Passband droop : ~0.57 dB at f_p = 0.1 * f_out (independent of R) Output precision: 16-bit Q15 (independent of R and N)

cic_state_t *cic = cic_create(16);   // R=16, N=4, M=1
size_t n_out = cic_decimate(cic, in, 1024, out);
cic_destroy(cic);

Public Functions Documentation

function cic_create

Create a 4-stage, M=1 CIC decimation filter. Allocates the state struct on the heap and pre-computes the normalisation right-shift (CIC_N * log2(R) bits). All integrator and comb accumulators are zeroed; the first output arrives after R input samples. Returns NULL for invalid R or OOM.

cic_state_t * cic_create (
    uint32_t R
) 

Parameters:

  • R Decimation ratio. Must be a power of two in [2, 4096]. Returns NULL for R=0, non-power-of-two, or R > 4096.

Returns:

Heap-allocated state, or NULL on invalid R or OOM.

>>> from doppler.resample import CIC
>>> cic = CIC(R=16)
>>> cic.R, cic.shift
(16, 16)

function cic_decimate

Decimate a block of CF32 samples through the CIC pipeline. Each sample is converted to offset-binary UQ16, pushed through CIC_N integrators (unsigned wrapping), and when the phase counter reaches R the integrated value is passed through CIC_N M=1 comb stages and converted back to CF32. State persists between calls. Feeding blocks that are multiples of R gives predictable output counts (exactly n_in/R samples per block).

JM_FORCEINLINE  JM_HOT size_t cic_decimate (
    cic_state_t * state,
    const float complex * in,
    size_t n_in,
    float complex * out
) 

Parameters:

  • state Pointer to a valid cic_state_t.
  • in CF32 input block.
  • n_in Number of input samples.
  • out Output buffer; must hold at least n_in elements.

Returns:

CF32 output array; length is floor((phase + n_in) / R).

>>> from doppler.resample import CIC
>>> import numpy as np
>>> cic = CIC(R=16)
>>> for _ in range(4):
...     _ = cic.decimate(np.zeros(16, dtype=np.complex64))
>>> y = cic.decimate(np.zeros(16, dtype=np.complex64))
>>> y.tolist(), y.dtype
([0j], dtype('complex64'))

function cic_decimate_max_out

Upper bound on decimate output — returns 0 (lazy-alloc signal).

size_t cic_decimate_max_out (
    cic_state_t * state
) 

The Python extension allocates n_in elements on the first call. Since n_in >= ceil(n_in/R) = n_out for all R >= 1, the buffer is always large enough as long as block size stays consistent.


function cic_destroy

void cic_destroy (
    cic_state_t * state
) 

Free resources. NULL is a no-op.


function cic_reconfigure

Change the decimation ratio in place and reset all filter state. Recomputes the normalisation shift (CIC_N * log2(R)) and zeros all accumulators so the filter behaves exactly like a freshly created one with the new R. Silently ignores R values that are not a power-of-two in [2, 4096] — the state is left unchanged in that case.

void cic_reconfigure (
    cic_state_t * state,
    uint32_t R
) 

Parameters:

>>> from doppler.resample import CIC
>>> cic = CIC(R=4)
>>> cic.reconfigure(8)
>>> cic.R, cic.shift
(8, 12)

function cic_reset

Zero all integrator and comb accumulators; preserve R and shift. The first output sample after reset arrives after R more input samples, matching post-create behaviour. Use between signal bursts to eliminate transient artefacts caused by residual pipeline state.

void cic_reset (
    cic_state_t * state
) 

>>> from doppler.resample import CIC
>>> cic = CIC(R=16)
>>> cic.reset()
>>> cic.R
16

Macro Definition Documentation

define CIC_N

#define CIC_N `4`

Fixed stage count. Alias rejection ~19.2 dB/stage at f_p=0.1.



The documentation for this class was generated from the following file native/inc/cic/cic_core.h