File dsss_burst_receiver_core.h¶
FileList > doppler > dsss_burst_receiver > dsss_burst_receiver_core.h
Go to the source code of this file
DsssBurstReceiver — the burst chain composed in C. More...
#include "doppler/clib_common.h"#include "doppler/jm_perf.h"#include "doppler/buffer/buffer.h"#include "doppler/dp_state.h"#include "doppler/burst_capture/burst_capture_core.h"#include "doppler/burst_acq/burst_acq_core.h"#include "doppler/acq/acq_core.h"#include "doppler/burst_demod/burst_demod_core.h"#include "doppler/burst_despreader/burst_despreader_core.h"#include "doppler/ppe/ppe_core.h"#include "doppler/corr/corr_core.h"#include "doppler/corr2d/corr2d_core.h"#include "doppler/fft2d/fft2d_core.h"#include "doppler/spectral/spectral_core.h"#include "doppler/loop_filter/loop_filter_core.h"#include "doppler/detection/detection_core.h"#include "doppler/fft/fft_core.h"#include "doppler/pn/pn_core.h"#include "doppler/conv/conv_core.h"#include "doppler/rs/rs_core.h"#include "doppler/gold/gold_core.h"#include "doppler/mpsk/mpsk_core.h"#include "doppler/cvt/cvt_core.h"#include "doppler/wfm/wfm_frame.h"
Classes¶
| Type | Name |
|---|---|
| struct | dp_dsss_burst_receiver_state_t DsssBurstReceiver state. |
| struct | dsss_br_event_t One completed burst's event, as events() hands it back. |
Public Functions¶
| Type | Name |
|---|---|
| int | dp_dsss_burst_receiver_configure_search_raw (dp_dsss_burst_receiver_state_t * state, size_t doppler_bins, size_t n_noncoh) Pin the acquisition search grid, bypassing the auto-sizing. |
| dp_dsss_burst_receiver_state_t * | dp_dsss_burst_receiver_create_desc (const uint8_t * acq_code, size_t acq_code_len, const uint8_t * data_code, size_t data_code_len, const wfm_frame_desc_t * frame, size_t reps, size_t spc, double chip_rate, double cn0_dbhz, double doppler_uncertainty, double pfa, double pd, double carrier_hz, double max_rate, size_t est_segments, const char ** why) Create a receiver from the frame DESCRIPTION the transmitter spread, and no hand-counted frame length. |
| dp_dsss_burst_receiver_state_t * | dp_dsss_burst_receiver_create_frame (const uint8_t * acq_code, size_t acq_code_len, const uint8_t * data_code, size_t data_code_len, const wfm_frame_desc_t * frame, size_t reps, size_t spc, double chip_rate, double cn0_dbhz, double doppler_uncertainty, double pfa, double pd, double carrier_hz, double max_rate, size_t est_segments) The Python binding's constructor: dp_dsss_burst_receiver_create_desc without the why out-parameter. |
| void | dp_dsss_burst_receiver_destroy (dp_dsss_burst_receiver_state_t * state) Destroy a dsss_burst_receiver instance and release all memory. |
| size_t | dp_dsss_burst_receiver_events (dp_dsss_burst_receiver_state_t * state, size_t n, dsss_br_event_t * out, size_t max_out) The event record for each burst the last push() returned. |
| size_t | dp_dsss_burst_receiver_events_max_out (dp_dsss_burst_receiver_state_t * state) Max records events() writes: one per burst the last push() returned. |
| double | dp_dsss_burst_receiver_get_cn0_dbhz_est (const dp_dsss_burst_receiver_state_t * state) |
| double | dp_dsss_burst_receiver_get_demod_cn0_dbhz (const dp_dsss_burst_receiver_state_t * state) |
| double | dp_dsss_burst_receiver_get_demod_timing_chips (const dp_dsss_burst_receiver_state_t * state) |
| double | dp_dsss_burst_receiver_get_doppler_hz_est (const dp_dsss_burst_receiver_state_t * state) |
| double | dp_dsss_burst_receiver_get_doppler_res_hz (const dp_dsss_burst_receiver_state_t * state) |
| uint64_t | dp_dsss_burst_receiver_get_dropped (const dp_dsss_burst_receiver_state_t * state) Samples of look-back the capture discarded while a queued burst still needed them never refused input, which push() does not do. A LOST BURST, not a statistic a lifetime count that survives reset(). |
| double | dp_dsss_burst_receiver_get_est_freq_hz (const dp_dsss_burst_receiver_state_t * state) |
| double | dp_dsss_burst_receiver_get_est_rate_hz (const dp_dsss_burst_receiver_state_t * state) |
| bool | dp_dsss_burst_receiver_get_frame_valid (const dp_dsss_burst_receiver_state_t * state) |
| size_t | dp_dsss_burst_receiver_get_min_gap (const dp_dsss_burst_receiver_state_t * state) |
| uint64_t | dp_dsss_burst_receiver_get_n_bursts (const dp_dsss_burst_receiver_state_t * state) |
| size_t | dp_dsss_burst_receiver_get_pending (const dp_dsss_burst_receiver_state_t * state) |
| uint64_t | dp_dsss_burst_receiver_get_preamble_start (const dp_dsss_burst_receiver_state_t * state) |
| size_t | dp_dsss_burst_receiver_get_refine_span (const dp_dsss_burst_receiver_state_t * state) |
| size_t | dp_dsss_burst_receiver_get_retain_span (const dp_dsss_burst_receiver_state_t * state) |
| void | dp_dsss_burst_receiver_get_state (const dp_dsss_burst_receiver_state_t * state, void * blob) Serialize state's cross-call state intoblob (caller-owned,dp_dsss_burst_receiver_state_bytes() long). |
| size_t | dp_dsss_burst_receiver_llrs (dp_dsss_burst_receiver_state_t * state, size_t n, float * out, size_t max_out) The SOFT bits of every burst the last push() returned. |
| size_t | dp_dsss_burst_receiver_llrs_max_out (dp_dsss_burst_receiver_state_t * state, size_t n) Max LLRs llrs() writes: frame bits x the bursts the last push returned. |
| size_t | dp_dsss_burst_receiver_push (dp_dsss_burst_receiver_state_t * state, const float _Complex * x, size_t x_len, uint8_t * out, size_t max_out) Stream samples; return the payload of EVERY burst that completed. |
| size_t | dp_dsss_burst_receiver_push_max_out (dp_dsss_burst_receiver_state_t * state, size_t x_len) Max bits push() can write for an input of x_len samples. |
| void | dp_dsss_burst_receiver_reset (dp_dsss_burst_receiver_state_t * state) Return to searching: drop the history and clear every read-back. |
| int | dp_dsss_burst_receiver_set_state (dp_dsss_burst_receiver_state_t * state, const void * blob) Restore cross-call state from blob (replacing it). |
| size_t | dp_dsss_burst_receiver_state_bytes (const dp_dsss_burst_receiver_state_t * state) Byte size of state's blob (envelope + payload + child). |
Macros¶
| Type | Name |
|---|---|
| define | DSSS_BURST_RECEIVER_STATE_MAGIC [**DP\_FOURCC**](dp__state_8h.md#define-dp_fourcc)('D', 'B', 'R', 'X')Per-object envelope tag: "DBRX" (DsssBurstReceiver). |
| define | DSSS_BURST_RECEIVER_STATE_VERSION 7u |
Detailed Description¶
Composes the three certified burst objects behind one push(): acquisition SEARCHES the stream, a refine stage recovers the exact preamble start, and the demodulator produces the payload. It owns the hand-off between them the epoch, the fold, and the look-back reaching back to a burst start that has already gone past which is the part every caller previously redid by hand. See docs/design/dsss-burst-receiver.md.
Lifecycle: create, then push() repeatedly, then destroy. There is no step()/steps(): a burst is a frame, not a sample.
uint8_t acq[31] = { 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0,
0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0 };
uint8_t data[8] = { 1, 0, 1, 1, 0, 0, 1, 0 }, sb[13] = { 0 };
const wfm_seq_t sync = { .kind = WFM_SEQ_LITERAL, .bits = sb, .len = 13 };
const wfm_seq_t body = { .kind = WFM_SEQ_DATA, .len = 32 };
wfm_frame_desc_t f;
dp_wfm_frame_fixed (&f, NULL, 0, &sync, &body, 1); // sync|data:32|crc16
dp_dsss_burst_receiver_state_t *rx = dp_dsss_burst_receiver_create_desc (
acq, 31, data, 8, &f, 4, 4, 1.0e6, 55.0, 0.0, 1e-3, 0.9, 0.0, 0.0, 10,
NULL);
if (!rx)
return 1;
float _Complex samples[256] = { 0 }; // the stream goes here
uint8_t bits[(13 + 32 + 16) * 4]; // frame_syms per burst: sync|payload|CRC
size_t n = dp_dsss_burst_receiver_push (rx, samples, 256, bits,
sizeof bits);
(void)n; // the bits are the FRAME as received
dp_dsss_burst_receiver_destroy (rx);
Public Functions Documentation¶
function dp_dsss_burst_receiver_configure_search_raw¶
Pin the acquisition search grid, bypassing the auto-sizing.
int dp_dsss_burst_receiver_configure_search_raw (
dp_dsss_burst_receiver_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) rather than the grid the cn0_dbhz/pfa/pd sizing chooses. Forwards to the embedded engine unchanged.
Parameters:
stateMust be non-NULL.doppler_binsCoherent depth to pin, in[1, reps].n_noncohNon-coherent looks to combine.
Returns:
0 on success, non-zero if the grid is out of range.
>>> import numpy as np
>>> from doppler.dsss import DsssBurstReceiver
>>> from doppler.wfm import Frame
>>> rng = np.random.default_rng(0)
>>> desc = Frame(sync=np.zeros(13, np.uint8), payload=np.zeros(3, np.uint8),
... crc="crc16") # 13 + 3 + 16 = 32 symbols a burst
>>> rx = DsssBurstReceiver(
... rng.integers(0, 2, 31).astype(np.uint8),
... rng.integers(0, 2, 8).astype(np.uint8), desc, reps=4, spc=4)
>>> rx.configure_search_raw(doppler_bins=1, n_noncoh=1)
function dp_dsss_burst_receiver_create_desc¶
Create a receiver from the frame DESCRIPTION the transmitter spread, and no hand-counted frame length.
dp_dsss_burst_receiver_state_t * dp_dsss_burst_receiver_create_desc (
const uint8_t * acq_code,
size_t acq_code_len,
const uint8_t * data_code,
size_t data_code_len,
const wfm_frame_desc_t * frame,
size_t reps,
size_t spc,
double chip_rate,
double cn0_dbhz,
double doppler_uncertainty,
double pfa,
double pd,
double carrier_hz,
double max_rate,
size_t est_segments,
const char ** why
)
The sync word is the description's field 0, frame_syms its layout's length (both from dp_wfm_frame_desc_rx), and a frame's verdict is dp_wfm_frame_desc_crc_ok over the description, so a frame with no CRC stage is never valid (decision D3 of docs/design/rx-frame-description.md). The description is copied (without its sequences), so the caller may free it at once. This is the one constructor: acquisition, refine and demodulation composed behind one push(), with the look-back span DERIVED from the geometry rather than asked of the caller.
Parameters:
acq_codepreamble code, 0/1 chips.acq_code_lenits length, chips.data_codepayload spreading code, 0/1 chips.data_code_lenits length, chips.framethe description (const wfm_frame_desc_t *).repspreamble code repetitions.spcsamples per chip.chip_ratechips per second.cn0_dbhzdesign C/N0 for the acquisition, dB-Hz (or NaN).doppler_uncertaintythe Doppler span to search, cycles/sample.pfafalse-alarm probability, in (0, 1).pddetection probability, in (0, 1).carrier_hzthe carrier the baseband is offset by, Hz.max_ratethe Doppler rate expected, cycles/sample^2.est_segmentspartials per acquisition period.whyon a NULL return, a static sentence naming the fix; may beNULL.
Returns:
the receiver, or NULL with why set.
const uint8_t sb[3] = { 1, 0, 1 }, acq[7] = { 1, 1, 1, 0, 1, 0, 0 };
const uint8_t dcode[4] = { 1, 0, 1, 1 };
const wfm_seq_t sync = { .kind = WFM_SEQ_LITERAL, .bits = sb, .len = 3 };
const wfm_seq_t data = { .kind = WFM_SEQ_DATA, .len = 8 };
wfm_frame_desc_t f;
dp_wfm_frame_fixed (&f, NULL, 0, &sync, &data, 1); // sync|data:8|crc16
const char *why = NULL;
dp_dsss_burst_receiver_state_t *rx = dp_dsss_burst_receiver_create_desc (
acq, 7, dcode, 4, &f, 3, 2, 1e6, 60.0, 0.0, 1e-3, 0.9, 0.0, 0.0, 10,
&why);
if (!rx || rx->frame_syms != 3 + 8 + 16)
return 1;
dp_dsss_burst_receiver_destroy (rx);
function dp_dsss_burst_receiver_create_frame¶
The Python binding's constructor: dp_dsss_burst_receiver_create_desc without thewhy out-parameter.
dp_dsss_burst_receiver_state_t * dp_dsss_burst_receiver_create_frame (
const uint8_t * acq_code,
size_t acq_code_len,
const uint8_t * data_code,
size_t data_code_len,
const wfm_frame_desc_t * frame,
size_t reps,
size_t spc,
double chip_rate,
double cn0_dbhz,
double doppler_uncertainty,
double pfa,
double pd,
double carrier_hz,
double max_rate,
size_t est_segments
)
An object's generated constructor has no channel for a reason, so a refused description surfaces as the manifest's create_error_message, which names the rules. C callers that want the reason call the _desc form.
Parameters:
acq_codepreamble code, 0/1 chips.acq_code_lenits length, chips.data_codepayload spreading code, 0/1 chips.data_code_lenits length, chips.framethe description (const wfm_frame_desc_t *).repspreamble code repetitions.spcsamples per chip.chip_ratechips per second.cn0_dbhzdesign C/N0 for the acquisition, dB-Hz (or NaN).doppler_uncertaintythe Doppler span to search, cycles/sample.pfafalse-alarm probability, in (0, 1).pddetection probability, in (0, 1).carrier_hzthe carrier the baseband is offset by, Hz.max_ratethe Doppler rate expected, cycles/sample^2.est_segmentspartials per acquisition period.
Returns:
the receiver, or NULL.
>>> import numpy as np
>>> from doppler.dsss import DsssBurstReceiver
>>> from doppler.wfm import Frame
>>> rng = np.random.default_rng(0)
>>> acq = rng.integers(0, 2, 31).astype(np.uint8)
>>> dat = rng.integers(0, 2, 8).astype(np.uint8)
>>> desc = Frame(sync=np.zeros(13, np.uint8), payload=np.zeros(3, np.uint8),
... crc="crc16") # the 32-symbol frame the burst carries
>>> rx = DsssBurstReceiver(acq, dat, desc, reps=4, spc=4)
>>> rx.n_bursts
0
function dp_dsss_burst_receiver_destroy¶
Destroy a dsss_burst_receiver instance and release all memory.
Parameters:
stateMay be NULL.
function dp_dsss_burst_receiver_events¶
The event record for each burst the last push() returned.
size_t dp_dsss_burst_receiver_events (
dp_dsss_burst_receiver_state_t * state,
size_t n,
dsss_br_event_t * out,
size_t max_out
)
Row i describes the payload at out[i*frame_syms ...] of that push. A single push can complete many bursts and each needs its own event, so these are a list rather than the scalar read-backs those still exist and still describe the LAST burst, but they cannot speak for the others.
Valid until the next push(), reset() or set_state(). Deliberately not serialized: it describes one call, and keeping it out of the blob is what holds state_bytes() to a pure function of configuration.
Parameters:
stateMust be non-NULL.nIgnored. The record count is whatever the last push() produced, not something a caller chooses; this parameter exists because every variable-output method carries one, and the binding uses it only as a floor on the buffer it allocates.outRecords, caller-owned,max_outlong.max_outCapacity ofout; see events_max_out().
Returns:
Records written to out min(events_max_out(), max_out).
>>> import numpy as np
>>> from doppler.dsss import DsssBurstReceiver
>>> from doppler.wfm import Frame
>>> rng = np.random.default_rng(0)
>>> desc = Frame(sync=np.zeros(13, np.uint8), payload=np.zeros(3, np.uint8),
... crc="crc16") # 13 + 3 + 16 = 32 symbols a burst
>>> rx = DsssBurstReceiver(
... rng.integers(0, 2, 31).astype(np.uint8),
... rng.integers(0, 2, 8).astype(np.uint8), desc, reps=4, spc=4)
>>> bits = rx.push(np.zeros(4096, dtype=np.complex64))
>>> len(rx.events()) == bits.size // 32 # one record per payload
True
function dp_dsss_burst_receiver_events_max_out¶
Max records events() writes: one per burst the last push() returned.
Parameters:
stateMust be non-NULL.
Returns:
The number of bursts the most recent push() completed.
function dp_dsss_burst_receiver_get_cn0_dbhz_est¶
function dp_dsss_burst_receiver_get_demod_cn0_dbhz¶
function dp_dsss_burst_receiver_get_demod_timing_chips¶
double dp_dsss_burst_receiver_get_demod_timing_chips (
const dp_dsss_burst_receiver_state_t * state
)
function dp_dsss_burst_receiver_get_doppler_hz_est¶
function dp_dsss_burst_receiver_get_doppler_res_hz¶
function dp_dsss_burst_receiver_get_dropped¶
Samples of look-back the capture discarded while a queued burst still needed them never refused input, which push() does not do. A LOST BURST, not a statistic a lifetime count that survives reset().
function dp_dsss_burst_receiver_get_est_freq_hz¶
function dp_dsss_burst_receiver_get_est_rate_hz¶
function dp_dsss_burst_receiver_get_frame_valid¶
function dp_dsss_burst_receiver_get_min_gap¶
function dp_dsss_burst_receiver_get_n_bursts¶
function dp_dsss_burst_receiver_get_pending¶
function dp_dsss_burst_receiver_get_preamble_start¶
function dp_dsss_burst_receiver_get_refine_span¶
function dp_dsss_burst_receiver_get_retain_span¶
function dp_dsss_burst_receiver_get_state¶
Serialize state's cross-call state intoblob (caller-owned,dp_dsss_burst_receiver_state_bytes() long).
function dp_dsss_burst_receiver_llrs¶
The SOFT bits of every burst the last push() returned.
size_t dp_dsss_burst_receiver_llrs (
dp_dsss_burst_receiver_state_t * state,
size_t n,
float * out,
size_t max_out
)
crealf(sym * derot) IS the log-likelihood ratio up to a scale, and the demodulator used to compute it, slice it to one bit and free it. A hard decision throws away roughly 2 dB of the coding gain a soft-input decoder exists to deliver (mpsk_soft_demap's own docstring), which is what makes a coded burst worth coding.
Concatenated the same way push()'s payloads are, one row of frame_syms per burst: burst i starts at i * frame_syms, in the order events() reports. The convention is mpsk_soft_demap's — positive means bit 0, so L < 0 reproduces exactly the bits push() returned. Spans the WHOLE frame rather than the payload alone, because a code covers what its description says it covers.
Valid until the next push(), reset() or set_state(); deliberately not serialized, for the same reason events() is not: it describes one call.
Parameters:
stateReceiver handle.nIgnored — the count is the last push's, not a request.outReceives the LLRs.max_outCapacity ofout; see llrs_max_out().
Returns:
LLRs written.
>>> import numpy as np
>>> from doppler.dsss import DsssBurstReceiver
>>> from doppler.wfm import Frame
>>> rng = np.random.default_rng(0)
>>> desc = Frame(sync=np.zeros(13, np.uint8), payload=np.zeros(3, np.uint8),
... crc="crc16") # 13 + 3 + 16 = 32 symbols a burst
>>> rx = DsssBurstReceiver(
... rng.integers(0, 2, 31).astype(np.uint8),
... rng.integers(0, 2, 8).astype(np.uint8), desc, reps=4, spc=4)
>>> bits = rx.push(np.zeros(4096, dtype=np.complex64))
>>> len(bits), len(rx.llrs(rx.llrs_max_out(1))) # nothing decoded
(0, 0)
function dp_dsss_burst_receiver_llrs_max_out¶
Max LLRs llrs() writes: frame bits x the bursts the last push returned.
Parameters:
stateReceiver handle.nIgnored, as in llrs().
function dp_dsss_burst_receiver_push¶
Stream samples; return the payload of EVERY burst that completed.
size_t dp_dsss_burst_receiver_push (
dp_dsss_burst_receiver_state_t * state,
const float _Complex * x,
size_t x_len,
uint8_t * out,
size_t max_out
)
Retains x in the history ring and feeds the embedded acquisition. When a detection fires, the refine stage correlates the whole preamble to recover the exact preamble start the quantity acquisition structurally cannot report, its code phase being a lag modulo one code period and the burst is demodulated once its last sample has arrived.
EVERY SAMPLE OF x IS CONSUMED, and every burst that completes is returned by the call that completed it. Payloads are concatenated, so burst i occupies out from i*frame_syms, and events() returns the matching record for each. Returning 0 is normal, not an error: it means no burst completed in this call.
This is the contract doppler#1008 broke. push() used to return at most one burst per call AND abandon the rest of its input to do it, so a block carrying several bursts lost all but the first measured at 6/6 decoded with 333-sample blocks against 1/6 with one large one. The history ring is a contiguous window over the stream and is never reset between bursts, so a payload whose tail falls outside one call is completed by a later one.
Parameters:
stateMust be non-NULL.xInput samples (cf32),x_lenlong.x_lenNumber of input samples.outPayload bits, caller-owned,max_outlong.max_outCapacity ofout; see push_max_out().
Returns:
Bits written to out n_bursts_returned * frame_syms.
>>> import numpy as np
>>> from doppler.dsss import DsssBurstReceiver
>>> from doppler.wfm import Frame
>>> rng = np.random.default_rng(0)
>>> desc = Frame(sync=np.zeros(13, np.uint8), payload=np.zeros(3, np.uint8),
... crc="crc16") # 13 + 3 + 16 = 32 symbols a burst
>>> rx = DsssBurstReceiver(
... rng.integers(0, 2, 31).astype(np.uint8),
... rng.integers(0, 2, 8).astype(np.uint8), desc, reps=4, spc=4)
>>> bits = rx.push(np.zeros(4096, dtype=np.complex64))
>>> bits.size # silence carries no burst
0
function dp_dsss_burst_receiver_push_max_out¶
Max bits push() can write for an input of x_len samples.
push() returns EVERY burst it completed, so the bound scales with the input: distinct bursts cannot overlap, so they are at least burst_len apart, and a push of x_len samples can complete at most x_len/burst_len + 1 of them plus every detection already queued from an earlier call, which is q_cap.
Parameters:
stateMust be non-NULL.x_lenNumber of input samples the caller is about to push.
Returns:
(x_len/burst_len + 1 + q_cap) * frame_syms.
function dp_dsss_burst_receiver_reset¶
Return to searching: drop the history and clear every read-back.
Resets the embedded acquisition, discards the retained look-back, and clears all the event fields, so a fresh stream cannot inherit the previous burst's verdict. The lifetime counters (n_bursts, dropped) deliberately survive a reset that zeroed them could hide that this receiver had already lost samples. Construction parameters are untouched.
Parameters:
stateMust be non-NULL.>>> import numpy as np >>> from doppler.dsss import DsssBurstReceiver >>> from doppler.wfm import Frame >>> rng = np.random.default_rng(0) >>> desc = Frame(sync=np.zeros(13, np.uint8), payload=np.zeros(3, np.uint8), ... crc="crc16") # 13 + 3 + 16 = 32 symbols a burst >>> rx = DsssBurstReceiver( ... rng.integers(0, 2, 31).astype(np.uint8), ... rng.integers(0, 2, 8).astype(np.uint8), desc, reps=4, spc=4) >>> _ = rx.push(np.zeros(1024, dtype=np.complex64)) >>> rx.reset()
function dp_dsss_burst_receiver_set_state¶
Restore cross-call state from blob (replacing it).
Returns:
DP_OK, or DP_ERR_INVALID if the envelope or any child rejects.
function dp_dsss_burst_receiver_state_bytes¶
Byte size of state's blob (envelope + payload + child).
Macro Definition Documentation¶
define DSSS_BURST_RECEIVER_STATE_MAGIC¶
Per-object envelope tag: "DBRX" (DsssBurstReceiver).
define DSSS_BURST_RECEIVER_STATE_VERSION¶
The documentation for this class was generated from the following file native/inc/doppler/dsss_burst_receiver/dsss_burst_receiver_core.h