mirror of
https://github.com/gnss-sdr/gnss-sdr
synced 2026-10-16 04:31:50 +00:00
Merge commit '5efbfd16c9eed710211f5532d30a0681cc67add2' into joebre-freq-error-reduction
This commit is contained in:
+4
-12
@@ -119,18 +119,10 @@ All notable changes to GNSS-SDR will be documented in this file.
|
||||
per bin and supports `high_dyn=true`. The `pull_in_time_s` and
|
||||
`bit_synchronization_time_limit_s` budgets start after the scan, allowing the
|
||||
tracking loops their full settling time. Contributed by @joebre.
|
||||
- Added `Acquisition_<Sig>.full_grid_search` (default: `false`) for PCPS
|
||||
acquisition. When enabled, the receiver uses all `max_dwells` integration
|
||||
periods before accepting or rejecting a signal, giving a stronger peak time to
|
||||
emerge from noise. This can improve peak selection at the cost of a longer
|
||||
acquisition wait. It has no effect when `bit_transition_flag` is enabled.
|
||||
Contributed by @joebre.
|
||||
- Added diagnostics to help investigate slow signal synchronization: console and
|
||||
log messages report the time to carrier phase lock and bit/secondary-code
|
||||
synchronization. Frequency-refinement scans also save the tested Doppler
|
||||
frequencies, their correlation power, and the selected frequency to
|
||||
`Tracking_<Sig>.f_error_dump_filename` (default: `./f_error_dump.csv`). Set
|
||||
the filename to an empty value to disable this CSV output. Channels sharing a
|
||||
- Added a CSV dump of the frequency-refinement scan: the tested Doppler
|
||||
frequencies, their correlation power, and the selected frequency are written
|
||||
to `Tracking_<Sig>.f_error_dump_filename` (default: `./f_error_dump.csv`). Set
|
||||
the filename to an empty value to disable this output. Channels sharing a
|
||||
filename write to the same file, with scan, satellite and channel identifiers;
|
||||
the first scan overwrites any previous file, and later scans in the same
|
||||
receiver run append their results.
|
||||
|
||||
@@ -842,47 +842,19 @@ void pcps_acquisition::acquisition_core(uint64_t sample_count)
|
||||
|
||||
if (!d_acq_parameters.bit_transition_flag)
|
||||
{
|
||||
if (d_acq_parameters.full_grid_search)
|
||||
if (result.test_statistics > get_threshold())
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
handle_threshold_reached(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (result.test_statistics > get_threshold())
|
||||
{
|
||||
handle_threshold_reached(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
d_buffer_count = 0;
|
||||
d_state = 1;
|
||||
}
|
||||
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,7 +83,6 @@ 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,14 +92,6 @@ 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};
|
||||
|
||||
@@ -1252,51 +1252,6 @@ dll_pll_veml_tracking::~dll_pll_veml_tracking()
|
||||
}
|
||||
|
||||
|
||||
// DIAGNOSTIC: report, once bit/secondary-code sync completes, how much of the total delay
|
||||
// since the tracking loop first started running (state first entered 2, whether directly
|
||||
// from state 1 or via state 5's scan) was spent waiting for the first carrier phase lock vs.
|
||||
// spent in the bit-sync accumulation phase after that first lock (including any resets from
|
||||
// lock drops during that phase, counted separately in d_bit_sync_reset_count). Deliberately
|
||||
// NOT measured from when d_pull_in_transitory clears: that flag is gated by a fixed timer
|
||||
// (pull_in_time_s) unrelated to actual convergence, and run_dll_pll() runs continuously from
|
||||
// state 2's very first cycle regardless of it -- measuring from the timer would report near-0
|
||||
// "time to phase lock" for any channel that happened to already be locked before the timer
|
||||
// expired, silently hiding however long that actually took. method identifies which of the
|
||||
// three bit-sync completion paths (secondary_code / preamble_correlation / histogram) this
|
||||
// call came from. No-op if the tracking loop never started for this channel (should not
|
||||
// happen at a completion call site, but guards against a coding error rather than crashing).
|
||||
void dll_pll_veml_tracking::log_time_to_fix_breakdown(const char *method)
|
||||
{
|
||||
if (d_tracking_loop_started_sample < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const auto now_sample = static_cast<int64_t>(this->nitems_read(0));
|
||||
const double total_ms = 1000.0 * static_cast<double>(now_sample - d_tracking_loop_started_sample) / d_trk_parameters.fs_in;
|
||||
std::ostringstream msg;
|
||||
msg << "Time-to-fix breakdown (" << method << ") ch=" << d_channel << " "
|
||||
<< Gnss_Satellite(d_systemName, d_acquisition_gnss_synchro->PRN)
|
||||
<< " total_time_to_bit_sync_ms:" << total_ms;
|
||||
if (d_phase_lock_first_sample >= 0)
|
||||
{
|
||||
const double phase_lock_ms = 1000.0 * static_cast<double>(d_phase_lock_first_sample - d_tracking_loop_started_sample) / d_trk_parameters.fs_in;
|
||||
const double bit_sync_ms = total_ms - phase_lock_ms;
|
||||
msg << " time_to_phase_lock_ms:" << phase_lock_ms
|
||||
<< " time_bit_sync_after_lock_ms:" << bit_sync_ms
|
||||
<< " bit_sync_lock_drops:" << d_bit_sync_reset_count
|
||||
<< " acquire_secondary_attempts:" << d_acquire_secondary_attempts
|
||||
<< " case2_cycles:" << d_case2_cycle_count
|
||||
<< " locked_branch_cycles:" << d_locked_branch_cycle_count;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg << " time_to_phase_lock_ms:never_reached";
|
||||
}
|
||||
std::cout << msg.str() << '\n';
|
||||
LOG(INFO) << msg.str();
|
||||
}
|
||||
|
||||
|
||||
bool dll_pll_veml_tracking::acquire_secondary()
|
||||
{
|
||||
// ******* preamble correlation ********
|
||||
@@ -1387,15 +1342,6 @@ bool dll_pll_veml_tracking::cn0_and_tracking_lock_status(double coh_integration_
|
||||
d_CN0_SNV_dB_Hz = d_cn0_smoother.smooth(d_CN0_SNV_dB_Hz_raw);
|
||||
// Carrier lock indicator
|
||||
d_carrier_lock_test = d_carrier_lock_test_smoother.smooth(carrier_lock_detector(&d_P_accu, 1));
|
||||
// DIAGNOSTIC: record the first time phase lock is reached since the tracking loop started,
|
||||
// for the time-to-fix breakdown (phase lock vs. bit/secondary-code sync) reported once bit
|
||||
// sync completes -- see d_tracking_loop_started_sample's declaration. Deliberately NOT
|
||||
// gated on !d_pull_in_transitory: that flag is a fixed timer, but d_carrier_lock_test is
|
||||
// computed (and can genuinely cross threshold) from the loop's very first cycle onward.
|
||||
if (d_tracking_loop_started_sample >= 0 && d_phase_lock_first_sample < 0 && d_carrier_lock_test >= d_carrier_lock_threshold)
|
||||
{
|
||||
d_phase_lock_first_sample = static_cast<int64_t>(this->nitems_read(0));
|
||||
}
|
||||
// Loss of lock detection
|
||||
if (!d_pull_in_transitory)
|
||||
{
|
||||
@@ -2343,12 +2289,6 @@ void dll_pll_veml_tracking::begin_wide_tracking(uint64_t sample_count)
|
||||
d_code_lock_fail_counter = 0;
|
||||
}
|
||||
d_state = 2;
|
||||
d_tracking_loop_started_sample = static_cast<int64_t>(sample_count);
|
||||
d_phase_lock_first_sample = -1;
|
||||
d_bit_sync_reset_count = 0;
|
||||
d_acquire_secondary_attempts = 0;
|
||||
d_case2_cycle_count = 0;
|
||||
d_locked_branch_cycle_count = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -2473,7 +2413,6 @@ int dll_pll_veml_tracking::general_work(int noutput_items __attribute__((unused)
|
||||
}
|
||||
case 2: // Wide tracking and symbol synchronization
|
||||
{
|
||||
d_case2_cycle_count++; // DIAGNOSTIC: raw cadence check for the time-to-fix breakdown
|
||||
do_correlation_step(in);
|
||||
// Save single correlation step variables
|
||||
if (d_veml)
|
||||
@@ -2547,18 +2486,12 @@ int dll_pll_veml_tracking::general_work(int noutput_items __attribute__((unused)
|
||||
// requirement than d_carrier_lock_threshold's coarse cos(2*phi) phase-lock
|
||||
// test. A prior attempt at this same removal (see
|
||||
// gnss_sdr_bitsync_differential project memory) was reverted after showing
|
||||
// loss-of-lock regressions -- but that was before the time-to-fix
|
||||
// instrumentation below existed to actually measure the effect, and the
|
||||
// regression may have been this same gate's own flakiness (bit_sync_lock_drops
|
||||
// in the low hundreds/thousands, see log_time_to_fix_breakdown) rather than
|
||||
// something the gate was protecting against. Re-testing with real numbers
|
||||
// this time; d_bit_sync_reset_count now stays at 0 by construction, kept in
|
||||
// the report only so the log format doesn't change shape between A/B runs.
|
||||
d_locked_branch_cycle_count++; // DIAGNOSTIC
|
||||
// loss-of-lock regressions, but re-testing with real numbers showed all
|
||||
// configured satellites used in the first fix every run with this gate
|
||||
// removed, versus some missing before -- kept removed.
|
||||
d_Prompt_circular_buffer.push_back(*d_Prompt);
|
||||
if (d_Prompt_circular_buffer.size() == d_secondary_code_length)
|
||||
{
|
||||
d_acquire_secondary_attempts++;
|
||||
next_state = acquire_secondary();
|
||||
if (next_state)
|
||||
{
|
||||
@@ -2566,7 +2499,6 @@ int dll_pll_veml_tracking::general_work(int noutput_items __attribute__((unused)
|
||||
<< " for satellite " << Gnss_Satellite(d_systemName, d_acquisition_gnss_synchro->PRN) << '\n';
|
||||
std::cout << d_systemName << " " << d_signal_pretty_name << " secondary code locked in channel " << d_channel
|
||||
<< " for satellite " << Gnss_Satellite(d_systemName, d_acquisition_gnss_synchro->PRN) << '\n';
|
||||
log_time_to_fix_breakdown("secondary_code");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2598,7 +2530,6 @@ int dll_pll_veml_tracking::general_work(int noutput_items __attribute__((unused)
|
||||
<< " for satellite " << Gnss_Satellite(d_systemName, d_acquisition_gnss_synchro->PRN);
|
||||
std::cout << d_systemName << " " << d_signal_pretty_name << " histogram bit synchronization locked in channel " << d_channel
|
||||
<< " for satellite " << Gnss_Satellite(d_systemName, d_acquisition_gnss_synchro->PRN) << '\n';
|
||||
log_time_to_fix_breakdown("histogram");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2611,7 +2542,6 @@ int dll_pll_veml_tracking::general_work(int noutput_items __attribute__((unused)
|
||||
d_Prompt_circular_buffer.push_back(*d_Prompt);
|
||||
if (d_Prompt_circular_buffer.size() == d_secondary_code_length)
|
||||
{
|
||||
d_acquire_secondary_attempts++;
|
||||
next_state = acquire_secondary();
|
||||
if (next_state)
|
||||
{
|
||||
@@ -2619,7 +2549,6 @@ int dll_pll_veml_tracking::general_work(int noutput_items __attribute__((unused)
|
||||
<< " for satellite " << Gnss_Satellite(d_systemName, d_acquisition_gnss_synchro->PRN);
|
||||
std::cout << d_systemName << " " << d_signal_pretty_name << " tracking bit synchronization locked in channel " << d_channel
|
||||
<< " for satellite " << Gnss_Satellite(d_systemName, d_acquisition_gnss_synchro->PRN) << '\n';
|
||||
log_time_to_fix_breakdown("preamble_correlation");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2778,6 +2707,8 @@ int dll_pll_veml_tracking::general_work(int noutput_items __attribute__((unused)
|
||||
update_tracking_vars(false);
|
||||
if (d_f_error_bin_index >= d_f_error_num_bins)
|
||||
{
|
||||
// scan complete: run_f_error_scan_step() already left d_carrier_doppler_hz
|
||||
// on the winning bin and re-seeded the carrier loop filter with it.
|
||||
// Begin the tracking budget at the first sample after the scan.
|
||||
begin_wide_tracking(this->nitems_read(0) + static_cast<uint64_t>(d_current_prn_length_samples));
|
||||
}
|
||||
|
||||
@@ -105,7 +105,6 @@ private:
|
||||
void configure_bit_synchronizer();
|
||||
bool cn0_and_tracking_lock_status(double coh_integration_time_s);
|
||||
bool acquire_secondary();
|
||||
void log_time_to_fix_breakdown(const char *method); // DIAGNOSTIC: phase lock vs. bit sync timing
|
||||
int64_t uint64diff(uint64_t first, uint64_t second);
|
||||
int32_t save_matfile() const;
|
||||
|
||||
@@ -258,15 +257,6 @@ private:
|
||||
bool d_wait_for_bit_edge{false};
|
||||
bool d_b1c_prelock_output_pending{false};
|
||||
bool d_carrier_phase_discontinuity{true}; // pending report of a new carrier phase ambiguity
|
||||
|
||||
// DIAGNOSTIC: time-to-fix breakdown (phase lock vs. bit/secondary-code sync), see
|
||||
// general_work()'s pull_in_transitory-clearing check and the accumulation gates.
|
||||
int64_t d_tracking_loop_started_sample{-1}; // nitems_read(0) when state first entered 2 (run_dll_pll() starts), -1 = not yet
|
||||
int64_t d_phase_lock_first_sample{-1}; // nitems_read(0) at first carrier_lock_test >= threshold since then, -1 = not yet
|
||||
int32_t d_bit_sync_reset_count{0}; // d_Prompt_circular_buffer resets due to lock drop, after first phase lock
|
||||
int32_t d_acquire_secondary_attempts{0}; // acquire_secondary() calls since the buffer first filled, reset per tracking attempt
|
||||
int64_t d_case2_cycle_count{0}; // case-2 epochs executed since tracking loop started, reset per tracking attempt
|
||||
int32_t d_locked_branch_cycle_count{0}; // of those, how many took the "d_carrier_lock_test >= threshold" branch
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user