GPS L5 is pre-operational, and the L5 health bit of CNAV message type 10 (IS-GPS-200N 30.3.3.1.1.2) is broadcast as "unhealthy" on every SV. As a result, every GPS L5 signal was reported with healthy = false in the monitor_pvt tracked_satellites list, and "not used for navigation" was printed for every GPS satellite. Neither affected the PVT solution: CNAV L2C/L5/J5 observables are not gated on these bits (the L5 exclusion was dropped in v0.0.17), and the CNAV ephemeris handed to RTKLIB carries no svh. GPS L5 now reports the LNAV SV health of the same satellite; GPS L2C and QZSS L5 keep the CNAV bits; and the console message is replaced by a LOG(INFO) line.
Rtklib_Solver::get_broadcast_signal_health() excluded these signals from
the fix based on CNAV message type 10's per-carrier L1/L2/L5 health bit
alone. Per IS-GPS-200N 30.3.3.1.1.2, that bit is only meaningful together
with the SV's LNAV configuration code (20.3.3.5.1.4), which this receiver
does not track; the spec's own instruction for a receiver in that position
is to assume every signal is available on every SV.
Confirmed this was excluding real, operational satellites: cross-checked
the adjacent Transmission Week Number field against ground truth (computed
expected GPS week for the capture date, matched exactly), ruling out a
decode/bit-offset bug. L2C/L5/J5 now fall through to the same SV-wide
health source L1 C/A already uses. Also removes the now-misleading
"not used for navigation" message tied to the same bit.
Tested against real EVK1029 hardware captures; GPS L5 satellites now
report correctly instead of being universally flagged unhealthy.
Covers the exact configuration the fix is for: GNSS-SDR.internal_fs_sps
not a multiple of 1 kHz (4000123 Hz), coherent_integration_time_ms (4)
spanning more than one code period (GPS L1 C/A's own code period is
1 ms), and max_dwells (5) > 1. Asserts a successful acquisition and
checks the reported Doppler/delay against the known synthetic signal's
truth, with tolerances the dwell-boundary drift this fix addresses
would clearly exceed.
Registering it in acq_test (tests/CMakeLists.txt) surfaced that the
target had apparently never actually linked successfully in this
environment before: it links FirFilter/SignalGenerator/GenSignalSource
but was missing input_filter_adapters/signal_generator_adapters/
signal_source_adapters, and separately hit a Linux-only undefined
libboost_thread symbol inside the system's packaged
libgnuradio-runtime.so, fixed with the standard
--copy-dt-needed-entries linker workaround (guarded to Linux only).
Neither is specific to this new test; fixing them was necessary to
build and run the suite at all, and the rest of acq_test (20 tests
total) now passes too.
d_samples_to_consume (pcps_acquisition_adapter.cc) is floor(sampled_ms *
samples_per_ms), truncating to an integer. When the configured internal_fs_sps
does not divide evenly into a dwell (any decimation ratio leaving a fractional
number of samples per dwell), every dwell is silently shorter than the true
dwell length by a fixed residual, and that residual compounds linearly across
non-coherent dwells: the true code epoch drifts within the accumulation grid
instead of landing on the same index every time, and the sample stamp handed
to tracking drifts out of sync with it. Tracking then reconstructs the wrong
code phase at pull-in. Only shows up for short, high-chip-rate codes (Galileo
E5a, GPS L5) accumulated over several dwells -- low chip rate codes (GPS L1,
Galileo E1) absorb the same absolute drift as a much smaller fraction of a
chip.
Fix: keep each dwell's FFT/correlation window at the existing fixed
d_samples_to_consume size (so none of the buffer/FFT-plan sizing set up at
construction needs to change), but let the actual stream consumption between
dwells track the true dwell boundary with a standard DDA/Bresenham
accumulator -- skip one extra sample (discarded, never buffered) whenever the
accumulated residual reaches a full sample. This keeps every dwell's window
within half a sample of the true boundary permanently, so the drift this fix
is about never accumulates in the first place. The residual itself is
computed once, in the constructor, directly from the integer resampled_fs
(not through the lossy intermediate float samples_per_ms), and is always in
[0, 1) by construction, so there is no signed/unsigned wraparound risk.
This lives entirely in the sample-consumption state machine in general_work(),
upstream of doppler_grid_cpu()/doppler_grid_cuda()/update_synchro(), so CUDA
builds and bit_transition_flag mode need no special-casing -- they share the
same corrected consumption automatically. It is also correct for
coherent_integration_time_ms spanning more than one code period, since the
residual is derived from the same sampled_ms used for d_samples_to_consume,
not from a single code period.
A no-op whenever samples_per_code is already an integer (any internal_fs_sps
dividing evenly into one dwell): the residual is then exactly 0, so the skip
counter never fires.
Verified against a real EVK1029 dual-band capture (Galileo E1+E5a, GPS
L1+L5): the untouched case (samples/dwell already integer) stayed at ~2
spurious loss-of-lock events per 90s; a case with 0.5 samples/dwell of
truncation error went from 50+ loss-of-lock events per 4 minutes (tracking's
own frequency-error-reduction diagnostic scan showing no discernible
correlation peak at all) down to 1 event per ~41s -- the same order as the
untouched case -- with sharp, correct correlation peaks and CN0 estimates
jumping from ~20-30 dB-Hz (meaningless, noise-floor) to ~42-52 dB-Hz (genuine
lock).
- CHANGELOG: document Tracking_<Sig>.f_error_dump (default: false) and the
new Octave/MATLAB scripts
- f_error_sim.m: match cn0_m2m4_estimator when sqrt argument is negative,
seed with rng() (fallback for Octave < 7), fix stale comments
- plot_all_f_error_scans.m: do not require gnuplot, restore the previous
graphics toolkit on exit
- Use fprintf instead of printf for MATLAB compatibility
load_f_error_dump/find_f_error_scans/plot_f_error_scan (utils/matlab/libs)
read and visualize the per-bin CSV written by the tracking f_error_dump
diagnostic, and plot_all_f_error_scans (utils/matlab) batches
plot_f_error_scan over every scan in a dump, optionally saving PNGs.
f_error_sim (utils/matlab/libs) is a self-contained Monte Carlo
simulation of the same bin-scan/CN0-estimator logic, useful for sizing
f_error_step_num/f_error_accumulation/f_error_doppler_step without a
live capture.
Dll_Pll_Conf::f_error_dump_filename has a non-empty default, so the
frequency-error-reduction scan's per-bin CSV dump was effectively
always on with no clean way to disable it. Add a new f_error_dump
boolean (default false), matching the existing dump/Tracking_<sig>.dump
convention, and gate the dump call on it.
Contrary to ICD, these satellites actually broadcast B2a (OTA upgraded
in the orbit).
Verified by decoding live signal in realtime configuration.
Signed-off-by: Vladislav P <vladisslav2011@gmail.com>
SatelliteVisibility::PredictedDopplerHz() previously required a live PVT
fix and was only used for a satellite's primary signal
(GNSSFlowgraph::acquisition_manager()). Two extensions:
1. Secondary signals (e.g. GPS L5, Galileo E5a) can now use the same
geometric prediction, opt-in per acquisition role via
<acq role>.alm_ephe_assisted_doppler_narrowing (default false). It's
only reached at all when dual-frequency assist is unavailable or
disabled (GNSS-SDR.assist_dual_frequency_acq=false), and is a distinct,
less-proven Doppler source than that dual-frequency projection, hence
opt-in rather than defaulted on.
2. Before a live fix exists, PredictedDopplerHz() can now predict from the
AGNSS reference position (zero assumed velocity) and the configured
nominal clock offset, opt-in via GNSS-SDR.doppler_prediction_before_fix
(default false). A new PredictedDopplerUncertaintyHz() reports the
Doppler spread implied by GNSS-SDR.receiver_max_velocity_m_s and
GNSS-SDR.clock_frequency_max_error_ppm for this pre-fix case (zero once
a fix exists, since position/velocity/clock are all solved then, not
assumed) -- the caller widens its acquisition Doppler-bin search by this
amount instead of trusting a single, possibly-wrong bin.
Verified against an EVK1029 GPS L5 capture (single-band, so every L5
channel is a secondary signal): with both flags enabled, the almanac-based
prediction narrows acquisition Doppler search before and after the fix,
logged via a new low-noise [alm_ephe_doppler] line; with them left at
their defaults, behavior is unchanged from before this change.