Add full_grid_search option to acquisition

Previously, the (possibly still noisy, partially non-coherently
accumulated) grid was checked against threshold after every single
dwell, and the first bin anywhere to cross it won immediately -- even
if a later dwell's fuller integration would have revealed a different,
genuinely stronger peak elsewhere in the same grid that never got the
chance to be compared.

Adds Acquisition_<Sig>.full_grid_search (default false, preserving the
existing early-exit behavior): when true, the accept/reject decision
is deferred until the final dwell, from the fully max_dwells-
accumulated grid, instead of exiting on the first dwell whose
still-partial accumulation happens to cross threshold.

Combined with the frequency-error reduction scan, this addresses a
recurring Galileo E1-B loss-of-lock failure mode: measured over 30
trials against a live capture, enabling full_grid_search alone still
left 26/30 trials with at least one loss-of-lock event (66 total
events); adding the frequency-error scan on top brought that down to
4/30 trials (5 total events).

Signed-off-by: joebre <joel.brenner@saphyrion.ch>
This commit is contained in:
joebre
2026-09-10 10:04:13 +02:00
parent 352e1e9fea
commit f278cc9dc8
3 changed files with 45 additions and 8 deletions
@@ -842,19 +842,47 @@ void pcps_acquisition::acquisition_core(uint64_t sample_count)
if (!d_acq_parameters.bit_transition_flag)
{
if (result.test_statistics > get_threshold())
if (d_acq_parameters.full_grid_search)
{
handle_threshold_reached(result);
// Search the entire acquisition grid (accumulate through the full max_dwells)
// before deciding accept/reject, instead of exiting as soon as any single dwell's
// (possibly still noisy, partially non-coherently accumulated) grid crosses
// threshold -- a later dwell's fuller integration can reveal a different, genuinely
// stronger peak elsewhere in the same grid that an early exit never gets the chance
// to compare against.
if (d_num_noncoherent_integrations_counter == d_acq_parameters.max_dwells)
{
if (result.test_statistics > get_threshold())
{
handle_threshold_reached(result);
}
else
{
handle_integration_done(result);
}
}
else
{
d_buffer_count = 0;
d_state = 1;
}
}
else
{
d_buffer_count = 0;
d_state = 1;
}
if (result.test_statistics > get_threshold())
{
handle_threshold_reached(result);
}
else
{
d_buffer_count = 0;
d_state = 1;
}
if (d_num_noncoherent_integrations_counter == d_acq_parameters.max_dwells)
{
handle_integration_done(result);
if (d_num_noncoherent_integrations_counter == d_acq_parameters.max_dwells)
{
handle_integration_done(result);
}
}
}
else
@@ -83,6 +83,7 @@ void Acq_Conf::SetFromConfiguration(const ConfigurationInterface *configuration,
make_2_steps = configuration->property(role + ".make_two_steps", make_2_steps);
blocking_on_standby = configuration->property(role + ".blocking_on_standby", blocking_on_standby);
enable_doppler_narrowing = configuration->property(role + ".enable_doppler_narrowing", enable_doppler_narrowing);
full_grid_search = configuration->property(role + ".full_grid_search", full_grid_search);
if (pfa <= 0.0)
{
@@ -92,6 +92,14 @@ public:
// flag has no effect and the full configured Doppler grid is always searched.
bool enable_doppler_narrowing{false};
// Accumulate through the full max_dwells before deciding accept/reject, instead
// of exiting as soon as any single dwell's (possibly still noisy, partially
// accumulated) grid crosses threshold -- a later dwell's fuller integration can
// reveal a different, genuinely stronger peak elsewhere in the grid that an early
// exit never gets the chance to compare against. Opt-in: off by default, enable
// per-implementation in the .conf (e.g. Acquisition_1B.full_grid_search = true).
bool full_grid_search{false};
// Specific to some implementations
bool acquire_pilot{false};
bool acquire_iq{false};