Commit Graph
7648 Commits
Author SHA1 Message Date
Carles Fernandez d60ddcbef8 Fix XB wrap-around in GPS L5 I5/Q5 code replicas. Fixes #1151 2026-10-19 18:06:37 +02:00
Carles Fernandez 39f735f946 Add optional CUDA filtering for EVK1029_Signal_Source bands
With -DENABLE_CUDA=ON, band<n>_cuda=true computes that band's
frequency-translating FIR (complex composite taps applied to the real
capture) on an NVIDIA GPU through the new CudaDdcEngine, instead of on
a CPU worker thread. As with the CUDA acquisition engine, the public
interface has no CUDA types and hides the implementation behind a
PIMPL. Each band falls back to the CPU, with a warning in the log, if
its engine cannot be created or a GPU call fails. Builds without CUDA,
and bands without the flag, are unchanged.

Taken from PR #1144, plus:
- Report the CUDA status per band with LOG instead of std::cout/cerr
- Check cudaGetLastError() after the kernel launch, so a failed launch
  is not mistaken for valid output
- Select the engine's device in set_taps(), compute() and the
  destructor, since compute() runs on the band worker threads
- Build the engine only with both ENABLE_CUDA and ENABLE_EVK1029
2026-10-19 15:44:04 +02:00
Carles Fernandez f16e6bd7a4 Fuse per-band frequency translation into EVK1029_Signal_Source
With enable_freq_xlating=true, Evk1029Source translates, filters and
decimates each band itself and exposes one gr_complex output port per
band, so Bypass Signal Conditioners can connect the channels straight
to the source. Each band's FIR runs on its own worker thread, and the
next chunk of the capture is decoded while the current one is filtered.
Per-band filter design: band<n>_IF, _decimation_factor, _bw, _tw,
_window, _attenuation_dB and _kaiser_beta.

Taken from PR #1143 without its SignalConditioner Bypass commits (next
already provides Bypass), plus:
- Center shorter band filters on the longest one, so every band has the
  same group delay (mismatched tap counts biased pseudoranges between
  bands by up to km)
- Fix source dump in fused mode: one gr_complex file per band (_ch<n>)
- Reject unequal or non-positive band decimation factors
- Build with GNU Radio < 3.9 (firdes window type) and VOLK < 2.5
  (rotator), use volk_gnsssdr::vector
- Link Gnuradio::filter privately and only with ENABLE_EVK1029
- Start worker threads after the capture file is opened
2026-10-19 15:09:44 +02:00
Carles Fernandez f7efcf54d9 Fix minor performance ineficiencies 2026-10-19 12:26:55 +02:00
Carles Fernandez 6205aec6f6 Fix RINEX tests 2026-10-19 11:15:31 +02:00
Carles Fernandez fc409f733e Merge branch 'fix-rinex-blank-unavailable-obs' of https://github.com/joebre/gnss-sdr into joebre-fix-rinex-blank-unavailable-obs 2026-10-19 10:53:23 +02:00
Carles Fernandez 66c15d9503 Merge branch 'improve-flowgraph' into next 2026-10-19 10:36:46 +02:00
Carles Fernandez 6c74e86db1 Keep non-blocking multi-dwell acquisition from stalling the stream. Fixes #1102
With blocking=false and max_dwells > 1, copy the whole integration window
into a per-block buffer, so that general_work() keeps consuming input while
the worker integrates. The shared conditioner buffer no longer holds back
every channel during a search.

- set_history() keeps the last window readable, so a search starts as soon
  as one new dwell arrives. Dwell offsets in the window follow the
  dwell-residual skip, and the sample stamp is the end of the window (the
  end of the first dwell while the window starts at stream index 0).
- The buffer is allocated at the first search and freed on a positive
  acquisition or when the channel is deactivated.
- Deactivation stops the worker and drops the search, so the next
  activation starts a new one. stop() replaces the destructor wait.
- Idle input is consumed in one call per wakeup, keeping the history.

Based on #1108 by Vladislav P.
2026-10-18 15:01:02 +02:00
Carles Fernandez ea533140aa Fix for GNU Radio 3.7 2026-10-18 14:27:52 +02:00
Carles Fernandez 66dcfe3f09 Bound configured batches by the observables timing budget
Replace the fixed 20 ms limit on SignalConditioner.batch_size_ms and
max_batch_latency_ms. A configured batch is accepted if it is no longer
than the automatic one (min(10 ms, observable_interval_ms)), or if the
buffer it needs (whole batches, with room for one on top of the largest
reader window) fits the alignment budget: the signal time channels can
trail the receiver clock before the observables miss epochs. Batches
are also limited to 4 MiB, since GNU Radio allocates two of them on
each side of the batching block.

