mirror of
https://github.com/gnss-sdr/gnss-sdr
synced 2026-10-08 16:51:51 +00:00
Fix SOH framing/sync-loss and other questions raised by the PM.
Keep BeiDou B1C/CNAV1-focused changes; remove B1I OPT_ACQ, outlier PR gating, DNAV toe/sqrt(A) sanity, and Flag_cycle_slip move-assignment submitted separately. Signed-off-by: OuWenhao16 <104730917+OuWenhao16@users.noreply.github.com>
This commit is contained in:
@@ -21,8 +21,6 @@
|
||||
GNSS-SDR.internal_fs_sps=18000000
|
||||
GNSS-SDR.use_acquisition_resampler=true
|
||||
|
||||
ControlThread.wait_for_flowgraph=false
|
||||
|
||||
;######### SIGNAL_SOURCE CONFIG ############
|
||||
SignalSource.implementation=File_Signal_Source
|
||||
SignalSource.filename=./L1_IF20KHz_FS18MHz.bin
|
||||
@@ -86,7 +84,7 @@ Acquisition_1D.doppler_step=25
|
||||
Tracking_1D.implementation=BEIDOU_B1C_DLL_PLL_VEML_Tracking
|
||||
Tracking_1D.item_type=gr_complex
|
||||
Tracking_1D.track_pilot=true
|
||||
Tracking_1D.extend_correlation_symbols=4
|
||||
Tracking_1D.extend_correlation_symbols=1
|
||||
Tracking_1D.bit_synchronization_time_limit_s=90
|
||||
Tracking_1D.pull_in_time_s=5
|
||||
Tracking_1D.pll_bw_hz=10.0
|
||||
|
||||
@@ -3503,6 +3503,7 @@ void Rinex_Printer::log_rinex_nav_bds_dnav(const std::map<int32_t, Beidou_Dnav_E
|
||||
|
||||
void Rinex_Printer::log_rinex_nav_bds_cnav1(const std::map<int32_t, Beidou_Cnav1_Ephemeris>& new_bds_eph)
|
||||
{
|
||||
// RINEX-3 D1-style stand-in; proper CNAV1 needs RINEX 4 EPH (see header).
|
||||
auto& out = navFile;
|
||||
const auto& sys_char = satelliteSystem.at("Beidou");
|
||||
|
||||
@@ -3524,8 +3525,10 @@ void Rinex_Printer::log_rinex_nav_bds_cnav1(const std::map<int32_t, Beidou_Cnav1
|
||||
const auto wn_d = static_cast<double>(eph.WN);
|
||||
out << get_nav_broadcast_orbit(&eph.idot, nullptr, &wn_d, nullptr) << '\n';
|
||||
|
||||
const double zero_health = 0.0;
|
||||
out << get_nav_broadcast_orbit(&zero_health, &zero_health, &eph.TGD_B1Cp, &eph.TGD_B2ap) << '\n';
|
||||
// Acc left 0; health = SF3 HS; TGD1/TGD2 hold TGD_B1Cp/TGD_B2ap.
|
||||
const double sv_accuracy = 0.0;
|
||||
const auto health_d = static_cast<double>(eph.hs);
|
||||
out << get_nav_broadcast_orbit(&sv_accuracy, &health_d, &eph.TGD_B1Cp, &eph.TGD_B2ap) << '\n';
|
||||
|
||||
const auto tow_d = static_cast<double>(eph.tow);
|
||||
const auto iodc_d = eph.IODC;
|
||||
|
||||
@@ -136,7 +136,10 @@ public:
|
||||
void log_rinex_nav_bds_dnav(const std::map<int32_t, Beidou_Dnav_Ephemeris>& new_bds_eph);
|
||||
|
||||
/*!
|
||||
* \brief Print RINEX annotation for BeiDou B-CNAV1 message
|
||||
* \brief Print RINEX navigation records for BeiDou B-CNAV1 ephemerides.
|
||||
*
|
||||
* Limitation: written as RINEX-3 D1-style BDS records (TGD_B1Cp/TGD_B2ap mapped
|
||||
* into TGD1/TGD2). Proper CNAV1 support would use a RINEX 4 EPH record.
|
||||
*/
|
||||
void log_rinex_nav_bds_cnav1(const std::map<int32_t, Beidou_Cnav1_Ephemeris>& new_bds_eph);
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
* -----------------------------------------------------------------------*/
|
||||
|
||||
#include "rtklib_solver.h"
|
||||
#include "Beidou_CNAV1.h"
|
||||
#include "Beidou_DNAV.h"
|
||||
#include "Galileo_CNAV.h"
|
||||
#include "gnss_obs_codes.h"
|
||||
@@ -44,7 +45,6 @@
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
@@ -1825,9 +1825,8 @@ bool Rtklib_Solver::get_PVT(const std::map<int, Gnss_Synchro> &gnss_observables_
|
||||
if (cnav1_iter != beidou_cnav1_ephemeris_map.cend())
|
||||
{
|
||||
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);
|
||||
if (page_it != beidou_cnav1_page_data_map.cend() && page_it->second.common.hs != 0)
|
||||
if (page_it != beidou_cnav1_page_data_map.cend())
|
||||
{
|
||||
eph_data[valid_obs].svh = page_it->second.common.hs;
|
||||
}
|
||||
@@ -1835,7 +1834,7 @@ bool Rtklib_Solver::get_PVT(const std::map<int, Gnss_Synchro> &gnss_observables_
|
||||
d_obs_data[valid_obs + glo_valid_obs] = insert_obs_to_rtklib(newobs,
|
||||
gnss_observables_iter->second,
|
||||
cnav1_iter->second.WN + BEIDOU_DNAV_BDT2GPST_WEEK_NUM_OFFSET,
|
||||
d_rtklib_band_index[sig_]);
|
||||
d_rtklib_band_index.at(sig_));
|
||||
valid_obs++;
|
||||
}
|
||||
else
|
||||
@@ -1852,7 +1851,7 @@ bool Rtklib_Solver::get_PVT(const std::map<int, Gnss_Synchro> &gnss_observables_
|
||||
bool found_B1I_obs = false;
|
||||
for (int i = 0; i < valid_obs; i++)
|
||||
{
|
||||
if (eph_data[i].sat == bds_sat && eph_data[i].code != 7)
|
||||
if (eph_data[i].sat == bds_sat && eph_data[i].code != BDS_EPH_SOURCE_CNAV1)
|
||||
{
|
||||
const unsigned char c0 = d_obs_data[i + glo_valid_obs].code[0];
|
||||
if (c0 == CODE_L2I || c0 == CODE_L1I)
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
*/
|
||||
#include "beidou_b1c_signal_replica.h"
|
||||
#include "Beidou_B1C.h"
|
||||
#include "Beidou_B1C_codes.h"
|
||||
#include "gnss_signal_replica.h"
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
@@ -187,20 +188,23 @@ void beidou_b1c_code_gen_float_sampled(own::span<float> dest, const std::array<c
|
||||
|
||||
if (is_pilot && secondary_flag)
|
||||
{
|
||||
const int32_t prn_index = static_cast<int32_t>(prn) - 1;
|
||||
const char* sec_code = BEIDOU_B1C_PILOT_SECONDARY_CODE[prn_index];
|
||||
|
||||
std::vector<float> signal_with_secondary(BEIDOU_B1C_PILOT_SECONDARY_CODE_LENGTH * samples_per_code);
|
||||
for (int32_t i = 0; i < BEIDOU_B1C_PILOT_SECONDARY_CODE_LENGTH; i++)
|
||||
if (prn >= 1 && prn <= static_cast<uint32_t>(BEIDOU_B1C_NUMBER_OF_PRNS))
|
||||
{
|
||||
const float sec_chip = sec_code[i] == '0' ? 1.0F : -1.0F;
|
||||
for (uint32_t k = 0; k < samples_per_code; k++)
|
||||
const int32_t prn_index = static_cast<int32_t>(prn) - 1;
|
||||
const char* sec_code = BEIDOU_B1C_PILOT_SECONDARY_CODE[prn_index];
|
||||
|
||||
std::vector<float> signal_with_secondary(BEIDOU_B1C_PILOT_SECONDARY_CODE_LENGTH * samples_per_code);
|
||||
for (int32_t i = 0; i < BEIDOU_B1C_PILOT_SECONDARY_CODE_LENGTH; i++)
|
||||
{
|
||||
signal_with_secondary[static_cast<size_t>(i) * samples_per_code + k] = signal_b1c[k] * sec_chip;
|
||||
const float sec_chip = sec_code[i] == '0' ? 1.0F : -1.0F;
|
||||
for (uint32_t k = 0; k < samples_per_code; k++)
|
||||
{
|
||||
signal_with_secondary[static_cast<size_t>(i) * samples_per_code + k] = signal_b1c[k] * sec_chip;
|
||||
}
|
||||
}
|
||||
samples_per_code *= static_cast<uint32_t>(BEIDOU_B1C_PILOT_SECONDARY_CODE_LENGTH);
|
||||
signal_b1c = std::move(signal_with_secondary);
|
||||
}
|
||||
samples_per_code *= static_cast<uint32_t>(BEIDOU_B1C_PILOT_SECONDARY_CODE_LENGTH);
|
||||
signal_b1c = std::move(signal_with_secondary);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < samples_per_code; i++)
|
||||
@@ -262,22 +266,25 @@ void beidou_b1c_code_gen_complex_sampled(own::span<std::complex<float>> dest, co
|
||||
|
||||
if (is_pilot && secondary_flag)
|
||||
{
|
||||
const int32_t prn_index = static_cast<int32_t>(prn) - 1;
|
||||
const char* sec_code = BEIDOU_B1C_PILOT_SECONDARY_CODE[prn_index];
|
||||
|
||||
std::vector<std::complex<float>> signal_with_secondary(
|
||||
static_cast<size_t>(BEIDOU_B1C_PILOT_SECONDARY_CODE_LENGTH) * samples_per_code);
|
||||
for (int32_t i = 0; i < BEIDOU_B1C_PILOT_SECONDARY_CODE_LENGTH; i++)
|
||||
if (prn >= 1 && prn <= static_cast<uint32_t>(BEIDOU_B1C_NUMBER_OF_PRNS))
|
||||
{
|
||||
const float sec_chip = sec_code[i] == '0' ? 1.0F : -1.0F;
|
||||
for (uint32_t k = 0; k < samples_per_code; k++)
|
||||
const int32_t prn_index = static_cast<int32_t>(prn) - 1;
|
||||
const char* sec_code = BEIDOU_B1C_PILOT_SECONDARY_CODE[prn_index];
|
||||
|
||||
std::vector<std::complex<float>> signal_with_secondary(
|
||||
static_cast<size_t>(BEIDOU_B1C_PILOT_SECONDARY_CODE_LENGTH) * samples_per_code);
|
||||
for (int32_t i = 0; i < BEIDOU_B1C_PILOT_SECONDARY_CODE_LENGTH; i++)
|
||||
{
|
||||
signal_with_secondary[static_cast<size_t>(i) * samples_per_code + k] =
|
||||
signal_b1c[k] * sec_chip;
|
||||
const float sec_chip = sec_code[i] == '0' ? 1.0F : -1.0F;
|
||||
for (uint32_t k = 0; k < samples_per_code; k++)
|
||||
{
|
||||
signal_with_secondary[static_cast<size_t>(i) * samples_per_code + k] =
|
||||
signal_b1c[k] * sec_chip;
|
||||
}
|
||||
}
|
||||
samples_per_code *= static_cast<uint32_t>(BEIDOU_B1C_PILOT_SECONDARY_CODE_LENGTH);
|
||||
signal_b1c = std::move(signal_with_secondary);
|
||||
}
|
||||
samples_per_code *= static_cast<uint32_t>(BEIDOU_B1C_PILOT_SECONDARY_CODE_LENGTH);
|
||||
signal_b1c = std::move(signal_with_secondary);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < samples_per_code; i++)
|
||||
|
||||
@@ -730,7 +730,7 @@ eph_t eph_to_rtklib(const Beidou_Cnav1_Ephemeris& bei_eph)
|
||||
rtklib_sat.e = bei_eph.ecc;
|
||||
rtklib_sat.Adot = bei_eph.Adot;
|
||||
rtklib_sat.ndot = bei_eph.delta_ndot;
|
||||
rtklib_sat.svh = 0;
|
||||
rtklib_sat.svh = bei_eph.hs;
|
||||
rtklib_sat.sva = 0;
|
||||
rtklib_sat.code = bei_eph.sig_type;
|
||||
rtklib_sat.flag = bei_eph.nav_type;
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
*/
|
||||
|
||||
#include "rtklib_ephemeris.h"
|
||||
#include "Beidou_CNAV1.h"
|
||||
#include "rtklib_preceph.h"
|
||||
#include "rtklib_rtkcmn.h"
|
||||
#include "rtklib_sbas.h"
|
||||
@@ -405,7 +406,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;
|
||||
const int is_bds_cnav1 = (sys == SYS_BDS && eph->code == BDS_EPH_SOURCE_CNAV1) ? 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;
|
||||
@@ -762,7 +763,8 @@ void seph2pos(gtime_t time, const seph_t *seph, double *rs, double *dts,
|
||||
|
||||
|
||||
/* select ephemeris --------------------------------------------------------
|
||||
* bds_eph_sel: -1=any, 0=DNAV (eph.code!=7), 7=B-CNAV1 (eph.code==7)
|
||||
* bds_eph_sel: -1=any, 0=DNAV (eph.code!=BDS_EPH_SOURCE_CNAV1),
|
||||
* BDS_EPH_SOURCE_CNAV1=B-CNAV1
|
||||
*-----------------------------------------------------------------------------*/
|
||||
eph_t *seleph(gtime_t time, int sat, int iode, const nav_t *nav, int bds_eph_sel)
|
||||
{
|
||||
@@ -798,11 +800,11 @@ eph_t *seleph(gtime_t time, int sat, int iode, const nav_t *nav, int bds_eph_sel
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (sys == SYS_BDS && bds_eph_sel == 7 && nav->eph[i].code != 7)
|
||||
if (sys == SYS_BDS && bds_eph_sel == BDS_EPH_SOURCE_CNAV1 && nav->eph[i].code != BDS_EPH_SOURCE_CNAV1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (sys == SYS_BDS && bds_eph_sel == 0 && nav->eph[i].code == 7)
|
||||
if (sys == SYS_BDS && bds_eph_sel == 0 && nav->eph[i].code == BDS_EPH_SOURCE_CNAV1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -1321,7 +1323,7 @@ void satposs(gtime_t teph, const obsd_t *obs, int n, const nav_t *nav,
|
||||
const unsigned char cj = obs[i].code[j];
|
||||
if (cj == CODE_L1D || cj == CODE_L1P)
|
||||
{
|
||||
bds_eph_sel = 7;
|
||||
bds_eph_sel = BDS_EPH_SOURCE_CNAV1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ void geph2pos(gtime_t time, const geph_t *geph, double *rs, double *dts,
|
||||
double seph2clk(gtime_t time, const seph_t *seph);
|
||||
void seph2pos(gtime_t time, const seph_t *seph, double *rs, double *dts,
|
||||
double *var);
|
||||
/* bds_eph_sel: -1=any, 0=DNAV (code!=7), 7=B-CNAV1 (code==7) */
|
||||
/* bds_eph_sel: -1=any, 0=DNAV (code!=BDS_EPH_SOURCE_CNAV1), BDS_EPH_SOURCE_CNAV1=B-CNAV1 */
|
||||
eph_t *seleph(gtime_t time, int sat, int iode, const nav_t *nav, int bds_eph_sel = -1);
|
||||
geph_t *selgeph(gtime_t time, int sat, int iode, const nav_t *nav);
|
||||
seph_t *selseph(gtime_t time, int sat, const nav_t *nav);
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#endif
|
||||
|
||||
#include "rtklib_pntpos.h"
|
||||
#include "Beidou_CNAV1.h"
|
||||
#include "beidou_bdgim.h"
|
||||
#include "gnss_frequencies.h"
|
||||
#include "rtklib_ephemeris.h"
|
||||
@@ -113,8 +114,8 @@ double gettgd(int sat, const nav_t *nav)
|
||||
|
||||
/* get tgd parameter (m) -----------------------------------------------------
|
||||
* 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):
|
||||
* BDS DNAV (eph.code!=BDS_EPH_SOURCE_CNAV1): tgd[0]=TGD1 (B1I vs B3I timing reference)
|
||||
* BDS CNAV1 (eph.code==BDS_EPH_SOURCE_CNAV1), 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)
|
||||
@@ -123,7 +124,7 @@ double gettgd(int sat, const nav_t *nav)
|
||||
* CODE_L1P is also GPS/GLO L1P — CNAV1 matching is SYS_BDS only.
|
||||
* No DNAV↔CNAV1 TGD fallback (TGD1 vs TGD_B1Cp).
|
||||
*-----------------------------------------------------------------------------*/
|
||||
double gettgd(int sat, const nav_t *nav, unsigned char obs_code)
|
||||
double gettgd_bds_by_obs_code(int sat, const nav_t *nav, unsigned char obs_code)
|
||||
{
|
||||
int i;
|
||||
int prn = 0;
|
||||
@@ -139,7 +140,7 @@ double gettgd(int sat, const nav_t *nav, unsigned char obs_code)
|
||||
|
||||
if (sys == SYS_BDS)
|
||||
{
|
||||
const int is_cnav1 = (nav->eph[i].code == 7) ? 1 : 0;
|
||||
const int is_cnav1 = (nav->eph[i].code == BDS_EPH_SOURCE_CNAV1) ? 1 : 0;
|
||||
/* B1C obs ↔ CNAV1 eph; B1I/other ↔ DNAV eph */
|
||||
if (is_b1c_obs != is_cnav1)
|
||||
{
|
||||
@@ -351,7 +352,7 @@ double prange(const obsd_t *obs, const nav_t *nav, const double *azel,
|
||||
{
|
||||
if (sys == SYS_BDS)
|
||||
{
|
||||
P1_P2 = gettgd(obs->sat, nav, obs->code[i]);
|
||||
P1_P2 = gettgd_bds_by_obs_code(obs->sat, nav, obs->code[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -50,7 +50,7 @@ double varerr(const prcopt_t *opt, double el, int sys);
|
||||
double gettgd(int sat, const nav_t *nav);
|
||||
double gettgd(int sat, const nav_t *nav, int tgd_index);
|
||||
/* BDS DNAV/CNAV1 TGD selection by observation code (CODE_L1D/L1P/...) */
|
||||
double gettgd(int sat, const nav_t *nav, unsigned char obs_code);
|
||||
double gettgd_bds_by_obs_code(int sat, const nav_t *nav, unsigned char obs_code);
|
||||
|
||||
/* select Galileo BGD from observation and ephemeris provenance -------------*/
|
||||
int galileo_bgd_index(unsigned char observation_code, int sat, const nav_t *nav);
|
||||
|
||||
@@ -51,7 +51,6 @@ 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,8 +598,6 @@ 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;
|
||||
double min_abs = 1e30;
|
||||
double min_pseudorange = 0.0;
|
||||
for (it = data.begin(); it != data.end(); it++)
|
||||
{
|
||||
if (it->Flag_valid_word)
|
||||
@@ -612,11 +610,6 @@ void hybrid_observables_gs::compute_pranges(std::vector<Gnss_Synchro> &data) con
|
||||
it->RX_time = current_T_rx_TOW_s;
|
||||
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))
|
||||
{
|
||||
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';
|
||||
// std::cout << "[" << it->Channel_ID << "] Diff d_T_rx_TOW_ms - interp_TOW_ms: " << static_cast<double>(d_T_rx_TOW_ms) - it->interp_TOW_ms << '\n';
|
||||
@@ -626,17 +619,6 @@ 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,7 +43,6 @@ public:
|
||||
bool dump_mat{false};
|
||||
bool enable_E6{true};
|
||||
bool enable_monitor{false};
|
||||
bool reject_outlier_pseudoranges{true};
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
+79
-35
@@ -23,6 +23,7 @@
|
||||
#include "beidou_cnav1_iono.h"
|
||||
#include "beidou_cnav1_utc_model.h"
|
||||
#include "display.h"
|
||||
#include "dump_logger_helper.h"
|
||||
#include "gnss_sdr_make_unique.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "tlm_crc_stats.h"
|
||||
@@ -44,23 +45,12 @@
|
||||
#include <absl/log/log.h>
|
||||
#endif
|
||||
|
||||
#define CRC_ERROR_LIMIT 8
|
||||
|
||||
namespace
|
||||
{
|
||||
// ICD §7.3: SOH marks subframe-1 start. Symbols from SOH to current = N - offset.
|
||||
int32_t resolve_b1c_frame_soh_offset(int32_t matched_offset, int32_t prev_candidate_offset)
|
||||
{
|
||||
if (matched_offset >= 0)
|
||||
{
|
||||
return matched_offset;
|
||||
}
|
||||
if (matched_offset == -2 && prev_candidate_offset >= 0)
|
||||
{
|
||||
return prev_candidate_offset;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
constexpr int32_t CRC_ERROR_LIMIT = 8;
|
||||
constexpr uint64_t B1C_REACQ_INTERVAL_SYMBOLS = static_cast<uint64_t>(BEIDOU_CNAV1_FRAME_SYMBOLS);
|
||||
constexpr int32_t B1C_SCAN_OFFSET_SPAN_LOOSE = 4;
|
||||
constexpr int32_t B1C_SCAN_MAX_FULL_DECODES = 16;
|
||||
|
||||
|
||||
uint32_t compute_b1c_tow_ms_at_current_symbol(
|
||||
@@ -276,6 +266,15 @@ beidou_b1c_telemetry_decoder_gs::beidou_b1c_telemetry_decoder_gs(
|
||||
d_nav_msg_packet.system = std::string("C");
|
||||
d_nav_msg_packet.signal = std::string("1D");
|
||||
}
|
||||
if (d_dump_crc_stats)
|
||||
{
|
||||
d_Tlm_CRC_Stats = std::make_unique<Tlm_CRC_Stats>();
|
||||
d_Tlm_CRC_Stats->initialize(conf.dump_crc_stats_filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
d_Tlm_CRC_Stats = nullptr;
|
||||
}
|
||||
configure_dump_file(d_channel, d_dump, d_dump_filename, d_dump_file);
|
||||
configure_crc_stats_channel(d_channel, d_dump_crc_stats, d_Tlm_CRC_Stats);
|
||||
}
|
||||
@@ -283,10 +282,7 @@ beidou_b1c_telemetry_decoder_gs::beidou_b1c_telemetry_decoder_gs(
|
||||
|
||||
beidou_b1c_telemetry_decoder_gs::~beidou_b1c_telemetry_decoder_gs()
|
||||
{
|
||||
if (d_dump_file.is_open())
|
||||
{
|
||||
d_dump_file.close();
|
||||
}
|
||||
tlm_cleanup_and_save_files(d_dump_file, d_dump_filename, d_dump, d_dump_mat, d_remove_dat);
|
||||
}
|
||||
|
||||
|
||||
@@ -317,7 +313,6 @@ void beidou_b1c_telemetry_decoder_gs::reset()
|
||||
d_stat = 0;
|
||||
d_CRC_error_counter = 0;
|
||||
d_frame_soh_offset = 0;
|
||||
d_prev_candidate_offset = -1;
|
||||
d_prev_valid_symbol_output = false;
|
||||
d_await_post_lock_frame_decode = false;
|
||||
d_post_lock_valid_symbols = 0U;
|
||||
@@ -398,8 +393,17 @@ void beidou_b1c_telemetry_decoder_gs::publish_navigation(double cn0_db_hz)
|
||||
<< " in channel " << d_channel
|
||||
<< " PageID=" << d_nav.get_page_data().common.page_id;
|
||||
}
|
||||
// Same port as DNAV/GPS: bit chars for the nav-data monitor (not a placeholder label).
|
||||
if (d_enable_navdata_monitor && !d_nav.get_last_nav_bits().empty())
|
||||
{
|
||||
d_nav_msg_packet.prn = static_cast<int32_t>(d_satellite.get_PRN());
|
||||
d_nav_msg_packet.tow_at_current_symbol_ms = static_cast<int32_t>(d_TOW_at_current_symbol_ms);
|
||||
d_nav_msg_packet.nav_message = d_nav.get_last_nav_bits();
|
||||
const auto tmp_obj = std::make_shared<Nav_Message_Packet>(d_nav_msg_packet);
|
||||
message_port_pub(pmt::mp("Nav_msg_from_TLM"), pmt::make_any(tmp_obj));
|
||||
d_nav_msg_packet.nav_message.clear();
|
||||
}
|
||||
d_nav.clear_flags();
|
||||
d_prev_candidate_offset = -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -430,7 +434,6 @@ int beidou_b1c_telemetry_decoder_gs::general_work(
|
||||
d_post_lock_valid_symbols++;
|
||||
}
|
||||
|
||||
// Decode on lock (or when 18 s history is ready), then only on frame boundaries.
|
||||
const bool at_frame_boundary =
|
||||
d_flag_frame_sync &&
|
||||
(d_sample_counter > d_frame_sync_index) &&
|
||||
@@ -438,6 +441,11 @@ int beidou_b1c_telemetry_decoder_gs::general_work(
|
||||
const bool post_lock_frame_ready =
|
||||
d_await_post_lock_frame_decode &&
|
||||
d_post_lock_valid_symbols >= static_cast<uint32_t>(BEIDOU_CNAV1_FRAME_SYMBOLS);
|
||||
const bool periodic_reacq =
|
||||
(d_stat == 0) &&
|
||||
current_symbol.Flag_valid_symbol_output &&
|
||||
!just_locked_event &&
|
||||
((d_sample_counter % B1C_REACQ_INTERVAL_SYMBOLS) == 0U);
|
||||
bool frame_decoded = false;
|
||||
|
||||
const bool history_ready = (d_sample_counter >= static_cast<uint64_t>(BEIDOU_CNAV1_FRAME_SYMBOLS));
|
||||
@@ -446,6 +454,7 @@ int beidou_b1c_telemetry_decoder_gs::general_work(
|
||||
const bool should_try_decode =
|
||||
current_symbol.Flag_valid_symbol_output &&
|
||||
((d_stat == 0 && just_locked_event) ||
|
||||
(d_stat == 0 && periodic_reacq) ||
|
||||
(d_stat == 0 && post_lock_frame_ready && !just_locked_event) ||
|
||||
(d_stat == 2 && at_frame_boundary &&
|
||||
d_sample_counter >= d_frame_sync_index + static_cast<uint64_t>(BEIDOU_CNAV1_FRAME_SYMBOLS)));
|
||||
@@ -454,7 +463,7 @@ int beidou_b1c_telemetry_decoder_gs::general_work(
|
||||
{
|
||||
bool invert = false;
|
||||
int32_t matched_offset = -1;
|
||||
if (d_stat == 0 && just_locked_event)
|
||||
if (d_stat == 0 && (just_locked_event || periodic_reacq))
|
||||
{
|
||||
const int32_t expected_prn_scan = d_satellite.get_PRN();
|
||||
std::vector<float> history_i;
|
||||
@@ -463,8 +472,11 @@ int beidou_b1c_telemetry_decoder_gs::general_work(
|
||||
const auto sec_candidates =
|
||||
find_secondary_code_candidates(history_q, expected_prn_scan);
|
||||
const auto& candidate_offsets = sec_candidates.second;
|
||||
constexpr int32_t local_offset_span = 32;
|
||||
// Exact secondary matches need no lag search; loose matches get a small span.
|
||||
const int32_t local_offset_span =
|
||||
(sec_candidates.first >= 1799.0F) ? 0 : B1C_SCAN_OFFSET_SPAN_LOOSE;
|
||||
const auto cn0_db_hz = static_cast<float>(current_symbol.CN0_dB_hz);
|
||||
int32_t full_decode_attempts = 0;
|
||||
|
||||
if (!candidate_offsets.empty())
|
||||
{
|
||||
@@ -472,10 +484,18 @@ int beidou_b1c_telemetry_decoder_gs::general_work(
|
||||
{
|
||||
for (int32_t delta = -local_offset_span; delta <= local_offset_span; delta++)
|
||||
{
|
||||
if (full_decode_attempts >= B1C_SCAN_MAX_FULL_DECODES)
|
||||
{
|
||||
break;
|
||||
}
|
||||
const int32_t start_offset =
|
||||
(candidate_offset + delta + BEIDOU_CNAV1_FRAME_SYMBOLS) % BEIDOU_CNAV1_FRAME_SYMBOLS;
|
||||
for (int inv_i = 0; inv_i < 2; ++inv_i)
|
||||
{
|
||||
if (full_decode_attempts >= B1C_SCAN_MAX_FULL_DECODES)
|
||||
{
|
||||
break;
|
||||
}
|
||||
const bool inv = (inv_i != 0);
|
||||
if (!probe_frame_from_iq_window(
|
||||
history_i, history_q, d_nav, expected_prn_scan, start_offset, inv))
|
||||
@@ -483,6 +503,7 @@ int beidou_b1c_telemetry_decoder_gs::general_work(
|
||||
continue;
|
||||
}
|
||||
int32_t fail_stage = -1;
|
||||
++full_decode_attempts;
|
||||
if (decode_frame_from_iq_window(
|
||||
history_i, history_q, d_nav, expected_prn_scan, start_offset, inv,
|
||||
cn0_db_hz, &fail_stage))
|
||||
@@ -498,7 +519,7 @@ int beidou_b1c_telemetry_decoder_gs::general_work(
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (frame_decoded)
|
||||
if (frame_decoded || full_decode_attempts >= B1C_SCAN_MAX_FULL_DECODES)
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -510,7 +531,7 @@ int beidou_b1c_telemetry_decoder_gs::general_work(
|
||||
d_await_post_lock_frame_decode = false;
|
||||
d_post_lock_valid_symbols = 0U;
|
||||
}
|
||||
else
|
||||
else if (just_locked_event)
|
||||
{
|
||||
d_await_post_lock_frame_decode = true;
|
||||
d_post_lock_valid_symbols = 1U;
|
||||
@@ -522,6 +543,7 @@ int beidou_b1c_telemetry_decoder_gs::general_work(
|
||||
if (!frame_decoded)
|
||||
{
|
||||
frame_decoded = decode_frame_from_window(frame_window, 0, true);
|
||||
invert = frame_decoded;
|
||||
}
|
||||
if (frame_decoded)
|
||||
{
|
||||
@@ -532,11 +554,12 @@ int beidou_b1c_telemetry_decoder_gs::general_work(
|
||||
}
|
||||
else
|
||||
{
|
||||
frame_decoded = decode_frame_from_window(frame_window, d_frame_soh_offset, false);
|
||||
if (!frame_decoded)
|
||||
// Use polarity confirmed at first sync.
|
||||
invert = d_flag_PLL_180_deg_phase_locked;
|
||||
frame_decoded = decode_frame_from_window(frame_window, d_frame_soh_offset, invert);
|
||||
if (frame_decoded)
|
||||
{
|
||||
frame_decoded = decode_frame_from_window(frame_window, d_frame_soh_offset, true);
|
||||
invert = frame_decoded;
|
||||
matched_offset = d_frame_soh_offset;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -545,10 +568,14 @@ int beidou_b1c_telemetry_decoder_gs::general_work(
|
||||
d_CRC_error_counter = 0;
|
||||
d_flag_PLL_180_deg_phase_locked = invert;
|
||||
d_frame_sync_index = d_sample_counter;
|
||||
const int32_t frame_soh_offset = resolve_b1c_frame_soh_offset(matched_offset, d_prev_candidate_offset);
|
||||
const int32_t frame_soh_offset = (matched_offset >= 0) ? matched_offset : 0;
|
||||
d_TOW_at_current_symbol_ms = compute_b1c_tow_ms_at_current_symbol(
|
||||
d_nav.get_tow_s(), frame_soh_offset, d_symbol_duration_ms);
|
||||
d_flag_valid_word = true;
|
||||
if (d_dump_crc_stats && d_Tlm_CRC_Stats)
|
||||
{
|
||||
d_Tlm_CRC_Stats->update_CRC_stats(true);
|
||||
}
|
||||
publish_navigation(current_symbol.CN0_dB_hz);
|
||||
|
||||
if (!d_flag_frame_sync)
|
||||
@@ -573,6 +600,10 @@ int beidou_b1c_telemetry_decoder_gs::general_work(
|
||||
else if (d_stat == 2)
|
||||
{
|
||||
d_CRC_error_counter++;
|
||||
if (d_dump_crc_stats && d_Tlm_CRC_Stats)
|
||||
{
|
||||
d_Tlm_CRC_Stats->update_CRC_stats(false);
|
||||
}
|
||||
if (d_CRC_error_counter > CRC_ERROR_LIMIT)
|
||||
{
|
||||
DLOG(INFO) << "B-CNAV1 frame sync lost for satellite " << d_satellite
|
||||
@@ -582,6 +613,8 @@ int beidou_b1c_telemetry_decoder_gs::general_work(
|
||||
d_frame_soh_offset = 0;
|
||||
d_stat = 0;
|
||||
d_CRC_error_counter = 0;
|
||||
d_await_post_lock_frame_decode = false;
|
||||
d_post_lock_valid_symbols = 0U;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -607,15 +640,26 @@ int beidou_b1c_telemetry_decoder_gs::general_work(
|
||||
}
|
||||
|
||||
consume_each(1);
|
||||
if (d_flag_frame_sync && d_frame_soh_offset > 0)
|
||||
{
|
||||
d_frame_soh_offset--;
|
||||
}
|
||||
|
||||
if (d_flag_valid_word)
|
||||
{
|
||||
current_symbol.TOW_at_current_symbol_ms = d_TOW_at_current_symbol_ms;
|
||||
current_symbol.Flag_valid_word = true;
|
||||
if (d_dump)
|
||||
{
|
||||
try
|
||||
{
|
||||
write_value(d_dump_file, static_cast<double>(d_TOW_at_current_symbol_ms) / 1000.0);
|
||||
write_value(d_dump_file, current_symbol.Tracking_sample_counter);
|
||||
write_value(d_dump_file, static_cast<double>(d_TOW_at_current_symbol_ms) / 1000.0);
|
||||
write_value(d_dump_file, current_symbol.Prompt_I > 0.0 ? 1 : -1);
|
||||
write_value(d_dump_file, static_cast<int32_t>(current_symbol.PRN));
|
||||
}
|
||||
catch (const std::ofstream::failure& e)
|
||||
{
|
||||
LOG(WARNING) << "Exception writing Telemetry BeiDou B1C dump file " << e.what();
|
||||
}
|
||||
}
|
||||
if (d_tow_to_trk)
|
||||
{
|
||||
const auto tow_obj = std::make_shared<TOW_to_trk>(TOW_to_trk(
|
||||
|
||||
@@ -75,7 +75,6 @@ private:
|
||||
bool d_prev_valid_symbol_output{false};
|
||||
bool d_await_post_lock_frame_decode{false};
|
||||
uint32_t d_post_lock_valid_symbols{0U};
|
||||
int32_t d_prev_candidate_offset{-1};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -152,14 +152,34 @@ void check_and_configure_trk_params(const ConfigurationInterface* configuration,
|
||||
|
||||
if (sig_flag == BDS_B1C)
|
||||
{
|
||||
// Default tracking QMBOC flag from Acquisition_1D.qmboc.
|
||||
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);
|
||||
std::string acq_role = role;
|
||||
const std::string tracking_prefix = "Tracking_";
|
||||
if (acq_role.compare(0, tracking_prefix.size(), tracking_prefix) == 0)
|
||||
{
|
||||
acq_role.replace(0, tracking_prefix.size(), "Acquisition_");
|
||||
}
|
||||
// Tracking_*.qmboc overrides Acquisition_*.qmboc; else inherit / default.
|
||||
const std::string acq_qmboc_key = acq_role + ".qmboc";
|
||||
const std::string trk_qmboc_key = role + ".qmboc";
|
||||
const bool acq_qmboc = configuration->property(acq_qmboc_key, trk_params.qmboc);
|
||||
trk_params.qmboc = configuration->property(trk_qmboc_key, acq_qmboc);
|
||||
if (configuration->is_present(acq_qmboc_key) && configuration->is_present(trk_qmboc_key) && acq_qmboc != trk_params.qmboc)
|
||||
{
|
||||
std::cout << TEXT_RED << "WARNING: BeiDou B1C: " << acq_qmboc_key << "=" << (acq_qmboc ? "true" : "false")
|
||||
<< " differs from " << trk_qmboc_key << "=" << (trk_params.qmboc ? "true" : "false")
|
||||
<< "; using tracking value." << TEXT_RESET << '\n';
|
||||
}
|
||||
|
||||
// Pilot secondary sync needs a full 18 s frame; disable extended coherent integration.
|
||||
// Pilot secondary sync needs one-symbol correlation.
|
||||
if (trk_params.track_pilot && trk_params.extend_correlation_symbols != 1)
|
||||
{
|
||||
std::cout << TEXT_RED << "WARNING: BeiDou B1C: extend_correlation_symbols forced to 1 while tracking the pilot "
|
||||
<< "(secondary-code sync requires one-symbol correlation). Configured value was "
|
||||
<< trk_params.extend_correlation_symbols << TEXT_RESET << '\n';
|
||||
trk_params.extend_correlation_symbols = 1;
|
||||
}
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "dll_pll_veml_tracking.h"
|
||||
#include "Beidou_B1C.h"
|
||||
#include "Beidou_B1C_codes.h"
|
||||
#include "Beidou_B1I.h"
|
||||
#include "Beidou_B3I.h"
|
||||
#include "GLONASS_L1_L2_CA.h"
|
||||
@@ -460,7 +461,7 @@ dll_pll_veml_tracking::dll_pll_veml_tracking(const Dll_Pll_Conf &conf_)
|
||||
d_code_length_chips = static_cast<int32_t>(BEIDOU_B1C_CODE_LENGTH_CHIPS);
|
||||
d_symbols_per_bit = BEIDOU_B1C_SYMBOLS_PER_BIT;
|
||||
d_correlation_length_ms = static_cast<int32_t>(BEIDOU_B1C_CODE_PERIOD_MS);
|
||||
d_code_samples_per_chip = d_trk_parameters.b1c_qmboc_tracking ? 12 : 2;
|
||||
d_code_samples_per_chip = d_trk_parameters.qmboc ? 12 : 2;
|
||||
d_veml = true;
|
||||
d_trk_parameters.spc = d_trk_parameters.early_late_space_chips;
|
||||
d_trk_parameters.slope = static_cast<float>(-CalculateSlopeAbs(&SinBocCorrelationFunction<1, 1>, d_trk_parameters.spc));
|
||||
@@ -987,10 +988,10 @@ void dll_pll_veml_tracking::start_tracking()
|
||||
const std::array<char, 3> pilot_signal = {{'1', 'P', '\0'}};
|
||||
// Pilot BOC(1,1) for the real multicorrelator (QMBOC is acquisition-only).
|
||||
beidou_b1c_code_gen_float_sampled(
|
||||
d_tracking_code, pilot_signal, d_trk_parameters.b1c_qmboc_tracking,
|
||||
d_tracking_code, pilot_signal, d_trk_parameters.qmboc,
|
||||
d_acquisition_gnss_synchro->PRN, b1c_replica_fs, 0, false);
|
||||
beidou_b1c_code_gen_float_sampled(
|
||||
d_data_code, Signal_, d_trk_parameters.b1c_qmboc_tracking,
|
||||
d_data_code, Signal_, d_trk_parameters.qmboc,
|
||||
d_acquisition_gnss_synchro->PRN, b1c_replica_fs, 0, false);
|
||||
d_Prompt_Data[0] = gr_complex(0.0, 0.0);
|
||||
d_multicorrelator_cpu.set_data_code_and_prompt_tap(
|
||||
@@ -1000,7 +1001,7 @@ void dll_pll_veml_tracking::start_tracking()
|
||||
else
|
||||
{
|
||||
beidou_b1c_code_gen_float_sampled(
|
||||
d_tracking_code, Signal_, d_trk_parameters.b1c_qmboc_tracking,
|
||||
d_tracking_code, Signal_, d_trk_parameters.qmboc,
|
||||
d_acquisition_gnss_synchro->PRN, b1c_replica_fs, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ void Dll_Pll_Conf::SetFromConfiguration(const ConfigurationInterface *configurat
|
||||
bs_tentative_events_required = configuration->property(role + ".bs_tentative_events_required", bs_tentative_events_required);
|
||||
bs_use_phase_dot_detector = configuration->property(role + ".bs_use_phase_dot_detector", bs_use_phase_dot_detector);
|
||||
|
||||
b1c_qmboc_tracking = configuration->property(role + ".b1c_qmboc_tracking", b1c_qmboc_tracking);
|
||||
qmboc = configuration->property(role + ".qmboc", qmboc);
|
||||
b1c_prompt_use_data_q = configuration->property(role + ".b1c_prompt_use_data_q", b1c_prompt_use_data_q);
|
||||
b1c_prompt_normalize_power = configuration->property(role + ".b1c_prompt_normalize_power", b1c_prompt_normalize_power);
|
||||
b1c_data_prompt_scale = configuration->property(role + ".b1c_data_prompt_scale", b1c_data_prompt_scale);
|
||||
|
||||
@@ -91,7 +91,7 @@ public:
|
||||
bool tow_to_trk{false};
|
||||
bool bs_use_phase_dot_detector{true};
|
||||
// BeiDou B1C-specific options.
|
||||
bool b1c_qmboc_tracking{true};
|
||||
bool qmboc{true}; //!< QMBOC local replica (same key as Acquisition_*.qmboc).
|
||||
bool b1c_prompt_use_data_q{true};
|
||||
bool b1c_prompt_normalize_power{true};
|
||||
float b1c_data_prompt_scale{1.7320508F}; // sqrt(3), compensates 1:3 data:pilot power.
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#ifndef GNSS_SDR_BEIDOU_B1C_H
|
||||
#define GNSS_SDR_BEIDOU_B1C_H
|
||||
|
||||
#include "Beidou_B1C_codes.h"
|
||||
#include "gnss_frequencies.h"
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -35,6 +35,9 @@ constexpr int32_t BEIDOU_CNAV1_CRC_BITS = 24;
|
||||
|
||||
constexpr int32_t BEIDOU_CNAV1_PAGE_IONO_UTC = 1;
|
||||
|
||||
//! RTKLIB eph_t::code / Beidou_Cnav1_Ephemeris::sig_type for B-CNAV1 (DNAV uses 0–6).
|
||||
constexpr int32_t BDS_EPH_SOURCE_CNAV1 = 7;
|
||||
|
||||
constexpr double BEIDOU_CNAV1_A_REF_MEO = 27906100.0;
|
||||
constexpr double BEIDOU_CNAV1_A_REF_IGSO = 42162200.0;
|
||||
constexpr double BEIDOU_CNAV1_TOE_TOC_LSB = 300.0;
|
||||
|
||||
@@ -55,13 +55,6 @@ constexpr double D1_CRC_LSB = TWO_N6;
|
||||
constexpr double D1_CRS_LSB = TWO_N6;
|
||||
constexpr double D1_SQRT_A_LSB = TWO_N19;
|
||||
constexpr double D1_TOE_LSB = TWO_P3;
|
||||
/* ICD B1I §5.2.4.12 Table 5-10: toe scale 2^3, valid range 0..604792 s */
|
||||
constexpr double D1_TOE_MAX_S = 604792.0;
|
||||
/* ICD B1I Table 5-10: sqrt(A) scale 2^-19, valid range 0..8192 m^1/2.
|
||||
* Practical MEO/IGSO/GEO broadcast values are ~4200..7000; reject clearly
|
||||
* corrupted assemblies (e.g. mismatched SF2/SF3) below this floor. */
|
||||
constexpr double D1_SQRT_A_MAX = 8192.0;
|
||||
constexpr double D1_SQRT_A_MIN_SANE = 4000.0;
|
||||
constexpr double D1_I0_LSB = PI_TWO_N31;
|
||||
constexpr double D1_CIC_LSB = TWO_N31;
|
||||
constexpr double D1_OMEGA_DOT_LSB = PI_TWO_N43;
|
||||
|
||||
@@ -28,6 +28,7 @@ set(SYSTEM_PARAMETERS_SOURCES
|
||||
beidou_cnav1_navigation_message.cc
|
||||
beidou_cnav1_ldpc.cc
|
||||
beidou_bdgim.cc
|
||||
Beidou_B1C_codes.cc
|
||||
sbas_ephemeris.cc
|
||||
gps_cnav_navigation_message.cc
|
||||
glonass_gnav_ephemeris.cc
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#ifndef GNSS_SDR_BEIDOU_CNAV1_EPHEMERIS_H
|
||||
#define GNSS_SDR_BEIDOU_CNAV1_EPHEMERIS_H
|
||||
|
||||
#include "Beidou_CNAV1.h"
|
||||
#include "gnss_ephemeris.h"
|
||||
#include <boost/serialization/nvp.hpp>
|
||||
|
||||
@@ -27,13 +28,14 @@ public:
|
||||
this->System = 'C';
|
||||
}
|
||||
|
||||
double TGD_B1Cp{}; //!< §7.6 B1C pilot group delay
|
||||
double TGD_B2ap{}; //!< §7.6 B2a pilot group delay
|
||||
double ISC_B1Cd{}; //!< §7.6 B1C data-to-pilot intra-frequency delay correction
|
||||
double IODC{}; //!< §7.4.2
|
||||
double IODE{}; //!< §7.4.1
|
||||
int32_t sig_type{7}; //!< B1C data component identifier for PVT
|
||||
int32_t nav_type{1}; //!< 0: GEO, 1: MEO/IGSO
|
||||
double TGD_B1Cp{}; //!< §7.6 B1C pilot group delay
|
||||
double TGD_B2ap{}; //!< §7.6 B2a pilot group delay
|
||||
double ISC_B1Cd{}; //!< §7.6 B1C data-to-pilot intra-frequency delay correction
|
||||
double IODC{}; //!< §7.4.2
|
||||
double IODE{}; //!< §7.4.1
|
||||
int32_t hs{}; //!< SF3 satellite health (2 bits, ICD §6.2.3)
|
||||
int32_t sig_type{BDS_EPH_SOURCE_CNAV1}; //!< RTKLIB eph.code for B-CNAV1
|
||||
int32_t nav_type{1}; //!< 0: GEO, 1: MEO/IGSO
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& archive, const unsigned int version)
|
||||
@@ -71,6 +73,7 @@ public:
|
||||
archive& BOOST_SERIALIZATION_NVP(ISC_B1Cd);
|
||||
archive& BOOST_SERIALIZATION_NVP(IODC);
|
||||
archive& BOOST_SERIALIZATION_NVP(IODE);
|
||||
archive& BOOST_SERIALIZATION_NVP(hs);
|
||||
archive& BOOST_SERIALIZATION_NVP(sig_type);
|
||||
archive& BOOST_SERIALIZATION_NVP(nav_type);
|
||||
}
|
||||
|
||||
@@ -22,10 +22,29 @@
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
{
|
||||
void append_bits_as_chars(std::string& out, const int32_t* bits, int32_t n)
|
||||
{
|
||||
out.reserve(out.size() + static_cast<size_t>(n));
|
||||
for (int32_t i = 0; i < n; i++)
|
||||
{
|
||||
out.push_back(bits[i] != 0 ? '1' : '0');
|
||||
}
|
||||
}
|
||||
|
||||
void append_bits_as_chars(std::string& out, const uint8_t* bits, int32_t n)
|
||||
{
|
||||
out.reserve(out.size() + static_cast<size_t>(n));
|
||||
for (int32_t i = 0; i < n; i++)
|
||||
{
|
||||
out.push_back(bits[i] != 0U ? '1' : '0');
|
||||
}
|
||||
}
|
||||
|
||||
struct BchCodebook
|
||||
{
|
||||
int32_t n = 0;
|
||||
@@ -343,7 +362,7 @@ void parse_subframe2(const uint8_t* bits, Beidou_Cnav1_Ephemeris& eph, double so
|
||||
eph.toc = static_cast<int32_t>(eph.toc * BEIDOU_CNAV1_TOE_TOC_LSB);
|
||||
eph.tow = static_cast<int32_t>(how * 3600 + soh_seconds);
|
||||
eph.nav_type = nav_type_from_sat_type(sat_type);
|
||||
eph.sig_type = 7;
|
||||
eph.sig_type = BDS_EPH_SOURCE_CNAV1;
|
||||
}
|
||||
|
||||
void parse_page_common(
|
||||
@@ -611,64 +630,15 @@ bool Beidou_Cnav1_Navigation_Message::decode_frame(
|
||||
|
||||
std::array<float, BEIDOU_CNAV1_SUBFRAME2_SYMBOLS> sf2_llr{};
|
||||
std::array<float, BEIDOU_CNAV1_SUBFRAME3_SYMBOLS> sf3_llr{};
|
||||
std::array<float, BEIDOU_CNAV1_SUBFRAME2_SYMBOLS> sf2_llr_inv{};
|
||||
std::array<float, BEIDOU_CNAV1_SUBFRAME2_SYMBOLS> sf2_llr_bitrev{};
|
||||
std::array<float, BEIDOU_CNAV1_SUBFRAME2_SYMBOLS> sf2_llr_bitrev_inv{};
|
||||
std::array<float, BEIDOU_CNAV1_SUBFRAME3_SYMBOLS> sf3_llr_inv{};
|
||||
std::array<float, BEIDOU_CNAV1_SUBFRAME3_SYMBOLS> sf3_llr_bitrev{};
|
||||
std::array<float, BEIDOU_CNAV1_SUBFRAME3_SYMBOLS> sf3_llr_bitrev_inv{};
|
||||
deinterleave_sf2_sf3(bit_llr.data() + BEIDOU_CNAV1_SUBFRAME1_SYMBOLS, sf2_llr.data(), sf3_llr.data());
|
||||
// Try normal / inverted / bit-reversed LLR polarity for GF(64) LDPC.
|
||||
for (int32_t i = 0; i < BEIDOU_CNAV1_SUBFRAME2_SYMBOLS; i++)
|
||||
{
|
||||
sf2_llr_inv[static_cast<size_t>(i)] = -sf2_llr[static_cast<size_t>(i)];
|
||||
}
|
||||
for (int32_t symbol = 0; symbol < BEIDOU_CNAV1_SUBFRAME2_SYMBOLS / 6; symbol++)
|
||||
{
|
||||
const auto base = static_cast<size_t>(symbol) * 6U;
|
||||
for (int32_t bit = 0; bit < 6; 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];
|
||||
}
|
||||
}
|
||||
for (int32_t i = 0; i < BEIDOU_CNAV1_SUBFRAME3_SYMBOLS; i++)
|
||||
{
|
||||
sf3_llr_inv[static_cast<size_t>(i)] = -sf3_llr[static_cast<size_t>(i)];
|
||||
}
|
||||
for (int32_t symbol = 0; symbol < BEIDOU_CNAV1_SUBFRAME3_SYMBOLS / 6; symbol++)
|
||||
{
|
||||
const auto base = static_cast<size_t>(symbol) * 6U;
|
||||
for (int32_t bit = 0; bit < 6; 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];
|
||||
}
|
||||
}
|
||||
|
||||
std::array<uint8_t, BEIDOU_CNAV1_SF2_DATA_BITS> sf2_data{};
|
||||
std::array<uint8_t, BEIDOU_CNAV1_SF3_DATA_BITS> sf3_data{};
|
||||
std::array<uint8_t, BEIDOU_CNAV1_SUBFRAME2_SYMBOLS> sf2_codeword_bits{};
|
||||
std::array<uint8_t, BEIDOU_CNAV1_SUBFRAME3_SYMBOLS> sf3_codeword_bits{};
|
||||
// One LLR polarity: PLL 180° is handled by the telemetry invert path upstream.
|
||||
bool sf2_ldpc_ok = beidou_cnav1_ldpc_decode_200_100_codeword(
|
||||
sf2_llr.data(), BEIDOU_CNAV1_SUBFRAME2_SYMBOLS, sf2_codeword_bits.data());
|
||||
if (!sf2_ldpc_ok)
|
||||
{
|
||||
sf2_ldpc_ok = beidou_cnav1_ldpc_decode_200_100_codeword(
|
||||
sf2_llr_inv.data(), BEIDOU_CNAV1_SUBFRAME2_SYMBOLS, sf2_codeword_bits.data());
|
||||
}
|
||||
if (!sf2_ldpc_ok)
|
||||
{
|
||||
sf2_ldpc_ok = beidou_cnav1_ldpc_decode_200_100_codeword(
|
||||
sf2_llr_bitrev.data(), BEIDOU_CNAV1_SUBFRAME2_SYMBOLS, sf2_codeword_bits.data());
|
||||
}
|
||||
if (!sf2_ldpc_ok)
|
||||
{
|
||||
sf2_ldpc_ok = beidou_cnav1_ldpc_decode_200_100_codeword(
|
||||
sf2_llr_bitrev_inv.data(), BEIDOU_CNAV1_SUBFRAME2_SYMBOLS, sf2_codeword_bits.data());
|
||||
}
|
||||
if (!sf2_ldpc_ok)
|
||||
{
|
||||
// Hard-slice LLRs if LDPC fails.
|
||||
@@ -691,21 +661,6 @@ bool Beidou_Cnav1_Navigation_Message::decode_frame(
|
||||
}
|
||||
bool sf3_ldpc_ok = beidou_cnav1_ldpc_decode_88_44_codeword(
|
||||
sf3_llr.data(), BEIDOU_CNAV1_SUBFRAME3_SYMBOLS, sf3_codeword_bits.data());
|
||||
if (!sf3_ldpc_ok)
|
||||
{
|
||||
sf3_ldpc_ok = beidou_cnav1_ldpc_decode_88_44_codeword(
|
||||
sf3_llr_inv.data(), BEIDOU_CNAV1_SUBFRAME3_SYMBOLS, sf3_codeword_bits.data());
|
||||
}
|
||||
if (!sf3_ldpc_ok)
|
||||
{
|
||||
sf3_ldpc_ok = beidou_cnav1_ldpc_decode_88_44_codeword(
|
||||
sf3_llr_bitrev.data(), BEIDOU_CNAV1_SUBFRAME3_SYMBOLS, sf3_codeword_bits.data());
|
||||
}
|
||||
if (!sf3_ldpc_ok)
|
||||
{
|
||||
sf3_ldpc_ok = beidou_cnav1_ldpc_decode_88_44_codeword(
|
||||
sf3_llr_bitrev_inv.data(), BEIDOU_CNAV1_SUBFRAME3_SYMBOLS, sf3_codeword_bits.data());
|
||||
}
|
||||
if (!sf3_ldpc_ok)
|
||||
{
|
||||
for (int32_t i = 0; i < BEIDOU_CNAV1_SF3_DATA_BITS; i++)
|
||||
@@ -734,6 +689,16 @@ bool Beidou_Cnav1_Navigation_Message::decode_frame(
|
||||
return false;
|
||||
}
|
||||
|
||||
// Nav monitor string (same role as DNAV subframe bit chars): SF1 BCH info + SF2 data [+ SF3 if CRC OK].
|
||||
last_nav_bits_.clear();
|
||||
append_bits_as_chars(last_nav_bits_, prn_bits, 6);
|
||||
append_bits_as_chars(last_nav_bits_, soh_bits, 8);
|
||||
append_bits_as_chars(last_nav_bits_, sf2_data.data(), BEIDOU_CNAV1_SF2_DATA_BITS);
|
||||
if (sf3_crc_ok)
|
||||
{
|
||||
append_bits_as_chars(last_nav_bits_, sf3_data.data(), BEIDOU_CNAV1_SF3_DATA_BITS);
|
||||
}
|
||||
|
||||
Beidou_Cnav1_Ephemeris candidate{};
|
||||
parse_subframe2(sf2_data.data(), candidate, soh_seconds);
|
||||
candidate.PRN = static_cast<int32_t>(prn);
|
||||
@@ -773,6 +738,11 @@ bool Beidou_Cnav1_Navigation_Message::decode_frame(
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (flag_new_page_data_)
|
||||
{
|
||||
// Copy SF3 HS into the stored ephemeris.
|
||||
ephemeris_.hs = page_data_.common.hs;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -848,6 +818,7 @@ void Beidou_Cnav1_Navigation_Message::clear_flags()
|
||||
flag_new_iono_ = false;
|
||||
flag_new_utc_ = false;
|
||||
flag_new_page_data_ = false;
|
||||
last_nav_bits_.clear();
|
||||
}
|
||||
|
||||
const Beidou_Cnav1_Ephemeris& Beidou_Cnav1_Navigation_Message::get_ephemeris() const
|
||||
@@ -870,6 +841,11 @@ const Bds3_B1c_PageData& Beidou_Cnav1_Navigation_Message::get_page_data() const
|
||||
return page_data_;
|
||||
}
|
||||
|
||||
const std::string& Beidou_Cnav1_Navigation_Message::get_last_nav_bits() const
|
||||
{
|
||||
return last_nav_bits_;
|
||||
}
|
||||
|
||||
double Beidou_Cnav1_Navigation_Message::get_tow_s() const
|
||||
{
|
||||
return tow_s_;
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#include "beidou_cnav1_iono.h"
|
||||
#include "beidou_cnav1_utc_model.h"
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
struct Bds3_B1c_PageCommon
|
||||
{
|
||||
@@ -114,6 +116,8 @@ public:
|
||||
const Beidou_Cnav1_Iono& get_iono() const;
|
||||
const Beidou_Cnav1_Utc_Model& get_utc_model() const;
|
||||
const Bds3_B1c_PageData& get_page_data() const;
|
||||
//! Last successfully decoded frame as '0'/'1' chars for Nav_msg_from_TLM (SF1 info + SF2 [+ SF3]).
|
||||
const std::string& get_last_nav_bits() const;
|
||||
double get_tow_s() const;
|
||||
|
||||
private:
|
||||
@@ -126,6 +130,7 @@ private:
|
||||
bool flag_new_page_data_{false};
|
||||
double tow_s_{0.0};
|
||||
Bds3_B1c_PageData page_data_{};
|
||||
std::string last_nav_bits_{};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -255,10 +255,6 @@ int32_t Beidou_Dnav_Navigation_Message::d1_subframe_decoder(std::string const& s
|
||||
|
||||
d_AODE = static_cast<double>(read_navigation_unsigned(subframe_bits, D1_AODE));
|
||||
|
||||
// New SF1: drop buffered SF2/SF3 (ICD B1I §5.2.4.12).
|
||||
flag_d1_sf2 = false;
|
||||
flag_d1_sf3 = false;
|
||||
|
||||
// Set system flags for message reception
|
||||
flag_d1_sf1 = true;
|
||||
flag_iono_valid = true;
|
||||
@@ -911,17 +907,6 @@ bool Beidou_Dnav_Navigation_Message::have_new_ephemeris() // Check if we have a
|
||||
// if all ephemeris pages have the same IOD, then they belong to the same block
|
||||
if (d_previous_aode != d_AODE)
|
||||
{
|
||||
const double toe_s = (d_Toe_sf2 + d_Toe_sf3) * D1_TOE_LSB;
|
||||
// ICD B1I Table 5-10 range checks.
|
||||
if (toe_s < 0.0 || toe_s > D1_TOE_MAX_S ||
|
||||
d_sqrt_A < D1_SQRT_A_MIN_SANE || d_sqrt_A > D1_SQRT_A_MAX)
|
||||
{
|
||||
flag_d1_sf1 = false;
|
||||
flag_d1_sf2 = false;
|
||||
flag_d1_sf3 = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clear flags for all received subframes
|
||||
flag_d1_sf1 = false;
|
||||
flag_d1_sf2 = false;
|
||||
|
||||
@@ -159,7 +159,6 @@ public:
|
||||
this->Flag_valid_word = other.Flag_valid_word;
|
||||
this->Flag_valid_pseudorange = other.Flag_valid_pseudorange;
|
||||
this->Flag_PLL_180_deg_phase_locked = other.Flag_PLL_180_deg_phase_locked;
|
||||
this->Flag_cycle_slip = other.Flag_cycle_slip;
|
||||
|
||||
// Leave the source object in a valid but unspecified state
|
||||
other.Signal[0] = '\0';
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
#include "Beidou_CNAV1.h"
|
||||
#include "MATH_CONSTANTS.h"
|
||||
#include "gnss_frequencies.h"
|
||||
#include "gnss_obs_codes.h"
|
||||
@@ -68,14 +69,14 @@ TEST(BeidouB1cPvtHelpersTest, GettgdAppliesIscForDataComponent)
|
||||
const double ep[] = {2020, 1, 1, 0, 0, 0};
|
||||
const gtime_t t0 = epoch2time(ep);
|
||||
std::vector<eph_t> ephs;
|
||||
ephs.push_back(make_bds_eph(sat, 7, 1.0e-9, 2.0e-9, t0));
|
||||
ephs.push_back(make_bds_eph(sat, BDS_EPH_SOURCE_CNAV1, 1.0e-9, 2.0e-9, t0));
|
||||
nav_t nav;
|
||||
std::memset(&nav, 0, sizeof(nav));
|
||||
nav.eph = ephs.data();
|
||||
nav.n = 1;
|
||||
|
||||
const double tgd_pilot = gettgd(sat, &nav, static_cast<unsigned char>(CODE_L1P));
|
||||
const double tgd_data = gettgd(sat, &nav, static_cast<unsigned char>(CODE_L1D));
|
||||
const double tgd_pilot = gettgd_bds_by_obs_code(sat, &nav, static_cast<unsigned char>(CODE_L1P));
|
||||
const double tgd_data = gettgd_bds_by_obs_code(sat, &nav, static_cast<unsigned char>(CODE_L1D));
|
||||
EXPECT_NEAR(tgd_pilot, SPEED_OF_LIGHT_M_S * 1.0e-9, 1.0e-6);
|
||||
EXPECT_NEAR(tgd_data, SPEED_OF_LIGHT_M_S * 3.0e-9, 1.0e-6);
|
||||
}
|
||||
@@ -86,19 +87,19 @@ TEST(BeidouB1cPvtHelpersTest, SelephPrefersCnav1WhenRequested)
|
||||
const double ep[] = {2020, 6, 1, 12, 0, 0};
|
||||
const gtime_t t0 = epoch2time(ep);
|
||||
std::vector<eph_t> ephs;
|
||||
ephs.push_back(make_bds_eph(sat, 1, 1.0e-9, 0.0, t0)); /* DNAV */
|
||||
ephs.push_back(make_bds_eph(sat, 7, 2.0e-9, 1.0e-9, t0)); /* CNAV1 */
|
||||
ephs.push_back(make_bds_eph(sat, 1, 1.0e-9, 0.0, t0)); /* DNAV */
|
||||
ephs.push_back(make_bds_eph(sat, BDS_EPH_SOURCE_CNAV1, 2.0e-9, 1.0e-9, t0)); /* CNAV1 */
|
||||
nav_t nav;
|
||||
std::memset(&nav, 0, sizeof(nav));
|
||||
nav.eph = ephs.data();
|
||||
nav.n = 2;
|
||||
|
||||
eph_t *e_cnav = seleph(t0, sat, -1, &nav, 7);
|
||||
eph_t *e_cnav = seleph(t0, sat, -1, &nav, BDS_EPH_SOURCE_CNAV1);
|
||||
eph_t *e_dnav = seleph(t0, sat, -1, &nav, 0);
|
||||
ASSERT_NE(e_cnav, static_cast<eph_t *>(nullptr));
|
||||
ASSERT_NE(e_dnav, static_cast<eph_t *>(nullptr));
|
||||
EXPECT_EQ(e_cnav->code, 7);
|
||||
EXPECT_NE(e_dnav->code, 7);
|
||||
EXPECT_EQ(e_cnav->code, BDS_EPH_SOURCE_CNAV1);
|
||||
EXPECT_NE(e_dnav->code, BDS_EPH_SOURCE_CNAV1);
|
||||
}
|
||||
|
||||
/* satwavelen BDS: frq0=B1I, frq1=B2, frq2=B3I */
|
||||
@@ -154,15 +155,15 @@ TEST(BeidouB1cPvtHelpersTest, GettgdPrefersMatchingEphTypeWhenBothPresent)
|
||||
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 TGD1 */
|
||||
ephs.push_back(make_bds_eph(sat, 7, 1.0e-9, 2.0e-9, t0)); /* CNAV1 */
|
||||
ephs.push_back(make_bds_eph(sat, 1, 5.0e-9, 0.0, t0)); /* DNAV TGD1 */
|
||||
ephs.push_back(make_bds_eph(sat, BDS_EPH_SOURCE_CNAV1, 1.0e-9, 2.0e-9, t0)); /* CNAV1 */
|
||||
nav_t nav;
|
||||
std::memset(&nav, 0, sizeof(nav));
|
||||
nav.eph = ephs.data();
|
||||
nav.n = 2;
|
||||
|
||||
const double tgd_b1c = gettgd(sat, &nav, static_cast<unsigned char>(CODE_L1D));
|
||||
const double tgd_b1i = gettgd(sat, &nav, static_cast<unsigned char>(CODE_L2I));
|
||||
const double tgd_b1c = gettgd_bds_by_obs_code(sat, &nav, static_cast<unsigned char>(CODE_L1D));
|
||||
const double tgd_b1i = gettgd_bds_by_obs_code(sat, &nav, static_cast<unsigned char>(CODE_L2I));
|
||||
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);
|
||||
}
|
||||
@@ -179,8 +180,8 @@ TEST(BeidouB1cPvtHelpersTest, GettgdReturnsZeroWhenOnlyWrongEphFamilyPresent)
|
||||
nav.eph = ephs.data();
|
||||
nav.n = 1;
|
||||
|
||||
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);
|
||||
EXPECT_DOUBLE_EQ(gettgd_bds_by_obs_code(sat, &nav, static_cast<unsigned char>(CODE_L1D)), 0.0);
|
||||
EXPECT_DOUBLE_EQ(gettgd_bds_by_obs_code(sat, &nav, static_cast<unsigned char>(CODE_L1P)), 0.0);
|
||||
}
|
||||
|
||||
/* CODE_L1P also means GPS L1P; CNAV1 filtering is SYS_BDS-only. */
|
||||
@@ -201,6 +202,6 @@ TEST(BeidouB1cPvtHelpersTest, GettgdGpsIgnoresB1cCodeSelection)
|
||||
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);
|
||||
EXPECT_NEAR(gettgd(sat, &nav, static_cast<unsigned char>(CODE_L1P)), SPEED_OF_LIGHT_M_S * 4.0e-9, 1.0e-6);
|
||||
EXPECT_NEAR(gettgd_bds_by_obs_code(sat, &nav, static_cast<unsigned char>(CODE_L1C)), SPEED_OF_LIGHT_M_S * 4.0e-9, 1.0e-6);
|
||||
EXPECT_NEAR(gettgd_bds_by_obs_code(sat, &nav, static_cast<unsigned char>(CODE_L1P)), SPEED_OF_LIGHT_M_S * 4.0e-9, 1.0e-6);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
@@ -136,6 +137,45 @@ std::vector<float> encode_bits_to_llr(const std::vector<uint8_t>& bits)
|
||||
}
|
||||
return llr;
|
||||
}
|
||||
|
||||
//! Synthetic B-CNAV1 frame with all-zero SF2/SF3 payloads.
|
||||
std::vector<float> build_valid_zero_payload_frame(uint32_t prn, uint32_t soh)
|
||||
{
|
||||
const auto sf2_llr = encode_bits_to_llr(std::vector<uint8_t>(BEIDOU_CNAV1_SUBFRAME2_SYMBOLS, 0U));
|
||||
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, prn);
|
||||
append_bch51_8(frame, soh);
|
||||
frame.insert(frame.end(), interleaved.begin(), interleaved.end());
|
||||
return frame;
|
||||
}
|
||||
|
||||
//! Decode one frame from stream[end-(N-1) .. end] at start_offset (decoder window layout).
|
||||
bool decode_from_sliding_window(
|
||||
const std::vector<float>& stream,
|
||||
int64_t end_index,
|
||||
int32_t start_offset,
|
||||
int32_t expected_prn)
|
||||
{
|
||||
const int32_t n = BEIDOU_CNAV1_FRAME_SYMBOLS;
|
||||
if (end_index < n - 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const int64_t start = end_index - (n - 1);
|
||||
std::vector<float> extracted(static_cast<size_t>(n));
|
||||
const int32_t offset = ((start_offset % n) + n) % n;
|
||||
for (int32_t i = 0; i < n; i++)
|
||||
{
|
||||
const int64_t abs_idx = start + ((offset + i) % n);
|
||||
extracted[static_cast<size_t>(i)] = stream[static_cast<size_t>(abs_idx)];
|
||||
}
|
||||
Beidou_Cnav1_Navigation_Message nav;
|
||||
return nav.decode_frame_symbols(extracted.data(), n, expected_prn);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(BeidouCnav1NavigationMessageTest, DecodeSubframe1OnlyFrameFailsWithoutSf2Crc)
|
||||
@@ -155,33 +195,56 @@ TEST(BeidouCnav1NavigationMessageTest, DecodeSubframe1OnlyFrameFailsWithoutSf2Cr
|
||||
|
||||
TEST(BeidouCnav1NavigationMessageTest, DecodeEphemerisFromZeroSf2Payload)
|
||||
{
|
||||
const auto sf2_llr = encode_bits_to_llr(std::vector<uint8_t>(BEIDOU_CNAV1_SUBFRAME2_SYMBOLS, 0U));
|
||||
const auto sf3_llr = encode_bits_to_llr(std::vector<uint8_t>(BEIDOU_CNAV1_SUBFRAME3_SYMBOLS, 0U));
|
||||
EXPECT_EQ(sf2_llr.size(), static_cast<size_t>(BEIDOU_CNAV1_SUBFRAME2_SYMBOLS));
|
||||
EXPECT_EQ(sf3_llr.size(), static_cast<size_t>(BEIDOU_CNAV1_SUBFRAME3_SYMBOLS));
|
||||
|
||||
std::vector<float> interleaved;
|
||||
deinterleave_like_icd(sf2_llr, sf3_llr, interleaved);
|
||||
EXPECT_EQ(interleaved.size(), static_cast<size_t>(BEIDOU_CNAV1_INTERLEAVED_SYMBOLS));
|
||||
|
||||
std::vector<float> frame;
|
||||
append_bch21_6(frame, 19U);
|
||||
append_bch51_8(frame, 1U);
|
||||
frame.insert(frame.end(), interleaved.begin(), interleaved.end());
|
||||
const std::vector<float> frame = build_valid_zero_payload_frame(19U, 1U);
|
||||
ASSERT_EQ(frame.size(), static_cast<size_t>(BEIDOU_CNAV1_FRAME_SYMBOLS));
|
||||
|
||||
Beidou_Cnav1_Navigation_Message nav;
|
||||
ASSERT_TRUE(nav.decode_frame_symbols(frame.data(), BEIDOU_CNAV1_FRAME_SYMBOLS, 19));
|
||||
EXPECT_TRUE(nav.have_new_ephemeris());
|
||||
EXPECT_EQ(nav.get_ephemeris().PRN, 19);
|
||||
EXPECT_DOUBLE_EQ(nav.get_tow_s(), 18.0);
|
||||
// SF1 info (6+8) + SF2 data (600) + SF3 data (264) when both CRCs pass.
|
||||
EXPECT_EQ(nav.get_last_nav_bits().size(), static_cast<size_t>(6 + 8 + BEIDOU_CNAV1_SF2_DATA_BITS + BEIDOU_CNAV1_SF3_DATA_BITS));
|
||||
EXPECT_EQ(nav.get_last_nav_bits().substr(0, 6), std::string("010011")); // PRN 19
|
||||
|
||||
const auto rtklib_eph = eph_to_rtklib(nav.get_ephemeris());
|
||||
EXPECT_EQ(rtklib_eph.iode, 0);
|
||||
EXPECT_EQ(rtklib_eph.iodc, 0);
|
||||
EXPECT_NEAR(rtklib_eph.A, BEIDOU_CNAV1_A_REF_MEO, 1.0);
|
||||
EXPECT_EQ(rtklib_eph.code, 7);
|
||||
EXPECT_EQ(rtklib_eph.code, BDS_EPH_SOURCE_CNAV1);
|
||||
EXPECT_DOUBLE_EQ(rtklib_eph.Adot, nav.get_ephemeris().Adot);
|
||||
EXPECT_DOUBLE_EQ(rtklib_eph.ndot, nav.get_ephemeris().delta_n0dot);
|
||||
EXPECT_DOUBLE_EQ(rtklib_eph.ndot, nav.get_ephemeris().delta_ndot);
|
||||
}
|
||||
|
||||
TEST(BeidouCnav1NavigationMessageTest, MultiFrameSteadyStateKeepsConstantSohOffset)
|
||||
{
|
||||
// Nonzero start_offset must stay constant across frame boundaries.
|
||||
constexpr uint32_t prn = 19U;
|
||||
constexpr int32_t matched_offset = 37;
|
||||
constexpr int32_t num_frames = 12; // > CRC_ERROR_LIMIT (8)
|
||||
constexpr int32_t n = BEIDOU_CNAV1_FRAME_SYMBOLS;
|
||||
|
||||
const std::vector<float> frame = build_valid_zero_payload_frame(prn, 1U);
|
||||
ASSERT_EQ(frame.size(), static_cast<size_t>(n));
|
||||
ASSERT_TRUE(Beidou_Cnav1_Navigation_Message{}.decode_frame_symbols(
|
||||
frame.data(), n, static_cast<int32_t>(prn)));
|
||||
|
||||
// stream[t] = frame[(t - matched_offset) mod N]
|
||||
const int64_t total_symbols = static_cast<int64_t>(num_frames) * n;
|
||||
std::vector<float> stream(static_cast<size_t>(total_symbols));
|
||||
for (int64_t t = 0; t < total_symbols; t++)
|
||||
{
|
||||
const int32_t idx = static_cast<int32_t>(
|
||||
((t - matched_offset) % n + n) % n);
|
||||
stream[static_cast<size_t>(t)] = frame[static_cast<size_t>(idx)];
|
||||
}
|
||||
|
||||
for (int32_t f = 1; f <= num_frames; f++)
|
||||
{
|
||||
const int64_t end_index = static_cast<int64_t>(f) * n - 1;
|
||||
EXPECT_TRUE(decode_from_sliding_window(stream, end_index, matched_offset, static_cast<int32_t>(prn)))
|
||||
<< "constant-offset decode failed at frame " << f;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(BeidouCnav1NavigationMessageTest, RejectsEphemerisWhenIodeIodcMismatch)
|
||||
|
||||
@@ -128,15 +128,3 @@ TEST(BeidouDnavNavigationMessageTest, D2Subframe1PagesAcceptSowWeekRollover)
|
||||
EXPECT_EQ(1, dnav_msg.d2_subframe_decoder(make_d2_subframe1_page(3, 3)));
|
||||
EXPECT_TRUE(dnav_msg.get_flag_CRC_test());
|
||||
}
|
||||
|
||||
|
||||
TEST(BeidouDnavNavigationMessageTest, D1ToeAndSqrtAIcdLimits)
|
||||
{
|
||||
// ICD B1I Table 5-10: toe max 604792 s, sqrt(A) max 8192 m^1/2.
|
||||
// The corrupt PRN7 DNAV case (toe=988032, sqrtA≈1698) must be rejected.
|
||||
EXPECT_GT(988032.0, D1_TOE_MAX_S);
|
||||
EXPECT_LT(1698.0, D1_SQRT_A_MIN_SANE);
|
||||
EXPECT_LE(201600.0, D1_TOE_MAX_S);
|
||||
EXPECT_GE(5282.0, D1_SQRT_A_MIN_SANE);
|
||||
EXPECT_LE(6493.0, D1_SQRT_A_MAX);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generate Beidou_B1C_codes.h from ICD Weil parameters in Beidou_B1C_prn.h.
|
||||
Generate Beidou_B1C_codes.h/.cc from ICD Weil parameters in Beidou_B1C_prn.h.
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
@@ -23,6 +23,7 @@ from pathlib import Path
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
PRN_HEADER = ROOT / "src/core/system_parameters/Beidou_B1C_prn.h"
|
||||
OUT_HEADER = ROOT / "src/core/system_parameters/Beidou_B1C_codes.h"
|
||||
OUT_SOURCE = ROOT / "src/core/system_parameters/Beidou_B1C_codes.cc"
|
||||
|
||||
|
||||
def extract(name: str, text: str) -> list[int]:
|
||||
@@ -62,9 +63,31 @@ def chips_to_binstr(chips: list[int]) -> str:
|
||||
return "".join(str(c) for c in chips)
|
||||
|
||||
|
||||
def emit_array(name: str, rows: list[str]) -> str:
|
||||
lines = [f"constexpr char {name}[63][{len(rows[0]) + 1}] = {{"]
|
||||
lines.extend(f' "{row}",' for row in rows)
|
||||
# Keep generated source under the CI long-line limit (512 chars).
|
||||
# Match the historical 76-char fragment width used in Beidou_B1C_codes.h.
|
||||
_CODE_LINE_WIDTH = 76
|
||||
|
||||
|
||||
def emit_array_decl(name: str, rows: list[str]) -> str:
|
||||
return f"extern const char {name}[63][{len(rows[0]) + 1}];"
|
||||
|
||||
|
||||
def emit_wrapped_string(row: str) -> list[str]:
|
||||
"""Emit one array element as adjacent string literals, one chunk per line."""
|
||||
chunks = [row[i : i + _CODE_LINE_WIDTH] for i in range(0, len(row), _CODE_LINE_WIDTH)]
|
||||
if not chunks:
|
||||
chunks = [""]
|
||||
out: list[str] = []
|
||||
for idx, chunk in enumerate(chunks):
|
||||
suffix = "," if idx == len(chunks) - 1 else ""
|
||||
out.append(f' "{chunk}"{suffix}')
|
||||
return out
|
||||
|
||||
|
||||
def emit_array_def(name: str, rows: list[str]) -> str:
|
||||
lines = [f"const char {name}[63][{len(rows[0]) + 1}] = {{"]
|
||||
for row in rows:
|
||||
lines.extend(emit_wrapped_string(row))
|
||||
lines.append("};")
|
||||
return "\n".join(lines)
|
||||
|
||||
@@ -92,12 +115,14 @@ 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
|
||||
|
||||
script_name = Path(__file__).name
|
||||
header = f"""/*!
|
||||
* \\file Beidou_B1C_codes.h
|
||||
* \\brief Precomputed BeiDou B1C primary and secondary codes
|
||||
* \\brief Precomputed BeiDou B1C primary and secondary codes (declarations)
|
||||
* \\author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
|
||||
*
|
||||
* Auto-generated by {Path(__file__).name}. Do not edit manually.
|
||||
* Auto-generated by {script_name}. Do not edit manually.
|
||||
* Definitions live in Beidou_B1C_codes.cc.
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
@@ -117,16 +142,45 @@ def main() -> None:
|
||||
constexpr size_t BEIDOU_B1C_PRIMARY_CODE_STR_LENGTH = 2558;
|
||||
constexpr size_t BEIDOU_B1C_PILOT_SECONDARY_CODE_STR_LENGTH = 1800;
|
||||
|
||||
{emit_array("BEIDOU_B1C_DATA_PRIMARY_CODE", data_hex)}
|
||||
{emit_array_decl("BEIDOU_B1C_DATA_PRIMARY_CODE", data_hex)}
|
||||
|
||||
{emit_array("BEIDOU_B1C_PILOT_PRIMARY_CODE", pilot_hex)}
|
||||
{emit_array_decl("BEIDOU_B1C_PILOT_PRIMARY_CODE", pilot_hex)}
|
||||
|
||||
{emit_array("BEIDOU_B1C_PILOT_SECONDARY_CODE", sec_bin)}
|
||||
{emit_array_decl("BEIDOU_B1C_PILOT_SECONDARY_CODE", sec_bin)}
|
||||
|
||||
#endif
|
||||
"""
|
||||
|
||||
source = f"""/*!
|
||||
* \\file Beidou_B1C_codes.cc
|
||||
* \\brief Precomputed BeiDou B1C primary and secondary codes
|
||||
* \\author Wenhao Ou, 2026. ouwh(at)mail2.sysu.edu.cn
|
||||
*
|
||||
* Auto-generated by {script_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
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
#include "Beidou_B1C_codes.h"
|
||||
|
||||
{emit_array_def("BEIDOU_B1C_DATA_PRIMARY_CODE", data_hex)}
|
||||
|
||||
{emit_array_def("BEIDOU_B1C_PILOT_PRIMARY_CODE", pilot_hex)}
|
||||
|
||||
{emit_array_def("BEIDOU_B1C_PILOT_SECONDARY_CODE", sec_bin)}
|
||||
"""
|
||||
|
||||
OUT_HEADER.write_text(header, encoding="utf-8")
|
||||
OUT_SOURCE.write_text(source, encoding="utf-8")
|
||||
print(f"Wrote {OUT_HEADER} ({len(header)} bytes)")
|
||||
print(f"Wrote {OUT_SOURCE} ({len(source)} bytes)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user