File burst_capture_core.h¶
FileList > burst_capture > burst_capture_core.h
Go to the source code of this file
BurstCapture — acquisition's output turned into aligned bursts. More...
#include "doppler/clib_common.h"#include "doppler/jm_perf.h"#include "doppler/buffer/buffer.h"#include "doppler/dp_state.h"#include "doppler/burst_acq/burst_acq_core.h"#include "doppler/acq/acq_core.h"#include "doppler/corr2d/corr2d_core.h"#include "doppler/fft2d/fft2d_core.h"#include "doppler/fft/fft_core.h"#include "doppler/detection/detection_core.h"#include "doppler/pn/pn_core.h"#include "doppler/cvt/cvt_core.h"
Classes¶
| Type | Name |
|---|---|
| struct | burst_capture_detection_t One raw detection, as the search reported it. |
| struct | burst_capture_event_t One captured burst's event, as events() hands it back. |
| struct | burst_capture_pending_t One detection between acquisition and emission. |
| struct | dp_burst_capture_state_t BurstCapture state. |
Public Functions¶
| Type | Name |
|---|---|
| int | dp_burst_capture_configure_search_raw (dp_burst_capture_state_t * state, size_t doppler_bins, size_t n_noncoh) Pin the embedded acquisition's search grid directly. |
| dp_burst_capture_state_t * | dp_burst_capture_create (const float _Complex * preamble, size_t preamble_len, size_t burst_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 capture: acquisition, refine and retention behind one push(). |
| dp_burst_capture_state_t * | dp_burst_capture_create_backed (const char * path, const float _Complex * preamble, size_t preamble_len, size_t burst_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 capture whose look-back lives in a FILE. |
| void | dp_burst_capture_destroy (dp_burst_capture_state_t * state) Release a capture and everything it owns. NULL-safe. |
| size_t | dp_burst_capture_detections (dp_burst_capture_state_t * state, size_t n, burst_capture_detection_t * out, size_t max_out) Every hit the search made in the last push(), unfiltered. |
| size_t | dp_burst_capture_detections_max_out (dp_burst_capture_state_t * state, size_t n) Raw detections available from the last push(). n is ignored. |
| const burst_capture_event_t * | dp_burst_capture_event_at (const dp_burst_capture_state_t * state, size_t i) Borrow event i of the last push(), or NULL if out of range. |
| size_t | dp_burst_capture_events (dp_burst_capture_state_t * state, size_t n, burst_capture_event_t * out, size_t max_out) The event record for each burst the last push() returned. |
| size_t | dp_burst_capture_events_max_out (dp_burst_capture_state_t * state, size_t n) Records available from the last push(). n is ignored. |
| double | dp_burst_capture_get_cn0_dbhz_est (const dp_burst_capture_state_t * state) |
| size_t | dp_burst_capture_get_code_bins (const dp_burst_capture_state_t * state) Code-phase hypotheses per Doppler row. |
| size_t | dp_burst_capture_get_doppler_bins (const dp_burst_capture_state_t * state) Doppler hypotheses searched: the coherent depth, or the window-tile count past the native span acq_grid_bins() (doppler#1512). |
| double | dp_burst_capture_get_doppler_hz_est (const dp_burst_capture_state_t * state) |
| double | dp_burst_capture_get_doppler_rate (const dp_burst_capture_state_t * state) Doppler rate (Hz/s) the coherent depth is bounded against; 0 is no bound. |
| double | dp_burst_capture_get_doppler_rate_hz_s (const dp_burst_capture_state_t * state) |
| double | dp_burst_capture_get_doppler_res_hz (const dp_burst_capture_state_t * state) |
| double | dp_burst_capture_get_doppler_span_hz (const dp_burst_capture_state_t * state) Unambiguous Doppler half-range, Hz (+/- this). |
| uint64_t | dp_burst_capture_get_dropped (const dp_burst_capture_state_t * state) |
| double | dp_burst_capture_get_eta (const dp_burst_capture_state_t * state) Coherent detection gate; in force when n_noncoh == 1 . |
| double | dp_burst_capture_get_eta_nc (const dp_burst_capture_state_t * state) Non-coherent gate; in force only when n_noncoh > 1 , which a burst search never chooses (it reads 0 unless a pin sets looks). |
| size_t | dp_burst_capture_get_min_gap (const dp_burst_capture_state_t * state) Dead air a caller must leave between bursts, edge to edge. |
| uint64_t | dp_burst_capture_get_n_bursts (const dp_burst_capture_state_t * state) |
| size_t | dp_burst_capture_get_n_noncoh (const dp_burst_capture_state_t * state) Non-coherent looks combined per decision. |
| double | dp_burst_capture_get_pd_burst (const dp_burst_capture_state_t * state) Detection probability of one burst: any dwell its preamble spans, at a uniform alignment (doppler#1498). What underpowered reads. |
| double | dp_burst_capture_get_pd_predicted (const dp_burst_capture_state_t * state) Detection probability of one dwell wholly inside the preamble. |
| size_t | dp_burst_capture_get_pending (const dp_burst_capture_state_t * state) |
| uint64_t | dp_burst_capture_get_preamble_start (const dp_burst_capture_state_t * state) |
| double | dp_burst_capture_get_psl_db (const dp_burst_capture_state_t * state) The preamble's peak sidelobe level, dB: dp_acq_psl_db() of the engine. A burst clearing the threshold by more than -psl_db also detects its own sidelobe, outside the peak zone; -INFINITY for a perfect sequence. |
| size_t | dp_burst_capture_get_rate_hypotheses (const dp_burst_capture_state_t * state) Doppler-rate hypotheses searched; 1 is none __ doppler_rate then only caps the coherent depth. |
| void | dp_burst_capture_get_state (const dp_burst_capture_state_t * state, void * blob) Serialize into blob , which must be state_bytes() long. |
| double | dp_burst_capture_get_straddle_loss (const dp_burst_capture_state_t * state) Correlation kept, worst case, by a burst landing between bins. |
| size_t | dp_burst_capture_push (dp_burst_capture_state_t * state, const float _Complex * x, size_t x_len, float _Complex * out, size_t max_out) Stream samples; get back every burst whose window has arrived. |
| size_t | dp_burst_capture_push_max_out (dp_burst_capture_state_t * state, size_t x_len) Upper bound on samples push() can return for x_len input. |
| size_t | dp_burst_capture_ready (const dp_burst_capture_state_t * state) Windows the last push() completed. |
| int | dp_burst_capture_release (dp_burst_capture_state_t * state, size_t i) Give back the span that window i of the last push() claimed. |
| void | dp_burst_capture_reset (dp_burst_capture_state_t * state) Return to the searching state. |
| int | dp_burst_capture_set_state (dp_burst_capture_state_t * state, const void * blob) Restore from blob . |
| size_t | dp_burst_capture_state_bytes (const dp_burst_capture_state_t * state) Bytes one blob occupies: a pure function of CONFIGURATION. |
| const float _Complex * | dp_burst_capture_window (const dp_burst_capture_state_t * state, size_t i) Borrow window i of the last push(), or NULL if out of range. |
Macros¶
| Type | Name |
|---|---|
| define | BURST_CAPTURE_EDGE_TWINS 4u |
| define | BURST_CAPTURE_HITS 16uDetections collected from acquisition per batch. |
| define | BURST_CAPTURE_MAX_PHASES 4u |
| define | BURST_CAPTURE_REFINE_INTERP 4u |
| define | BURST_CAPTURE_STATE_MAGIC [**DP\_FOURCC**](dp__state_8h.md#define-dp_fourcc) ('B', 'C', 'A', 'P')State blob magic — a wrong blob is rejected, not reinterpreted. |
| define | BURST_CAPTURE_STATE_VERSION 6uState blob layout version. 6: every queued burst and the last event carry refine's winning rate hypothesis (doppler#2219). 5: the blob carries the whole ring, and dropped counts look-back discarded while a burst still needed it, not refused input (doppler#2015). An older blob is refused rather than carried over: a v5 blob, written by v0.66.0 through v0.71.0, is restored by the release that wrote it. |
Detailed Description¶
Between a detector and whatever consumes a burst there is a stage nobody owned: acquisition reports an END anchor and a code phase that is a lag MODULO one code period, so it fixes the alignment WITHIN a repetition and never says WHICH one. A burst has a frame that begins in one specific repetition, so somebody has to resolve the period and reach BACK to a start that has already gone past. This object is that somebody.
It searches, refines, retains, and emits the burst's SAMPLES. It stops there — demodulating, recording, or shipping a window elsewhere is the caller's business. DsssBurstReceiver is this plus BurstDemod.
It OWNS its acquisition engine rather than accepting someone else's results, and that is a correctness choice rather than a convenience one: acq_result_t::samples_consumed is stream-absolute only for an engine fed continuously and never reset, in the caller's own sample coordinates. An object taking foreign results would have to require that and could not check it — and a violated assumption is not a slightly wrong window, it is refine searching the wrong repetition, which returns noise rather than a degraded frame. push() defining the coordinate system makes the invariant internal. See docs/design/dsss-burst-receiver.md §11.
Lifecycle: create, then push() repeatedly, then destroy. There is no step()/steps(): a burst is a frame, not a sample.
// a 31-chip code held 4 samples a chip (bin_to_nrz, then the hold)
uint8_t code[31];
for (size_t i = 0; i < 31; i++) code[i] = (uint8_t)(i & 1u);
float nrz[31];
bin_to_nrz (code, 31, nrz, 31);
float _Complex pre[124];
for (size_t i = 0; i < 124; i++) pre[i] = nrz[i / 4];
dp_burst_capture_state_t *cap = dp_burst_capture_create (
pre, 124, 4096, 4, 4.0e6, 55.0, 0.0, 1e-3, 0.9, 0, 0.0, 1);
float _Complex x[2048] = { 0 };
float _Complex win[4096];
size_t n = dp_burst_capture_push (cap, x, 2048, win, 4096);
// n is a multiple of burst_len: burst i starts at i*burst_len
dp_burst_capture_destroy (cap);
Public Functions Documentation¶
function dp_burst_capture_configure_search_raw¶
Pin the embedded acquisition's search grid directly.
int dp_burst_capture_configure_search_raw (
dp_burst_capture_state_t * state,
size_t doppler_bins,
size_t n_noncoh
)
The escape hatch for a caller who wants a specific (doppler_bins, n_noncoh). Forwards to the engine, with one refusal of this object's own: a grid whose anchor can lag the preamble by more than refine reaches n_noncoh * doppler_bins code periods against k_lo is rejected rather than accepted and silently mis-refined. Acquisition stamps a hit at the end of the LAST accumulated look, so every look past the one holding the preamble moves the anchor a whole frame later; a burst has one frame of preamble, so n_noncoh = 1 is the grid a capture wants and the sizer now always picks (doppler#1181).
Returns:
DP_OK, or DP_ERR_INVALID if this object or the engine refused the grid.
>>> import numpy as np
>>> from doppler.acquire import BurstCapture
>>> code = np.array([1, 1, 1, 0, 1, 0, 0], dtype=np.uint8)
>>> pre = np.repeat(np.where(code, -1.0, 1.0), 2).astype(np.complex64)
>>> cap = BurstCapture(pre, burst_len=512, reps=4, fs=2e6)
>>> cap.configure_search_raw(4, 1) # 4 Doppler bins, coherent only
function dp_burst_capture_create¶
Create a burst capture: acquisition, refine and retention behind one push().
dp_burst_capture_state_t * dp_burst_capture_create (
const float _Complex * preamble,
size_t preamble_len,
size_t burst_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
)
Give it the preamble one period of its SAMPLES and the geometry, say how long a burst is, and stream samples in. It searches blindly, recovers the exact preamble start, and hands back the burst's samples once they have all arrived. A doppler_uncertainty wider than the native span +/- fs/(2n) is searched in window tiles, and the event carries the absolute Doppler, tile included (doppler#1512) for a preamble with a single-peak ambiguity, such as a PN code. A Zadoff-Chu preamble cannot be resolved past the native span: its delay-Doppler ridge correlates at full magnitude at (k tiles, k*u^-1 samples) for every k, so keep its Doppler inside the span.
The look-back buffer is NOT a parameter. Its span is derived from the geometry here (detection lag + refine search + the burst itself), because every term is already known and a caller asked to size a history buffer is a caller handed a way to lose bursts silently.
Any repeated preamble: a chirp, a Zadoff-Chu sequence, shaped PSK, or a PN code mapped by dp_bin_to_nrz() and held spc samples a chip (at fs = chip_rate*spc). One chip is one sample: a period is preamble_len samples. Refine correlates each candidate position against the acquisition engine's own reference row the preamble at unit RMS so there is one replica of it (doppler#1470).
Parameters:
preambleOne period of the preamble,preamble_lensamples; not all zero, every sample finite. Read, not kept.preamble_lenSamples per repetition (>= 1).burst_lenSamples in one burst what gets captured.repsPreamble repetitions (>= 1).fsSample rate, Hz (> 0); 1 for normalized units.cn0_dbhzC/N0 the search is sized for, dB-Hz, of the preamble's mean power: any finite value, or NaN (ACQ_CN0_NONE) for no design point.doppler_uncertaintyDoppler search half-range, Hz (0 = native).pfaTarget false-alarm probability, in (0, 1).pdTarget detection probability, in (0, 1).noise_modeCFAR reference: 0=mean, 1=median, 2=min, 3=max.doppler_rateDoppler rate, Hz/s (>= 0), that caps the acquisition's coherent depth atf_epoch/sqrt(2*doppler_rate)repetitions (doppler#1482); 0 is no bound. Withrate_hypotheses> 1 it is the span instead.rate_hypothesesDoppler-rate hypotheses to search (0 = auto, else 1 to ACQ_RATE_HYPS_MAX; auto picks the fewest the span needs, see dp_acq_create_burst(), and dp_burst_capture_get_rate_hypotheses() reads the choice); 1 searches none anddoppler_rateonly caps the depth. Above 1 the capture COMPENSATES the rate:doppler_rateis the span[-R, +R], the acquisition de-chirps its coherent block at this many rates across it, and the depth is bounded by the RESIDUAL,R/(n - 1)deeper blocks, more sensitivity, aboutntimes the per-dwell cost, and a false-alarm budget split over the hypotheses (dp_acq_create_burst() owns the semantics). Refine follows the engine: its coherent sum over the preamble's periods tries the rates too, on a finer grid because it integrates the whole preamble, and its Doppler cells widen by the shift a rate puts between the detecting dwell's middle and the preamble's so a burst the engine found by rate is placed by rate. Needs a non-zerodoppler_rate(the span). Measured: validate_acq_rate_pd.
Returns:
Heap state, or NULL if any parameter is out of range.
>>> import numpy as np
>>> from doppler.acquire import BurstCapture
>>> code = np.array([1, 1, 1, 0, 1, 0, 0], dtype=np.uint8)
>>> pre = np.repeat(np.where(code, -1.0, 1.0), 2).astype(np.complex64)
>>> cap = BurstCapture(pre, burst_len=512, reps=4, fs=2e6)
>>> cap.burst_len
512
>>> cap.retain_span == cap.refine_span + cap.burst_len
True
A carrier that sweeps caps the coherent depth at its rate; searching
Doppler-rate hypotheses compensates it, and buys the depth back:
>>> import warnings
>>> k = np.arange(127)
>>> zc = np.exp(-1j * np.pi * 5 * k * (k + 1) / 127).astype(np.complex64)
>>> def make(**kw):
... with warnings.catch_warnings():
... warnings.simplefilter("ignore") # under-powered, below
... return BurstCapture(zc, burst_len=2432, 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_capture_create_backed¶
Create a capture whose look-back lives in a FILE.
dp_burst_capture_state_t * dp_burst_capture_create_backed (
const char * path,
const float _Complex * preamble,
size_t preamble_len,
size_t burst_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
)
Same object, same behaviour, one difference in where the history ring's pages come from: they are a MAP_SHARED mapping of path, so the ring's samples ARE the file's contents. There is no copy and no separate flush path — the kernel writes the pages back, and get_state() forces the point so a checkpoint and its history agree.
Two things follow, and they are the reason to reach for this constructor:
- The blob stops carrying the look-back. For an in-RAM capture the retained history IS the blob (measured 2026-10-10: 8.47 MB at a 1029-symbol frame, 33.64 MB at 8029). Backed,
state_bytes()is the detection queue plus the acquisition child (83-88 kB there), because the samples are already durable and the blob only has to name where in the ring they sit. - The history outlives the process. Point a new capture at the same path and the samples are there; restore the blob and it reaches back across the restart into a burst that began before it.
The file is created if absent and truncated to the ring's mapped size, which zeroes it. An existing file of exactly that size is adopted as it stands. That size is the capacity in bytes, capacity * sizeof(float _Complex), rounded up to a whole page do not compute it from burst_len. The blob saves exactly the capacity in bytes; on a large-page system the file can be bigger.
A blob from a backed capture does NOT restore into an in-RAM one, or the reverse: state_bytes() differs, so jm's length check rejects it. That is the intent — they are different configurations, and silently accepting one for the other would resume a capture whose history was somewhere else.
Parameters:
pathFile to back the ring with; not NULL and not empty.preambleOne period of the preamble,preamble_lensamples.preamble_lenSamples per repetition (>= 1).burst_lenSamples in one burst what gets captured.repsPreamble repetitions (>= 1).fsSample rate, Hz (> 0).cn0_dbhzC/N0 the search is sized for, dB-Hz: any finite value, or NaN (ACQ_CN0_NONE) for no design point.doppler_uncertaintyDoppler search half-range, Hz (0 = native).pfaTarget false-alarm probability, in (0, 1).pdTarget detection probability, in (0, 1).noise_modeCFAR reference: 0=mean, 1=median, 2=min, 3=max.doppler_rateDoppler rate, Hz/s (>= 0), that caps the acquisition's coherent depth atf_epoch/sqrt(2*doppler_rate)repetitions (doppler#1482); 0 is no bound. Withrate_hypotheses> 1 it is the span instead.rate_hypothesesDoppler-rate hypotheses to search; see dp_burst_capture_create().
Returns:
Heap state, or NULL if a parameter is out of range or the file could not be opened, sized or mapped.
>>> import numpy as np, tempfile, os
>>> from doppler.acquire import BurstCapture, PersistentBurstCapture
>>> code = np.array([1, 1, 1, 0, 1, 0, 0], dtype=np.uint8)
>>> pre = np.repeat(np.where(code, -1.0, 1.0), 2).astype(np.complex64)
>>> path = os.path.join(tempfile.mkdtemp(), "ring.cf32")
>>> cap = PersistentBurstCapture(path, pre, burst_len=512,
... reps=4, fs=2e6)
>>> ram = BurstCapture(pre, burst_len=512, reps=4, fs=2e6)
>>> _ = cap.push(np.zeros(4096, dtype=np.complex64))
>>> # the look-back is in the file, so the blob stops carrying the
>>> # ring: twice retain_span rounded up to a power of two, 8 B a sample
>>> ring = 1 << (2 * ram.retain_span - 1).bit_length()
>>> ram.state_bytes() - cap.state_bytes() == ring * 8
True
>>> os.path.getsize(path) >= ring * 8 # rounded up to a page
True
function dp_burst_capture_destroy¶
Release a capture and everything it owns. NULL-safe.
function dp_burst_capture_detections¶
Every hit the search made in the last push(), unfiltered.
size_t dp_burst_capture_detections (
dp_burst_capture_state_t * state,
size_t n,
burst_capture_detection_t * out,
size_t max_out
)
BEFORE the claim rule and the suppression window: several rows can name one preamble, and a row can be a false alarm. That is the point this is what acquisition FOUND, and events() is what survived. Valid until the next push(), reset() or set_state().
>>> import numpy as np
>>> from doppler.acquire import BurstCapture
>>> code = np.array([1, 1, 1, 0, 1, 0, 0], dtype=np.uint8)
>>> pre = np.repeat(np.where(code, -1.0, 1.0), 2).astype(np.complex64)
>>> cap = BurstCapture(pre, burst_len=512, reps=4, fs=2e6)
>>> _ = cap.push(np.zeros(4096, dtype=np.complex64))
>>> # what the search found, against what became a burst
>>> len(cap.detections()) >= len(cap.events())
True
function dp_burst_capture_detections_max_out¶
Raw detections available from the last push(). n is ignored.
function dp_burst_capture_event_at¶
Borrow event i of the last push(), or NULL if out of range.
const burst_capture_event_t * dp_burst_capture_event_at (
const dp_burst_capture_state_t * state,
size_t i
)
function dp_burst_capture_events¶
The event record for each burst the last push() returned.
size_t dp_burst_capture_events (
dp_burst_capture_state_t * state,
size_t n,
burst_capture_event_t * out,
size_t max_out
)
Row i describes the window at i*burst_len. Valid until the next push(), reset() or set_state().
>>> import numpy as np
>>> from doppler.acquire import BurstCapture
>>> code = np.array([1, 1, 1, 0, 1, 0, 0], dtype=np.uint8)
>>> pre = np.repeat(np.where(code, -1.0, 1.0), 2).astype(np.complex64)
>>> cap = BurstCapture(pre, burst_len=512, reps=4, fs=2e6)
>>> win = cap.push(np.zeros(4096, dtype=np.complex64))
>>> len(cap.events()) == win.size // cap.burst_len
True
function dp_burst_capture_events_max_out¶
Records available from the last push(). n is ignored.
function dp_burst_capture_get_cn0_dbhz_est¶
function dp_burst_capture_get_code_bins¶
Code-phase hypotheses per Doppler row.
function dp_burst_capture_get_doppler_bins¶
Doppler hypotheses searched: the coherent depth, or the window-tile count past the native span acq_grid_bins() (doppler#1512).
function dp_burst_capture_get_doppler_hz_est¶
function dp_burst_capture_get_doppler_rate¶
Doppler rate (Hz/s) the coherent depth is bounded against; 0 is no bound.
function dp_burst_capture_get_doppler_rate_hz_s¶
function dp_burst_capture_get_doppler_res_hz¶
function dp_burst_capture_get_doppler_span_hz¶
Unambiguous Doppler half-range, Hz (+/- this).
function dp_burst_capture_get_dropped¶
function dp_burst_capture_get_eta¶
Coherent detection gate; in force when n_noncoh == 1 .
function dp_burst_capture_get_eta_nc¶
Non-coherent gate; in force only when n_noncoh > 1 , which a burst search never chooses (it reads 0 unless a pin sets looks).
function dp_burst_capture_get_min_gap¶
Dead air a caller must leave between bursts, edge to edge.
function dp_burst_capture_get_n_bursts¶
function dp_burst_capture_get_n_noncoh¶
Non-coherent looks combined per decision.
function dp_burst_capture_get_pd_burst¶
Detection probability of one burst: any dwell its preamble spans, at a uniform alignment (doppler#1498). What underpowered reads.
It models the ENGINE; what refine loses afterwards is not in the model. Measured on a Zadoff-Chu 127 x 8 preamble at every depth, the capture delivers at least this at a 0.6 design point and at 0.9 (the default pd): its loss against the engine is refine naming the wrong repetition, about 2% of trials (native/validation/capture_dwell_pd.c; validation report §2.8). Until doppler#1519 it also lost a code phase along the ZC delay-Doppler ridge and fell 0.026 short at 0.9.
function dp_burst_capture_get_pd_predicted¶
Detection probability of one dwell wholly inside the preamble.
function dp_burst_capture_get_pending¶
function dp_burst_capture_get_preamble_start¶
function dp_burst_capture_get_psl_db¶
The preamble's peak sidelobe level, dB: dp_acq_psl_db() of the engine. A burst clearing the threshold by more than-psl_db also detects its own sidelobe, outside the peak zone; -INFINITY for a perfect sequence.
function dp_burst_capture_get_rate_hypotheses¶
Doppler-rate hypotheses searched; 1 is none __doppler_rate then only caps the coherent depth.
function dp_burst_capture_get_state¶
Serialize into blob , which must be state_bytes() long.
function dp_burst_capture_get_straddle_loss¶
Correlation kept, worst case, by a burst landing between bins.
function dp_burst_capture_push¶
Stream samples; get back every burst whose window has arrived.
size_t dp_burst_capture_push (
dp_burst_capture_state_t * state,
const float _Complex * x,
size_t x_len,
float _Complex * out,
size_t max_out
)
Windows are concatenated: burst i occupies burst_len samples starting at i*burst_len, and events() returns the matching record for each. Every sample of x is consumed. An empty return is normal it means no burst completed in this call.
It aborts the process in two places, both a defect in this object and neither reachable from any input or from any blob set_state() accepts: a history ring with no room after trim (the bound in the implementation's trim proves room), and an acquisition call that took nothing while input was left (each call starts with its result empty, and a push with its result empty takes at least the next frame, or the rest as carry). Each stops rather than spinning forever and in a Python host it ends the interpreter.
Parameters:
stateCapture.xInput samples,x_lenlong.x_lenSamples inx.outWritten with the completed windows; may be NULL to drop.max_outCapacity ofout, in samples.
Returns:
Samples written always a multiple of burst_len.
>>> import numpy as np
>>> from doppler.acquire import BurstCapture
>>> code = np.array([1, 1, 1, 0, 1, 0, 0], dtype=np.uint8)
>>> pre = np.repeat(np.where(code, -1.0, 1.0), 2).astype(np.complex64)
>>> cap = BurstCapture(pre, burst_len=512, reps=4, fs=2e6)
>>> win = cap.push(np.zeros(4096, dtype=np.complex64))
>>> win.size % cap.burst_len # whole windows, never a partial
0
>>> win.size # silence, so no burst completed
0
function dp_burst_capture_push_max_out¶
Upper bound on samples push() can return for x_len input.
Distinct bursts cannot overlap, so x_len samples complete at most x_len/burst_len + 1 of them, plus whatever is already queued.
function dp_burst_capture_ready¶
Windows the last push() completed.
The C consumer's face, and the reason a composing object pays no second copy: dp_burst_capture_window() borrows straight out of the scratch that push() filled.
function dp_burst_capture_release¶
Give back the span that window i of the last push() claimed.
An emitted window owns its whole span: a detection inside it is the payload firing against the acquisition code, not a new burst, so it is HELD rather than reported. Whether the window WAS a burst is a verdict this object cannot reach it stops at samples; error detection, whatever form the frame gives it, is the consumer's so a consumer that knows better calls this for that window, and the held detections are searched again on the next push(). Unreleased, they are dropped when the next push() begins, which is exactly the behaviour a consumer with no verdict always had.
What it prevents (doppler#1181): a spurious window ending just after a real burst begins used to swallow that burst's first detections the receiver's own design says only a DECODED burst may own a span (§10.3, doppler#1004), and the capture underneath had been owning it on emission.
Must be called BEFORE the next push(): i indexes THIS push's windows.
Returns:
DP_OK, or DP_ERR_INVALID if i is not a window of the last push().
>>> import numpy as np
>>> from doppler.acquire import BurstCapture
>>> code = np.array([1, 1, 1, 0, 1, 0, 0], dtype=np.uint8)
>>> pre = np.repeat(np.where(code, -1.0, 1.0), 2).astype(np.complex64)
>>> cap = BurstCapture(pre, burst_len=512, reps=4, fs=2e6)
>>> _ = cap.push(np.zeros(4096, dtype=np.complex64))
>>> cap.release(0) # no window 0 in a quiet push
Traceback (most recent call last):
...
ValueError: release failed (rc=-4)
function dp_burst_capture_reset¶
Return to the searching state.
Resets the embedded acquisition, rewinds the history ring, clears every queued detection and every read-back. Construction parameters are untouched; dropped deliberately survives, because a lost burst stays lost.
>>> import numpy as np
>>> from doppler.acquire import BurstCapture
>>> code = np.array([1, 1, 1, 0, 1, 0, 0], dtype=np.uint8)
>>> pre = np.repeat(np.where(code, -1.0, 1.0), 2).astype(np.complex64)
>>> cap = BurstCapture(pre, burst_len=512, reps=4, fs=2e6)
>>> cap.push(np.zeros(4096, dtype=np.complex64)).size
0
>>> cap.reset()
>>> cap.pending
0
function dp_burst_capture_set_state¶
Restore from blob .
Returns:
DP_OK or DP_ERR_INVALID.
A refusal writes nothing of the capture: every check runs before any of it is written. The acquisition child is restored to check where it stands, and on a mismatch handed its own blob back, which resets the acq read-backs no blob carries (dwells, surface_at). So the capture's next get_state() is byte-identical to its last, and its next push() is the one it would have made.
A wrong-object, wrong-version, wrong-size or foreign-endian blob is refused, never reinterpreted; so is a blob from the other flavour (a backed and an in-RAM capture have different state_bytes()). A backed capture restores POSITIONS only the samples are the file's so it also refuses a blob whose retained span the file cannot hold: a file create() made fresh that this capture has not written that far into, or a span the ring has since wrapped past (more than the ring's capacity pushed since the checkpoint). A capture restoring a checkpoint it took itself is the normal case and is accepted (doppler#1190): set_state(blob) -> push(chunk) -> get_state() per call is a service shape this object supports, on both flavours.
The blob holds all the look-back the ring held, so a capture resumes with every burst it would have emitted. A blob is checked rather than trusted: a stream position below 2^63 and a suppression span that ends by it; the queue's phases within their array, its readings finite, its anchors in order and none past the stream position, and a refined start within the range refine chooses from; a look-back no longer than the stream; and an acquisition child that fits its region and stands at the same stream position as the capture. A queued entry its look-back cannot reach is dropped counted in dropped if it was a burst, uncounted if it was shadowed as a forged blob can name one.
function dp_burst_capture_state_bytes¶
Bytes one blob occupies: a pure function of CONFIGURATION.
function dp_burst_capture_window¶
Borrow window i of the last push(), or NULL if out of range.
Contiguous, burst_len samples, valid until the next push(), reset() or set_state(). The caller must not free it.
Macro Definition Documentation¶
define BURST_CAPTURE_EDGE_TWINS¶
Extra Doppler cells refine scores at the other alias when a cell sits at the edge of the native span (doppler#1519).
define BURST_CAPTURE_HITS¶
Detections collected from acquisition per batch.
A BATCHING parameter, never a correctness one: push() keeps looping until acq has absorbed the whole chunk, offering it the rest again after every call, so a smaller array means more iterations and nothing else.
define BURST_CAPTURE_MAX_PHASES¶
Distinct code phases a pending burst remembers for refine to score. Two is what a Zadoff-Chu preamble at the edge of the native span produces (doppler#1519); the rest is room for a false alarm merged into the same burst. A phase past the cap is not recorded, and refine then behaves as it did before phases were kept.
define BURST_CAPTURE_REFINE_INTERP¶
Refine's Doppler cells per native bin at depth reps: twice acquisition's own zero-padding. Choosing a repetition compares two close scores, and the straddle that costs a detection little tips that comparison: measured on Zadoff-Chu 127 x 8, 2 cells a bin chose the wrong period for 45 of 656 engine hits at D = 5, 4 for 26, and 8 for 25 (doppler#1502).
define BURST_CAPTURE_STATE_MAGIC¶
State blob magic — a wrong blob is rejected, not reinterpreted.
define BURST_CAPTURE_STATE_VERSION¶
State blob layout version. 6: every queued burst and the last event carry refine's winning rate hypothesis (doppler#2219). 5: the blob carries the whole ring, and dropped counts look-back discarded while a burst still needed it, not refused input (doppler#2015). An older blob is refused rather than carried over: a v5 blob, written by v0.66.0 through v0.71.0, is restored by the release that wrote it.
The documentation for this class was generated from the following file native/inc/doppler/burst_capture/burst_capture_core.h