At observable_interval_ms=20 and 4 MSps gr_complex this allows about
90 ms instead of 20 ms. At 5 ms it now rejects batches that the old
limit let through. In automatic mode, an explicit max_batch_latency_ms
sets the batch size, so that batch gets the same check. Defaults are
unchanged.
2026-10-17 15:04:10 +02:00
Carles Fernandez b837b59531 Fix Labsat 4 file reading when active channels have different BW_DIV values. Fixes #1149 2026-10-15 18:58:31 +02:00
joebre 717f5c8c0a Emit blank RINEX fields for signals with no valid observation
add_obs_sat_record_line() formatted every observable group (pseudorange,
carrier phase, Doppler, CN0) unconditionally, even for the placeholder
Gnss_Synchro{} slot get_constellation_observables_map() creates for a
signal with no valid observation for a given satellite (e.g. untracked,
or simply a frequency that satellite doesn't transmit, such as a GPS
PRN5/PRN7 which has an L5 slot but only tracks/transmits L1). That
placeholder is all-zero, so it printed as a formatted "0.000" value with
a Signal Strength Indicator floored to its minimum class (1), which
reads as a real but degraded observation rather than "no data".

Per RINEX 3/4, an unavailable observation must be a fully blank field,
not a zero value. Gate the formatter on Flag_valid_pseudorange, which
already reliably distinguishes a real observable (set true by the
Observables block) from the placeholder (defaults false) -- write 64
spaces instead when false.
2026-10-15 15:59:47 +02:00
Carles Fernandez bb74c0f76d Add work on the new stream batcher 2026-10-14 20:08:59 +02:00
Carles Fernandez 4e1302d39a Fix bugprone-misplaced-widening-cast 2026-10-10 22:51:11 +02:00
Carles Fernandez fda4c648ab Fix bugprone-misplaced-widening-cast 2026-10-10 22:46:54 +02:00
Carles Fernandez 297492f0e8 Make clang-tidy happy 2026-10-10 22:44:35 +02:00
Carles Fernandez 79f1247f44 Set 10 ms as default batch in the Signal Conditioner 2026-10-10 22:01:22 +02:00
Carles Fernandez dd077fa0d2 Set maximum of max_batch_latency_ms to 20 ms, fix building with old GNU Radio 2026-10-10 16:39:03 +02:00
Carles Fernandez 1dab504b09 Optimize Signal Conditioner scheduling with portable automatic batching
Select batch targets from sample rate, latency and memory limits, and
consumer buffer requirements. Replace eligible pass-through stages before
channel fan-out without machine-specific profiles or configuration edits.

Preserve samples and tags across partial batches, deadlines, and EOF.
Handle conditioned output ports consistently and clean up stream and
message connections on disconnect or connection failure.
2026-10-10 15:56:55 +02:00
Carles Fernandez 54137892d4 Bump version to v0.0.22 2026-10-04 08:51:05 +02:00
Carles Fernandez 26f97cf691 Merge branch 'fix/fpga-acquisition-version-check' of https://github.com/mmajoral/gnss-sdr into mmajoral-fix/fpga-acquisition-version-check 2026-10-03 08:25:00 +02:00
Carles Fernandez 9148192435 Update comments 2026-10-03 08:20:23 +02:00
Carles Fernandez ee4e9a24ec Report GPS L5 health from LNAV while L5 is pre-operational
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.
2026-10-03 08:13:19 +02:00
Marc Majoral b65be4196b Update acquisition IP core version check 2026-10-02 17:34:31 +02:00
Carles Fernandez 54ee9aad30 Merge branch 'fix-gps-l2c-l5-health-exclusion' of https://github.com/joebre/gnss-sdr into joebre-fix-gps-l2c-l5-health-exclusion 2026-10-02 17:34:04 +02:00
Carles Fernandez 97b6f151ec Merge branch 'refactor/fpga-signal-sources' of https://github.com/mmajoral/gnss-sdr into mmajoral-refactor/fpga-signal-sources 2026-10-02 11:32:16 +02:00
Marc Majoral 84ae396ba0 Refactor FPGA signal sources and improve error handling 2026-10-02 10:16:31 +02:00
joebre ed1dd3dce3 Fix GPS L2C/L5/J5 satellites being wrongly excluded as unhealthy
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.
2026-10-02 10:15:54 +02:00
Carles Fernandez 60def7fc9d Fix default dump filenames for multichannel UHD sources 2026-10-01 22:18:09 +02:00
Carles Fernandez 6ea188a8ee Remove old channel count 2026-10-01 22:17:17 +02:00
Carles Fernandez 190979945d Fix reading of GNSS-SDR.SUPL_MNC config parameter 2026-10-01 21:25:53 +02:00
Carles Fernandez 17ca85235e Merge branch 'next' of https://github.com/carlesfernandez/gnss-sdr into next 2026-10-01 15:25:57 +02:00
Carles Fernandez b23a2af572 Fix building 2026-10-01 11:57:15 +02:00
Carles Fernandez 3c256e991b Compute PCPS dwell length with integer arithmetic
Replace std::clamp with std::min/std::max for C++11/14 compatibility
2026-10-01 10:58:13 +02:00
Carles Fernandez 2185dc647d Merge branch 'fix-pcps-acquisition-sample-truncation' of https://github.com/joebre/gnss-sdr into joebre-fix-pcps-acquisition-sample-truncation 2026-10-01 09:58:08 +02:00
joebre 3d16234434 Fix PCPS acquisition sample-count truncation when samples_per_code is non-integer
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).
2026-10-01 08:01:14 +02:00
Carles Fernandez 943ef814ab Merge branch 'f_error-dump-opt-in' of https://github.com/joebre/gnss-sdr into joebre-f_error-dump-opt-in 2026-09-30 18:44:29 +02:00
Carles Fernandez c6405d824a Improve BeiDou B-CNAV1 decoding sensitivity 2026-09-30 12:08:49 +02:00
Carles Fernandez 28e14a3c7c Merge branch 'b1c_enable_sumproduct' of https://github.com/vladisslav2011/gnss-sdr into vladisslav2011-b1c_enable_sumproduct 2026-09-30 09:10:53 +00:00
joebre cd4f643a20 Make the f_error CSV dump opt-in via Tracking_<sig>.f_error_dump
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.
2026-09-30 07:42:39 +02:00
Vladislav P b24f930abc BeiDou B1C: Enable sum-product algorithm in LDPC decoder
This reduces minimum CN0 for stable telemetry decoding from ~28 dB*Hz to
~21 dB*Hz.

