Skip to content

File burst_acq_core.h

FileList > burst_acq > burst_acq_core.h

Go to the source code of this file

BurstAcquisition — thin forwarder onto acq_core.c's shared engine. More...

  • #include "doppler/acq/acq_core.h"
  • #include "doppler/clib_common.h"
  • #include "doppler/jm_perf.h"
  • #include "doppler/cvt/cvt_core.h"

Classes

Type Name
struct dp_burst_acq_state_t
BurstAcquisition state: a pure wrapper around one shared dp_acq_state_t engine.

Public Functions

Type Name
int dp_burst_acq_configure_search_raw (dp_burst_acq_state_t * state, size_t doppler_bins, size_t n_noncoh)
Pin the search grid directly, bypassing the auto-sizing search.
size_t dp_burst_acq_consumed (const dp_burst_acq_state_t * state)
Input samples the last dp_burst_acq_push() took: the embedded engine'sdp_acq_consumed() , per call (not the cumulativeacq_result_t::samples_consumed ).
dp_burst_acq_state_t * dp_burst_acq_create (const float _Complex * preamble, size_t preamble_len, size_t reps, double fs, double cn0_dbhz, double doppler_uncertainty, double pfa, double pd, int noise_mode, double doppler_rate, size_t rate_hypotheses)
Create a burst-mode acquisition engine for any repeated preamble, given as its samples (forwards to dp_acq_create_burst() __ see its doc comment inacq_core.h for the full physics).
void dp_burst_acq_destroy (dp_burst_acq_state_t * state)
Destroy and free an instance.
void dp_burst_acq_get_state (const dp_burst_acq_state_t * state, void * blob)
size_t dp_burst_acq_push (dp_burst_acq_state_t * state, const float _Complex * x, size_t n_in, acq_result_t * result, size_t max_results)
Stream raw samples; emit one event per CFAR dump above threshold.
void dp_burst_acq_reset (dp_burst_acq_state_t * state)
Drain the input ring and reset the coherent accumulator.
int dp_burst_acq_set_max_peaks (dp_burst_acq_state_t * state, size_t n)
How many peaks a dwell may report: the peak list's capacity.
int dp_burst_acq_set_state (dp_burst_acq_state_t * state, const void * blob)
size_t dp_burst_acq_state_bytes (const dp_burst_acq_state_t * state)

Detailed Description

Composes dp_acq_state_t (native/inc/doppler/acq/acq_core.h) as an embedded pointer, built via dp_acq_create_burst() the BURST front door onto the SAME shared engine Acquisition (acq_core.h) composes via dp_acq_create_continuous(). Every function here is a direct forward to the corresponding acq_* call; the entire algorithm lives in acq_core.c exactly once (see docs/design/async-dsss-receiver.md's Acquisition/BurstAcquisition split and CLAUDE.md's "every algorithm lives in C exactly once" rule).

// a 127-sample Zadoff-Chu preamble, 8 repetitions, at 1 MS/s
float _Complex zc[127];
for (int k = 0; k < 127; k++)
  zc[k] = cexpf (-I * (float)(M_PI * 5.0 * k * (k + 1) / 127.0));
dp_burst_acq_state_t *obj = dp_burst_acq_create(zc, 127, 8, 1.0e6, 50.0,
                                          0.0, 1e-3, 0.9, 0, 0.0, 1);
acq_result_t hits[64];
size_t nh = dp_burst_acq_push(obj, samples, n_samples, hits, 64);
dp_burst_acq_destroy(obj);

Public Functions Documentation

function dp_burst_acq_configure_search_raw

Pin the search grid directly, bypassing the auto-sizing search.

int dp_burst_acq_configure_search_raw (
    dp_burst_acq_state_t * state,
    size_t doppler_bins,
    size_t n_noncoh
) 

Forwards to dp_acq_configure_search_raw() on the embedded engine (see its doc comment in acq_core.h): resizes every grid-dependent buffer/plan, re-derives the threshold ladder for the pinned grid, and clears in-flight accumulation — call between push() calls, never a substitute for one.

Parameters:

  • state Allocated engine (non-NULL).
  • doppler_bins Coherent depth to pin, in [1, reps].
  • n_noncoh Non-coherent look count to pin, in [1, ACQ_N_NONCOH_SAFETY_CEILING].

Returns:

0 on success, -1 if either argument is out of range or an allocation fails (the engine keeps its prior grid on failure).

>>> import numpy as np
>>> from doppler.acquire import BurstAcquisition
>>> from doppler.wfm import PN, mls_poly
>>> code = np.asarray(PN(poly=mls_poly(5), seed=1,
...                      length=5).generate(31)).astype(np.uint8)
>>> s0 = np.repeat(np.where(code & 1, -1.0, 1.0), 4).astype(
...     np.complex64)
>>> b = BurstAcquisition(s0, reps=8, fs=4e6, cn0_dbhz=50.0)
>>> b.configure_search_raw(doppler_bins=4, n_noncoh=2)  # pin the grid
>>> b.doppler_bins, b.n_noncoh
(4, 2)
>>> burst = np.tile(np.roll(s0, 17), 8).astype(np.complex64)
>>> b.push(burst)[0][:2]      # detects at the pinned grid
(0, 17)


