Skip to content

File detector_core.h

FileList > detector > detector_core.h

Go to the source code of this file

1-D streaming signal detector with FFT-based correlation, integrate-and-dump, and configurable noise-referenced threshold. More...

  • #include "doppler/corr/corr_core.h"
  • #include "doppler/dp_state.h"
  • #include "doppler/f32_buffer/f32_buffer_core.h"

Classes

Type Name
struct det_result_t
Detection event returned by dp_detector_push() .
struct dp_detector_state_t
1-D signal detector state.

Public Types

Type Name
enum det_noise_mode_t

Public Functions

Type Name
size_t dp_detector_consumed (const dp_detector_state_t * state)
Input samples the last dp_detector_push() took.
dp_detector_state_t * dp_detector_create (const float _Complex * ref, size_t ref_len, size_t dwell, size_t noise_lo, size_t noise_hi, det_noise_mode_t noise_mode, float threshold, int nthreads)
Allocate a 1-D streaming signal detector backed by an FFT correlator. Combines a dp_corr_state_t with a double-mapped ring buffer so that arbitrary chunk sizes can be pushed. After every int-dump the peak-to-noise test statistic is compared againstthreshold ; adet_result_t is emitted when it passes. Settingthreshold to 0.0 unconditionally fires on every dump. The ring capacity is next_pow_two(max(n, 512)) complex samples.
void dp_detector_destroy (dp_detector_state_t * state)
Destroy and free a detector instance.
void dp_detector_get_state (const dp_detector_state_t * state, void * blob)
size_t dp_detector_push (dp_detector_state_t * state, const float _Complex * in, size_t n_in, det_result_t * result, size_t max_results)
Stream an arbitrary-length CF32 chunk through the detector pipeline. Takes the input in order, runs each complete n-sample frame through the correlator, and on every int-dump computes the test statistic peak_mag / noise_est. Detections that pass the threshold are appended to the Python return list as (lag, peak_mag, noise_est, test_stat) tuples. In Python the result is always a list, even when empty.
void dp_detector_reset (dp_detector_state_t * state)
Reset the correlator, the carry, and last-corr flag. Discards any partial frame carried between pushes and zeroes the coherent accumulator. Equivalent to starting fresh from the same reference without rebuilding any internal object.
void dp_detector_set_ref (dp_detector_state_t * state, const float _Complex * ref)
Replace the reference signal and recompute conj(FFT(ref)).
int dp_detector_set_state (dp_detector_state_t * state, const void * blob)
void dp_detector_set_threshold (dp_detector_state_t * state, float threshold)
Change the threshold without rebuilding the object.
size_t dp_detector_state_bytes (const dp_detector_state_t * state)

Macros

