Skip to content

File acq_core.h

File List > acq > acq_core.h

Go to the documentation of this file

#ifndef ACQ_CORE_H
#define ACQ_CORE_H

#include "buffer/buffer.h"
#include "clib_common.h"
#include "corr2d/corr2d_core.h"
#include "detection/detection_core.h"
#include "dp_state.h"
#include "fft/fft_core.h"
#include "jm_perf.h"
/* detector2d_core.h supplies det_noise_mode_t (guarded typedef). */
#include "detector2d/detector2d_core.h"
#include "fft2d/fft2d_core.h"
#include "dp_parallel.h"
#include "dp_tlm/dp_tlm_core.h"

#ifdef __cplusplus
extern "C"
{
#endif

  typedef struct
  {
    size_t doppler_bin; 
    size_t code_phase; 
    float  peak_mag;   
    float  noise_est;  
    float  test_stat;  
    float cn0_dbhz_est;        
    uint64_t samples_consumed; 
  } acq_result_t;

  typedef struct
  {
    dp_tlm_t *ctx;      
    int32_t id_stat;    
    int32_t id_gate;    
    int32_t id_noise;   
    int32_t id_peak;    
    int32_t id_row;     
    int32_t id_col;     
    int32_t id_n_peaks; 
    int32_t id_n_held;  
    int32_t id_conc;    
    int32_t id_hit;     
  } acq_tlm_t;

  typedef void (*acq_surface_sink_fn) (void *ctx, const float *surface,
                                       size_t rows, size_t cols,
                                       uint64_t samples_consumed);

  typedef struct
  {
    float  ref;  
    size_t best; 
  } acq_part_t;

  typedef struct
  {
    corr2d_state_t *corr; 
    fft_state_t *slow_fft; 
    dp_f32_t    *ring;  
    float _Complex *ref; 
    float _Complex *yframe;  
    float _Complex *colbuf;  
    float _Complex *colout;  
    float _Complex *out_buf; 
    float *mag_buf;       
    float *noise_scratch; 
    float *nc_surface;    
    /* Wideband mode only (window_bins > 1) — see the file doc comment.
     * Independent of corr/slow_fft/yframe/colbuf/colout above (unused, but
     * left allocated at their trivial coherent_bins=1 size, in this mode). */
    fft_state_t *wide_fwd; 
    fft_state_t *wide_inv; 
    float _Complex
        *wide_ref_spec; 
    float _Complex
        *wide_spec; 
    float _Complex *wide_prod; 
    size_t
        coherent_bins; 
    size_t window_bins; 
    size_t code_bins; 
    size_t n; 
    size_t n_surf; 
    size_t interp; 
    size_t frame_n; 
    size_t sf;      
    size_t spc;     
    size_t reps;    
    size_t
        searched_bins; 
    size_t n_noncoh;   
    size_t nc_count; 
    size_t ring_cap; 
    size_t noise_lo; 
    size_t noise_hi; 
    det_noise_mode_t noise_mode; 
    double chip_rate; 
    double fs;        
    double cn0_dbhz;  
    double
        doppler_span_hz; 
    double
        doppler_res_hz; 
    double pfa;         
    double doppler_uncertainty; 
    double symbol_rate; 
    double epochs_per_symbol;  
    size_t code_only_epochs; 
    double doppler_rate; 
    double carrier_freq_hz; 
    float _Complex *blk; 
    size_t blk_epoch;    
    /* The roll per thread (design §2.3): the tiles are independent after
       the one forward transform, so the per-epoch tile loop and the
       block-end column loop run through a persistent pool. The scratch is
       PER TILE, not per thread -- a pocketfft plan carries its own work
       buffers, and a tile lands on whichever worker takes it -- so the
       serial and the fanned paths run the same code on the same buffers
       and the surface is bit-identical either way. */
    dp_pool_t       *pool;      
    int              threads;   
    fft_state_t    **tile_inv;  
    float _Complex **tile_prod; 
    fft_state_t    **tile_slow; 
    float _Complex **tile_col;  
    size_t **tile_rows;         
    acq_part_t *parts;          
    float  threshold; 
    float  eta;       
    float  eta_nc;    
    double pfa_cell;  
    double pd;        
    double pd_predicted;  
    double straddle_loss; 
    uint8_t underpowered; 
    uint64_t
        samples_consumed; 
    /* The peak list (docs/design/async-dsss-receiver.md §7.1): up to
       `max_peaks` peaks per dwell, each above the same gate, strongest
       first, with an exclusion zone of one Doppler row (`interp` surface
       rows) by one chip (`spc` columns), circular, around each; every
       listed peak is one acq_result_t. `band_mask` marks the cells outside
       the searched Doppler band (rebuilt with the thresholds); `peak_mask`
       is the per-dwell working copy the list marks its zones into. The
       two-epoch rule: a peak within a chip of an already-listed peak's code
       phase, at any row, is that emitter's candidate twin -- held, not
       listed, unless it was there at the same row on the previous dwell,
       listed or held. `twin_*` are the previous dwell's picks (native
       rows). */
    size_t      max_peaks; 
    size_t      n_peaks;   
    det_peak_t *peaks;     
    uint8_t    *band_mask; 
    uint8_t    *peak_mask; 
    uint32_t   *twin_row;  
    uint32_t   *twin_col;  
    size_t      n_twins;   
    /* Observability (design §2.4): attach-on-demand, nothing in blobs. */
    acq_tlm_t tlm;       
    int       keep_surface; 
    float *stat_surface; 
    uint64_t surface_at; 
    uint64_t dwells;     
    acq_surface_sink_fn sink; 
    void    *sink_ctx;
    uint32_t sink_decim; 
    size_t   n_held;     
    float    peak_conc;  
    /* Last-dump bookkeeping (for inspection): the strongest pick. */
    size_t peak_row;
    size_t peak_col;
    float  peak_mag;
    float  noise_est;
    float  test_stat;
  } acq_state_t;

  typedef struct
  {
    uint16_t has_nc; 
    uint16_t _pad;
    uint32_t n_noncoh;         
    uint64_t n;                
    uint64_t samples_consumed; 
    uint32_t nc_count;     
    uint32_t n_unconsumed; 
    uint32_t max_peaks;    
    uint32_t n_twins;      
    uint32_t blk_epoch; 
  } acq_extra_t;

#define ACQ_STATE_MAGIC DP_FOURCC ('A', 'C', 'Q', 'R')
#define ACQ_STATE_VERSION 3u /* v3: the block-coherent accumulator rides along */

#define ACQ_MAX_PEAKS 64u

#define ACQ_COL_CHUNK 32u

#define ACQ_N_NONCOH_SAFETY_CEILING 256u

  acq_state_t *acq_create_burst (const uint8_t *code, size_t code_len,
                                 size_t reps, size_t spc, double chip_rate,
                                 double cn0_dbhz, double doppler_uncertainty,
                                 double pfa, double pd, int noise_mode);

  acq_state_t *acq_create_continuous (const uint8_t *code, size_t code_len,
                                      size_t spc, double chip_rate,
                                      double symbol_rate, double cn0_dbhz,
                                      double doppler_uncertainty, double pfa,
                                      double pd, int noise_mode,
                                      size_t code_only_epochs,
                                      double doppler_rate);

  void acq_destroy (acq_state_t *state);

  void acq_reset (acq_state_t *state);

  int acq_configure_search_raw (acq_state_t *state, size_t doppler_bins,
                                size_t n_noncoh);

  int acq_set_max_peaks (acq_state_t *state, size_t n);

  int acq_set_carrier_freq_hz (acq_state_t *state, double carrier_freq_hz);

  int acq_set_threads (acq_state_t *state, int n);
  int acq_set_telemetry (acq_state_t *state, dp_tlm_t *tlm,
                         const char *prefix, uint32_t decim);

  size_t acq_surface (acq_state_t *state, float *out, size_t n_out);

  size_t acq_surface_doppler_hz (acq_state_t *state, double *out,
                                 size_t n_out);

  size_t acq_surface_chip_phase (acq_state_t *state, double *out,
                                 size_t n_out);

  void acq_set_surface_sink (acq_state_t *state, acq_surface_sink_fn fn,
                             void *ctx, uint32_t decim);

  size_t acq_push (acq_state_t *state, const float _Complex *x, size_t n_in,
                   acq_result_t *result, size_t max_results);

  typedef struct
  {
    uint64_t samples_consumed; 
    double
        chip_phase; 
    double doppler_hz_est;  
    double doppler_res_hz;  
    double cn0_dbhz_est;    
    float  peak_mag;        
    float  noise_est;       
    float  test_stat;       
  } acq_handoff_t;

  /* The FFT-bin convention this engine reports in -- `0 = DC`, ascending
   * positive, then wrapping negative -- is `dp_fftfreq_index()` in
   * clib_common.h, and its doc comment there is the one definition. It was
   * declared here, and four call sites outside C restated the fold in three
   * mutually inconsistent ways; the engine's wideband search and its own
   * hand-off were two of them, which surfaced as a receiver reporting
   * `tracking == 1` while decoding noise. Every consumer -- this engine's
   * search, its hand-off, and any composing receiver -- now includes the
   * SAME inline rather than restating the formula. */

  void acq_build_handoff (const acq_state_t *state, const acq_result_t *hit,
                          size_t code_len, size_t spc, acq_handoff_t *out);

  /* ── Serializable state — the elastic / pure-transducer face
   * ─────────────────
   *
   * The OO engine above is convenient but stateful.  These match the rest of
   * the library's serializable objects (lo/cic/fir/ddcr): serialize a
   * channel's cross-call state to a flat POD, ship (descriptor, state, input)
   * to any thread/process/pod, rebuild the engine from the descriptor
   * (acq_create), inject the state, and continue — bit-identical to an
   * uninterrupted run.
   */

  size_t acq_state_bytes (const acq_state_t *state);

  void acq_get_state (const acq_state_t *state, void *blob);

  int acq_set_state (acq_state_t *state, const void *blob);

  size_t acq_run (acq_state_t *state, const void *state_in, void *state_out,
                  const float _Complex *in, size_t n_in, acq_result_t *result,
                  size_t max_results);

#ifdef __cplusplus
}
#endif

#endif /* ACQ_CORE_H */