function dp_burst_acq_consumed

Input samples the last dp_burst_acq_push() took: the embedded engine'sdp_acq_consumed() , per call (not the cumulativeacq_result_t::samples_consumed ).

size_t dp_burst_acq_consumed (
    const dp_burst_acq_state_t * state
) 

Parameters:

  • state Must be non-NULL.

function dp_burst_acq_create

Create a burst-mode acquisition engine for any repeated preamble, given as its samples (forwards to dp_acq_create_burst() __ see its doc comment inacq_core.h for the full physics).

dp_burst_acq_state_t * dp_burst_acq_create (
    const float _Complex * preamble,
    size_t preamble_len,
    size_t reps,
    double fs,
    double cn0_dbhz,
    double doppler_uncertainty,
    double pfa,
    double pd,
    int noise_mode,
    double doppler_rate,
    size_t rate_hypotheses
) 

One period of preamble_len complex samples at fs, repeated up to reps times: a chirp, a Zadoff-Chu sequence, shaped PSK, or a PN code mapped by dp_bin_to_nrz() and held spc samples each (at fs = chip_rate*spc). One chip is one sample: sf = preamble_len, spc = 1, chip_rate = fs, and code_phase is the delay into the repetition, in samples (for a Zadoff-Chu preamble, plus u^-1 mod n samples per native bin of Doppler the search did not resolve before correlating). fs of 1 is normalized units, where Doppler is in cycles/sample and cn0_dbhz is the per-sample SNR in dB.