Signed-off-by: Vladislav P <vladisslav2011@gmail.com>
2026-09-29 23:27:47 +03:00
Carles Fernandez 2e682cce8e Update to new BeiDou PRN assignments
See http://www.beidou.gov.cn/yw/xwzx/202603/t20260320_29266.html
2026-09-29 13:01:30 +02:00
Carles Fernandez d971f6080e Merge branch 'enable_B2a_PRN6_18' of https://github.com/vladisslav2011/gnss-sdr into vladisslav2011-enable_B2a_PRN6_18 2026-09-29 10:11:04 +02:00
Carles Fernandez 09f143fe07 Summarize comments 2026-09-29 10:10:24 +02:00
Carles Fernandez 88fea3099a Harden pre-fix Doppler prediction bounds and grid sizing 2026-09-29 10:07:18 +02:00
Vladislav P b27cf0a1d5 Enable BeiDou B2a PRN6...PRN18
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>
2026-09-29 03:55:05 +03:00
Carles Fernandez 6f108b7497 Merge branch 'alm-ephe-secondary-doppler-aiding' of https://github.com/joebre/gnss-sdr into joebre-alm-ephe-secondary-doppler-aiding 2026-09-28 23:50:19 +02:00
Carles Fernandez 7050b48564 Add loading of QZSS L5 assistance 2026-09-28 23:44:52 +02:00
joebre f74ed54ee9 Extend almanac/ephemeris Doppler prediction to secondary signals and to before a fix
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.
2026-09-28 16:00:42 +02:00
joebre 17bd8a4860 Fix stale fix-freshness check ordering in visibility-aware Doppler prediction
SatelliteVisibility::PredictedDopplerHz() gates its live-fix path on a
freshness check against last_fix_time_s_/last_fix_receiver_time_s_, both of
which are only stamped by SatelliteVisibility::Tick() (called from
GNSSFlowgraph::MaybeUpdateVisibility()).

In ControlThread::event_dispatcher(), acquisition_manager() -- which calls
PredictedDopplerHz() -- ran before MaybeUpdateVisibility() in the same
iteration. Since PVT updates roughly once per second, PredictedDopplerHz()
was therefore always comparing against a timestamp exactly one PVT epoch
stale, failing its own freshness check on every single call once a live
fix existed. The prediction only ever succeeded in the brief window right
after a periodic full visibility recompute happened to re-trigger a fresh
pick synchronously within the same call.

Reordering so MaybeUpdateVisibility() (and therefore Tick()'s stamping)
runs before acquisition_manager() each iteration fixes this: the timestamp
PredictedDopplerHz() checks is always current for the epoch it's
evaluating. acquisition_manager()'s existing "only when idle" gate is
preserved exactly (previously the implicit else of the valid_event check,
now an explicit !valid_event condition).

Verified against an EVK1029 GPS L5 capture with visibility-aware search
and almanac-based Doppler prediction enabled: before this fix, the
prediction log line never appeared after the first fix except in narrow
bursts tied to the ~120s full-recompute interval; after, it fires
continuously for every still-unlocked visible satellite each cycle.
2026-09-28 15:59:53 +02:00