GNSS-SDR  0.0.21
An Open Source GNSS Software Defined Receiver
bit_synchronizer.h
Go to the documentation of this file.
1 /*!
2  * \file bit_synchronizer.h
3  * \brief Histogram-based bit-edge synchronizer for GNSS prompt correlator outputs.
4  * \author Carles Fernandez-Prades, 2026 cfernandez(at)cttc.es
5  *
6  * -----------------------------------------------------------------------------
7  *
8  * GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
9  * This file is part of GNSS-SDR.
10  *
11  * Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
12  * SPDX-License-Identifier: GPL-3.0-or-later
13  *
14  * -----------------------------------------------------------------------------
15  */
16 
17 #ifndef GNSS_SDR_BIT_SYNCHRONIZER_H
18 #define GNSS_SDR_BIT_SYNCHRONIZER_H
19 
20 #include <cmath>
21 #include <complex>
22 #include <cstdint>
23 #include <vector>
24 
25 /** \addtogroup Tracking
26  * \{ */
27 /** \addtogroup Tracking_libs
28  * \{ */
29 
30 
31 /**
32  * @brief Histogram-based navigation data bit synchronizer.
33  */
35 {
36 public:
37  /**
38  * @brief Configuration parameters for HistogramBitSynchronizer.
39  *
40  * These parameters define the bit period, the update cadence, the lock criteria,
41  * and the transition detection method.
42  */
43  struct Config
44  {
45  /**
46  * @brief Navigation data bit period in milliseconds.
47  *
48  * This is the nominal duration of one navigation data bit.
49  */
51 
52  /**
53  * @brief Time interval between successive calls to update(), in milliseconds.
54  *
55  * This should match the minimum integration interval (epoch) produced by the
56  * tracking loop and used to generate the provided prompt correlator output.
57  */
58  int epoch_ms;
59 
60  /**
61  * @brief Minimum number of detected transition events required before lock evaluation.
62  *
63  * The histogram is built from detected candidate transitions. Lock decisions are
64  * not attempted until at least this many events have been accumulated.
65  *
66  * Trade-offs:
67  * - Larger values increase robustness against false locks but increase time-to-lock.
68  * - Smaller values reduce time-to-lock but increase sensitivity to noise/spurious transitions.
69  */
71 
72  /**
73  * @brief Required dominance ratio of the winning histogram bin.
74  *
75  * Lock requires the most frequent histogram bin to be sufficiently dominant:
76  * @f[
77  * \text{dominance\_ratio} = \frac{\text{best\_bin\_count}}{\text{total\_detected\_events}}
78  * @f]
79  *
80  * Guidance:
81  * - Values near 0.5 may lock faster but increase false-lock probability.
82  * - Values closer to 1.0 are conservative and require a clearly dominant phase.
83  */
85 
86  /**
87  * @brief Required stability of the dominant histogram bin (consecutive evaluations).
88  *
89  * Even if the dominance ratio is met, the algorithm requires that the same histogram
90  * bin remains dominant for this many consecutive lock evaluations before declaring lock.
91  *
92  * This helps prevent locking on transient peaks caused by noise or short-lived disturbances.
93  */
95 
96  /**
97  * @brief Minimum magnitude of the prompt correlator output.
98  *
99  * Candidate transition detection is suppressed when @f$|P| < \text{min\_prompt\_mag}@f$,
100  * where @f$P@f$ is the prompt correlator output.
101  *
102  * Use this to avoid counting unreliable transitions when tracking quality is poor or
103  * the prompt output is dominated by noise.
104  */
106 
107  /**
108  * @brief Select the transition detection method.
109  *
110  * If true (recommended), uses a “phase-dot” detector:
111  * - A candidate transition is detected when:
112  * @f[
113  * \Re\{ P_k \cdot P^*_{k-1} \} < 0
114  * @f]
115  * where @f$P_k@f$ is the current prompt and @f$P^*_{k-1}@f$ the conjugate of the previous.
116  *
117  * This method is largely insensitive to constant carrier phase rotations and is often
118  * more robust during early tracking / imperfect carrier phase alignment.
119  *
120  * If false, uses a simpler sign-change detector on the real part:
121  * - A candidate transition is detected when sign(Re(P_k)) != sign(Re(P_{k-1})).
122  *
123  * This assumes the prompt output is already aligned with the data bit polarity
124  * (i.e., stable PLL lock and correct navigation bit polarity mapping).
125  */
127 
128  Config()
129  : bit_period_ms(20),
130  epoch_ms(1),
132  dominance_ratio(0.6),
134  min_prompt_mag(0.0f),
136  {
137  }
138  };
139 
140  /**
141  * @brief Construct a histogram bit synchronizer with the provided configuration.
142  *
143  * Initializes internal counters and allocates the histogram with bins() entries,
144  * all set to zero.
145  *
146  * @param cfg Configuration parameters.
147  */
148  explicit HistogramBitSynchronizer(const Config& cfg)
149  : cfg_(cfg),
150  hist_(),
151  total_events_(0),
152  epoch_count_(0),
153  last_prompt_(0.0f, 0.0f),
154  edge_phase_(-1),
155  last_sign_(+1),
156  last_best_bin_(0),
157  stable_best_count_(0),
158  locked_(false),
159  has_last_prompt_(false),
160  has_last_sign_(false),
161  has_last_best_bin_(false)
162  {
163  hist_.assign(bins(), 0);
164  }
165 
166  /**
167  * @brief Reset the synchronizer state.
168  *
169  * Clears the histogram and all internal counters/flags, returning the instance to the
170  * pre-lock state:
171  * - locked() becomes false
172  * - edge_phase() becomes -1
173  * - total_events and epoch_count are reset to zero
174  */
175  void reset();
176 
177  /**
178  * @brief Update the synchronizer once per epoch.
179  *
180  * This method should be called at a fixed cadence defined by Config::epoch_ms
181  *
182  * The method:
183  * - Advances the internal epoch counter,
184  * - Optionally performs candidate transition detection if tracking quality is acceptable,
185  * - Updates the phase histogram on detected transitions,
186  * - Evaluates lock once enough events have been gathered.
187  *
188  * @param prompt Prompt correlator output for the current epoch.
189  * @param tracking_quality_ok Indicates whether tracking quality is sufficient to trust
190  * the prompt sample for transition detection (e.g., code/carrier lock metrics).
191  *
192  * @return True only on the epoch when lock is first declared; false otherwise
193  * (including subsequent epochs after lock has been achieved).
194  */
195  bool update(const std::complex<float>& prompt, bool tracking_quality_ok);
196 
197  /**
198  * @brief Query whether the synchronizer has achieved lock.
199  *
200  * @return True if lock has been declared, false otherwise.
201  */
202  bool locked() const { return locked_; }
203 
204  /**
205  * @brief Get the estimated bit edge phase bin.
206  *
207  * The edge phase is expressed as an integer histogram bin index in the range
208  * [0, bins()-1] when locked. The interpretation is “which epoch phase within the
209  * bit period is most likely to contain a navigation bit transition.”
210  *
211  * @return Estimated edge phase bin index, or -1 if not locked.
212  */
213  int edge_phase() const { return edge_phase_; }
214 
215  /**
216  * @brief Predict whether a given epoch index corresponds to a bit edge.
217  *
218  * For a given epoch index @p k (0-based), this function returns true when @p k is
219  * aligned with the currently estimated edge phase (i.e., the predicted transition epoch),
220  * and false otherwise.
221  *
222  * If not locked, this always returns false.
223  *
224  * @param k Epoch index (0-based, consistent with the caller's epoch counting).
225  * @return True if @p k is the predicted edge epoch; false otherwise.
226  */
227  bool is_edge_epoch(std::int64_t k) const;
228 
229  /**
230  * @brief Return the number of histogram bins.
231  *
232  * Derived from the bit period and epoch duration, e.g.:
233  * bins = bit_period_ms / epoch_ms
234  *
235  * @return Number of histogram bins.
236  */
237  int bins() const;
238 
239  /**
240  * @brief Access the internal histogram (read-only).
241  *
242  * Each entry counts how many detected candidate transitions occurred at the
243  * corresponding phase bin within the bit period.
244  *
245  * @return Reference to the histogram vector.
246  */
247  const std::vector<int>& get_histogram() const { return hist_; }
248 
249  /**
250  * @brief Total number of detected transition events accumulated into the histogram.
251  *
252  * @return Total detected events.
253  */
254  std::int64_t get_total_events() const { return total_events_; }
255 
256  /**
257  * @brief Total number of epochs processed by update().
258  *
259  * This counter increments once per call to update(), regardless of whether a transition
260  * is detected or whether tracking_quality_ok is true.
261  *
262  * @return Total processed epochs.
263  */
264  std::int64_t get_epoch_count() const { return epoch_count_; }
265 
266  /**
267  * @brief Return the number of epochs until the next predicted navigation bit edge.
268  *
269  * When the synchronizer is locked, this function computes the forward distance
270  * (in epochs) from the most recently processed epoch to the next epoch that is
271  * aligned with the estimated bit-edge phase.
272  *
273  * The result is expressed modulo the bit period and has the following meaning:
274  * - 0 : the current epoch corresponds to the predicted start of a new navigation bit
275  * - >0 : number of epochs remaining until the next bit boundary
276  *
277  * The computation is based on the internal epoch counter advanced by update(),
278  * assuming that update() is called once per epoch with a constant cadence
279  * equal to Config::epoch_ms.
280  *
281  * If the synchronizer is not locked, or if the configuration yields an invalid
282  * number of bins, this function returns -1.
283  *
284  * @return Number of epochs until the next predicted bit edge, or -1 if not locked
285  * or if the bit period configuration is invalid.
286  */
287  int epochs_until_next_edge() const;
288 
289 private:
290  void best_bin_and_count(int& best_bin, int& best_count) const;
291 
292  Config cfg_;
293  std::vector<int> hist_;
294 
295  std::int64_t total_events_;
296  std::int64_t epoch_count_;
297 
298  std::complex<float> last_prompt_; // Sign history (for simple detector)
299 
300  int edge_phase_;
301  int last_sign_; // Sign history (for simple detector)
302  int last_best_bin_; // Stability tracking for best bin
303  int stable_best_count_; // Stability tracking for best bin
304 
305  bool locked_;
306  bool has_last_prompt_; // Prompt history (for dot detector)
307  bool has_last_sign_; // Sign history (for simple detector)
308  bool has_last_best_bin_; // Stability tracking for best bin
309 };
310 
311 /** \} */
312 /** \} */
313 #endif // GNSS_SDR_BIT_SYNCHRONIZER_H
const std::vector< int > & get_histogram() const
Access the internal histogram (read-only).
int epoch_ms
Time interval between successive calls to update(), in milliseconds.
Histogram-based navigation data bit synchronizer.
bool update(const std::complex< float > &prompt, bool tracking_quality_ok)
Update the synchronizer once per epoch.
std::int64_t get_total_events() const
Total number of detected transition events accumulated into the histogram.
bool use_phase_dot_detector
Select the transition detection method.
bool is_edge_epoch(std::int64_t k) const
Predict whether a given epoch index corresponds to a bit edge.
int epochs_until_next_edge() const
Return the number of epochs until the next predicted navigation bit edge.
double dominance_ratio
Required dominance ratio of the winning histogram bin.
float min_prompt_mag
Minimum magnitude of the prompt correlator output.
void reset()
Reset the synchronizer state.
int edge_phase() const
Get the estimated bit edge phase bin.
std::int64_t get_epoch_count() const
Total number of epochs processed by update().
int stable_best_required
Required stability of the dominant histogram bin (consecutive evaluations).
int bit_period_ms
Navigation data bit period in milliseconds.
HistogramBitSynchronizer(const Config &cfg)
Construct a histogram bit synchronizer with the provided configuration.
int min_events_for_lock
Minimum number of detected transition events required before lock evaluation.
Configuration parameters for HistogramBitSynchronizer.
bool locked() const
Query whether the synchronizer has achieved lock.
int bins() const
Return the number of histogram bins.