GNSS-SDR 0.0.21
An Open Source GNSS Software Defined Receiver
Loading...
Searching...
No Matches
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{
36public:
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 */
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 */
288
289private:
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
bool is_edge_epoch(std::int64_t k) const
Predict whether a given epoch index corresponds to a bit edge.
void reset()
Reset the synchronizer state.
std::int64_t get_total_events() const
Total number of detected transition events accumulated into the histogram.
bool update(const std::complex< float > &prompt, bool tracking_quality_ok)
Update the synchronizer once per epoch.
HistogramBitSynchronizer(const Config &cfg)
Construct a histogram bit synchronizer with the provided configuration.
const std::vector< int > & get_histogram() const
Access the internal histogram (read-only).
bool locked() const
Query whether the synchronizer has achieved lock.
int epochs_until_next_edge() const
Return the number of epochs until the next predicted navigation bit edge.
int bins() const
Return the number of histogram bins.
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().
Configuration parameters for HistogramBitSynchronizer.
int min_events_for_lock
Minimum number of detected transition events required before lock evaluation.
double dominance_ratio
Required dominance ratio of the winning histogram bin.
int epoch_ms
Time interval between successive calls to update(), in milliseconds.
float min_prompt_mag
Minimum magnitude of the prompt correlator output.
int stable_best_required
Required stability of the dominant histogram bin (consecutive evaluations).
int bit_period_ms
Navigation data bit period in milliseconds.
bool use_phase_dot_detector
Select the transition detection method.