Parameters:

  • preamble One period of the preamble, preamble_len samples; not all zero, every sample finite.
  • preamble_len Samples per repetition (>= 1).
  • reps Max coherent repetitions (>= 1).
  • fs Sample rate in Hz (> 0); 1 for normalized units.
  • cn0_dbhz Design carrier-to-noise density in dB-Hz, of the preamble's mean power: any finite value, or NaN (ACQ_CN0_NONE) for no design point size for the whole preamble.
  • doppler_uncertainty One-sided Doppler search half-range in Hz.
  • pfa Target system false-alarm probability (0,1).
  • pd Target detection probability (0,1).
  • noise_mode CFAR mode index: 0=mean, 1=median, 2=min, 3=max.
  • doppler_rate Doppler rate in Hz/s (>= 0) that caps the coherent depth at f_epoch/sqrt(2*doppler_rate) repetitions (doppler#1482); 0 is no bound.
  • rate_hypotheses Doppler-rate hypotheses to search (0 = auto, else 1 to ACQ_RATE_HYPS_MAX); 1 searches none. Above 1 the engine COMPENSATES the rate rather than only capping the depth: doppler_rate becomes the span [-R, +R], the coherent block is de-chirped at this many rates across it, and the depth is bounded by the residual R/(n - 1) see dp_acq_create_burst(), which owns the semantics, including 0 = auto (the engine chooses the count; read it back from engine->rate_hyps). Needs a non-zero doppler_rate.

Returns:

Heap-allocated state, or NULL on bad arguments / allocation failure.

>>> import numpy as np
>>> from doppler.cvt import bin_to_nrz
>>> from doppler.acquire import BurstAcquisition
>>> from doppler.wfm import PN, mls_poly
>>> code = np.asarray(PN(poly=mls_poly(5), seed=1,
...                      length=5).generate(31)).astype(np.uint8)
>>> nrz = np.zeros(31, np.float32)
>>> _ = bin_to_nrz(code & 1, nrz)
>>> s0 = np.repeat(nrz, 4).astype(np.complex64)    # 4 samples a chip
>>> burst = np.tile(np.roll(s0, 17), 24)
>>> b = BurstAcquisition(s0, reps=8, fs=4e6, cn0_dbhz=50.0)
>>> b.push(burst)[0][:2]      # detects (Doppler bin, delay in samples)
(0, 17)

Any repeated preamble is searched the same way -- here a 127-sample
Zadoff-Chu sequence, in normalized units:

>>> k = np.arange(127)
>>> zc = np.exp(-1j * np.pi * 5 * k * (k + 1) / 127).astype(
...     np.complex64)
>>> z = BurstAcquisition(zc, reps=8)
>>> z.code_bins                # samples per repetition
127
>>> z.push(np.tile(np.roll(zc, 40), 10))[0][:2]
(0, 40)

A carrier that sweeps caps the coherent depth; searching Doppler-rate
hypotheses compensates it instead, and buys the depth back:

>>> import warnings
>>> def make(**kw):
...     with warnings.catch_warnings():
...         warnings.simplefilter("ignore")   # under-powered, below
...         return BurstAcquisition(zc, reps=16, fs=1.0, cn0_dbhz=-12.5,
...                                 doppler_rate=9e-7, **kw)
>>> make().doppler_bins, make().underpowered      # the rate caps it
(5, True)
>>> c = make(rate_hypotheses=9)
>>> c.rate_hypotheses, c.doppler_bins, c.underpowered
(9, 9, False)


function dp_burst_acq_destroy

Destroy and free an instance.

void dp_burst_acq_destroy (
    dp_burst_acq_state_t * state
) 

Parameters:

  • state May be NULL.

function dp_burst_acq_get_state

void dp_burst_acq_get_state (
    const dp_burst_acq_state_t * state,
    void * blob
) 

function dp_burst_acq_push

Stream raw samples; emit one event per CFAR dump above threshold.

size_t dp_burst_acq_push (
    dp_burst_acq_state_t * state,
    const float _Complex * x,
    size_t n_in,
    acq_result_t * result,
    size_t max_results
) 

Forwards to dp_acq_push() on the embedded engine (see its doc comment in acq_core.h for the framing/CFAR mechanics). Each event carries the peak's Doppler bin and code phase (the two search axes), its CFAR statistic, and an estimated C/N0 — see acq_result_t.

Python's push() has room for 1024 events a call, with dp_acq_push()'s rule for what a push that fills it loses.

Parameters:

  • state Allocated engine (non-NULL).
  • x Raw input, interleaved CF32, n_in complex samples.
  • n_in Number of complex input samples.
  • result Output array for detection events.
  • max_results Capacity of result: dp_acq_push()'s contract, so a full result never loses input and dp_burst_acq_consumed() says where to resume.

Returns:

Number of events written (0 … max_results).

>>> import numpy as np
>>> from doppler.acquire import BurstAcquisition
>>> from doppler.wfm import PN, mls_poly
>>> code = np.asarray(PN(poly=mls_poly(5), seed=1,
...                      length=5).generate(31)).astype(np.uint8)
>>> s0 = np.repeat(np.where(code & 1, -1.0, 1.0), 4).astype(
...     np.complex64)
>>> burst = np.tile(np.roll(s0, 17), 24).astype(np.complex64)
>>> b = BurstAcquisition(s0, reps=8, fs=4e6, cn0_dbhz=50.0)
>>> b.push(burst)[0][:2]      # (Doppler bin, delay in samples)
(0, 17)


function dp_burst_acq_reset

Drain the input ring and reset the coherent accumulator.

void dp_burst_acq_reset (
    dp_burst_acq_state_t * state
) 

Forwards to dp_acq_reset() on the embedded engine: discards any buffered samples that have not yet completed a frame and clears the non-coherent power accumulator and dwell bookkeeping, so the next push() begins a fresh search from an empty ring. Construction parameters are untouched.

Parameters:

  • state Must be non-NULL.
    >>> import numpy as np
    >>> from doppler.acquire import BurstAcquisition
    >>> from doppler.wfm import PN, mls_poly
    >>> code = np.asarray(PN(poly=mls_poly(5), seed=1,
    ...                      length=5).generate(31)).astype(np.uint8)
    >>> s0 = np.repeat(np.where(code & 1, -1.0, 1.0), 4).astype(
    ...     np.complex64)
    >>> burst = np.tile(np.roll(s0, 17), 24).astype(np.complex64)
    >>> b = BurstAcquisition(s0, reps=8, fs=4e6, cn0_dbhz=50.0)
    >>> _ = b.push(burst[:100])   # a partial frame, buffered mid-stream
    >>> b.reset()                 # drop it before it can bias a detection
    >>> b.push(burst)[0][:2]      # (Doppler bin, delay in samples)
    (0, 17)
    

function dp_burst_acq_set_max_peaks

How many peaks a dwell may report: the peak list's capacity.

int dp_burst_acq_set_max_peaks (
    dp_burst_acq_state_t * state,
    size_t n
) 

Forwards to dp_acq_set_max_peaks() on the embedded engine (see its doc comment in acq_core.h): one is the classic gated maximum; more is the list of docs/design/async-dsss-receiver.md §7.1 every peak above the same gate, strongest first, an exclusion zone of one Doppler bin by the reference's first autocorrelation null (one chip for a PN code) around each, and the two-epoch rule for a peak at an already-listed code phase. Each listed peak is one result from push().

Parameters:

  • state Allocated engine (non-NULL).
  • n 1 … ACQ_MAX_PEAKS.

Returns:

0, or -1 (engine untouched) when n is out of range.

>>> import numpy as np
>>> from doppler.acquire import BurstAcquisition
>>> code = (np.arange(31) * 5 % 2).astype(np.uint8)
>>> s0 = np.repeat(np.where(code & 1, -1.0, 1.0), 4).astype(
...     np.complex64)
>>> b = BurstAcquisition(s0, reps=8, fs=4e6, cn0_dbhz=50.0)
>>> b.set_max_peaks(4)
>>> b.max_peaks
4


function dp_burst_acq_set_state

int dp_burst_acq_set_state (
    dp_burst_acq_state_t * state,
    const void * blob
) 

function dp_burst_acq_state_bytes

size_t dp_burst_acq_state_bytes (
    const dp_burst_acq_state_t * state
) 


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