Remove redundant BeiDou PVT pre-filters in the src/algorithms/PVT/libs/rtklib_solver.cc.

Update pseudorange outlier rejection
- Gate relative pseudorange rejection behind a new configuration flag
  `Observables.reject_outlier_pseudoranges` (default: true) to prevent
  premature PVT breakage and improve space compatibility.
Align B1C with unified adapters.
- Fold B1C acquisition/tracking/telemetry into PcpsAcquisitionAdapter,
  DllPllTrackingAdapter, and TelemetryDecoderAdapter, and add SPDX author
  headers for the B1C/CNAV1 contribution files.
Fix B1C ICD alignment for eph, TGD, UTC, and PR gating.
Gate CNAV1 publish on IODE/IODC, fix gettgd SYS_BDS selection,
correct LS/LSF and UTC scales, and drop the MEO-biased PR median cut.

Signed-off-by: OuWenhao16 <104730917+OuWenhao16@users.noreply.github.com>
This commit is contained in:
OuWenhao16
2026-08-01 02:32:17 +08:00
parent 5eeaec3f48
commit da9eba4af2
54 changed files with 556 additions and 1376 deletions
@@ -1,6 +1,7 @@
; BeiDou B1C (1D) file-input example (IF = +20 kHz, Fs = 18 MHz, ibyte)
; Test data: https://drive.google.com/drive/folders/1UkjJwOMlKEFZiXYUdK4HKzTASYHXMsHL
; SPDX-License-Identifier: GPL-3.0-or-later
; SPDX-FileCopyrightText: (C) 2010-2026 (see AUTHORS file for a list of contributors)
;
; Mixed B1I+B1C PVT policy (same PRN):
; - RTKLIB bands: B1I=0 (FREQ1_BDS), B1C/1D=1 (FREQ1), B3I=2 (FREQ3_BDS).
+8 -105
View File
@@ -1781,86 +1781,23 @@ bool Rtklib_Solver::get_PVT(const std::map<int, Gnss_Synchro> &gnss_observables_
case 'C':
{
// BEIDOU B1I / B1C / B3I
// Prefer B1C when the same PRN has a usable CNAV1 ephemeris.
// If B1C is tracked but CNAV1 is missing/stale, keep B1I+DNAV.
// Prefer B1C when the same PRN has CNAV1 ephemeris and a B1C observation.
// If B1C is tracked but CNAV1 is missing, keep B1I+DNAV.
const std::string sig_(gnss_observables_iter->second.Signal);
const int bds_sat = static_cast<int>(gnss_observables_iter->second.PRN + NSATGPS + NSATGLO + NSATGAL + NSATQZS);
auto bds_toe_age_ok = [](double obs_tow_gpst_s, double toe_bdt_s) -> bool {
const double toe_gpst_s = toe_bdt_s + static_cast<double>(BEIDOU_DNAV_BDT2GPST_LEAP_SEC_OFFSET);
double toe_age_s = std::fabs(obs_tow_gpst_s - toe_gpst_s);
if (toe_age_s > 302400.0)
{
toe_age_s = 604800.0 - toe_age_s;
}
return toe_age_s <= MAXDTOE_BDS;
};
// Absolute PR floor/ceiling alone is unreliable: RX TOW is the
// latest TX TOW, so common bias can make all PRs look "short".
// Reject only clearly impossible values here; relative outliers
// (e.g. C35 ~3e6 m vs peers ~15e6 m) are filtered below.
auto bds_pr_absolute_ok = [](double pr_m) -> bool {
return pr_m > 1.0e5 && pr_m < 1.0e8;
};
auto dnav_eph_sane = [](const Beidou_Dnav_Ephemeris &eph) -> bool {
// ICD B1I Table 5-10: toe ∈ [0, 604792], sqrt(A) ∈ [0, 8192]
return eph.toe >= 0 && eph.toe <= static_cast<int32_t>(D1_TOE_MAX_S) &&
eph.sqrtA >= D1_SQRT_A_MIN_SANE && eph.sqrtA <= D1_SQRT_A_MAX;
};
// Relative PR outlier vs other BeiDou obs in this epoch (~10 ms · c).
// Catches per-SV TOW desync (C35 B1I/B1C both ~3.18e6 m while peers ~15e6 m).
std::vector<double> bds_prs;
bds_prs.reserve(gnss_observables_map.size());
for (const auto &obs_pair : gnss_observables_map)
{
if (obs_pair.second.System == 'C' && bds_pr_absolute_ok(obs_pair.second.Pseudorange_m))
{
bds_prs.push_back(obs_pair.second.Pseudorange_m);
}
}
double bds_pr_median = 0.0;
if (!bds_prs.empty())
{
std::nth_element(bds_prs.begin(), bds_prs.begin() + static_cast<std::ptrdiff_t>(bds_prs.size() / 2), bds_prs.end());
bds_pr_median = bds_prs[bds_prs.size() / 2];
}
constexpr double bds_pr_outlier_m = 5.0e6; // ~16.7 ms · c
auto bds_pr_sane = [&](double pr_m) -> bool {
if (!bds_pr_absolute_ok(pr_m))
{
return false;
}
if (bds_prs.size() >= 3 && bds_pr_median > 0.0 &&
std::fabs(pr_m - bds_pr_median) > bds_pr_outlier_m)
{
return false;
}
return true;
};
if (sig_ == "B1")
{
if (!bds_pr_sane(gnss_observables_iter->second.Pseudorange_m))
{
DLOG(INFO) << "Skip B1I PRN " << gnss_observables_iter->second.PRN
<< " (absurd/outlier pseudorange " << gnss_observables_iter->second.Pseudorange_m << " m)";
break;
}
bool prefer_b1c = false;
for (const auto &obs_pair : gnss_observables_map)
{
if (obs_pair.second.System == 'C' &&
obs_pair.second.PRN == gnss_observables_iter->second.PRN &&
std::string(obs_pair.second.Signal, 2) == "1D" &&
bds_pr_sane(obs_pair.second.Pseudorange_m))
beidou_cnav1_ephemeris_map.find(obs_pair.second.PRN) != beidou_cnav1_ephemeris_map.cend())
{
const auto cnav1_it = beidou_cnav1_ephemeris_map.find(obs_pair.second.PRN);
if (cnav1_it != beidou_cnav1_ephemeris_map.cend() &&
bds_toe_age_ok(obs_pair.second.RX_time, static_cast<double>(cnav1_it->second.toe)))
{
prefer_b1c = true;
break;
}
prefer_b1c = true;
break;
}
}
if (prefer_b1c)
@@ -1872,21 +1809,6 @@ bool Rtklib_Solver::get_PVT(const std::map<int, Gnss_Synchro> &gnss_observables_
beidou_ephemeris_iter = beidou_dnav_ephemeris_map.find(gnss_observables_iter->second.PRN);
if (beidou_ephemeris_iter != beidou_dnav_ephemeris_map.cend())
{
if (!dnav_eph_sane(beidou_ephemeris_iter->second))
{
DLOG(INFO) << "Skip corrupt DNAV ephemeris for SV "
<< beidou_ephemeris_iter->second.PRN
<< " toe=" << beidou_ephemeris_iter->second.toe
<< " sqrtA=" << beidou_ephemeris_iter->second.sqrtA;
break;
}
if (!bds_toe_age_ok(gnss_observables_iter->second.RX_time,
static_cast<double>(beidou_ephemeris_iter->second.toe)))
{
DLOG(INFO) << "Skip stale DNAV ephemeris for SV "
<< beidou_ephemeris_iter->second.PRN;
break;
}
eph_data[valid_obs] = eph_to_rtklib(beidou_ephemeris_iter->second);
obsd_t newobs{};
d_obs_data[valid_obs + glo_valid_obs] = insert_obs_to_rtklib(newobs,
@@ -1902,24 +1824,9 @@ bool Rtklib_Solver::get_PVT(const std::map<int, Gnss_Synchro> &gnss_observables_
}
if (sig_ == "1D")
{
if (!bds_pr_sane(gnss_observables_iter->second.Pseudorange_m))
{
DLOG(INFO) << "Skip B1C PRN " << gnss_observables_iter->second.PRN
<< " (absurd/outlier pseudorange " << gnss_observables_iter->second.Pseudorange_m << " m)";
break;
}
const auto cnav1_iter = beidou_cnav1_ephemeris_map.find(gnss_observables_iter->second.PRN);
if (cnav1_iter != beidou_cnav1_ephemeris_map.cend())
{
// Reject stale CNAV1 before it enters the epoch (MAXDTOE_BDS = 6 h).
// RX_time is GPST; CNAV1 toe is BDT (ICD §7.7) — convert before age check.
if (!bds_toe_age_ok(gnss_observables_iter->second.RX_time,
static_cast<double>(cnav1_iter->second.toe)))
{
DLOG(INFO) << "Skip stale B-CNAV1 ephemeris for SV "
<< cnav1_iter->second.PRN;
break;
}
eph_data[valid_obs] = eph_to_rtklib(cnav1_iter->second);
// Apply SF3 health (HS) when available
const auto page_it = beidou_cnav1_page_data_map.find(cnav1_iter->second.PRN);
@@ -1943,10 +1850,7 @@ bool Rtklib_Solver::get_PVT(const std::map<int, Gnss_Synchro> &gnss_observables_
if (sig_ == "B3")
{
beidou_ephemeris_iter = beidou_dnav_ephemeris_map.find(gnss_observables_iter->second.PRN);
if (beidou_ephemeris_iter != beidou_dnav_ephemeris_map.cend() &&
dnav_eph_sane(beidou_ephemeris_iter->second) &&
bds_toe_age_ok(gnss_observables_iter->second.RX_time,
static_cast<double>(beidou_ephemeris_iter->second.toe)))
if (beidou_ephemeris_iter != beidou_dnav_ephemeris_map.cend())
{
bool found_B1I_obs = false;
for (int i = 0; i < valid_obs; i++)
@@ -2147,7 +2051,7 @@ bool Rtklib_Solver::get_PVT(const std::map<int, Gnss_Synchro> &gnss_observables_
d_nav_data.utc_cmp[1] = beidou_cnav1_utc_model.A1;
d_nav_data.utc_cmp[2] = static_cast<double>(beidou_cnav1_utc_model.tot);
d_nav_data.utc_cmp[3] = static_cast<double>(beidou_cnav1_utc_model.WN_t);
d_nav_data.leaps = beidou_cnav1_utc_model.delta_t_LSF;
d_nav_data.leaps = beidou_cnav1_utc_model.delta_t_LS;
}
/* update carrier wave length using native function call in RTKlib */
@@ -2171,9 +2075,8 @@ bool Rtklib_Solver::get_PVT(const std::map<int, Gnss_Synchro> &gnss_observables_
bool has_b1c = false;
for (int k = 0; k < nobs_total; k++)
{
for (int b = 0; b < NFREQ; b++)
for (const unsigned char cb : d_obs_data[k].code)
{
const unsigned char cb = d_obs_data[k].code[b];
if (cb == CODE_L1D || cb == CODE_L1P)
{
has_b1c = true;
@@ -8,15 +8,11 @@
set(ACQ_ADAPTER_SOURCES
pcps_acquisition_adapter.cc
pcps_acquisition_adapter_custom.cc
base_pcps_acquisition.cc
beidou_b1c_pcps_ambiguous_acquisition.cc
)
set(ACQ_ADAPTER_HEADERS
pcps_acquisition_adapter.h
pcps_acquisition_adapter_custom.h
base_pcps_acquisition.h
beidou_b1c_pcps_ambiguous_acquisition.h
)
if(ENABLE_FPGA)
@@ -1,222 +0,0 @@
/*!
* \file base_ca_pcps_acquisition.h
* \brief Adapts a PCPS acquisition block to an AcquisitionInterface
* \authors <ul>
* <li> Mathieu Favreau, 2025. favreau.mathieu(at)hotmail.com
* </ul>
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2025 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "base_pcps_acquisition.h"
#include "acq_conf.h"
#include "configuration_interface.h"
#include "gnss_sdr_flags.h"
#if USE_GLOG_AND_GFLAGS
#include <glog/logging.h>
#else
#include <absl/log/log.h>
#endif
#if HAS_STD_SPAN
#include <span>
namespace own = std;
#else
#include <gsl-lite/gsl-lite.hpp>
namespace own = gsl_lite;
#endif
namespace
{
Acq_Conf get_acq_conf(const ConfigurationInterface* configuration, const std::string& role, double chip_rate, double opt_freq, uint32_t ms_per_code)
{
Acq_Conf acq_parameters;
acq_parameters.ms_per_code = ms_per_code;
acq_parameters.sampled_ms = ms_per_code; // Set as default value
acq_parameters.SetFromConfiguration(configuration, role, chip_rate, opt_freq);
#if USE_GLOG_AND_GFLAGS
if (FLAGS_doppler_max != 0)
{
acq_parameters.doppler_max = FLAGS_doppler_max;
}
if (FLAGS_doppler_step != 0)
{
acq_parameters.doppler_step = FLAGS_doppler_step;
}
#else
if (absl::GetFlag(FLAGS_doppler_max) != 0)
{
acq_parameters.doppler_max = absl::GetFlag(FLAGS_doppler_max);
}
if (absl::GetFlag(FLAGS_doppler_step) != 0)
{
acq_parameters.doppler_step = absl::GetFlag(FLAGS_doppler_step);
}
#endif
return acq_parameters;
}
} // namespace
BasePcpsAcquisition::BasePcpsAcquisition(
const ConfigurationInterface* configuration,
const std::string& role,
unsigned int in_streams,
unsigned int out_streams,
double chip_rate,
double opt_freq,
double code_length_chips,
uint32_t ms_per_code) : acq_parameters_(get_acq_conf(configuration, role, chip_rate, opt_freq, ms_per_code)),
gnss_synchro_(nullptr),
role_(role),
vector_length_(std::floor(acq_parameters_.sampled_ms * acq_parameters_.samples_per_ms) * (acq_parameters_.bit_transition_flag ? 2.0 : 1.0)),
code_length_(static_cast<unsigned int>(std::floor(static_cast<double>(acq_parameters_.resampled_fs) / (chip_rate / code_length_chips)))),
code_(vector_length_),
acquisition_(pcps_make_acquisition(acq_parameters_))
{
DLOG(INFO) << "role " << role;
DLOG(INFO) << "acquisition(" << acquisition_->unique_id() << ")";
if (acq_parameters_.item_type == "cbyte")
{
cbyte_to_float_x2_ = make_complex_byte_to_float_x2();
float_to_complex_ = gr::blocks::float_to_complex::make();
}
if (in_streams > 1)
{
LOG(ERROR) << "This implementation only supports one input stream";
}
if (out_streams > 0)
{
LOG(ERROR) << "This implementation does not provide an output stream";
}
}
void BasePcpsAcquisition::stop_acquisition()
{
acquisition_->set_active(false);
}
void BasePcpsAcquisition::set_doppler_center(int doppler_center)
{
acquisition_->set_doppler_center(doppler_center);
}
void BasePcpsAcquisition::set_gnss_synchro(Gnss_Synchro* gnss_synchro)
{
gnss_synchro_ = gnss_synchro;
acquisition_->set_gnss_synchro(gnss_synchro_);
}
signed int BasePcpsAcquisition::mag()
{
return acquisition_->mag();
}
void BasePcpsAcquisition::reset()
{
acquisition_->set_active(true);
}
void BasePcpsAcquisition::connect(gr::top_block_sptr top_block)
{
if (acq_parameters_.item_type == "gr_complex" || acq_parameters_.item_type == "cshort")
{
// nothing to connect
}
else if (acq_parameters_.item_type == "cbyte")
{
// Since a byte-based acq implementation is not available,
// we just convert cshorts to gr_complex
top_block->connect(cbyte_to_float_x2_, 0, float_to_complex_, 0);
top_block->connect(cbyte_to_float_x2_, 1, float_to_complex_, 1);
top_block->connect(float_to_complex_, 0, acquisition_, 0);
}
else
{
LOG(WARNING) << acq_parameters_.item_type << " unknown acquisition item type";
}
}
void BasePcpsAcquisition::disconnect(gr::top_block_sptr top_block)
{
if (acq_parameters_.item_type == "gr_complex" || acq_parameters_.item_type == "cshort")
{
// nothing to disconnect
}
else if (acq_parameters_.item_type == "cbyte")
{
top_block->disconnect(cbyte_to_float_x2_, 0, float_to_complex_, 0);
top_block->disconnect(cbyte_to_float_x2_, 1, float_to_complex_, 1);
top_block->disconnect(float_to_complex_, 0, acquisition_, 0);
}
else
{
LOG(WARNING) << acq_parameters_.item_type << " unknown acquisition item type";
}
}
gr::basic_block_sptr BasePcpsAcquisition::get_left_block()
{
if (acq_parameters_.item_type == "gr_complex" || acq_parameters_.item_type == "cshort")
{
return acquisition_;
}
if (acq_parameters_.item_type == "cbyte")
{
return cbyte_to_float_x2_;
}
LOG(WARNING) << acq_parameters_.item_type << " unknown acquisition item type";
return nullptr;
}
gr::basic_block_sptr BasePcpsAcquisition::get_right_block()
{
return acquisition_;
}
void BasePcpsAcquisition::set_resampler_latency(uint32_t latency_samples)
{
acquisition_->set_resampler_latency(latency_samples);
}
void BasePcpsAcquisition::set_local_code()
{
volk_gnsssdr::vector<std::complex<float>> code(code_length_);
const auto sampling_freq = acq_parameters_.use_automatic_resampler ? acq_parameters_.resampled_fs : acq_parameters_.fs_in;
code_gen_complex_sampled(code, gnss_synchro_->PRN, sampling_freq);
const auto num_codes = acq_parameters_.sampled_ms / acq_parameters_.ms_per_code;
own::span<gr_complex> code_span(code_.data(), vector_length_);
for (unsigned int i = 0; i < num_codes; i++)
{
std::copy_n(code.data(), code_length_, code_span.subspan(i * code_length_, code_length_).data());
}
acquisition_->set_local_code(code_.data());
}
@@ -1,146 +0,0 @@
/*!
* \file base_ca_pcps_acquisition.h
* \brief Adapts a PCPS acquisition block to an AcquisitionInterface
* \authors <ul>
* <li> Mathieu Favreau, 2025. favreau.mathieu(at)hotmail.com
* </ul>
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2025 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_BASE_PCPS_ACQUISITION_H
#define GNSS_SDR_BASE_PCPS_ACQUISITION_H
#include "acq_conf.h"
#include "channel_fsm.h"
#include "complex_byte_to_float_x2.h"
#include "gnss_synchro.h"
#include "pcps_acquisition.h"
#include <gnuradio/blocks/float_to_complex.h>
#include <volk_gnsssdr/volk_gnsssdr_alloc.h>
/** \addtogroup Acquisition
* Classes for GNSS signal acquisition
* \{ */
/** \addtogroup Acq_adapters acquisition_adapters
* Wrap GNU Radio acquisition blocks with an AcquisitionInterface
* \{ */
class ConfigurationInterface;
/*!
* \brief This class adapts a PCPS acquisition block to an AcquisitionInterface
*/
class BasePcpsAcquisition : public AcquisitionInterface
{
public:
BasePcpsAcquisition(
const ConfigurationInterface* configuration,
const std::string& role,
unsigned int in_streams,
unsigned int out_streams,
double chip_rate,
double opt_freq,
double code_length_chips,
uint32_t ms_per_code);
~BasePcpsAcquisition() override = default;
inline std::string role() override
{
return role_;
}
inline size_t item_size() override
{
return acq_parameters_.it_size;
}
void connect(gr::top_block_sptr top_block) override;
void disconnect(gr::top_block_sptr top_block) override;
gr::basic_block_sptr get_left_block() override;
gr::basic_block_sptr get_right_block() override;
/*!
* \brief Set acquisition/tracking common Gnss_Synchro object pointer
* to efficiently exchange synchronization data between acquisition and
* tracking blocks
*/
void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override;
/*!
* \brief Set acquisition channel unique ID
*/
inline void set_channel(unsigned int channel) override
{
acquisition_->set_channel(channel);
}
/*!
* \brief Set channel fsm associated to this acquisition instance
*/
inline void set_channel_fsm(std::weak_ptr<ChannelFsm> channel_fsm) override
{
acquisition_->set_channel_fsm(std::move(channel_fsm));
}
/*!
* \brief Set Doppler center for the grid search
*/
void set_doppler_center(int doppler_center) override;
/*!
* \brief Returns the maximum peak of grid search
*/
signed int mag() override;
/*!
* \brief Restart acquisition algorithm
*/
void reset() override;
/*!
* \brief Stop running acquisition
*/
void stop_acquisition() override;
/*!
* \brief Sets the resampler latency to account it in the acquisition code delay estimation
*/
void set_resampler_latency(uint32_t latency_samples) override;
/*!
* \brief Sets local code
*/
void set_local_code() override;
private:
/*!
* \brief Generate code
*/
virtual void code_gen_complex_sampled(own::span<std::complex<float>> dest, uint32_t prn, int32_t sampling_freq) = 0;
const Acq_Conf acq_parameters_;
gr::blocks::float_to_complex::sptr float_to_complex_;
complex_byte_to_float_x2_sptr cbyte_to_float_x2_;
Gnss_Synchro* gnss_synchro_;
const std::string role_;
const unsigned int vector_length_;
const unsigned int code_length_;
volk_gnsssdr::vector<std::complex<float>> code_;
pcps_acquisition_sptr acquisition_;
};
/** \} */
/** \} */
#endif // GNSS_SDR_BASE_PCPS_ACQUISITION_H
@@ -1,64 +0,0 @@
/*!
* \file beidou_b1c_pcps_ambiguous_acquisition.cc
* \brief Adapts a PCPS acquisition block to an AcquisitionInterface for
* BeiDou B1C Signals
* \author GNSS-SDR contributors
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2020 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "beidou_b1c_pcps_ambiguous_acquisition.h"
#include "Beidou_B1C.h"
#include "beidou_b1c_signal_replica.h"
BeidouB1cPcpsAmbiguousAcquisition::BeidouB1cPcpsAmbiguousAcquisition(
const ConfigurationInterface* configuration,
const std::string& role,
unsigned int in_streams,
unsigned int out_streams)
: BasePcpsAcquisition(configuration,
role,
in_streams,
out_streams,
BEIDOU_B1C_CODE_RATE_CPS,
BEIDOU_B1C_OPT_ACQ_FS_SPS,
BEIDOU_B1C_CODE_LENGTH_CHIPS,
BEIDOU_B1C_CODE_PERIOD_MS),
acquire_pilot_(configuration->property(role + ".acquire_pilot", true)),
qmboc_(configuration->property(role + ".qmboc", true)),
gnss_synchro_(nullptr)
{
}
void BeidouB1cPcpsAmbiguousAcquisition::set_gnss_synchro(Gnss_Synchro* p_gnss_synchro)
{
gnss_synchro_ = p_gnss_synchro;
BasePcpsAcquisition::set_gnss_synchro(p_gnss_synchro);
}
void BeidouB1cPcpsAmbiguousAcquisition::code_gen_complex_sampled(own::span<std::complex<float>> dest, uint32_t prn, int32_t sampling_freq)
{
if (acquire_pilot_)
{
const std::array<char, 3> pilot_signal = {{'1', 'P', '\0'}};
beidou_b1c_code_gen_complex_sampled(dest, pilot_signal, qmboc_, prn, sampling_freq, 0, false);
}
else
{
std::array<char, 3> signal_id{};
signal_id[0] = gnss_synchro_->Signal[0];
signal_id[1] = gnss_synchro_->Signal[1];
signal_id[2] = '\0';
beidou_b1c_code_gen_complex_sampled(dest, signal_id, qmboc_, prn, sampling_freq, 0, false);
}
}
@@ -1,56 +0,0 @@
/*!
* \file beidou_b1c_pcps_ambiguous_acquisition.h
* \brief Adapts a PCPS acquisition block to an AcquisitionInterface for
* BeiDou B1C Signals
* \author GNSS-SDR contributors
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2020 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_BEIDOU_B1C_PCPS_AMBIGUOUS_ACQUISITION_H
#define GNSS_SDR_BEIDOU_B1C_PCPS_AMBIGUOUS_ACQUISITION_H
#include "base_pcps_acquisition.h"
/** \addtogroup Acquisition
* \{ */
/** \addtogroup Acq_adapters
* \{ */
class BeidouB1cPcpsAmbiguousAcquisition : public BasePcpsAcquisition
{
public:
BeidouB1cPcpsAmbiguousAcquisition(
const ConfigurationInterface* configuration,
const std::string& role,
unsigned int in_streams,
unsigned int out_streams);
~BeidouB1cPcpsAmbiguousAcquisition() override = default;
inline std::string implementation() override
{
return "BEIDOU_B1C_PCPS_Ambiguous_Acquisition";
}
void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override;
private:
void code_gen_complex_sampled(own::span<std::complex<float>> dest, uint32_t prn, int32_t sampling_freq) override;
const bool acquire_pilot_;
const bool qmboc_;
Gnss_Synchro* gnss_synchro_;
};
/** \} */
/** \} */
#endif // GNSS_SDR_BEIDOU_B1C_PCPS_AMBIGUOUS_ACQUISITION_H
@@ -1,5 +1,5 @@
/*!
* \file base_ca_pcps_acquisition.h
* \file pcps_acquisition_adapter.cc
* \brief Adapts a PCPS acquisition block to an AcquisitionInterface
* \authors <ul>
* <li> Mathieu Favreau, 2025. favreau.mathieu(at)hotmail.com
@@ -17,6 +17,7 @@
*/
#include "pcps_acquisition_adapter.h"
#include "Beidou_B1C.h"
#include "Beidou_B1I.h"
#include "Beidou_B3I.h"
#include "GLONASS_L1_L2_CA.h"
@@ -28,6 +29,7 @@
#include "Galileo_E5b.h"
#include "Galileo_E6.h"
#include "acq_conf.h"
#include "beidou_b1c_signal_replica.h"
#include "beidou_b1i_signal_replica.h"
#include "beidou_b3i_signal_replica.h"
#include "configuration_interface.h"
@@ -96,6 +98,8 @@ signal_info get_signal_info(signal_flag sig_flag)
return {BEIDOU_B1I_CODE_RATE_CPS, BEIDOU_B1I_OPT_ACQ_FS_SPS, BEIDOU_B1I_CODE_LENGTH_CHIPS, BEIDOU_B1I_CODE_PERIOD_MS};
case BDS_B3:
return {BEIDOU_B3I_CODE_RATE_CPS, BEIDOU_B3I_OPT_ACQ_FS_SPS, BEIDOU_B3I_CODE_LENGTH_CHIPS, BEIDOU_B3I_CODE_PERIOD_MS};
case BDS_B1C:
return {BEIDOU_B1C_CODE_RATE_CPS, BEIDOU_B1C_OPT_ACQ_FS_SPS, BEIDOU_B1C_CODE_LENGTH_CHIPS, BEIDOU_B1C_CODE_PERIOD_MS};
case QZS_J1:
return {QZSS_L1_CHIP_RATE, QZSS_L1_OPT_ACQ_FS_SPS, QZSS_L1_CODE_LENGTH, QZSS_L1_PERIOD_MS};
case QZS_J5:
@@ -174,6 +178,12 @@ void code_gen_complex_sampled(signal_flag sig_flag, const Acq_Conf& conf, const
case BDS_B3:
beidou_b3i_code_gen_complex_sampled(dest, gnss_synchro.PRN, sampling_freq, 0);
break;
case BDS_B1C:
{
const auto sig = conf.acquire_pilot ? std::array<char, 3>{'1', 'P', '\0'} : std::array<char, 3>{gnss_synchro.Signal[0], gnss_synchro.Signal[1], '\0'};
beidou_b1c_code_gen_complex_sampled(dest, sig, conf.qmboc, gnss_synchro.PRN, sampling_freq, 0, false);
}
break;
case QZS_J1:
qzss_l1_code_gen_complex_sampled(dest, gnss_synchro.PRN, sampling_freq);
break;
@@ -207,6 +217,12 @@ Acq_Conf get_acq_conf(const ConfigurationInterface* configuration, const std::st
acq_parameters.acquire_iq = configuration->property(role + ".acquire_iq", acq_parameters.acquire_iq);
}
if (sig_flag == BDS_B1C)
{
acq_parameters.acquire_pilot = configuration->property(role + ".acquire_pilot", true);
acq_parameters.qmboc = configuration->property(role + ".qmboc", true);
}
#if USE_GLOG_AND_GFLAGS
if (FLAGS_doppler_max != 0)
{
@@ -79,6 +79,7 @@ public:
bool acquire_pilot{false};
bool acquire_iq{false};
bool cboc{false};
bool qmboc{false};
int zero_padding{0};
uint32_t folding_factor{0};
@@ -1,7 +1,7 @@
/*!
* \file beidou_b1c_signal_replica.cc
* \brief Library for BeiDou B1C signal replica generation
* \author GNSS-SDR contributors
* \author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
*
* Primary codes loaded from precomputed hex tables (Galileo E1 style).
* Pilot secondary codes from per-PRN binary strings (Galileo E5a-Q style).
@@ -12,12 +12,11 @@
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2020 (see AUTHORS file for a list of contributors)
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "beidou_b1c_signal_replica.h"
#include "Beidou_B1C.h"
#include "gnss_signal_replica.h"
@@ -1,19 +1,18 @@
/*!
* \file beidou_b1c_signal_replica.h
* \brief Library for BeiDou B1C signal replica generation
* \author GNSS-SDR contributors
* \author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2020 (see AUTHORS file for a list of contributors)
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_BEIDOU_B1C_SIGNAL_REPLICA_H
#define GNSS_SDR_BEIDOU_B1C_SIGNAL_REPLICA_H
@@ -754,6 +754,7 @@ eph_t eph_to_rtklib(const Beidou_Cnav1_Ephemeris& bei_eph)
rtklib_sat.f0 = bei_eph.af0;
rtklib_sat.f1 = bei_eph.af1;
rtklib_sat.f2 = bei_eph.af2;
/* ICD §7.6 → gettgd(): tgd[0]=TGD_B1Cp, tgd[1]=TGD_B2ap, tgd[2]=ISC_B1Cd */
rtklib_sat.tgd[0] = bei_eph.TGD_B1Cp;
rtklib_sat.tgd[1] = bei_eph.TGD_B2ap;
rtklib_sat.tgd[2] = bei_eph.ISC_B1Cd;
@@ -405,6 +405,7 @@ void eph2pos(gtime_t time, const eph_t *eph, double *rs, double *dts,
omge = GNSS_OMEGA_EARTH_DOT;
break;
}
const int is_bds_cnav1 = (sys == SYS_BDS && eph->code == 7) ? 1 : 0;
Ak = eph->A + eph->Adot * tk;
delta_na = eph->deln + 0.5 * eph->ndot * tk;
na = sqrt(mu / (eph->A * eph->A * eph->A)) + delta_na;
@@ -437,8 +438,8 @@ void eph2pos(gtime_t time, const eph_t *eph, double *rs, double *dts,
y = r * sin(u);
cosi = cos(i);
/* beidou geo satellite (ref [9]) */
if (sys == SYS_BDS && (prn <= 5 || prn > 58))
/* BeiDou GEO 5° transform is DNAV-only (B1I ICD). B-CNAV1 Table 7-9 is MEO/IGSO only */
if (sys == SYS_BDS && !is_bds_cnav1 && (prn <= 5 || prn > 58))
{
O = eph->OMG0 + eph->OMGd * tk - omge * eph->toes;
sinO = sin(O);
@@ -554,7 +555,10 @@ void eph2pos(gtime_t time, const eph_t *eph, double *rs, double *dts,
}
else
{
*dts -= 2.0 * sqrt(mu * A) * eph->e * sinE / (SPEED_OF_LIGHT_M_S * SPEED_OF_LIGHT_M_S);
/* ICD B1C (7-3): Δtr = F·e·√A0·sin(Ek); F = -2√μ/c².
* Use reference semi-major axis A0 for B-CNAV1 (not Ak). */
const double A_rel = is_bds_cnav1 ? A0 : A;
*dts -= 2.0 * sqrt(mu * A_rel) * eph->e * sinE / (SPEED_OF_LIGHT_M_S * SPEED_OF_LIGHT_M_S);
}
/* position and clock error variance */
+29 -25
View File
@@ -112,45 +112,49 @@ double gettgd(int sat, const nav_t *nav)
/* get tgd parameter (m) -----------------------------------------------------
* BDS DNAV: tgd[0]=TGD1
* BDS CNAV1 (code==7): tgd[0]=TGD_B1Cp, tgd[2]=ISC_B1Cd (ICD B1C §7.6)
* For B1C data (CODE_L1D) apply TGD_B1Cp+ISC_B1Cd; for pilot (CODE_L1P) TGD_B1Cp only.
* GPS/QZS/GAL: return c·tgd[0] from the first matching ephemeris (classic RTKLIB).
* BDS DNAV (eph.code!=7): tgd[0]=TGD1 (B1I vs B3I timing reference)
* BDS CNAV1 (eph.code==7), stored by eph_to_rtklib(Beidou_Cnav1_Ephemeris):
* tgd[0]=TGD_B1Cp, tgd[1]=TGD_B2ap, tgd[2]=ISC_B1Cd (ICD B1C §7.6)
* BDS user algorithm (applied in prange as PC = P - c·Δt_TGD):
* B1C pilot CODE_L1P: (Δtsv)_B1Cp = Δtsv - TGD_B1Cp → (7-4)
* B1C data CODE_L1D: (Δtsv)_B1Cd = Δtsv - TGD_B1Cp - ISC_B1Cd → (7-5)
* B1I (CODE_L2I/L1I): use DNAV TGD1; B3I skips TGD in prange.
* NOTE: CODE_L1P is also GPS/GLO L1P in RINEX — CNAV1 selection is SYS_BDS only.
* Do not fall back across DNAV↔CNAV1: TGD1 and TGD_B1Cp are different quantities.
*-----------------------------------------------------------------------------*/
double gettgd(int sat, const nav_t *nav, unsigned char obs_code)
{
int i;
const int prefer_cnav1 = (obs_code == CODE_L1D || obs_code == CODE_L1P) ? 1 : 0;
int fallback = -1;
int prn = 0;
const int sys = satsys(sat, &prn);
const int is_b1c_obs = (obs_code == CODE_L1D || obs_code == CODE_L1P) ? 1 : 0;
for (i = 0; i < nav->n; i++)
{
if (nav->eph[i].sat != sat)
{
continue;
}
const int is_cnav1 = (nav->eph[i].code == 7) ? 1 : 0;
if (prefer_cnav1 != is_cnav1)
if (sys == SYS_BDS)
{
if (fallback < 0)
const int is_cnav1 = (nav->eph[i].code == 7) ? 1 : 0;
/* B1C obs ↔ CNAV1 eph; B1I/other ↔ DNAV eph */
if (is_b1c_obs != is_cnav1)
{
fallback = i;
continue;
}
continue;
double tgd_s = nav->eph[i].tgd[0];
if (is_cnav1 && obs_code == CODE_L1D)
{
tgd_s += nav->eph[i].tgd[2];
}
return SPEED_OF_LIGHT_M_S * tgd_s;
}
double tgd_s = nav->eph[i].tgd[0];
if (is_cnav1 && obs_code == CODE_L1D)
{
tgd_s += nav->eph[i].tgd[2];
}
return SPEED_OF_LIGHT_M_S * tgd_s;
}
if (fallback >= 0)
{
double tgd_s = nav->eph[fallback].tgd[0];
if (nav->eph[fallback].code == 7 && obs_code == CODE_L1D)
{
tgd_s += nav->eph[fallback].tgd[2];
}
return SPEED_OF_LIGHT_M_S * tgd_s;
/* GPS/GAL/QZS/... : first ephemeris for this sat */
return SPEED_OF_LIGHT_M_S * nav->eph[i].tgd[0];
}
return 0.0;
}
@@ -51,6 +51,7 @@ HybridObservables::HybridObservables(const ConfigurationInterface* configuration
conf.always_output_gs = configuration->property("PVT.an_output_enabled", conf.always_output_gs) || configuration->property(role + ".always_output_gs", conf.always_output_gs);
conf.enable_E6 = configuration->property(role + ".enable_E6", conf.enable_E6);
conf.enable_monitor = configuration->property("Monitor.enable_monitor", conf.enable_monitor);
conf.reject_outlier_pseudoranges = configuration->property(role + ".reject_outlier_pseudoranges", conf.reject_outlier_pseudoranges);
#if USE_GLOG_AND_GFLAGS
if (FLAGS_carrier_smoothing_factor == DEFAULT_CARRIER_SMOOTHING_FACTOR)
@@ -598,11 +598,8 @@ void hybrid_observables_gs::compute_pranges(std::vector<Gnss_Synchro> &data) con
std::vector<Gnss_Synchro>::iterator it;
const auto current_T_rx_TOW_ms = static_cast<double>(d_T_rx_TOW_ms);
const double current_T_rx_TOW_s = current_T_rx_TOW_ms / 1000.0;
// RX TOW is initialized from the latest satellite transmit TOW, so absolute
// travel times are often far below the physical ~60–90 ms (common bias → clock).
// Only reject clearly impossible values (negative / multi-second).
constexpr double min_travel_ms = 1.0;
constexpr double max_travel_ms = 500.0;
double min_abs = 1e30;
double min_pseudorange = 0.0;
for (it = data.begin(); it != data.end(); it++)
{
if (it->Flag_valid_word)
@@ -613,18 +610,12 @@ void hybrid_observables_gs::compute_pranges(std::vector<Gnss_Synchro> &data) con
traveltime_ms = 604800000.0 + current_T_rx_TOW_ms - it->interp_TOW_ms;
}
it->RX_time = current_T_rx_TOW_s;
if (traveltime_ms >= min_travel_ms && traveltime_ms <= max_travel_ms)
it->Pseudorange_m = traveltime_ms * SPEED_OF_LIGHT_M_MS;
it->Flag_valid_pseudorange = true;
if (d_conf.reject_outlier_pseudoranges && (std::abs(it->Pseudorange_m) < min_abs))
{
it->Pseudorange_m = traveltime_ms * SPEED_OF_LIGHT_M_MS;
it->Flag_valid_pseudorange = true;
}
else
{
it->Pseudorange_m = 0.0;
it->Flag_valid_pseudorange = false;
DLOG(INFO) << "Reject absurd travel time " << traveltime_ms
<< " ms on ch " << it->Channel_ID << " PRN " << it->PRN
<< " Signal " << it->Signal;
min_pseudorange = it->Pseudorange_m;
min_abs = std::abs(it->Pseudorange_m);
}
// debug code
// std::cout << "[" << it->Channel_ID << "] interp_TOW_ms: " << it->interp_TOW_ms << '\n';
@@ -635,6 +626,17 @@ void hybrid_observables_gs::compute_pranges(std::vector<Gnss_Synchro> &data) con
it->RX_time = current_T_rx_TOW_s;
}
}
if (d_conf.reject_outlier_pseudoranges)
{
// Reject satellites whose pseudorange differs from the nearest-to-zero
for (it = data.begin(); it != data.end(); it++)
{
if (it->Flag_valid_pseudorange && (std::abs(it->Pseudorange_m - min_pseudorange) > 1e8))
{
it->Flag_valid_pseudorange = false;
}
}
}
}
@@ -43,6 +43,7 @@ public:
bool dump_mat{false};
bool enable_E6{true};
bool enable_monitor{false};
bool reject_outlier_pseudoranges{true};
};
/** \} */
@@ -7,14 +7,10 @@
set(TELEMETRY_DECODER_ADAPTER_SOURCES
telemetry_decoder_adapter.cc
telemetry_decoder_adapter_base.cc
beidou_b1c_telemetry_decoder.cc
)
set(TELEMETRY_DECODER_ADAPTER_HEADERS
telemetry_decoder_adapter.h
telemetry_decoder_adapter_base.h
beidou_b1c_telemetry_decoder.h
)
list(SORT TELEMETRY_DECODER_ADAPTER_HEADERS)
@@ -1,17 +0,0 @@
/*!
* \file beidou_b1c_telemetry_decoder.cc
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "beidou_b1c_telemetry_decoder.h"
#include "beidou_b1c_telemetry_decoder_gs.h"
BeidouB1cTelemetryDecoder::BeidouB1cTelemetryDecoder(
const ConfigurationInterface* configuration,
const std::string& role,
unsigned int in_streams,
unsigned int out_streams)
: TelemetryDecoderAdapterBase(configuration, role, in_streams, out_streams)
{
InitializeDecoder(beidou_b1c_make_telemetry_decoder_gs(satellite(), tlm_parameters_));
}
@@ -1,25 +0,0 @@
/*!
* \file beidou_b1c_telemetry_decoder.h
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef GNSS_SDR_BEIDOU_B1C_TELEMETRY_DECODER_H
#define GNSS_SDR_BEIDOU_B1C_TELEMETRY_DECODER_H
#include "telemetry_decoder_adapter_base.h"
class BeidouB1cTelemetryDecoder : public TelemetryDecoderAdapterBase
{
public:
BeidouB1cTelemetryDecoder(const ConfigurationInterface* configuration,
const std::string& role,
unsigned int in_streams,
unsigned int out_streams);
inline std::string implementation() override
{
return "BEIDOU_B1C_Telemetry_Decoder";
}
};
#endif
@@ -1,135 +0,0 @@
/*!
* \file telemetry_decoder_adapter_base.cc
* \brief Common functionality for telemetry decoder adapters
* \author Carles Fernandez-Prades, 2025 cfernandez@cttc.es
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2025 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "telemetry_decoder_adapter_base.h"
#include <utility>
#if USE_GLOG_AND_GFLAGS
#include <glog/logging.h>
#else
#include <absl/log/log.h>
#endif
TelemetryDecoderAdapterBase::TelemetryDecoderAdapterBase(const ConfigurationInterface* configuration,
const std::string& role,
unsigned int in_streams,
unsigned int out_streams) : role_(role),
in_streams_(in_streams),
out_streams_(out_streams)
{
DLOG(INFO) << "role " << role_;
if (configuration != nullptr)
{
tlm_parameters_.SetFromConfiguration(configuration, role_);
}
if (in_streams_ > 1)
{
LOG(ERROR) << "This implementation only supports one input stream ("
<< in_streams_ << " provided)";
}
if (out_streams_ > 1)
{
LOG(ERROR) << "This implementation only supports one output stream ("
<< out_streams_ << " provided)";
}
}
void TelemetryDecoderAdapterBase::InitializeDecoder(telemetry_impl_interface_sptr decoder)
{
telemetry_decoder_ = std::move(decoder);
DLOG(INFO) << "telemetry_decoder(" << telemetry_decoder_->unique_id() << ")";
}
void TelemetryDecoderAdapterBase::connect(gr::top_block_sptr top_block)
{
if (top_block)
{
/* top_block is not null */
}
DLOG(INFO) << "nothing to connect internally";
}
void TelemetryDecoderAdapterBase::disconnect(gr::top_block_sptr top_block)
{
if (top_block)
{
/* top_block is not null */
}
}
gr::basic_block_sptr TelemetryDecoderAdapterBase::get_left_block()
{
return telemetry_decoder_;
}
gr::basic_block_sptr TelemetryDecoderAdapterBase::get_right_block()
{
return telemetry_decoder_;
}
void TelemetryDecoderAdapterBase::set_satellite(const Gnss_Satellite& satellite)
{
satellite_ = Gnss_Satellite(satellite.get_system(), satellite.get_PRN());
if (telemetry_decoder_)
{
telemetry_decoder_->set_satellite(satellite_);
DLOG(INFO) << "TELEMETRY DECODER: satellite set to " << satellite_;
}
}
std::string TelemetryDecoderAdapterBase::role()
{
return role_;
}
void TelemetryDecoderAdapterBase::set_channel(int channel)
{
if (telemetry_decoder_)
{
telemetry_decoder_->set_channel(channel);
}
}
void TelemetryDecoderAdapterBase::reset()
{
if (telemetry_decoder_)
{
telemetry_decoder_->reset();
}
}
size_t TelemetryDecoderAdapterBase::item_size()
{
return sizeof(Gnss_Synchro);
}
const Gnss_Satellite& TelemetryDecoderAdapterBase::satellite() const
{
return satellite_;
}
@@ -1,88 +0,0 @@
/*!
* \file telemetry_decoder_adapter_base.h
* \brief Common functionality for telemetry decoder adapters
* \author Carles Fernandez-Prades, 2025 cfernandez@cttc.es
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2025 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_TELEMETRY_DECODER_ADAPTER_BASE_CLASS_H
#define GNSS_SDR_TELEMETRY_DECODER_ADAPTER_BASE_CLASS_H
#include "configuration_interface.h"
#include "gnss_satellite.h"
#include "gnss_synchro.h"
#include "telemetry_decoder_interface.h"
#include "telemetry_impl_interface.h"
#include "tlm_conf.h"
#include <gnuradio/runtime_types.h>
#include <cstddef>
#include <string>
class ConfigurationInterface;
/** \addtogroup Telemetry_Decoder
* \{
*/
/** \addtogroup Telemetry_Decoder_adapters
* \{
*/
/*!
* \brief Base class for Telemetry Decoder adapters
*/
class TelemetryDecoderAdapterBase : public TelemetryDecoderInterface
{
public:
TelemetryDecoderAdapterBase(const ConfigurationInterface* configuration,
const std::string& role,
unsigned int in_streams,
unsigned int out_streams);
~TelemetryDecoderAdapterBase() override = default;
void connect(gr::top_block_sptr top_block) override;
void disconnect(gr::top_block_sptr top_block) override;
gr::basic_block_sptr get_left_block() override;
gr::basic_block_sptr get_right_block() override;
void set_satellite(const Gnss_Satellite& satellite) override;
std::string role() override;
void set_channel(int channel) override;
void reset() override;
size_t item_size() override;
protected:
void InitializeDecoder(telemetry_impl_interface_sptr decoder);
const Gnss_Satellite& satellite() const;
Tlm_Conf tlm_parameters_;
private:
telemetry_impl_interface_sptr telemetry_decoder_;
Gnss_Satellite satellite_;
std::string role_;
unsigned int in_streams_ = 0;
unsigned int out_streams_ = 0;
};
/** \} */
/** \} */
#endif // GNSS_SDR_TELEMETRY_DECODER_ADAPTER_BASE_CLASS_H
@@ -1,9 +1,18 @@
/*!
* \file beidou_b1c_telemetry_decoder_gs.cc
* \brief BeiDou B1C B-CNAV1 telemetry decoder block
* \author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "beidou_b1c_telemetry_decoder_gs.h"
#include "Beidou_B1C.h"
#include "Beidou_B1C_codes.h"
@@ -138,18 +147,18 @@ bool apply_llr_scale_and_decode(
{
const int32_t n = BEIDOU_CNAV1_FRAME_SYMBOLS;
double rms_acc = 0.0;
for (size_t i = 0; i < frame.size(); ++i)
for (float sample : frame)
{
const double v = static_cast<double>(frame[i]);
const auto v = static_cast<double>(sample);
rms_acc += v * v;
}
const double rms = std::sqrt(rms_acc / static_cast<double>(n)) + 1e-6;
const double cn0_linear = std::pow(10.0, static_cast<double>(cn0_db_hz) / 10.0);
const double snr_sym = std::max(cn0_linear * (BEIDOU_B1C_CODE_PERIOD_MS * 1e-3), 1e-4);
const double llr_scale = std::min(std::max(std::sqrt(snr_sym) / rms, 0.05), 50.0);
for (size_t i = 0; i < frame.size(); ++i)
for (float& sample : frame)
{
frame[i] = static_cast<float>(static_cast<double>(frame[i]) * llr_scale);
sample = static_cast<float>(static_cast<double>(sample) * llr_scale);
}
return nav.decode_frame_symbols(frame.data(), BEIDOU_CNAV1_FRAME_SYMBOLS, expected_prn, fail_stage);
}
@@ -469,9 +478,8 @@ int beidou_b1c_telemetry_decoder_gs::general_work(
if (!candidate_offsets.empty())
{
for (size_t cidx = 0; cidx < candidate_offsets.size(); ++cidx)
for (int32_t candidate_offset : candidate_offsets)
{
const int32_t candidate_offset = candidate_offsets[cidx];
for (int32_t delta = -local_offset_span; delta <= local_offset_span; delta++)
{
const int32_t start_offset =
@@ -1,9 +1,18 @@
/*!
* \file beidou_b1c_telemetry_decoder_gs.h
* \brief BeiDou B1C B-CNAV1 telemetry decoder block
* \author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_BEIDOU_B1C_TELEMETRY_DECODER_GS_H
#define GNSS_SDR_BEIDOU_B1C_TELEMETRY_DECODER_GS_H
@@ -28,8 +28,6 @@ endif()
set(TRACKING_ADAPTER_SOURCES
dll_pll_tracking_adapter.cc
base_dll_pll_tracking.cc
beidou_b1c_dll_pll_veml_tracking.cc
galileo_e1_tcp_connector_tracking.cc
gps_l1_ca_tcp_connector_tracking.cc
gps_l1_ca_gaussian_tracking.cc
@@ -39,8 +37,6 @@ set(TRACKING_ADAPTER_SOURCES
set(TRACKING_ADAPTER_HEADERS
dll_pll_tracking_adapter.h
base_dll_pll_tracking.h
beidou_b1c_dll_pll_veml_tracking.h
galileo_e1_tcp_connector_tracking.h
gps_l1_ca_tcp_connector_tracking.h
gps_l1_ca_gaussian_tracking.h
@@ -1,98 +0,0 @@
/*!
* \file base_dll_pll_tracking.cc
* \brief Base class providing shared logic for DLL+PLL VEML tracking adapters.
* \authors Carles Fernandez, 2025. carles.fernandez(at)cttc.cat
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2025 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "base_dll_pll_tracking.h"
#include "configuration_interface.h"
#if USE_GLOG_AND_GFLAGS
#include <glog/logging.h>
#else
#include <absl/log/log.h>
#endif
BaseDllPllTracking::BaseDllPllTracking(
const ConfigurationInterface* configuration,
std::string role,
unsigned int in_streams,
unsigned int out_streams)
: role_(std::move(role)),
item_size_(sizeof(gr_complex))
{
trk_params_.SetFromConfiguration(configuration, role_);
if (in_streams > 1)
{
LOG(ERROR) << "Only one input stream is supported.";
}
if (out_streams > 1)
{
LOG(ERROR) << "Only one output stream is supported.";
}
DLOG(INFO) << "role " << role_;
}
void BaseDllPllTracking::connect(gr::top_block_sptr top_block)
{
if (top_block)
{ /* no connection needed */
}
}
void BaseDllPllTracking::disconnect(gr::top_block_sptr top_block)
{
if (top_block)
{ /* no disconnection needed */
}
}
gr::basic_block_sptr BaseDllPllTracking::get_left_block()
{
return tracking_sptr_;
}
gr::basic_block_sptr BaseDllPllTracking::get_right_block()
{
return tracking_sptr_;
}
void BaseDllPllTracking::set_channel(unsigned int channel)
{
tracking_sptr_->set_channel(channel);
}
void BaseDllPllTracking::set_gnss_synchro(Gnss_Synchro* p_gnss_synchro)
{
tracking_sptr_->set_gnss_synchro(p_gnss_synchro);
}
void BaseDllPllTracking::start_tracking()
{
tracking_sptr_->start_tracking();
}
void BaseDllPllTracking::stop_tracking()
{
tracking_sptr_->stop_tracking();
}
@@ -1,126 +0,0 @@
/*!
* \file base_dll_pll_tracking.h
* \brief Base class providing shared logic for DLL+PLL VEML tracking adapters.
* \authors Carles Fernandez, 2025. carles.fernandez(at)cttc.cat
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2025 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_BASE_DLL_PLL_TRACKING_H
#define GNSS_SDR_BASE_DLL_PLL_TRACKING_H
#include "dll_pll_conf.h"
#include "dll_pll_veml_tracking.h"
#include "tracking_interface.h"
#include <cstddef>
#include <string>
/** \addtogroup Tracking
* Classes for GNSS signal tracking.
* \{ */
/** \addtogroup Tracking_adapters tracking_adapters
* Wrap GNU Radio blocks for GNSS signal tracking with a TrackingInterface
* \{ */
class ConfigurationInterface;
/*!
* \brief Base class providing shared logic for DLL+PLL tracking loop adapters
* for GNSS signals.
*/
class BaseDllPllTracking : public TrackingInterface
{
public:
/*!
* \brief Base constructor of a Tracking block adapter
*/
explicit BaseDllPllTracking(const ConfigurationInterface* configuration,
std::string role,
unsigned int in_streams,
unsigned int out_streams);
/*!
* \brief Default destructor of the Tracking block adapter
*/
~BaseDllPllTracking() override = default;
/*!
* \brief Get role from the Tracking block adapter
*/
inline std::string role() final { return role_; }
/*!
* \brief Get item_size from the Tracking block adapter
*/
inline size_t item_size() final { return item_size_; }
/*!
* \brief Connect the Tracking block adapter
*/
void connect(gr::top_block_sptr top_block) final;
/*!
* \brief Disconnect the sTracking block adapter
*/
void disconnect(gr::top_block_sptr top_block) final;
/*!
* \brief Get left block from the Tracking block adapter
*/
gr::basic_block_sptr get_left_block() final;
/*!
* \brief Get right block from the Tracking block adapter
*/
gr::basic_block_sptr get_right_block() final;
/*!
* \brief Set tracking channel unique ID
*/
void set_channel(unsigned int channel) final;
/*!
* \brief Set acquisition Gnss_Synchro object pointer
* to exchange synchronization data between acquisition and tracking blocks
*/
void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) final;
/*!
* \brief Start the Tracking block
*/
void start_tracking() final;
/*!
* \brief Stop the Tracking block
*/
void stop_tracking() final;
protected:
// Can be used by each derived class
inline Dll_Pll_Conf& config_params() { return trk_params_; }
inline const Dll_Pll_Conf& config_params() const { return trk_params_; }
inline void set_item_size(size_t item_size) { item_size_ = item_size; }
// Must be implemented / filled by each derived class
virtual void configure_tracking_parameters(const ConfigurationInterface* configuration) = 0;
virtual void create_tracking_block() = 0;
dll_pll_veml_tracking_sptr tracking_sptr_;
private:
// Managed by the base class
Dll_Pll_Conf trk_params_;
const std::string role_;
size_t item_size_;
};
/** \} */
/** \} */
#endif // GNSS_SDR_BASE_DLL_PLL_TRACKING_H
@@ -1,101 +0,0 @@
/*!
* \file beidou_b1c_dll_pll_veml_tracking.cc
* \brief Adapts a DLL+PLL VEML tracking loop block to a TrackingInterface
* for BeiDou B1C signals
* \author GNSS-SDR contributors
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2020 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "beidou_b1c_dll_pll_veml_tracking.h"
#include "Beidou_B1C.h"
#include "configuration_interface.h"
#include "display.h"
#include <algorithm>
#include <array>
#if USE_GLOG_AND_GFLAGS
#include <glog/logging.h>
#else
#include <absl/log/log.h>
#endif
BeidouB1cDllPllVemlTracking::BeidouB1cDllPllVemlTracking(
const ConfigurationInterface* configuration,
const std::string& role,
unsigned int in_streams,
unsigned int out_streams)
: BaseDllPllTracking(configuration, role, in_streams, out_streams)
{
configure_tracking_parameters(configuration);
create_tracking_block();
}
void BeidouB1cDllPllVemlTracking::configure_tracking_parameters(
const ConfigurationInterface* configuration [[maybe_unused]])
{
config_params().vector_length = static_cast<int>(std::round(
config_params().fs_in / (BEIDOU_B1C_CODE_RATE_CPS / BEIDOU_B1C_CODE_LENGTH_CHIPS)));
config_params().system = 'C';
const std::array<char, 3> sig{'1', 'D', '\0'};
std::copy_n(sig.data(), 3, config_params().signal);
const bool acq_qmboc = configuration->property("Acquisition_1D.qmboc", config_params().b1c_qmboc_tracking);
// Keep tracking-side local replica modulation aligned with acquisition by default.
config_params().b1c_qmboc_tracking = configuration->property(role() + ".b1c_qmboc_tracking", acq_qmboc);
if (config_params().extend_correlation_symbols < 1)
{
config_params().extend_correlation_symbols = 1;
std::cout << TEXT_RED << "WARNING: BeiDou B1C. extend_correlation_symbols must be bigger than 0. Coherent integration has been set to 1 symbol (10 ms)" << TEXT_RESET << '\n';
}
else if (!config_params().track_pilot && config_params().extend_correlation_symbols > 1)
{
config_params().extend_correlation_symbols = 1;
std::cout << TEXT_RED << "WARNING: BeiDou B1C. Extended coherent integration is not allowed when tracking the data component. Coherent integration has been set to 10 ms (1 symbol)" << TEXT_RESET << '\n';
}
// B1C pilot: 1800-chip secondary sync spans one B-CNAV1 frame (18 s).
// Keep coherent integration at 1 symbol (10 ms); extended correlator is not used.
if (config_params().track_pilot)
{
config_params().extend_correlation_symbols = 1;
const auto min_bit_sync_limit_s = static_cast<uint32_t>(
BEIDOU_B1C_FRAME_PERIOD_S + 30);
if (config_params().bit_synchronization_time_limit_s < min_bit_sync_limit_s)
{
config_params().bit_synchronization_time_limit_s = min_bit_sync_limit_s;
}
}
if ((config_params().extend_correlation_symbols > 1) &&
(config_params().pll_bw_narrow_hz > config_params().pll_bw_hz ||
config_params().dll_bw_narrow_hz > config_params().dll_bw_hz))
{
std::cout << TEXT_RED << "WARNING: BeiDou B1C. PLL or DLL narrow tracking bandwidth is higher than wide tracking one" << TEXT_RESET << '\n';
}
}
void BeidouB1cDllPllVemlTracking::create_tracking_block()
{
if (config_params().item_type == "gr_complex")
{
tracking_sptr_ = dll_pll_veml_make_tracking(config_params());
DLOG(INFO) << "tracking(" << tracking_sptr_->unique_id() << ")";
}
else
{
set_item_size(0);
tracking_sptr_ = nullptr;
LOG(WARNING) << config_params().item_type << " unknown tracking item type.";
}
}
@@ -1,41 +0,0 @@
/*!
* \file beidou_b1c_dll_pll_veml_tracking.h
* \brief Adapts a DLL+PLL VEML tracking loop block to a TrackingInterface
* for BeiDou B1C signals
* \author GNSS-SDR contributors
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2020 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_BEIDOU_B1C_DLL_PLL_VEML_TRACKING_H
#define GNSS_SDR_BEIDOU_B1C_DLL_PLL_VEML_TRACKING_H
#include "base_dll_pll_tracking.h"
class BeidouB1cDllPllVemlTracking : public BaseDllPllTracking
{
public:
BeidouB1cDllPllVemlTracking(const ConfigurationInterface* configuration,
const std::string& role,
unsigned int in_streams,
unsigned int out_streams);
inline std::string implementation() override
{
return "BEIDOU_B1C_DLL_PLL_VEML_Tracking";
}
private:
void configure_tracking_parameters(const ConfigurationInterface* configuration) override;
void create_tracking_block() override;
};
#endif // GNSS_SDR_BEIDOU_B1C_DLL_PLL_VEML_TRACKING_H
@@ -15,6 +15,7 @@
*/
#include "dll_pll_tracking_adapter.h"
#include "Beidou_B1C.h"
#include "Beidou_B1I.h"
#include "Beidou_B3I.h"
#include "GLONASS_L1_L2_CA.h"
@@ -28,6 +29,7 @@
#include "configuration_interface.h"
#include "display.h"
#include "qzss.h"
#include "signal_flag.h"
#if USE_GLOG_AND_GFLAGS
#include <glog/logging.h>
@@ -76,6 +78,8 @@ signal_info get_signal_info(signal_flag sig_flag)
return {'C', {'B', '1', '\0'}, "BEIDOU B1I", BEIDOU_B1I_CODE_RATE_CPS, BEIDOU_B1I_CODE_LENGTH_CHIPS, 20, false, true};
case BDS_B3:
return {'C', {'B', '3', '\0'}, "BEIDOU B3I", BEIDOU_B3I_CODE_RATE_CPS, BEIDOU_B3I_CODE_LENGTH_CHIPS, 20, false, false}; // Does false, false make sense?
case BDS_B1C:
return {'C', {'1', 'D', '\0'}, "BeiDou B1C", BEIDOU_B1C_CODE_RATE_CPS, BEIDOU_B1C_CODE_LENGTH_CHIPS, 1, true, false};
case QZS_J1:
return {'J', {'J', '1', '\0'}, "QZSS L1 C/A", QZSS_L1_CHIP_RATE, QZSS_L1_CODE_LENGTH, 20, false, true};
case QZS_J5:
@@ -87,7 +91,7 @@ signal_info get_signal_info(signal_flag sig_flag)
return {};
}
void check_and_configure_trk_params(const ConfigurationInterface* configuration, const std::string& role, const signal_info& sig_info, Dll_Pll_Conf& trk_params)
void check_and_configure_trk_params(const ConfigurationInterface* configuration, const std::string& role, signal_flag sig_flag, const signal_info& sig_info, Dll_Pll_Conf& trk_params)
{
trk_params.SetFromConfiguration(configuration, role);
trk_params.vector_length = static_cast<int>(std::round(trk_params.fs_in / (sig_info.code_chip_rate / sig_info.code_length_chips)));
@@ -145,6 +149,24 @@ void check_and_configure_trk_params(const ConfigurationInterface* configuration,
{
std::cout << TEXT_RED << "WARNING: " << sig_info.sig_name << ". PLL or DLL narrow tracking bandwidth is higher than wide tracking one" << TEXT_RESET << '\n';
}
if (sig_flag == BDS_B1C)
{
// Keep tracking-side local replica modulation aligned with acquisition by default.
const bool acq_qmboc = configuration->property("Acquisition_1D.qmboc", trk_params.b1c_qmboc_tracking);
trk_params.b1c_qmboc_tracking = configuration->property(role + ".b1c_qmboc_tracking", acq_qmboc);
// B1C pilot secondary sync spans one B-CNAV1 frame (18 s); keep coherent integration at 1 symbol.
if (trk_params.track_pilot)
{
trk_params.extend_correlation_symbols = 1;
const auto min_bit_sync_limit_s = static_cast<uint32_t>(BEIDOU_B1C_FRAME_PERIOD_S + 30);
if (trk_params.bit_synchronization_time_limit_s < min_bit_sync_limit_s)
{
trk_params.bit_synchronization_time_limit_s = min_bit_sync_limit_s;
}
}
}
}
} // namespace
@@ -166,7 +188,7 @@ DllPllTrackingAdapter::DllPllTrackingAdapter(
if (!sig_info.sig_name.empty())
{
check_and_configure_trk_params(configuration, role_, sig_info, trk_params_);
check_and_configure_trk_params(configuration, role_, sig_flag, sig_info, trk_params_);
if (trk_params_.item_type == "gr_complex")
{
+7 -11
View File
@@ -28,9 +28,6 @@
#include "acquisition_interface.h"
#include "array_signal_conditioner.h"
#include "beamformer_filter.h"
#include "beidou_b1c_dll_pll_veml_tracking.h"
#include "beidou_b1c_pcps_ambiguous_acquisition.h"
#include "beidou_b1c_telemetry_decoder.h"
#include "beidou_b1c_telemetry_decoder_gs.h"
#include "beidou_dnav_telemetry_decoder_gs.h"
#include "byte_to_short.h"
@@ -499,7 +496,7 @@ std::unique_ptr<AcquisitionInterface> get_acq_block(
}
else if (implementation == "BEIDOU_B1C_PCPS_Ambiguous_Acquisition")
{
return std::make_unique<BeidouB1cPcpsAmbiguousAcquisition>(configuration, role, in_streams, out_streams);
return std::make_unique<PcpsAcquisitionAdapter>(configuration, role, implementation, in_streams, out_streams, BDS_B1C);
}
else if (implementation == "BEIDOU_B3I_PCPS_Acquisition")
{
@@ -615,7 +612,7 @@ std::unique_ptr<TrackingInterface> get_trk_block(
}
else if (implementation == "BEIDOU_B1C_DLL_PLL_VEML_Tracking")
{
return std::make_unique<BeidouB1cDllPllVemlTracking>(configuration, role, in_streams, out_streams);
return std::make_unique<DllPllTrackingAdapter>(configuration, role, implementation, in_streams, out_streams, BDS_B1C);
}
else if (implementation == "BEIDOU_B3I_DLL_PLL_Tracking")
{
@@ -696,14 +693,13 @@ std::unique_ptr<TelemetryDecoderInterface> get_tlm_block(
unsigned int in_streams,
unsigned int out_streams)
{
if (implementation == "BEIDOU_B1C_Telemetry_Decoder")
{
return std::make_unique<BeidouB1cTelemetryDecoder>(configuration, role, in_streams, out_streams);
}
telemetry_impl_interface_sptr telemetry;
if (implementation == "GPS_L1_CA_Telemetry_Decoder")
if (implementation == "BEIDOU_B1C_Telemetry_Decoder")
{
telemetry = beidou_b1c_make_telemetry_decoder_gs(Gnss_Satellite{}, get_tlm_conf(configuration, role));
}
else if (implementation == "GPS_L1_CA_Telemetry_Decoder")
{
telemetry = gps_l1_ca_make_telemetry_decoder_gs(get_tlm_conf(configuration, role));
}
+3 -4
View File
@@ -1,7 +1,7 @@
/*!
* \file Beidou_B1C.h
* \brief Defines system parameters for BeiDou B1C open service signal
* \author GNSS-SDR contributors
* \brief Defines system parameters for BeiDou B1C open service signal
* \author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
*
* Parameter values from BDS-SIS-ICD-B1C-1.0 (2017-12).
*
@@ -10,12 +10,11 @@
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2020 (see AUTHORS file for a list of contributors)
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_BEIDOU_B1C_H
#define GNSS_SDR_BEIDOU_B1C_H
+17 -1
View File
@@ -1,4 +1,20 @@
/* Auto-generated by generate_beidou_b1c_codes.py. Do not edit manually. */
/*!
* \file Beidou_B1C_codes.h
* \brief Precomputed BeiDou B1C primary and secondary codes
* \author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
*
* Auto-generated by generate_beidou_b1c_codes.py. Do not edit manually.
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_BEIDOU_B1C_CODES_H
#define GNSS_SDR_BEIDOU_B1C_CODES_H
+8 -2
View File
@@ -1,10 +1,16 @@
/*!
* \file Beidou_B1C_prn.h
* \brief B1C PRN Weil code parameters from BDS-SIS-ICD-B1C-1.0 Table 5-2/5-3/5-4
* \author GNSS-SDR contributors
* \brief B1C PRN Weil code parameters from BDS-SIS-ICD-B1C-1.0 Table 5-2/5-3/5-4
* \author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_BEIDOU_B1C_PRN_H
+19 -1
View File
@@ -1,9 +1,18 @@
/*!
* \file Beidou_CNAV1.h
* \brief B-CNAV1 navigation message constants (BDS-SIS-ICD-B1C-1.0 §6.2, §7)
* \author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_BEIDOU_CNAV1_H
#define GNSS_SDR_BEIDOU_CNAV1_H
@@ -28,18 +37,27 @@ constexpr double GNSS_SDR_TWO_N32 = 2.3283064365386963e-10;
#ifndef GNSS_SDR_TWO_N34
constexpr double GNSS_SDR_TWO_N34 = 5.820766091346741e-11;
#endif
#ifndef GNSS_SDR_TWO_N35
constexpr double GNSS_SDR_TWO_N35 = 2.910383045673370e-11;
#endif
#ifndef GNSS_SDR_TWO_N44
constexpr double GNSS_SDR_TWO_N44 = 5.684341886080802e-14;
#endif
#ifndef GNSS_SDR_TWO_N50
constexpr double GNSS_SDR_TWO_N50 = 8.881784197001252e-16;
#endif
#ifndef GNSS_SDR_TWO_N51
constexpr double GNSS_SDR_TWO_N51 = 4.440892098500626e-16;
#endif
#ifndef GNSS_SDR_TWO_N57
constexpr double GNSS_SDR_TWO_N57 = 6.938893903907228e-18;
#endif
#ifndef GNSS_SDR_TWO_N66
constexpr double GNSS_SDR_TWO_N66 = 1.3552527156068805e-20;
#endif
#ifndef GNSS_SDR_TWO_N68
constexpr double GNSS_SDR_TWO_N68 = 3.3881317890172013e-21;
#endif
constexpr int32_t BEIDOU_CNAV1_FRAME_SYMBOLS = 1800;
constexpr int32_t BEIDOU_CNAV1_FRAME_PERIOD_S = 18;
+10 -1
View File
@@ -1,9 +1,18 @@
/*!
* \file beidou_bdgim.cc
* \brief BeiDou Global Ionospheric delay Model (BDGIM), BDS-SIS-ICD-B1C-1.0 §7.8
* \author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "beidou_bdgim.h"
#include "MATH_CONSTANTS.h"
#include <cmath>
+10 -1
View File
@@ -1,9 +1,18 @@
/*!
* \file beidou_bdgim.h
* \brief BeiDou Global Ionospheric delay Model (BDGIM), BDS-SIS-ICD-B1C-1.0 §7.8
* \author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_BEIDOU_BDGIM_H
#define GNSS_SDR_BEIDOU_BDGIM_H
@@ -1 +1,16 @@
/*!
* \file beidou_cnav1_ephemeris.cc
* \brief BeiDou B-CNAV1 ephemeris storage (BDS-SIS-ICD-B1C-1.0 §7)
* \author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "beidou_cnav1_ephemeris.h"
@@ -1,13 +1,18 @@
/*!
* \file beidou_cnav1_ephemeris.h
* \brief BeiDou B-CNAV1 ephemeris storage (BDS-SIS-ICD-B1C-1.0 §7)
* \author GNSS-SDR contributors
* \author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_BEIDOU_CNAV1_EPHEMERIS_H
#define GNSS_SDR_BEIDOU_CNAV1_EPHEMERIS_H
+10 -1
View File
@@ -1,9 +1,18 @@
/*!
* \file beidou_cnav1_iono.h
* \brief BeiDou B-CNAV1 ionospheric model (§7.8)
* \author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_BEIDOU_CNAV1_IONO_H
#define GNSS_SDR_BEIDOU_CNAV1_IONO_H
@@ -1,9 +1,18 @@
/*!
* \file beidou_cnav1_ldpc.cc
* \brief B-CNAV1 LDPC graph and non-binary BP decoder over GF(64)
* \author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "beidou_cnav1_ldpc.h"
#include <algorithm>
#include <array>
+10 -1
View File
@@ -1,9 +1,18 @@
/*!
* \file beidou_cnav1_ldpc.h
* \brief B-CNAV1 NB-LDPC decoder (BDS-SIS-ICD-B1C-1.0 §6.2.2, Appendix)
* \author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_BEIDOU_CNAV1_LDPC_DECODER_H
#define GNSS_SDR_BEIDOU_CNAV1_LDPC_DECODER_H
@@ -1,9 +1,18 @@
/*!
* \file beidou_cnav1_navigation_message.cc
* \brief B-CNAV1 navigation message parser (BDS-SIS-ICD-B1C-1.0 §6.2)
* \author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "beidou_cnav1_navigation_message.h"
#include "Beidou_B1C.h"
#include "Beidou_CNAV1.h"
@@ -469,15 +478,16 @@ void parse_page1(
{
int32_t offset = 0;
parse_page_common(bits, true, true, page_data, offset);
iono.alpha1 = static_cast<double>(read_signed(bits, offset, 10)) * 0.125;
// ICD Table 7-10
iono.alpha1 = static_cast<double>(read_unsigned(bits, offset, 10)) * 0.125;
offset += 10;
iono.alpha2 = static_cast<double>(read_signed(bits, offset, 8)) * 0.125;
offset += 8;
iono.alpha3 = static_cast<double>(read_signed(bits, offset, 8)) * 0.125;
iono.alpha3 = static_cast<double>(read_unsigned(bits, offset, 8)) * 0.125;
offset += 8;
iono.alpha4 = static_cast<double>(read_signed(bits, offset, 8)) * 0.125;
iono.alpha4 = static_cast<double>(read_unsigned(bits, offset, 8)) * 0.125;
offset += 8;
iono.alpha5 = static_cast<double>(read_signed(bits, offset, 8)) * 0.125;
iono.alpha5 = static_cast<double>(read_unsigned(bits, offset, 8)) * 0.125;
offset += 8;
iono.alpha6 = static_cast<double>(read_signed(bits, offset, 8)) * 0.125;
offset += 8;
@@ -488,15 +498,16 @@ void parse_page1(
iono.alpha9 = static_cast<double>(read_signed(bits, offset, 8)) * 0.125;
offset += 8;
utc.A0 = static_cast<double>(read_signed(bits, offset, 16)) * GNSS_SDR_TWO_N34;
// ICD Table 7-20 / Figure 6-17
utc.A0 = static_cast<double>(read_signed(bits, offset, 16)) * GNSS_SDR_TWO_N35;
offset += 16;
utc.A1 = static_cast<double>(read_signed(bits, offset, 13)) * GNSS_SDR_TWO_N50;
utc.A1 = static_cast<double>(read_signed(bits, offset, 13)) * GNSS_SDR_TWO_N51;
offset += 13;
utc.A2 = static_cast<double>(read_signed(bits, offset, 7)) * GNSS_SDR_TWO_N66;
utc.A2 = static_cast<double>(read_signed(bits, offset, 7)) * GNSS_SDR_TWO_N68;
offset += 7;
utc.delta_t_LSF = static_cast<int32_t>(read_signed(bits, offset, 8));
utc.delta_t_LS = static_cast<int32_t>(read_signed(bits, offset, 8));
offset += 8;
utc.tot = static_cast<int32_t>(read_unsigned(bits, offset, 16));
utc.tot = static_cast<int32_t>(read_unsigned(bits, offset, 16)) * 16; // scale 2^4
offset += 16;
utc.WN_t = static_cast<int32_t>(read_unsigned(bits, offset, 13));
offset += 13;
@@ -504,7 +515,7 @@ void parse_page1(
offset += 13;
utc.DN = static_cast<int32_t>(read_unsigned(bits, offset, 3));
offset += 3;
(void)read_signed(bits, offset, 8);
utc.delta_t_LSF = static_cast<int32_t>(read_signed(bits, offset, 8));
}
void parse_page2(const uint8_t* bits, Bds3_B1c_PageData& page_data)
@@ -628,13 +639,12 @@ bool Beidou_Cnav1_Navigation_Message::decode_frame(
// Reverse bit order within each 6-LLR GF(64) symbol; inv applies the same polarity flip.
for (int32_t symbol = 0; symbol < BEIDOU_CNAV1_SUBFRAME2_SYMBOLS / 6; symbol++)
{
const int32_t base = symbol * 6;
const auto base = static_cast<size_t>(symbol) * 6U;
for (int32_t bit = 0; bit < 6; bit++)
{
sf2_llr_bitrev[static_cast<size_t>(base + bit)] =
sf2_llr[static_cast<size_t>(base + (5 - bit))];
sf2_llr_bitrev_inv[static_cast<size_t>(base + bit)] =
-sf2_llr_bitrev[static_cast<size_t>(base + bit)];
const auto bit_u = static_cast<size_t>(bit);
sf2_llr_bitrev[base + bit_u] = sf2_llr[base + (5U - bit_u)];
sf2_llr_bitrev_inv[base + bit_u] = -sf2_llr_bitrev[base + bit_u];
}
}
// SF3: same four LLR variants as SF2.
@@ -644,13 +654,12 @@ bool Beidou_Cnav1_Navigation_Message::decode_frame(
}
for (int32_t symbol = 0; symbol < BEIDOU_CNAV1_SUBFRAME3_SYMBOLS / 6; symbol++)
{
const int32_t base = symbol * 6;
const auto base = static_cast<size_t>(symbol) * 6U;
for (int32_t bit = 0; bit < 6; bit++)
{
sf3_llr_bitrev[static_cast<size_t>(base + bit)] =
sf3_llr[static_cast<size_t>(base + (5 - bit))];
sf3_llr_bitrev_inv[static_cast<size_t>(base + bit)] =
-sf3_llr_bitrev[static_cast<size_t>(base + bit)];
const auto bit_u = static_cast<size_t>(bit);
sf3_llr_bitrev[base + bit_u] = sf3_llr[base + (5U - bit_u)];
sf3_llr_bitrev_inv[base + bit_u] = -sf3_llr_bitrev[base + bit_u];
}
}
@@ -742,17 +751,23 @@ bool Beidou_Cnav1_Navigation_Message::decode_frame(
return false;
}
parse_subframe2(sf2_data.data(), ephemeris_, soh_seconds);
#if 0
if (ephemeris_.sqrtA < 5000.0 || ephemeris_.sqrtA > 6700.0)
// Parse SF2 into a candidate so a mismatched IODE/IODC pair cannot overwrite a good set.
Beidou_Cnav1_Ephemeris candidate{};
parse_subframe2(sf2_data.data(), candidate, soh_seconds);
candidate.PRN = static_cast<int32_t>(prn);
// ICD B1C §7.4.3: usable only when IODE equals IODC low 8 bits.
const auto iode = static_cast<uint32_t>(candidate.IODE);
const auto iodc_lo = static_cast<uint32_t>(candidate.IODC) & 0xFFU;
tow_s_ = static_cast<double>(candidate.tow);
if (iode != iodc_lo)
{
set_fail(7);
return false;
// Frame CRC is OK; do not publish unmatched eph/clock (keep prior ephemeris_).
}
else
{
ephemeris_ = candidate;
flag_new_ephemeris_ = true;
}
#endif
ephemeris_.PRN = static_cast<int32_t>(prn);
tow_s_ = static_cast<double>(ephemeris_.tow);
flag_new_ephemeris_ = true;
if (sf3_crc_ok)
{
@@ -1,9 +1,18 @@
/*!
* \file beidou_cnav1_navigation_message.h
* \brief B-CNAV1 navigation message parser (§6.2)
* \author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_BEIDOU_CNAV1_NAVIGATION_MESSAGE_H
#define GNSS_SDR_BEIDOU_CNAV1_NAVIGATION_MESSAGE_H
@@ -1,12 +1,23 @@
/*!
* \file beidou_cnav1_utc_model.h
* \brief BeiDou B-CNAV1 UTC model (§7.12)
* \brief BeiDou B-CNAV1 UTC model (BDS-SIS-ICD-B1C-1.0 §7.12)
* \author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_BEIDOU_CNAV1_UTC_MODEL_H
#define GNSS_SDR_BEIDOU_CNAV1_UTC_MODEL_H
#include <cstdint>
class Beidou_Cnav1_Utc_Model
{
public:
@@ -17,7 +28,8 @@ public:
int32_t WN_t{};
int32_t WN_LSF{};
int32_t DN{};
int32_t delta_t_LSF{};
int32_t delta_t_LS{}; //!< Table 7-20: leap seconds before new leap effective [s]
int32_t delta_t_LSF{}; //!< Table 7-20: leap seconds after new leap effective [s]
bool valid{};
};
@@ -1,21 +1,19 @@
/*!
* \file beidou_b1c_pcps_acquisition_test.cc
* \brief Unit tests for BeidouB1cPcpsAmbiguousAcquisition
* \author GNSS-SDR contributors
* \brief Unit tests for BeiDou B1C PCPS acquisition via PcpsAcquisitionAdapter
* \author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2020 (see AUTHORS file for a list of contributors)
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "acquisition_interface.h"
#include "beidou_b1c_pcps_ambiguous_acquisition.h"
#include "gnss_block_factory.h"
#include "gnss_block_interface.h"
#include "in_memory_configuration.h"
@@ -1,19 +1,18 @@
/*!
* \file beidou_b1c_signal_replica_test.cc
* \brief Unit tests for BeiDou B1C signal replica generation
* \author GNSS-SDR contributors
* \author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2020 (see AUTHORS file for a list of contributors)
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "Beidou_B1C.h"
#include "beidou_b1c_signal_replica.h"
#include <gtest/gtest.h>
@@ -1,9 +1,18 @@
/*!
* \file beidou_b1c_dll_pll_veml_tracking_test.cc
* \brief Unit tests for BeiDou B1C DLL+PLL VEML tracking via DllPllTrackingAdapter
* \author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "beidou_b1c_dll_pll_veml_tracking.h"
#include "gnss_block_factory.h"
#include "gnss_block_interface.h"
#include "in_memory_configuration.h"
@@ -1,9 +1,18 @@
/*!
* \file beidou_b1c_pvt_helpers_test.cc
* \brief Unit tests for B1C PVT helpers (TGD/ISC, seleph, band/wavelength map)
* \author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "MATH_CONSTANTS.h"
#include "gnss_frequencies.h"
#include "gnss_obs_codes.h"
@@ -164,3 +173,43 @@ TEST(BeidouB1cPvtHelpersTest, GettgdPrefersMatchingEphTypeWhenBothPresent)
EXPECT_NEAR(tgd_b1c, SPEED_OF_LIGHT_M_S * 3.0e-9, 1.0e-6);
EXPECT_NEAR(tgd_b1i, SPEED_OF_LIGHT_M_S * 5.0e-9, 1.0e-6);
}
TEST(BeidouB1cPvtHelpersTest, GettgdReturnsZeroWhenOnlyWrongEphFamilyPresent)
{
const int sat = NSATGPS + NSATGLO + NSATGAL + NSATQZS + 22;
const double ep[] = {2021, 3, 1, 0, 0, 0};
const gtime_t t0 = epoch2time(ep);
std::vector<eph_t> ephs;
ephs.push_back(make_bds_eph(sat, 1, 5.0e-9, 0.0, t0)); /* DNAV only */
nav_t nav;
std::memset(&nav, 0, sizeof(nav));
nav.eph = ephs.data();
nav.n = 1;
/* B1C obs must not fall back to DNAV TGD1 */
EXPECT_DOUBLE_EQ(gettgd(sat, &nav, static_cast<unsigned char>(CODE_L1D)), 0.0);
EXPECT_DOUBLE_EQ(gettgd(sat, &nav, static_cast<unsigned char>(CODE_L1P)), 0.0);
}
/* CODE_L1P is shared with GPS L1P — CNAV1 filtering must be SYS_BDS-only. */
TEST(BeidouB1cPvtHelpersTest, GettgdGpsIgnoresB1cCodeSelection)
{
const int sat = satno(SYS_GPS, 5);
const double ep[] = {2020, 1, 1, 0, 0, 0};
const gtime_t t0 = epoch2time(ep);
eph_t e{};
std::memset(&e, 0, sizeof(e));
e.sat = sat;
e.code = 0; /* LNAV, not CNAV1 */
e.tgd[0] = 4.0e-9;
e.toe = t0;
e.toc = t0;
nav_t nav;
std::memset(&nav, 0, sizeof(nav));
nav.eph = &e;
nav.n = 1;
EXPECT_NEAR(gettgd(sat, &nav, static_cast<unsigned char>(CODE_L1C)), SPEED_OF_LIGHT_M_S * 4.0e-9, 1.0e-6);
/* Must still return GPS TGD even though CODE_L1P is also used for B1C pilot */
EXPECT_NEAR(gettgd(sat, &nav, static_cast<unsigned char>(CODE_L1P)), SPEED_OF_LIGHT_M_S * 4.0e-9, 1.0e-6);
}
@@ -1,9 +1,18 @@
/*!
* \file beidou_bdgim_test.cc
* \brief Smoke tests for BDGIM ionospheric model
* \author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "MATH_CONSTANTS.h"
#include "beidou_bdgim.h"
#include "gnss_frequencies.h"
@@ -1,9 +1,18 @@
/*!
* \file beidou_cnav1_ldpc_test.cc
* \brief Unit tests for B-CNAV1 NB-LDPC decoder
* \author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "Beidou_CNAV1.h"
#include "beidou_cnav1_ldpc.h"
#include <gtest/gtest.h>
@@ -1,9 +1,18 @@
/*!
* \file beidou_cnav1_navigation_message_test.cc
* \brief Unit tests for B-CNAV1 navigation message parser
* \author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "Beidou_CNAV1.h"
#include "beidou_cnav1_navigation_message.h"
#include "rtklib_conversions.h"
@@ -164,5 +173,67 @@ TEST(BeidouCnav1NavigationMessageTest, DecodeEphemerisFromZeroSf2Payload)
EXPECT_EQ(rtklib_eph.code, 7);
/* Adot/ndot retained through parse → RTKLIB conversion (may be zero in synthetic frame) */
EXPECT_DOUBLE_EQ(rtklib_eph.Adot, nav.get_ephemeris().Adot);
EXPECT_DOUBLE_EQ(rtklib_eph.ndot, nav.get_ephemeris().delta_n_dot);
EXPECT_DOUBLE_EQ(rtklib_eph.ndot, nav.get_ephemeris().delta_n0dot);
}
TEST(BeidouCnav1NavigationMessageTest, RejectsEphemerisWhenIodeIodcMismatch)
{
// Build SF2 with IODC=0x101 (low 8 = 0x01) and IODE=0x02 → §7.4.3 mismatch.
std::vector<uint8_t> sf2_bits(static_cast<size_t>(BEIDOU_CNAV1_SUBFRAME2_SYMBOLS), 0U);
// WN=0, HOW=0 already zero; set IODC at bit 21 (13+8), 10 bits = 0x101
const int32_t iodc_offset = 13 + 8;
const uint32_t iodc = 0x101U;
for (int32_t b = 0; b < 10; b++)
{
sf2_bits[static_cast<size_t>(iodc_offset + b)] = static_cast<uint8_t>((iodc >> static_cast<uint32_t>(9 - b)) & 1U);
}
// IODE at bit 31, 8 bits = 0x02
const int32_t iode_offset = iodc_offset + 10;
const uint32_t iode = 0x02U;
for (int32_t b = 0; b < 8; b++)
{
sf2_bits[static_cast<size_t>(iode_offset + b)] = static_cast<uint8_t>((iode >> static_cast<uint32_t>(7 - b)) & 1U);
}
// SatType MEO = 0b11 at toe(11)+offset after IODE
const int32_t sat_type_offset = iode_offset + 8 + 11;
sf2_bits[static_cast<size_t>(sat_type_offset)] = 1U;
sf2_bits[static_cast<size_t>(sat_type_offset + 1)] = 1U;
// Append valid CRC24Q over first 576 bits (zeros+fields above) — use decoder's CRC by
// zeroing CRC field and computing via a local copy of the same polynomial as the parser.
auto crc24q_bits = [](const uint8_t* bits, int32_t num_bits) -> uint32_t {
uint32_t crc = 0;
const uint32_t POLY = 0x864CFBU;
for (int32_t i = 0; i < num_bits; i++)
{
uint32_t msb = (crc >> 23) & 1U;
uint32_t bit = (bits[i] != 0U) ? 1U : 0U;
crc = (crc << 1) & 0xFFFFFFU;
if (msb ^ bit)
{
crc ^= POLY;
}
}
return crc;
};
const uint32_t crc = crc24q_bits(sf2_bits.data(), BEIDOU_CNAV1_SF2_DATA_BITS - BEIDOU_CNAV1_CRC_BITS);
for (int32_t b = 0; b < BEIDOU_CNAV1_CRC_BITS; b++)
{
sf2_bits[static_cast<size_t>(BEIDOU_CNAV1_SF2_DATA_BITS - BEIDOU_CNAV1_CRC_BITS + b)] =
static_cast<uint8_t>((crc >> static_cast<uint32_t>(23 - b)) & 1U);
}
const auto sf2_llr = encode_bits_to_llr(sf2_bits);
const auto sf3_llr = encode_bits_to_llr(std::vector<uint8_t>(BEIDOU_CNAV1_SUBFRAME3_SYMBOLS, 0U));
std::vector<float> interleaved;
deinterleave_like_icd(sf2_llr, sf3_llr, interleaved);
std::vector<float> frame;
append_bch21_6(frame, 19U);
append_bch51_8(frame, 1U);
frame.insert(frame.end(), interleaved.begin(), interleaved.end());
Beidou_Cnav1_Navigation_Message nav;
ASSERT_TRUE(nav.decode_frame_symbols(frame.data(), BEIDOU_CNAV1_FRAME_SYMBOLS, 19));
EXPECT_FALSE(nav.have_new_ephemeris());
}
+32 -2
View File
@@ -1,5 +1,19 @@
#!/usr/bin/env python3
"""Generate Beidou_B1C_codes.h from ICD Weil parameters in Beidou_B1C_prn.h."""
"""
Generate Beidou_B1C_codes.h from ICD Weil parameters in Beidou_B1C_prn.h.
-----------------------------------------------------------------------------
GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
This file is part of GNSS-SDR.
Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
SPDX-License-Identifier: GPL-3.0-or-later
Author: Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
-----------------------------------------------------------------------------
"""
from __future__ import annotations
@@ -78,7 +92,23 @@ def main() -> None:
expected = "101011111111011001001110"
assert "".join(str(b) for b in weil_code(L10243, 10243, data_w[0], data_p[0], 10230)[:24]) == expected
header = f"""/* Auto-generated by {Path(__file__).name}. Do not edit manually. */
header = f"""/*!
* \\file Beidou_B1C_codes.h
* \\brief Precomputed BeiDou B1C primary and secondary codes
* \\author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
*
* Auto-generated by {Path(__file__).name}. Do not edit manually.
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_BEIDOU_B1C_CODES_H
#define GNSS_SDR_BEIDOU_B1C_CODES_H
+32 -2
View File
@@ -1,5 +1,19 @@
#!/usr/bin/env python3
"""Generate Beidou_CNAV1_ldpc.h from BDS-SIS-ICD-B1C-1.0 PDF text."""
"""
Generate Beidou_CNAV1_ldpc.h from BDS-SIS-ICD-B1C-1.0 PDF text.
-----------------------------------------------------------------------------
GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
This file is part of GNSS-SDR.
Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
SPDX-License-Identifier: GPL-3.0-or-later
Author: Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
-----------------------------------------------------------------------------
"""
from __future__ import annotations
@@ -110,7 +124,23 @@ def main() -> None:
exp, log = gf64_tables()
header: list[str] = [
"/* Auto-generated by utils/generate_beidou_cnav1_ldpc.py. Do not edit manually. */",
"/*!",
" * \\file Beidou_CNAV1_ldpc.h",
" * \\brief Precomputed B-CNAV1 NB-LDPC matrices and GF(64) LUTs",
" * \\author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn",
" *",
" * Auto-generated by utils/generate_beidou_cnav1_ldpc.py. Do not edit manually.",
" *",
" * -----------------------------------------------------------------------------",
" *",
" * GNSS-SDR is a Global Navigation Satellite System software-defined receiver.",
" * This file is part of GNSS-SDR.",
" *",
" * Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)",
" * SPDX-" "License-Identifier: GPL-3.0-or-later",
" *",
" * -----------------------------------------------------------------------------",
" */",
"#ifndef GNSS_SDR_BEIDOU_CNAV1_LDPC_MATRICES_H",
"#define GNSS_SDR_BEIDOU_CNAV1_LDPC_MATRICES_H",
"#include <cstdint>",