- Rename doppler_uncertanty -> doppler_uncertainty throughout (interface,
adapter, block, comments, log message).
- Fix the out-of-bounds write and mis-detection in narrowed-mode
activation: track it with an explicit d_doppler_search_narrowed flag,
set directly in set_doppler_uncertainty() and guarded on
d_num_doppler_bins > 1, instead of inferring it from
"d_num_doppler_bins_active < d_num_doppler_bins". That inferred check
broke two ways: for a 1-bin full grid (a valid, already-accepted
config), it still forced 2 active bins, writing past the grid/wipeoff
allocations sized to d_num_doppler_bins; for a 2-bin full grid, an
actually-narrowed request computes the same active count as the full
grid (2 < 2 is false), so it silently searched the full grid's own
bin layout instead of the assisted {center, center + doppler_max}
pair. Reproduced the out-of-bounds write directly (temporarily
reverting to the inferred check crashes with "corrupted double-linked
list" under the new NarrowingEnabledOneBinGrid test) and the
mis-detection (NarrowingEnabledTwoBinGrid).
- Keep the noise-reference bin out of candidate selection: both
max_to_input_power_statistic() and first_vs_second_peak_statistic()
now take a separate candidate_count, used only for the peak-search
loop bound; the CFAR path's own num_doppler_bins (used for the
"opposite bin" reference lookup) is unchanged. In narrowed mode,
candidate_count is 1 -- the noise-reference bin can no longer win and
be reported as the acquisition result, regardless of its own power
(verified against a live signal deliberately placed in the reference
bin instead of the known/candidate bin: reverting this fix, both the
CFAR and peak-ratio statistic mistakenly report the reference bin's
Doppler as if it were the assisted center).
- Recalibrate the acceptance threshold for narrowed acquisition: adds
d_threshold_narrowed, computed like d_threshold but with
num_doppler_bins = 1 (the true candidate count in narrowed mode)
instead of the full grid's bin count, since compute_threshold() folds
the candidate count into the false-alarm probability and reusing the
full-grid threshold in narrowed mode was effectively far stricter
than the requested PFA. get_threshold() returns it whenever
d_doppler_search_narrowed is true.
- Fix narrowed dump layout/metadata: ensure_dump_grid_allocated() now
sizes acq_grid to d_num_doppler_bins_active (not the full grid width),
matching what copy_magnitude_grid_to_dump_grid() actually writes each
cycle -- this also makes narrow<->full transitions between dumps
self-correcting. dump_results() writes accurate, mode-appropriate
doppler_max/doppler_step (the same {0, doppler_max} encoding
compute_statistics() uses internally when narrowed), plus new
doppler_center and doppler_narrowed fields so offline post-processing
can tell which encoding is in effect. Verified end to end against a
live dump (NarrowedDumpMetadata).
- Strengthen Acq_Conf::enable_doppler_narrowing's doc comment (default,
which implementations it applies to, its dependency on the caller
passing doppler_uncertainty == 0) and add a commented usage example
next to GNSS-SDR.assist_dual_frequency_acq in a dual-frequency
Galileo E1B+E5a example config.
- Add tests/.../pcps_acquisition_doppler_narrowing_test.cc: 8 focused
tests (narrowing enabled/disabled, 1-bin and 2-bin full-grid
configurations, CFAR and peak-ratio statistics, a full-narrow-full
transition, two-step acquisition with step-1 narrowing, and narrowed
dump metadata), built on the same real-capture/known-ground-truth
pattern as gps_l1_ca_pcps_acquisition_test.cc. Confirmed each targeted
regression test actually fails without its corresponding fix before
finalizing them.
Signed-off-by: joebre <joel.brenner@saphyrion.ch>
When acquiring a secondary frequency (e.g. Galileo E5a) of a satellite
whose primary frequency (e.g. E1) is already tracked, the primary's
Doppler can be projected onto the secondary and passed in as an
already-known estimate (GNSS-SDR.assist_dual_frequency_acq). Even so,
the acquisition still searched the full configured Doppler grid around
that estimate rather than trusting it.
Adds Acquisition_<Sig>.enable_doppler_narrowing (default false): when
the projected Doppler is passed in as exact (doppler_uncertanty == 0),
collapses the search to just that bin plus one reference bin (used as
the existing noise-power estimate, at the same separation the full
grid search already uses for its own "opposite" bin), instead of
running the full multi-bin grid. Falls back to the full grid whenever
the Doppler isn't known this precisely (single-frequency acquisition,
or dual-frequency assist disabled/unavailable).
set_doppler_uncertanty() threads through
AcquisitionInterface -> PcpsAcquisitionAdapter -> pcps_acquisition,
and Channel::assist_acquisition_doppler() gains a doppler_uncertanty
parameter (default 1, i.e. "not known precisely") so existing callers
are unaffected unless they opt in.
Also fixes max_to_input_power_statistic()'s "opposite bin" index to
wrap using its own num_doppler_bins parameter instead of the
always-full-grid d_num_doppler_bins member, which was already
inconsistent with step-two's narrower bin count and would have been
wrong for the narrowed 2-bin case introduced here.
Signed-off-by: joebre <joel.brenner@saphyrion.ch>
An application monitoring gnss-sdr (e.g. for a sky plot or a per-satellite
status view) typically needs to know which satellites and signals actually
contributed to the current PVT solution, and their azimuth/elevation --
none of that is currently exposed on the MonitorPvt UDP interface, only the
scalar solution.
Adds a repeated UsedSatellite field to MonitorPvt (proto field 37), one
entry per satellite/signal used in the fix: PRN, constellation, signal ID,
azimuth, elevation, and whether that satellite's signals were combined
(e.g. Galileo E1+E5a iono-free). Signals are kept as separate entries
rather than merged into one per-satellite record, since a receiver may
track more than one signal of the same satellite independently and a
consumer may care which ones specifically contributed.
Populated in Rtklib_Solver::get_PVT() from RTKLIB's per-satellite azel
solution (pvt_ssat[]) intersected with the observables actually used in
that epoch.
Signed-off-by: joebre <joel.brenner@saphyrion.ch>
ground_speed_ms was declared and left at its 0.0 initializer instead
of being assigned the computed ENU ground speed, so the
"ground_speed_ms >= 1.0" gate in Rtklib_Solver::get_PVT() was always
false. As a result, Course Over Ground was permanently left at its
-9999.0 "not estimated" sentinel regardless of actual receiver speed.
Signed-off-by: joebre <joel.brenner@saphyrion.ch>
apply_conf_defaults() picked the plotted signal-type label as
signals[0].signal whenever --first-channel/--channels was passed
explicitly to override the conf-derived channel range. signals[0] is
just the first enabled signal in SIGNAL_ORDER for the given conf, so
it is only correct when the manually requested range happens to start
at channel 0 of that same signal; for any other range it mislabels
every plot with the wrong signal type, even though the correct .dat
files are read (since --file-prefix is typically also given
explicitly in that case).
For example, running
--conf EVK1029_Galileo_E1_E5a.conf --file-prefix trk_GalileoE5a_ch \
--first-channel 10 --channels 10
correctly reads the Galileo E5a tracking dumps but titled every plot
"Galileo E1B", the conf's first enabled signal.
Resolve each requested channel's signal type from the conf's own
per-signal channel ranges (SignalConfig.channels) instead, falling
back to the previous behavior only when no conf is available at all.
Signed-off-by: joebre <joel.brenner@saphyrion.ch>
SIGNAL_ORDER in utils/python/lib/gnss_sdr_conf.py is meant to mirror
signal_mapping in src/core/receiver/gnss_block_factory.cc, which
GetChannels() walks in a fixed order to assign absolute channel IDs
(consuming Channels_<type>.count channels per entry; the per-channel
Channel<N>.signal lines in the conf file are not used for this).
SIGNAL_ORDER was missing "1D" (BeiDou B1C) and "S1" (SBAS L1), both
present in signal_mapping. Any configuration enabling BeiDou B1C or
SBAS alongside other signal types would get every later signal's
inferred channel range shifted, mislabeling tracking dump plots for
those channels. Added both entries, in the same relative position as
signal_mapping, along with their SignalType metadata.
Signed-off-by: joebre <joel.brenner@saphyrion.ch>
log_data() read the data-arm Prompt correlator's real/imaginary parts
directly instead of going through map_correlator_to_iq(), unlike
assign_correlators_to_synchro() which already applies that mapping
correctly. For signals whose data and pilot components are true RF
quadrature (Galileo E5a/E5b, GPS L5), d_interchange_iq is true and the
actual data bits live on the imaginary axis of d_Prompt_Data, not the
real one used unconditionally by the dump.
This only affected the tracking .dat/.mat dump used by the Python/MATLAB
plotting utilities (utils/python/dll_pll_veml_plot_sample.py and
utils/matlab equivalents) for signals such as Galileo E5a: the "Prompt
I" / "Bits of the navigation message" plots showed pilot phase-noise
leakage on the wrong axis instead of the actual navigation bits, while
receiver operation itself (telemetry decoding, PVT) was unaffected
since it uses assign_correlators_to_synchro().
Signed-off-by: joebre <joel.brenner@saphyrion.ch>
Add a sample configuration enabling GPS L1 C/A, Galileo E1, GLONASS
L1 C/A, and BeiDou B1I simultaneously through the new native
Bladerf_Signal_Source, following the same single-capture, multi-band
split already used by the existing USRP X300 and HackRF multichannel
templates.
One 56 Msps / 56 MHz wideband capture centered at 1584 MHz (the
maximum bandwidth supported by a bladeRF 2.0 Micro) is split via
SignalSource.RF_channels=3 into three Freq_Xlating_Fir_Filter
sub-bands tapping the same physical stream: BeiDou B1I (1561.098
MHz), GPS L1 C/A + Galileo E1 (co-located at 1575.42 MHz), and
GLONASS L1 C/A (FDMA, centered near 1602 MHz). The frequency plan and
per-system acquisition/tracking parameters are carried over from the
existing USRP X300 four-constellation template.
Verified against a physical bladeRF 2.0 Micro: the receiver
simultaneously acquired and tracked real satellites on all four
systems from a live antenna.
Signed-off-by: Oleksandr Suvorov <cryosay@gmail.com>
Add a Bladerf_Signal_Source that streams RX samples directly from
bladeRF front-ends (bladeRF x40, x115, and bladeRF 2.0 Micro xA4/xA9)
through libbladeRF, instead of going through the generic gr-osmosdr
wrapper. gr-osmosdr's bladerf backend does not expose usable per-stage
gain control on bladeRF2 hardware and has no support for the RX bias
tee used to power an active antenna on the 2.0 Micro.
The new gr::sync_block (bladerf_source) talks to libbladeRF's
synchronous streaming API directly; the adapter (BladerfSignalSource)
follows the existing SignalSourceBase pattern used by the other signal
sources, with valve and dump support wired the same way. Building it
is gated behind -DENABLE_BLADERF=ON, degrades gracefully when
libbladeRF is absent, and requires libbladeRF v2.6.0 or newer,
enforced at configure time.
Verified against a physical bladeRF 2.0 Micro: the receiver opens the
device, streams live samples, and acquires and tracks real GPS
satellites end to end using the new sample configuration file.
Only single-channel (SISO) reception is supported for now; the second
RX channel is not yet exposed.
Signed-off-by: Oleksandr Suvorov <cryosay@gmail.com>