From 99e2ea05091c4880dec60483b7f4e2d87302ecf1 Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Wed, 21 Jan 2026 19:21:02 +0100 Subject: [PATCH] Improve documentation --- .../tracking/libs/bit_synchronizer.h | 247 +++++++++++++++--- 1 file changed, 213 insertions(+), 34 deletions(-) diff --git a/src/algorithms/tracking/libs/bit_synchronizer.h b/src/algorithms/tracking/libs/bit_synchronizer.h index 11df18ca5..bc318784d 100644 --- a/src/algorithms/tracking/libs/bit_synchronizer.h +++ b/src/algorithms/tracking/libs/bit_synchronizer.h @@ -22,24 +22,114 @@ #include #include +/** \addtogroup Tracking + * \{ */ +/** \addtogroup Tracking_libs + * \{ */ + + +/** + * @brief Histogram-based navigation data bit synchronizer. + */ class HistogramBitSynchronizer { public: + /** + * @brief Configuration parameters for HistogramBitSynchronizer. + * + * These parameters define the bit period, the update cadence, the lock criteria, + * and the transition detection method. + */ struct Config { - int bit_period_ms; // e.g., 20 for GPS L1 C/A - int epoch_ms; // e.g., 1 - int min_events_for_lock; // e.g., 30 - double dominance_ratio; // e.g., 0.55 - int stable_best_required; // e.g., 5 - float min_prompt_mag; // gate: ignore |P| below this - bool use_phase_dot_detector; // true: dot(Pk,Pk-1) sign; false: sign(Re(P)) + /** + * @brief Navigation data bit period in milliseconds. + * + * This is the nominal duration of one navigation data bit. + */ + int bit_period_ms; + + /** + * @brief Time interval between successive calls to update(), in milliseconds. + * + * This should match the minimum integration interval (epoch) produced by the + * tracking loop and used to generate the provided prompt correlator output. + */ + int epoch_ms; + + /** + * @brief Minimum number of detected transition events required before lock evaluation. + * + * The histogram is built from detected candidate transitions. Lock decisions are + * not attempted until at least this many events have been accumulated. + * + * Trade-offs: + * - Larger values increase robustness against false locks but increase time-to-lock. + * - Smaller values reduce time-to-lock but increase sensitivity to noise/spurious transitions. + */ + int min_events_for_lock; + + /** + * @brief Required dominance ratio of the winning histogram bin. + * + * Lock requires the most frequent histogram bin to be sufficiently dominant: + * @f[ + * \text{dominance\_ratio} = \frac{\text{best\_bin\_count}}{\text{total\_detected\_events}} + * @f] + * + * Guidance: + * - Values near 0.5 may lock faster but increase false-lock probability. + * - Values closer to 1.0 are conservative and require a clearly dominant phase. + */ + double dominance_ratio; + + /** + * @brief Required stability of the dominant histogram bin (consecutive evaluations). + * + * Even if the dominance ratio is met, the algorithm requires that the same histogram + * bin remains dominant for this many consecutive lock evaluations before declaring lock. + * + * This helps prevent locking on transient peaks caused by noise or short-lived disturbances. + */ + int stable_best_required; + + /** + * @brief Minimum magnitude of the prompt correlator output. + * + * Candidate transition detection is suppressed when @f$|P| < \text{min\_prompt\_mag}@f$, + * where @f$P@f$ is the prompt correlator output. + * + * Use this to avoid counting unreliable transitions when tracking quality is poor or + * the prompt output is dominated by noise. + */ + float min_prompt_mag; + + /** + * @brief Select the transition detection method. + * + * If true (recommended), uses a “phase-dot” detector: + * - A candidate transition is detected when: + * @f[ + * \Re\{ P_k \cdot P^*_{k-1} \} < 0 + * @f] + * where @f$P_k@f$ is the current prompt and @f$P^*_{k-1}@f$ the conjugate of the previous. + * + * This method is largely insensitive to constant carrier phase rotations and is often + * more robust during early tracking / imperfect carrier phase alignment. + * + * If false, uses a simpler sign-change detector on the real part: + * - A candidate transition is detected when sign(Re(P_k)) != sign(Re(P_{k-1})). + * + * This assumes the prompt output is already aligned with the data bit polarity + * (i.e., stable PLL lock and correct navigation bit polarity mapping). + */ + bool use_phase_dot_detector; Config() : bit_period_ms(20), epoch_ms(1), min_events_for_lock(30), - dominance_ratio(0.55), + dominance_ratio(0.6), stable_best_required(5), min_prompt_mag(0.0f), use_phase_dot_detector(true) @@ -47,66 +137,155 @@ public: } }; + /** + * @brief Construct a histogram bit synchronizer with the provided configuration. + * + * Initializes internal counters and allocates the histogram with bins() entries, + * all set to zero. + * + * @param cfg Configuration parameters. + */ explicit HistogramBitSynchronizer(const Config& cfg) : cfg_(cfg), + hist_(), total_events_(0), epoch_count_(0), - locked_(false), - edge_phase_(-1), - has_last_prompt_(false), last_prompt_(0.0f, 0.0f), - has_last_sign_(false), + edge_phase_(-1), last_sign_(+1), - has_last_best_bin_(false), last_best_bin_(0), - stable_best_count_(0) + stable_best_count_(0), + locked_(false), + has_last_prompt_(false), + has_last_sign_(false), + has_last_best_bin_(false) { hist_.assign(bins(), 0); } + /** + * @brief Reset the synchronizer state. + * + * Clears the histogram and all internal counters/flags, returning the instance to the + * pre-lock state: + * - locked() becomes false + * - edge_phase() becomes -1 + * - total_events and epoch_count are reset to zero + */ void reset(); - // Call once per epoch (e.g., per coherent integration output). - // Returns true ONLY on the epoch when lock is first declared. + /** + * @brief Update the synchronizer once per epoch. + * + * This method should be called at a fixed cadence defined by Config::epoch_ms + * + * The method: + * - Advances the internal epoch counter, + * - Optionally performs candidate transition detection if tracking quality is acceptable, + * - Updates the phase histogram on detected transitions, + * - Evaluates lock once enough events have been gathered. + * + * @param prompt Prompt correlator output for the current epoch. + * @param tracking_quality_ok Indicates whether tracking quality is sufficient to trust + * the prompt sample for transition detection (e.g., code/carrier lock metrics). + * + * @return True only on the epoch when lock is first declared; false otherwise + * (including subsequent epochs after lock has been achieved). + */ bool update(const std::complex& prompt, bool tracking_quality_ok); + /** + * @brief Query whether the synchronizer has achieved lock. + * + * @return True if lock has been declared, false otherwise. + */ bool locked() const { return locked_; } - // Returns -1 if not locked. + /** + * @brief Get the estimated bit edge phase bin. + * + * The edge phase is expressed as an integer histogram bin index in the range + * [0, bins()-1] when locked. The interpretation is “which epoch phase within the + * bit period is most likely to contain a navigation bit transition.” + * + * @return Estimated edge phase bin index, or -1 if not locked. + */ int edge_phase() const { return edge_phase_; } - // For a given epoch index k (0-based), tells you if it's the predicted edge epoch. + /** + * @brief Predict whether a given epoch index corresponds to a bit edge. + * + * For a given epoch index @p k (0-based), this function returns true when @p k is + * aligned with the currently estimated edge phase (i.e., the predicted transition epoch), + * and false otherwise. + * + * If not locked, this always returns false. + * + * @param k Epoch index (0-based, consistent with the caller's epoch counting). + * @return True if @p k is the predicted edge epoch; false otherwise. + */ bool is_edge_epoch(std::int64_t k) const; + /** + * @brief Return the number of histogram bins. + * + * Derived from the bit period and epoch duration, e.g.: + * bins = bit_period_ms / epoch_ms + * + * @return Number of histogram bins. + */ int bins() const; - const std::vector& histogram() const { return hist_; } - std::int64_t total_events() const { return total_events_; } - std::int64_t epoch_count() const { return epoch_count_; } + /** + * @brief Access the internal histogram (read-only). + * + * Each entry counts how many detected candidate transitions occurred at the + * corresponding phase bin within the bit period. + * + * @return Reference to the histogram vector. + */ + const std::vector& get_histogram() const { return hist_; } + + /** + * @brief Total number of detected transition events accumulated into the histogram. + * + * @return Total detected events. + */ + std::int64_t get_total_events() const { return total_events_; } + + /** + * @brief Total number of epochs processed by update(). + * + * This counter increments once per call to update(), regardless of whether a transition + * is detected or whether tracking_quality_ok is true. + * + * @return Total processed epochs. + */ + std::int64_t get_epoch_count() const { return epoch_count_; } private: void best_bin_and_count(int& best_bin, int& best_count) const; Config cfg_; std::vector hist_; + std::int64_t total_events_; std::int64_t epoch_count_; - bool locked_; + std::complex last_prompt_; // Sign history (for simple detector) + int edge_phase_; + int last_sign_; // Sign history (for simple detector) + int last_best_bin_; // Stability tracking for best bin + int stable_best_count_; // Stability tracking for best bin - // Prompt history (for dot detector) - bool has_last_prompt_; - std::complex last_prompt_; - // Sign history (for simple detector) - bool has_last_sign_; - int last_sign_; - - // Stability tracking for best bin - bool has_last_best_bin_; - int last_best_bin_; - int stable_best_count_; + bool locked_; + bool has_last_prompt_; // Prompt history (for dot detector) + bool has_last_sign_; // Sign history (for simple detector) + bool has_last_best_bin_; // Stability tracking for best bin }; -#endif // GNSS_SDR_BIT_SYNCHRONIZER_H \ No newline at end of file +/** \} */ +/** \} */ +#endif // GNSS_SDR_BIT_SYNCHRONIZER_H \ No newline at end of file