Type Name
define DETECTOR_STATE_MAGIC [**DP\_FOURCC**](dp__state_8h.md#define-dp_fourcc) ('D','E','T','1')
define DETECTOR_STATE_VERSION 2u
define DET_NOISE_MODE_T_DEFINED
Selects how noise power is estimated from the correlation magnitude vector over bins [noise_lo, noise_hi].

Detailed Description

Wraps a dp_corr_state_t (FFT correlator + coherent int-dump) behind the ring's framed face (DECLARE_DP_BUFFER_FRAMES) so that arbitrary-length sample streams can be fed in any chunk size: the detections are a function of the input stream, not of how it was split into calls. After every int-dump a test statistic is computed:

test_stat = peak_mag / noise_est

where peak_mag = max|R[τ]| and noise_est is an aggregate (mean, median, min, or max) of |R[τ]| over a user-supplied bin range [noise_lo, noise_hi]. A detection event is emitted when test_stat > threshold (or whenever threshold == 0.0, which means "always fire").

The detector operates as a single-threaded object; do not call dp_detector_push() concurrently from multiple threads.

Lifecycle:

float _Complex ref[N] = { ... };
dp_detector_state_t *det = dp_detector_create(ref, N, 1,
    1, N-1, DET_NOISE_MEAN, 0.0f, 1);
det_result_t results[64];
// stream loop: a full results[] stops a push, and the input it did not
// take is offered again -- dp_detector_consumed(det) says where it stopped
while (recv(chunk, CHUNK_SZ)) {
    for (size_t off = 0; off < CHUNK_SZ; off += dp_detector_consumed(det)) {
        size_t n = dp_detector_push(det, chunk + off, CHUNK_SZ - off,
                                    results, 64);
        for (size_t i = 0; i < n; i++)
            printf("lag=%zu stat=%.2f\n", results[i].lag,
                   results[i].test_stat);
    }
}
dp_detector_destroy(det);

Public Types Documentation

enum det_noise_mode_t

enum det_noise_mode_t {
    DET_NOISE_MEAN = 0,
    DET_NOISE_MEDIAN = 1,
    DET_NOISE_MIN = 2,
    DET_NOISE_MAX = 3
};

Public Functions Documentation

function dp_detector_consumed

Input samples the last dp_detector_push() took.

size_t dp_detector_consumed (
    const dp_detector_state_t * state
) 

Equal to its n_in unless result filled up; then the caller resumes at in + consumed. 0 after create, reset and set_state.

Parameters:

  • state Must be non-NULL.

function dp_detector_create

Allocate a 1-D streaming signal detector backed by an FFT correlator. Combines a dp_corr_state_t with a double-mapped ring buffer so that arbitrary chunk sizes can be pushed. After every int-dump the peak-to-noise test statistic is compared againstthreshold ; adet_result_t is emitted when it passes. Settingthreshold to 0.0 unconditionally fires on every dump. The ring capacity is next_pow_two(max(n, 512)) complex samples.

dp_detector_state_t * dp_detector_create (
    const float _Complex * ref,
    size_t ref_len,
    size_t dwell,
    size_t noise_lo,
    size_t noise_hi,
    det_noise_mode_t noise_mode,
    float threshold,
    int nthreads
) 

Parameters:

  • ref Reference signal, CF32 ndarray of length ref_len.
  • ref_len Reference / FFT length in complex samples.
  • dwell Int-dump depth; must be >= 1.
  • noise_lo Lower noise bin index (inclusive, 0-based).
  • noise_hi Upper noise bin index (inclusive, < n). A value at or beyond the window clamps to n - 1, so the default sentinel selects the full window.
  • noise_mode Noise aggregation: "mean", "median", "min", or "max".
  • threshold Test-stat gate; 0.0 = always emit.
  • nthreads Accepted for API compatibility; ignored.

Returns:

Heap-allocated state, or NULL on allocation failure.

>>> from doppler.spectral import CorrDetector
>>> import numpy as np
>>> ref = np.zeros(8, dtype=np.complex64); ref[0] = 1.0
>>> det = CorrDetector(ref=ref, dwell=1, noise_lo=1, noise_hi=7,
...                noise_mode="mean", threshold=0.0)
>>> det.n, det.dwell, det.ring_cap
(8, 1, 512)


function dp_detector_destroy

Destroy and free a detector instance.

void dp_detector_destroy (
    dp_detector_state_t * state
) 

Parameters:

  • state May be NULL.

function dp_detector_get_state

void dp_detector_get_state (
    const dp_detector_state_t * state,
    void * blob
) 

function dp_detector_push

Stream an arbitrary-length CF32 chunk through the detector pipeline. Takes the input in order, runs each complete n-sample frame through the correlator, and on every int-dump computes the test statistic peak_mag / noise_est. Detections that pass the threshold are appended to the Python return list as (lag, peak_mag, noise_est, test_stat) tuples. In Python the result is always a list, even when empty.

size_t dp_detector_push (
    dp_detector_state_t * state,
    const float _Complex * in,
    size_t n_in,
    det_result_t * result,
    size_t max_results
) 

Python's push() has room for 1024 detections a call, so a push that completes at most 1024 frames, counting the carry, loses nothing. One that fills the room loses the rest of its input, every later frame whether or not it would have made a detection, unless the rest is shorter than a frame: that is kept as the carry. The next push stays on the frame grid only when what was lost is a whole number of frames; otherwise its lags are off by the remainder. Before v0.66 the room was 64, and a push past it kept up to ring_cap/n - 1 of those frames for the next call and dropped the rest. #1992 and just-buildit/just-makeit#2184 track sizing the list to the call.

Parameters:

  • state Allocated detector (non-NULL).
  • in CF32 input chunk of arbitrary length.
  • n_in Number of input samples in in.
  • result Caller-supplied array of at least max_results det_result_t structs; filled on return.
  • max_results Capacity of result (maximum detections to emit). A full result never loses input: a frame yields at most one detection, and once result is full the push takes the rest of its input only if the rest completes no frame (it is then the carry); otherwise it takes nothing more, stopping on the boundary of its last frame. dp_detector_consumed() says how many samples it took, and the caller offers the rest again. Input that runs out mid-frame is the carry, held inside (fewer than n samples). With room 0 the push is full from the start, so a resume loop needs room for at least one; with that, a push of any input takes at least one sample.

Returns:

Number of det_result_t entries written to result.

>>> from doppler.spectral import CorrDetector
>>> import numpy as np
>>> ref = np.zeros(8, dtype=np.complex64); ref[0] = 1.0
>>> det = CorrDetector(ref=ref, dwell=1, noise_lo=1, noise_hi=7,
...                noise_mode="mean", threshold=0.0)
>>> results = det.push(np.ones(8, dtype=np.complex64))
>>> len(results)
1
>>> lag, peak, noise, stat = results[0]
>>> lag, round(peak, 4), round(noise, 4), round(stat, 4)
(0, 1.0, 1.0, 1.0)


function dp_detector_reset

Reset the correlator, the carry, and last-corr flag. Discards any partial frame carried between pushes and zeroes the coherent accumulator. Equivalent to starting fresh from the same reference without rebuilding any internal object.

void dp_detector_reset (
    dp_detector_state_t * state
) 

>>> from doppler.spectral import CorrDetector
>>> import numpy as np
>>> ref = np.zeros(8, dtype=np.complex64); ref[0] = 1.0
>>> det = CorrDetector(ref=ref, dwell=1, noise_lo=1, noise_hi=7,
...                noise_mode="mean", threshold=0.0)
>>> _ = det.push(np.ones(8, dtype=np.complex64))
>>> det.reset()
>>> det.count
0

function dp_detector_set_ref

Replace the reference signal and recompute conj(FFT(ref)).

void dp_detector_set_ref (
    dp_detector_state_t * state,
    const float _Complex * ref
) 

Also resets (see dp_detector_reset()). The new reference must have the same length n that was passed to dp_detector_create().

Parameters:

  • state Must be non-NULL.
  • ref New reference, CF32, length state->n.

function dp_detector_set_state

int dp_detector_set_state (
    dp_detector_state_t * state,
    const void * blob
) 

function dp_detector_set_threshold

Change the threshold without rebuilding the object.

void dp_detector_set_threshold (
    dp_detector_state_t * state,
    float threshold
) 

Parameters:

  • state Must be non-NULL.
  • threshold New threshold; 0.0 = always fire.

function dp_detector_state_bytes

size_t dp_detector_state_bytes (
    const dp_detector_state_t * state
) 

Macro Definition Documentation

define DETECTOR_STATE_MAGIC

#define DETECTOR_STATE_MAGIC `DP_FOURCC ('D','E','T','1')`

define DETECTOR_STATE_VERSION

#define DETECTOR_STATE_VERSION `2u`

define DET_NOISE_MODE_T_DEFINED

Selects how noise power is estimated from the correlation magnitude vector over bins [noise_lo, noise_hi].

#define DET_NOISE_MODE_T_DEFINED 

The test statistic is peak_mag / noise_est. A zero noise_est (e.g., when all bins in the range are zero) yields test_stat = 0.



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