Skip to content

File dsss_receiver_core.h

File List > dsss_receiver > dsss_receiver_core.h

Go to the documentation of this file

#ifndef DSSS_RECEIVER_CORE_H
#define DSSS_RECEIVER_CORE_H

#include "RateConverter/RateConverter_core.h"
#include "acq/acq_core.h"
#include "cic/cic_core.h"
#include "dll/dll_core.h"
#include "dp_state.h"
#include "hbdecim/hbdecim_core.h"
#include "mpsk_receiver/mpsk_receiver_core.h"
#include "resamp/resamp_core.h"
#include "resample/resample_core.h"
#include <complex.h>
#include <stddef.h>
#include "costas/costas_core.h"
#include "snr/snr_core.h"
#include "ber/ber_core.h"

#ifdef __cplusplus
extern "C"
{
#endif

  typedef struct
  {
    acq_state_t           *acq;
    dll_state_t           *dll;
    RateConverter_state_t *rc;
    mpsk_receiver_state_t *rx;

    /* Pre-despread carrier loop -- embedded by value, same pattern
     * despreader_state_t's own `car` uses. Rebuilt alongside dll/rc/rx
     * (create-time placeholder, every real hit, configure_chain_raw(),
     * reset()). One update per code period (`tsamps` samples). */
    costas_state_t car;
    size_t         tsamps; 
    /* Scratch, not state: sized `tsamps`, allocated once (never per
     * steps() call -- no allocation in the hot streaming path). */
    float _Complex *car_wiped_buf;
    /* Genuinely stateful: raw samples handed to steps() that didn't fill
     * a whole code period yet, carried to the NEXT call so the carrier
     * loop always wipes exact, complete periods regardless of how a
     * caller chunks its input (this object's own "any block size, state
     * carries across calls" contract). Capacity `tsamps`; only the first
     * `car_carry_len` samples are valid. Must be serialized (see below)
     * or a resumed instance silently loses its period alignment. */
    float _Complex *car_carry_buf;
    size_t         car_carry_len;

    /* Own copy of the spreading code -- acq_create_continuous()/
     * dll_create()'s own borrow-vs-copy semantics aren't part of either's
     * public contract,
     * so this object keeps a persistent copy rather than depend on being
     * able to read it back out of a child, or on the caller's original
     * buffer outliving construction. */
    uint8_t *code;
    size_t   code_len;

    /* Config carried across a dll/rc/rx rebuild (create-time or
     * configure_chain_raw()) — everything mpsk_receiver_create() and
     * dll_create() need that isn't re-derived from the acquisition hit. */
    size_t spc;
    int    m;
    int    differential;
    size_t segments; 
    size_t sps;      
    int    n;        
    double chip_rate;
    double symbol_rate;

    int      tracking; 
    double   doppler_hz_est; 
    double   cn0_dbhz_est;   
    uint64_t samples_fed;    
  } dsss_receiver_state_t;

  /* SPEC-derived defaults for the pre-despread carrier loop (bn<=0.01
   * rule + this session's own validated test_costas_core.c/floor-sweep
   * bn_fll=0.03 calibration point) -- hardcoded, not public constructor
   * params, matching how Dll's own bn=0.002/zeta/spacing are already
   * hardcoded (this object's "just works" philosophy: only the signal's
   * physical parameters are required). */
#define DSSS_RX_BN_CARRIER 0.01
#define DSSS_RX_BN_FLL 0.03

  dsss_receiver_state_t *
  dsss_receiver_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 segments, size_t sps, int differential);

  void dsss_receiver_destroy (dsss_receiver_state_t *state);

  void dsss_receiver_reset (dsss_receiver_state_t *state);

  size_t dsss_receiver_steps_max_out (dsss_receiver_state_t *state);

  size_t dsss_receiver_steps (dsss_receiver_state_t *state,
                              const float _Complex *x, size_t x_len,
                              float _Complex *out, size_t max_out);

  int dsss_receiver_configure_search_raw (dsss_receiver_state_t *state,
                                          size_t                 doppler_bins,
                                          size_t                 n_noncoh);

  void dsss_receiver_configure_lock_raw (dsss_receiver_state_t *state,
                                         double up_thresh, double down_thresh,
                                         size_t n_looks, double alpha,
                                         uint32_t n_up, uint32_t n_down);

  int dsss_receiver_configure_chain_raw (dsss_receiver_state_t *state,
                                         size_t segments, size_t sps, int n);

  int    dsss_receiver_get_tracking (const dsss_receiver_state_t *state);
  double dsss_receiver_get_doppler_hz (const dsss_receiver_state_t *state);
  double dsss_receiver_get_cn0_dbhz_est (const dsss_receiver_state_t *state);
  size_t dsss_receiver_get_segments (const dsss_receiver_state_t *state);
  size_t dsss_receiver_get_sps (const dsss_receiver_state_t *state);
  int    dsss_receiver_get_n (const dsss_receiver_state_t *state);
  double dsss_receiver_get_chip_phase (const dsss_receiver_state_t *state);
  double dsss_receiver_get_code_rate (const dsss_receiver_state_t *state);
  double dsss_receiver_get_lock (const dsss_receiver_state_t *state);
  double dsss_receiver_get_norm_freq (const dsss_receiver_state_t *state);

  /* ── Serializable state (standard bytes interface; see dp_state.h) ──────
   * Composition: acq + car + dll + rc + rx, always all five (a fixed
   * shape -- see the state struct's own doc comment for why `tracking`
   * doesn't gate child presence here). `segments`/`sps`/`n` are the
   * layout key: set_state rejects a blob whose grid disagrees with the
   * live engine's, the same way ddc_extra_t's `rate` is checked before
   * touching any child. `car_carry_len` sizes the variable-length carry
   * buffer packed after `extra` (length-prefixed, 0..tsamps-1 samples) --
   * losing it on resume would desync the carrier loop's period
   * alignment, so it's part of the fixed-layout `extra` struct even
   * though the buffer bytes themselves are variable-length. */

  typedef struct
  {
    uint8_t  tracking;
    uint8_t  _pad[7];
    double   doppler_hz_est;
    double   cn0_dbhz_est;
    uint64_t segments;
    uint64_t sps;
    uint64_t n;
    uint64_t car_carry_len;
  } dsss_receiver_extra_t;

#define DSSS_RECEIVER_STATE_MAGIC DP_FOURCC ('D', 'S', 'R', 'X')
#define DSSS_RECEIVER_STATE_VERSION 2u /* v2: pre-despread costas_state_t
                                           car + carry buffer added */

  size_t dsss_receiver_state_bytes (const dsss_receiver_state_t *state);
  void   dsss_receiver_get_state (const dsss_receiver_state_t *state,
                                  void                        *blob);
  int dsss_receiver_set_state (dsss_receiver_state_t *state, const void *blob);

#ifdef __cplusplus
}
#endif

#endif /* DSSS_RECEIVER_CORE_H */