File async_dsss_pool_core.h¶
FileList > async_dsss_pool > async_dsss_pool_core.h
Go to the source code of this file
AsyncDsssPool one object holds the population: a searcher, a pool of hand-off receivers, the assigned table and the event log (docs/design/async-dsss-receiver.md section 8.2).More...
#include "clib_common.h"#include "jm_perf.h"#include "async_dsss_receiver/async_dsss_receiver_core.h"#include "acq/acq_core.h"#include "dp_event_log/dp_event_log_core.h"#include "dll/dll_core.h"#include "costas/costas_core.h"#include "RateConverter/RateConverter_core.h"#include "mpsk_receiver/mpsk_receiver_core.h"#include "cic/cic_core.h"#include "resample/resample_core.h"#include "psd/psd_core.h"#include "detector/detector_core.h"#include "detection/detection_core.h"#include "corr2d/corr2d_core.h"#include "fft2d/fft2d_core.h"#include "fft/fft_core.h"#include "dp_tlm/dp_tlm_core.h"#include "carrier_acq/carrier_acq_core.h"#include "resamp/resamp_core.h"#include "hbdecim/hbdecim_core.h"#include "dp_parallel.h"#include "dp_state.h"
Classes¶
| Type | Name |
|---|---|
| struct | async_dsss_pool_row_t |
| struct | async_dsss_pool_slot_t One slot's picture, by value what status() returns. |
| struct | async_dsss_pool_state_t AsyncDsssPool state. |
Public Functions¶
| Type | Name |
|---|---|
| async_dsss_pool_state_t * | async_dsss_pool_create (const uint8_t * code, size_t code_len, double chip_rate, double symbol_rate, size_t spc, int m, double cn0_dbhz, double pfa, double pd, double doppler_uncertainty, size_t code_only_epochs, double doppler_rate, size_t max_peaks, size_t n_slots, int threads, double carrier_freq_hz, double lost_confirm_s, double max_emitter_on_time_secs, size_t segments, size_t sps, int differential, double refine_max_error_db, size_t refine_samples_per_symbol, double refine_design_margin_db, size_t refine_n_fft, size_t refine_zero_pad, bool refine_sequential, size_t refine_max_n_blocks) Create a async_dsss_pool instance. |
| void | async_dsss_pool_destroy (async_dsss_pool_state_t * state) Destroy a async_dsss_pool instance and release all memory. |
| void | async_dsss_pool_get_state (const async_dsss_pool_state_t * state, void * blob) |
| size_t | async_dsss_pool_push (async_dsss_pool_state_t * state, const float _Complex * x, size_t x_len) One block of raw cf32 samples through the population. |
| void | async_dsss_pool_reset (async_dsss_pool_state_t * state) Release every slot and start over: the searcher reset, every receiver back to idle, the table cleared, the counters zeroed. |
| int | async_dsss_pool_set_event_log (async_dsss_pool_state_t * state, dp_event_log_t * log) Attach the run's event log (design section 8.1); NULL detaches. |
| int | async_dsss_pool_set_refine_min_blocks (async_dsss_pool_state_t * state, size_t n_blocks) Floor every receiver's refine dwell at n_blocks (async_dsss_receiver_set_refine_min_blocks() ; design section 12.16, #1265). |
| int | async_dsss_pool_set_state (async_dsss_pool_state_t * state, const void * blob) |
| size_t | async_dsss_pool_state_bytes (const async_dsss_pool_state_t * state) |
| async_dsss_pool_slot_t | async_dsss_pool_status (async_dsss_pool_state_t * state, size_t slot) One slot's picture, by value ( async_dsss_pool_slot_t ). |
| size_t | async_dsss_pool_symbols (async_dsss_pool_state_t * state, size_t slot, float _Complex * out, size_t max_out) The symbols slot slot's receiver decided on the last push(). |
| size_t | async_dsss_pool_symbols_max_out (async_dsss_pool_state_t * state) The per-slot symbol capacity symbols() can return grown with the largest block pushed so far (0 before the first push). |
Macros¶
| Type | Name |
|---|---|
| define | ASYNC_DSSS_POOL_MAX_EMITTER_ON_TIME_SECS (15.0 \* 60.0) |
| define | ASYNC_DSSS_POOL_STATE_MAGIC [**DP\_FOURCC**](dp__state_8h.md#define-dp_fourcc) ('A', 'D', 'P', 'L') |
| define | ASYNC_DSSS_POOL_STATE_VERSION 1u |
Detailed Description¶
The multi-emitter use case (design section 6) has one channel searching without pause and one AsyncDsssReceiver per emitter, assigned once from a detection and tracking until its own loss decision. This object is the holder of that lifecycle, in C, with the Python face as glue:
- one searcher,
Acquisitionin continuous mode with the block coherence its code-only window buys (section 2.3) and a peak list (section 7.1), its tiles fanned across the threads the pool is given; - **
n_slotshand-off receivers**, created idle. An idle or lost receiver consumes and discards what it is fed, so every receiver is fed every block and the feed has no per-state branch; they run across the same threads (dp_parallel.h); - the assigned table, one row per slot: the seed's coordinates, and the row's CURRENT Doppler and chip phase the live loop's once the receiver tracks, the seed's advanced by the clock dilation before refreshed before every dwell is read, because an emitter drifts between windows and the seed is the wrong key (section 9);
- the event log, borrowed by attachment (the telemetry shape): the pool is the one component that stamps.
One push() per block does, in order: feed the searcher; refresh the table; drop every peak within one chip of a live row's code phase, at any Doppler, as that emitter's own (the zone is the code axis alone: a tracked emitter's data blocks put smeared copies of it at its own phase rows away, section 12.14); for each survivor, acq_build_handoff() and seed() into a free slot, or count it dropped when there is none; feed every receiver; then, for each slot whose receiver reports lost, or has held its slot past the maximum on-air time, clear the row, reset() the receiver to idle and log released. seed()'s own refusal on a receiver that is not idle is the second guard behind the table, so a bookkeeping error cannot become a double assignment. The transitions seeded, tracking, degrade, lost, released, dropped are the log's annotations, at the sample they happened, with the slot, the receiver's state, the Doppler, the chip phase and the C/N0 staged as doppler:<name> fields beside the label (core:label).
Nothing about the waveform or the population is baked in: every number is a create parameter whose default is the operating point of section 6.1 (twelve slots, a peak list of sixteen, a 2 s release interval, a 15 min maximum on-air time), and the searcher's and the receivers' own parameters pass through untouched. A physically-coupled carrier (carrier_freq_hz > 0) is told to the searcher as well as the receivers: its hand-off advances a hit's code phase by the drift over half its dwell and its coherent blocks align their epochs (#1254, #1256), and the table's rows are advanced by the same dilation.
What comes out, per slot and by index: the status record by value (status()), and the symbols the receiver decided on this push, borrowed from the pool's own buffer (symbols()). Nothing allocates per push once a block size has been seen, the pool never exceeds n_slots, and a released emitter still on the air is a new detection at its next window into whichever slot is free the one re-assignment the lifecycle permits. Replay and live runs produce the same records, because nothing here sees a time: every stamp is a stream position.
Not built: section 11.4's replica. The operating spread of 10 dB picks the list branch (section 9), so the pool is off the searcher's push path and subtracts nothing.
Lifecycle: create, then push / status / symbols / reset as often as wanted, then destroy.
>>> import numpy as np
>>> from doppler.dsss import AsyncDsssPool
>>> from doppler.wfm import Gold
>>> code = np.asarray(Gold().generate(1023)).astype(np.uint8)
>>> pool = AsyncDsssPool(code, chip_rate=5e6, symbol_rate=2700.0,
... spc=2, cn0_dbhz=45.0, doppler_uncertainty=5e3,
... n_slots=4, lost_confirm_s=0.5)
>>> (pool.n_slots, pool.n_assigned, pool.status(0).assigned)
(4, 0, 0)
>>> int(pool.push(np.zeros(2046, np.complex64))) # noise-free silence
0
Public Functions Documentation¶
function async_dsss_pool_create¶
Create a async_dsss_pool instance.
async_dsss_pool_state_t * async_dsss_pool_create (
const uint8_t * code,
size_t code_len,
double chip_rate,
double symbol_rate,
size_t spc,
int m,
double cn0_dbhz,
double pfa,
double pd,
double doppler_uncertainty,
size_t code_only_epochs,
double doppler_rate,
size_t max_peaks,
size_t n_slots,
int threads,
double carrier_freq_hz,
double lost_confirm_s,
double max_emitter_on_time_secs,
size_t segments,
size_t sps,
int differential,
double refine_max_error_db,
size_t refine_samples_per_symbol,
double refine_design_margin_db,
size_t refine_n_fft,
size_t refine_zero_pad,
bool refine_sequential,
size_t refine_max_n_blocks
)
Everything is sized once, here: the searcher with its list and its threads, n_slots idle receivers, the table. NULL for a NULL or empty code, a non-positive rate, spc or n_slots of 0, max_peaks outside the searcher's own range, a negative lost_confirm_s or max_emitter_on_time_secs, or a child that fails to open.
Parameters:
codeSpreading code, one 0/1 chip per element.code_lenChips incode.chip_rateChip rate, Hz (default: 1000000.0).symbol_rateData-symbol rate, Hz (default: 1000.0).spcSamples per chip (default: 2).mPSK order of the receivers (default: 2).cn0_dbhzDesign C/N0 for the searcher's sizing and the receivers' (default: 55.0).pfaFalse-alarm target, the searcher's and the refine's (default: 1e-3).pdDetection-probability target (default: 0.9).doppler_uncertaintyThe searcher's one-sided span, Hz (default: 100.0).code_only_epochsWhole code-only epochs the waveform's window holds at any chip phase the block depth of section 2.3; 1 = no window (default: 1).doppler_rateDoppler rate the depth is bounded against, Hz/s; 0 leaves the window as the only bound (default: 0.0).max_peaksThe searcher's list capacity per dwell (default: 16).n_slotsReceivers held (default: 12).threadsThreads the receivers and the searcher's fan run across; <= 0 picks the online core count, 1 is serial (default: 1).carrier_freq_hzRF carrier the Doppler is physically coupled to, Hz, told to the searcher and every receiver; 0.0 = uncoupled (default: 0.0).lost_confirm_sThe release rule's interval, seconds (section 10) (default: 2.0).max_emitter_on_time_secsMaximum on-air time of one emitter, seconds: a slot held longer is released (reasonon_time); 0 = never (default: 900.0, ASYNC_DSSS_POOL_MAX_EMITTER_ON_ TIME_SECS).segmentsThe receivers' live Dll segments (default: 4).spsThe receivers' samples per symbol (default: 8).differentialThe receivers' differential demap (default: 0).refine_max_error_dbAs async_dsss_receiver_create() (default: 0.5).refine_samples_per_symbolAs async_dsss_receiver_create() (default: 4).refine_design_margin_dbAs async_dsss_receiver_create() (default: 14.0).refine_n_fftAs async_dsss_receiver_create() (default: 64).refine_zero_padAs async_dsss_receiver_create() (default: 8).refine_sequentialAs async_dsss_receiver_create() (default: false).refine_max_n_blocksAs async_dsss_receiver_create() (default: 100000).
Returns:
Heap-allocated state, or NULL on allocation failure.
Note:
Caller must call async_dsss_pool_destroy() when done.
>>> import numpy as np
>>> from doppler.dsss import AsyncDsssPool
>>> from doppler.wfm import Gold
>>> code = np.asarray(Gold().generate(1023)).astype(np.uint8)
>>> pool = AsyncDsssPool(code, chip_rate=5e6, symbol_rate=2700.0,
... spc=2, cn0_dbhz=45.0, doppler_uncertainty=5e3,
... n_slots=4, lost_confirm_s=0.5)
>>> (pool.n_slots, pool.n_assigned, pool.coherent_bins)
(4, 0, 1)
>>> round(pool.doppler_res_hz) # one Doppler row of the searcher
4888
function async_dsss_pool_destroy¶
Destroy a async_dsss_pool instance and release all memory.
Parameters:
stateMay be NULL.
function async_dsss_pool_get_state¶
function async_dsss_pool_push¶
One block of raw cf32 samples through the population.
size_t async_dsss_pool_push (
async_dsss_pool_state_t * state,
const float _Complex * x,
size_t x_len
)
In order: the searcher; the table refreshed; every peak within one chip of a live row's code phase, at any Doppler, dropped as that emitter's own; each survivor seeded into a free slot or counted dropped; every receiver fed, across the pool's threads; every receiver that reports lost, or has held its slot past the maximum on-air time, released. Every transition goes to the attached log at the sample it happened. Accepts any block size: a hit decided inside the block is referred to the block's start before it seeds (the receiver is fed the whole block), on the dilated clock when the carrier is known.
Parameters:
stateMust be non-NULL.xInput samples.x_lenSamples inx.
Returns:
Receivers assigned after this push.
>>> import numpy as np
>>> from doppler.dsss import AsyncDsssPool
>>> from doppler.wfm import Gold
>>> code = np.asarray(Gold().generate(1023)).astype(np.uint8)
>>> pool = AsyncDsssPool(code, chip_rate=5e6, symbol_rate=2700.0,
... spc=2, cn0_dbhz=45.0, n_slots=2)
>>> int(pool.push(np.zeros(4 * 2046, np.complex64))) # silence: no one
0
>>> pool.samples_consumed # the stream position
8184
function async_dsss_pool_reset¶
Release every slot and start over: the searcher reset, every receiver back to idle, the table cleared, the counters zeroed.
The attached log stays attached and nothing is logged a reset is the holder's decision, not an emitter's transition.
Parameters:
stateMust be non-NULL.>>> import numpy as np >>> from doppler.dsss import AsyncDsssPool >>> from doppler.wfm import Gold >>> code = np.asarray(Gold().generate(1023)).astype(np.uint8) >>> pool = AsyncDsssPool(code, chip_rate=5e6, symbol_rate=2700.0, ... spc=2, cn0_dbhz=45.0, n_slots=2) >>> _ = pool.push(np.zeros(2046, np.complex64)) >>> pool.samples_consumed 2046 >>> pool.reset() >>> (pool.samples_consumed, pool.n_assigned, pool.events) (0, 0, 0)
function async_dsss_pool_set_event_log¶
Attach the run's event log (design section 8.1); NULL detaches.
Borrowed, never owned: the holder opens, finalizes and closes it. From now on every transition is appended at the sample it happened, with slot, state, doppler_hz, chip_phase and cn0_dbhz staged as doppler:<name> fields beside the label (core:label) and, on released, reason (lost or on_time). A log that has already failed keeps failing (its error is sticky); the pool counts the transition either way.
Parameters:
stateMust be non-NULL.logThe log, or NULL.
Returns:
DP_OK.
>>> import os, tempfile
>>> import numpy as np
>>> from doppler.dsss import AsyncDsssPool
>>> from doppler.telemetry import EventLog
>>> from doppler.wfm import Gold
>>> code = np.asarray(Gold().generate(1023)).astype(np.uint8)
>>> pool = AsyncDsssPool(code, chip_rate=5e6, symbol_rate=2700.0,
... spc=2, cn0_dbhz=45.0, n_slots=2)
>>> log = EventLog(os.path.join(tempfile.mkdtemp(), "run.events"))
>>> pool.set_event_log(log) # attached: transitions go here
>>> _ = pool.push(np.zeros(2046, np.complex64))
>>> pool.set_event_log(None) # detached
>>> log.close()
function async_dsss_pool_set_refine_min_blocks¶
Floor every receiver's refine dwell at n_blocks (async_dsss_receiver_set_refine_min_blocks() ; design section 12.16, #1265).
Forwarded to all n_slots receivers; each applies it to the next refine chain it builds, so a slot already refining keeps its dwell. The receivers' default is 7 blocks. Config, not running state.
Parameters:
stateMust be non-NULL.n_blocksThe floor, blocks; 0 removes it.
Returns:
DP_OK.
>>> import numpy as np
>>> from doppler.dsss import AsyncDsssPool
>>> from doppler.wfm import Gold
>>> code = np.asarray(Gold().generate(1023)).astype(np.uint8)
>>> pool = AsyncDsssPool(code, chip_rate=5e6, symbol_rate=2700.0,
... spc=2, cn0_dbhz=45.0, n_slots=2)
>>> pool.refine_min_blocks
7
>>> pool.set_refine_min_blocks(12)
>>> pool.refine_min_blocks
12
function async_dsss_pool_set_state¶
function async_dsss_pool_state_bytes¶
function async_dsss_pool_status¶
One slot's picture, by value ( async_dsss_pool_slot_t ).
Allocation-free: the row plus the receiver's own status record. A slot outside [0, n_slots) returns a zero record with state -1.
Parameters:
stateMust be non-NULL.slotThe slot.
Returns:
The record.
>>> import numpy as np
>>> from doppler.dsss import AsyncDsssPool
>>> from doppler.wfm import Gold
>>> code = np.asarray(Gold().generate(1023)).astype(np.uint8)
>>> pool = AsyncDsssPool(code, chip_rate=5e6, symbol_rate=2700.0,
... spc=2, cn0_dbhz=45.0, n_slots=2)
>>> r = pool.status(1)
>>> (r.slot, r.assigned, r.state) # idle: 3, nothing assigned
(1, 0, 3)
>>> pool.status(2).state # no such slot
-1
function async_dsss_pool_symbols¶
The symbols slot slot's receiver decided on the last push().
size_t async_dsss_pool_symbols (
async_dsss_pool_state_t * state,
size_t slot,
float _Complex * out,
size_t max_out
)
Copied from the pool's own buffer, which the next push() overwrites. Empty while the slot is idle, refining or lost, and for a slot outside [0, n_slots).
Parameters:
stateMust be non-NULL.slotThe slot.outCaller buffer.max_outIts capacity.
Returns:
Symbols written.
>>> import numpy as np
>>> from doppler.dsss import AsyncDsssPool
>>> from doppler.wfm import Gold
>>> code = np.asarray(Gold().generate(1023)).astype(np.uint8)
>>> pool = AsyncDsssPool(code, chip_rate=5e6, symbol_rate=2700.0,
... spc=2, cn0_dbhz=45.0, n_slots=2)
>>> _ = pool.push(np.zeros(2046, np.complex64))
>>> pool.symbols(0).shape # idle: nothing decided
(0,)
function async_dsss_pool_symbols_max_out¶
The per-slot symbol capacity symbols() can return grown with the largest block pushed so far (0 before the first push).
Macro Definition Documentation¶
define ASYNC_DSSS_POOL_MAX_EMITTER_ON_TIME_SECS¶
The one constant of design section 6.1: the maximum on-air time of a single emitter, seconds the default of max_emitter_on_time_secs, which the false-release budget and the soak are sized against.
define ASYNC_DSSS_POOL_STATE_MAGIC¶
define ASYNC_DSSS_POOL_STATE_VERSION¶
The documentation for this class was generated from the following file native/inc/async_dsss_pool/async_dsss_pool_core.h