From 03b0cbe5143d0e0ccec761d51a482ec18f58e8bd Mon Sep 17 00:00:00 2001 From: joebre Date: Thu, 17 Sep 2026 11:21:00 +0200 Subject: [PATCH] Fix observables per-channel history too short for fast-producing channels hybrid_observables_gs::interp_trk_obs() bracket-interpolates each channel's pseudorange against d_Rx_clock_buffer.front(), a timestamp deliberately roughly (clock buffer capacity - 1) observable intervals behind "now" (its own comment: "gives the channels time to compute the tracking observables"). The per-channel history it interpolates from, d_gnss_synchro_history, was a fixed 1000-entry capacity regardless of that channel's own valid-word production rate. That fixed entry count covers a shrinking time span as a channel's own rate grows. A channel producing valid words close to its raw tracking rate can fill all 1000 entries in only a couple of seconds, leaving a window shorter than the clock buffer's lag -- interp_trk_obs() then never finds a pair bracketing the target timestamp for that channel, no matter how healthy its tracking/decoding otherwise is. The channel keeps reporting via the tracking-only fallback (visible as tracked, correct CN0/Doppler) but Flag_valid_pseudorange stays permanently false, so it never contributes a pseudorange and PVT reports it without position/ elevation data. Bump the capacity 5x. This is a per-channel structure holding a small struct, so the added memory is negligible; it comfortably covers a channel whose production rate is close to its raw tracking rate while leaving slower channels' existing multi-second margin untouched. Verified against a channel type whose valid-word rate is high enough to exhaust the old capacity within ~1-1.5s of first becoming valid: with the fix, its interpolation gap against the clock target dropped from a mostly-failing ~0.9-1.9s (essentially the whole clock lag) to a stable sub-millisecond match, sustained over the rest of the run. --- .../gnuradio_blocks/hybrid_observables_gs.cc | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/algorithms/observables/gnuradio_blocks/hybrid_observables_gs.cc b/src/algorithms/observables/gnuradio_blocks/hybrid_observables_gs.cc index 4bc66b9c4..1dd82a5cf 100644 --- a/src/algorithms/observables/gnuradio_blocks/hybrid_observables_gs.cc +++ b/src/algorithms/observables/gnuradio_blocks/hybrid_observables_gs.cc @@ -98,7 +98,21 @@ hybrid_observables_gs::hybrid_observables_gs(const Obs_Conf &conf_) // Send Channel status to gnss_flowgraph this->message_port_register_out(pmt::mp("status")); - d_gnss_synchro_history = std::make_unique>(1000, d_nchannels_out); + // interp_trk_obs() needs this per-channel history to reach back far + // enough to bracket d_Rx_clock_buffer.front() -- deliberately the OLDEST + // of the clock buffer's several most recent ticks (see its own comment + // just below), roughly (its capacity - 1) * d_T_rx_step_ms behind "now". + // A fixed entry COUNT covers a time span that shrinks as a channel's own + // valid-word rate grows: a slow channel's 1000 entries can span many + // seconds, but a channel producing them near its raw tracking rate can + // fill all 1000 in a couple of seconds, leaving a window shorter than + // that clock lag -- interp_trk_obs() can then never find a bracketing + // pair for that channel, so Flag_valid_pseudorange stays permanently + // false for it even though it is tracking and decoding correctly (PVT + // reports it as tracked but without a usable pseudorange). 5x headroom + // comfortably covers that case without being a meaningful memory cost + // (Gnss_Synchro is small; this capacity is per-channel). + d_gnss_synchro_history = std::make_unique>(5000, d_nchannels_out); d_Rx_clock_buffer.set_capacity(std::min(std::max(300U / d_T_rx_step_ms, 3U), 20U)); d_Rx_clock_buffer.clear();