Merge branch 'vladisslav2011-qzss_l1cb' into next

This commit is contained in:
Carles Fernandez
2026-07-04 17:13:49 +02:00
13 changed files with 178 additions and 81 deletions
+1 -1
View File
@@ -27,7 +27,7 @@ In the L1 band:
- 🛰 GPS L1 C/A (centered at 1575.420 MHz) ✅
- 🛰 Galileo E1b/c (centered at 1575.420 MHz) ✅
- 🛰 BeiDou B1I (centered at 1561.098 MHz) ✅
- 🛰 QZSS L1 C/A, where available (centered at 1575.420 MHz) ✅
- 🛰 QZSS L1 C/A and C/B, where available (centered at 1575.420 MHz) ✅
In the E6 band:
+5
View File
@@ -57,6 +57,11 @@ All notable changes to GNSS-SDR will be documented in this file.
- Improved validation of GPS/QZSS CNAV Clock, Ephemeris, Integrity (CEI)
dataset.
- Implemented QZSS LNAV almanac/auxiliary pages decoding.
- Added support for the QZSS L1 C/B signal (PRNs 203-206), broadcast by
satellites configured to transmit it in place of L1 C/A. Observables and
ephemerides from L1 C/B PRNs are attributed to the PRN of the satellite's
nominal PNT signals in PVT and output products, following the RINEX 4.00
convention. Contributed by @vladisslav2011.
- Hardened BeiDou DNAV and Glonass GNAV decoding.
- Improved performance of Galileo's Viterbi decoder.
- Fixed edge cases in the retrieving of GPS L1 C/A navigation data.
@@ -55,6 +55,7 @@
#include "nmea_printer.h"
#include "osnma_data.h"
#include "pvt_conf.h"
#include "qzss.h"
#include "rinex_printer.h"
#include "rtcm_printer.h"
#include "rtklib_rtkcmn.h"
@@ -1203,6 +1204,9 @@ void rtklib_pvt_gs::msg_handler_telemetry(const pmt::pmt_t& msg)
{
// ### GPS EPHEMERIS ###
const auto gps_eph = wht::any_cast<std::shared_ptr<Gps_Ephemeris>>(pmt::any_ref(msg));
// Ephemeris decoded from QZSS L1 C/B (PRN 203-206) belongs to the satellite
// identified by the PRN of its nominal PNT signals (RINEX 4.00, Table 6)
gps_eph->PRN = qzss_l1cb_prn_to_nominal_prn(gps_eph->PRN);
DLOG(INFO) << "Ephemeris record has arrived from SAT ID "
<< gps_eph->PRN << " (Block "
<< gps_eph->satelliteBlock[gps_eph->PRN] << ")"
@@ -2012,21 +2016,29 @@ int rtklib_pvt_gs::work(int noutput_items, gr_vector_const_void_star& input_item
// ############ 1. READ PSEUDORANGES ####
for (uint32_t i = 0; i < d_nchannels; i++)
{
if (in[i][epoch].Flag_valid_pseudorange)
Gnss_Synchro gnss_synchro = in[i][epoch];
if (gnss_synchro.System == 'J')
{
const auto tmp_eph_iter_gps = d_internal_pvt_solver->gps_ephemeris_map.find(in[i][epoch].PRN);
const auto tmp_eph_iter_gal = d_internal_pvt_solver->galileo_ephemeris_map.find(in[i][epoch].PRN);
const auto tmp_eph_iter_cnav = d_internal_pvt_solver->gps_cnav_ephemeris_map.find(in[i][epoch].PRN);
const auto tmp_eph_iter_glo_gnav = d_internal_pvt_solver->glonass_gnav_ephemeris_map.find(in[i][epoch].PRN);
const auto tmp_eph_iter_bds_dnav = d_internal_pvt_solver->beidou_dnav_ephemeris_map.find(in[i][epoch].PRN);
// QZSS L1 C/B is broadcast with a dedicated PRN (203-206), but the
// transmitting satellite is identified by the PRN of its nominal PNT
// signals (RINEX 4.00, Table 6: QZSS PRN to RINEX Satellite Identifier)
gnss_synchro.PRN = qzss_l1cb_prn_to_nominal_prn(gnss_synchro.PRN);
}
if (gnss_synchro.Flag_valid_pseudorange)
{
const auto tmp_eph_iter_gps = d_internal_pvt_solver->gps_ephemeris_map.find(gnss_synchro.PRN);
const auto tmp_eph_iter_gal = d_internal_pvt_solver->galileo_ephemeris_map.find(gnss_synchro.PRN);
const auto tmp_eph_iter_cnav = d_internal_pvt_solver->gps_cnav_ephemeris_map.find(gnss_synchro.PRN);
const auto tmp_eph_iter_glo_gnav = d_internal_pvt_solver->glonass_gnav_ephemeris_map.find(gnss_synchro.PRN);
const auto tmp_eph_iter_bds_dnav = d_internal_pvt_solver->beidou_dnav_ephemeris_map.find(gnss_synchro.PRN);
bool store_valid_observable = false;
if (!d_osnma_strict && tmp_eph_iter_gps != d_internal_pvt_solver->gps_ephemeris_map.cend())
{
const uint32_t prn_aux = tmp_eph_iter_gps->second.PRN;
if ((prn_aux == in[i][epoch].PRN) &&
((std::string(in[i][epoch].Signal, 2) == std::string("1C")) || (std::string(in[i][epoch].Signal, 2) == std::string("J1"))) &&
if ((prn_aux == gnss_synchro.PRN) &&
((std::string(gnss_synchro.Signal, 2) == std::string("1C")) || (std::string(gnss_synchro.Signal, 2) == std::string("J1"))) &&
(d_use_unhealthy_sats || (tmp_eph_iter_gps->second.SV_health == 0)))
{
store_valid_observable = true;
@@ -2035,10 +2047,10 @@ int rtklib_pvt_gs::work(int noutput_items, gr_vector_const_void_star& input_item
if (tmp_eph_iter_gal != d_internal_pvt_solver->galileo_ephemeris_map.cend())
{
const uint32_t prn_aux = tmp_eph_iter_gal->second.PRN;
if ((prn_aux == in[i][epoch].PRN) &&
(((std::string(in[i][epoch].Signal, 2) == std::string("1B")) && (d_use_unhealthy_sats || ((tmp_eph_iter_gal->second.E1B_DVS == false) && (tmp_eph_iter_gal->second.E1B_HS == 0)))) ||
((std::string(in[i][epoch].Signal, 2) == std::string("5X")) && (d_use_unhealthy_sats || ((tmp_eph_iter_gal->second.E5a_DVS == false) && (tmp_eph_iter_gal->second.E5a_HS == 0)))) ||
((std::string(in[i][epoch].Signal, 2) == std::string("7X")) && (d_use_unhealthy_sats || ((tmp_eph_iter_gal->second.E5b_DVS == false) && (tmp_eph_iter_gal->second.E5b_HS == 0))))))
if ((prn_aux == gnss_synchro.PRN) &&
(((std::string(gnss_synchro.Signal, 2) == std::string("1B")) && (d_use_unhealthy_sats || ((tmp_eph_iter_gal->second.E1B_DVS == false) && (tmp_eph_iter_gal->second.E1B_HS == 0)))) ||
((std::string(gnss_synchro.Signal, 2) == std::string("5X")) && (d_use_unhealthy_sats || ((tmp_eph_iter_gal->second.E5a_DVS == false) && (tmp_eph_iter_gal->second.E5a_HS == 0)))) ||
((std::string(gnss_synchro.Signal, 2) == std::string("7X")) && (d_use_unhealthy_sats || ((tmp_eph_iter_gal->second.E5b_DVS == false) && (tmp_eph_iter_gal->second.E5b_HS == 0))))))
{
if (d_osnma_strict)
{
@@ -2083,7 +2095,7 @@ int rtklib_pvt_gs::work(int noutput_items, gr_vector_const_void_star& input_item
if (!d_osnma_strict && tmp_eph_iter_cnav != d_internal_pvt_solver->gps_cnav_ephemeris_map.cend())
{
const uint32_t prn_aux = tmp_eph_iter_cnav->second.PRN;
if ((prn_aux == in[i][epoch].PRN) && (((std::string(in[i][epoch].Signal, 2) == std::string("2S")) || (std::string(in[i][epoch].Signal, 2) == std::string("L5")) || (std::string(in[i][epoch].Signal, 2) == std::string("J5")))))
if ((prn_aux == gnss_synchro.PRN) && (((std::string(gnss_synchro.Signal, 2) == std::string("2S")) || (std::string(gnss_synchro.Signal, 2) == std::string("L5")) || (std::string(gnss_synchro.Signal, 2) == std::string("J5")))))
{
store_valid_observable = true;
}
@@ -2091,7 +2103,7 @@ int rtklib_pvt_gs::work(int noutput_items, gr_vector_const_void_star& input_item
if (!d_osnma_strict && tmp_eph_iter_glo_gnav != d_internal_pvt_solver->glonass_gnav_ephemeris_map.cend())
{
const uint32_t prn_aux = tmp_eph_iter_glo_gnav->second.PRN;
if ((prn_aux == in[i][epoch].PRN) && ((std::string(in[i][epoch].Signal, 2) == std::string("1G")) || (std::string(in[i][epoch].Signal, 2) == std::string("2G"))))
if ((prn_aux == gnss_synchro.PRN) && ((std::string(gnss_synchro.Signal, 2) == std::string("1G")) || (std::string(gnss_synchro.Signal, 2) == std::string("2G"))))
{
store_valid_observable = true;
}
@@ -2099,12 +2111,12 @@ int rtklib_pvt_gs::work(int noutput_items, gr_vector_const_void_star& input_item
if (!d_osnma_strict && tmp_eph_iter_bds_dnav != d_internal_pvt_solver->beidou_dnav_ephemeris_map.cend())
{
const uint32_t prn_aux = tmp_eph_iter_bds_dnav->second.PRN;
if ((prn_aux == in[i][epoch].PRN) && (((std::string(in[i][epoch].Signal, 2) == std::string("B1")) || (std::string(in[i][epoch].Signal, 2) == std::string("B3"))) && (d_use_unhealthy_sats || (tmp_eph_iter_bds_dnav->second.SV_health == 0))))
if ((prn_aux == gnss_synchro.PRN) && (((std::string(gnss_synchro.Signal, 2) == std::string("B1")) || (std::string(gnss_synchro.Signal, 2) == std::string("B3"))) && (d_use_unhealthy_sats || (tmp_eph_iter_bds_dnav->second.SV_health == 0))))
{
store_valid_observable = true;
}
}
if (std::string(in[i][epoch].Signal, 2) == std::string("E6"))
if (std::string(gnss_synchro.Signal, 2) == std::string("E6"))
{
if (d_osnma_strict)
{
@@ -2119,7 +2131,7 @@ int rtklib_pvt_gs::work(int noutput_items, gr_vector_const_void_star& input_item
if (store_valid_observable)
{
// store valid observables in a map.
d_gnss_observables_map.insert(std::pair<int, Gnss_Synchro>(i, in[i][epoch]));
d_gnss_observables_map.insert(std::pair<int, Gnss_Synchro>(i, gnss_synchro));
}
if (d_rtcm_enabled)
@@ -2130,28 +2142,28 @@ int rtklib_pvt_gs::work(int noutput_items, gr_vector_const_void_star& input_item
{
if (tmp_eph_iter_gps != d_internal_pvt_solver->gps_ephemeris_map.cend())
{
d_rtcm_printer->lock_time(d_internal_pvt_solver->gps_ephemeris_map.find(in[i][epoch].PRN)->second, in[i][epoch].RX_time, in[i][epoch]); // keep track of locking time
d_rtcm_printer->lock_time(d_internal_pvt_solver->gps_ephemeris_map.find(gnss_synchro.PRN)->second, gnss_synchro.RX_time, gnss_synchro); // keep track of locking time
}
}
if (d_internal_pvt_solver->galileo_ephemeris_map.empty() == false)
{
if (tmp_eph_iter_gal != d_internal_pvt_solver->galileo_ephemeris_map.cend())
{
d_rtcm_printer->lock_time(d_internal_pvt_solver->galileo_ephemeris_map.find(in[i][epoch].PRN)->second, in[i][epoch].RX_time, in[i][epoch]); // keep track of locking time
d_rtcm_printer->lock_time(d_internal_pvt_solver->galileo_ephemeris_map.find(gnss_synchro.PRN)->second, gnss_synchro.RX_time, gnss_synchro); // keep track of locking time
}
}
if (d_internal_pvt_solver->gps_cnav_ephemeris_map.empty() == false)
{
if (tmp_eph_iter_cnav != d_internal_pvt_solver->gps_cnav_ephemeris_map.cend())
{
d_rtcm_printer->lock_time(d_internal_pvt_solver->gps_cnav_ephemeris_map.find(in[i][epoch].PRN)->second, in[i][epoch].RX_time, in[i][epoch]); // keep track of locking time
d_rtcm_printer->lock_time(d_internal_pvt_solver->gps_cnav_ephemeris_map.find(gnss_synchro.PRN)->second, gnss_synchro.RX_time, gnss_synchro); // keep track of locking time
}
}
if (d_internal_pvt_solver->glonass_gnav_ephemeris_map.empty() == false)
{
if (tmp_eph_iter_glo_gnav != d_internal_pvt_solver->glonass_gnav_ephemeris_map.cend())
{
d_rtcm_printer->lock_time(d_internal_pvt_solver->glonass_gnav_ephemeris_map.find(in[i][epoch].PRN)->second, in[i][epoch].RX_time, in[i][epoch]); // keep track of locking time
d_rtcm_printer->lock_time(d_internal_pvt_solver->glonass_gnav_ephemeris_map.find(gnss_synchro.PRN)->second, gnss_synchro.RX_time, gnss_synchro); // keep track of locking time
}
}
}
+1 -1
View File
@@ -1366,7 +1366,7 @@ bool Rtklib_Solver::get_PVT(const std::map<int, Gnss_Synchro> &gnss_observables_
case 'G':
case 'J':
{
const bool is_qzss = (gnss_observables_iter->second.PRN >= 193 && gnss_observables_iter->second.PRN <= 203);
const bool is_qzss = (gnss_observables_iter->second.PRN >= 193 && gnss_observables_iter->second.PRN <= 206);
// GPS/QZSS L1
// find the ephemeris for the current SV observation. The SV PRN ID is the map key
const std::string gnss_str = is_qzss ? "QZSS" : "GPS";
+39 -28
View File
@@ -31,6 +31,7 @@ struct QzssL1Entry
{
uint16_t g2_delay_chips; // Provided by ICD, but not applied if using init_g2_octal
uint16_t init_g2_octal; // 10-bit value expressed in octal digits in ICD
uint8_t BOC; // L1 C/B BOC(1,1) flag
};
@@ -40,36 +41,36 @@ static QzssL1Entry qzss_l1_table(uint32_t prn)
switch (prn)
{
case 193:
return {339, 01050};
return {339, 01050, 0};
case 194:
return {208, 01607};
return {208, 01607, 0};
case 195:
return {711, 01747};
return {711, 01747, 0};
case 196:
return {189, 01305};
return {189, 01305, 0};
case 197:
return {263, 00540};
return {263, 00540, 0};
case 198:
return {537, 01363}; // non-standard
return {537, 01363, 0}; // non-standard
case 199:
return {663, 00727};
return {663, 00727, 0};
case 200:
return {942, 00147};
return {942, 00147, 0};
case 201:
return {173, 01206};
return {173, 01206, 0};
case 202:
return {900, 01045}; // non-standard
return {900, 01045, 0}; // non-standard
case 203:
return {30, 00476}; // L1C/B
return {30, 00476, 1}; // L1C/B
case 204:
return {500, 00604}; // L1C/B
return {500, 00604, 1}; // L1C/B
case 205:
return {935, 01757}; // L1C/B
return {935, 01757, 1}; // L1C/B
case 206:
return {556, 01330}; // L1C/B
return {556, 01330, 1}; // L1C/B
default:
LOG(WARNING) << "Unsupported QZSS L1 PRN";
return {0, 0};
return {0, 0, 0};
}
}
@@ -119,15 +120,21 @@ static QzssL5Entry qzss_l5_table(uint32_t prn)
// -----------------------------------
// QZSS L1 C/A code generation
// QZSS L1 C/A(B) code generation
// -----------------------------------
// Generates real QZSS L1 C/A code (1023 chips, +/-1)
bool qzss_l1_code_is_boc(uint32_t prn)
{
return qzss_l1_table(prn).BOC != 0;
}
// Generates real QZSS L1 C/A(B) replica (1023 chips, 2 samples per chip, +/-1)
void qzss_l1_code_gen_float(own::span<float> dest, uint32_t prn)
{
if (dest.size() < QZSS_L1_CODE_LENGTH)
if (dest.size() < QZSS_L1_SAMPLES_PER_CHIP * QZSS_L1_CODE_LENGTH)
{
LOG(WARNING) << "QZSS L1 code must be >= 1023 chips";
LOG(WARNING) << "QZSS L1 code buffer must hold >= " << QZSS_L1_SAMPLES_PER_CHIP * QZSS_L1_CODE_LENGTH << " samples";
return;
}
@@ -143,15 +150,19 @@ void qzss_l1_code_gen_float(own::span<float> dest, uint32_t prn)
g2[s] = static_cast<uint8_t>((init >> s) & 0x1);
}
// Generate 1023 chips
for (int i = 0; i < QZSS_L1_CODE_LENGTH; ++i)
// Generate 1023 chips, 2 samples per chip: both halves equal for L1 C/A,
// sinBOC(1,1) subchips for L1 C/B
for (int i = 0, j = 0; i < QZSS_L1_CODE_LENGTH; ++i)
{
const uint8_t g1_out = g1[9];
const uint8_t g2_out = g2[9];
const auto prn_bit = static_cast<uint8_t>(g1_out ^ g2_out);
// Map 0 -> -1, 1 -> +1
dest[i] = prn_bit ? 1.0F : -1.0F;
// BOC sequence starts with 1, so invert the first subchip
// when generating L1 C/B code
dest[j++] = (prn_bit ^ entry.BOC) ? 1.0F : -1.0F;
dest[j++] = prn_bit ? 1.0F : -1.0F;
// Step G1
const auto g1_fb = static_cast<uint8_t>(g1[2] ^ g1[9]);
@@ -184,7 +195,7 @@ void qzss_l1_code_gen_complex_sampled(
return;
}
std::array<float, QZSS_L1_CODE_LENGTH> code{};
std::array<float, QZSS_L1_SAMPLES_PER_CHIP * QZSS_L1_CODE_LENGTH> code{};
qzss_l1_code_gen_float(own::span<float>(code.data(), code.size()), prn);
const double phase_step = QZSS_L1_CHIP_RATE / static_cast<double>(sampling_freq);
@@ -192,9 +203,9 @@ void qzss_l1_code_gen_complex_sampled(
for (auto& d : dest)
{
int chip = static_cast<int>(code_phase);
chip %= QZSS_L1_CODE_LENGTH;
d = {code[static_cast<size_t>(chip)], 0.0F};
auto sample = static_cast<int>(code_phase * QZSS_L1_SAMPLES_PER_CHIP);
sample %= QZSS_L1_SAMPLES_PER_CHIP * QZSS_L1_CODE_LENGTH;
d = {code[static_cast<size_t>(sample)], 0.0F};
code_phase += phase_step;
if (code_phase >= QZSS_L1_CODE_LENGTH)
@@ -305,7 +316,7 @@ void qzss_l5i_code_gen_complex_sampled(
for (auto& d : dest)
{
int chip = static_cast<int>(code_phase);
auto chip = static_cast<int>(code_phase);
chip %= QZSS_L5_CODE_LENGTH;
d = {code[static_cast<size_t>(chip)], 0.0F};
@@ -338,7 +349,7 @@ void qzss_l5q_code_gen_complex_sampled(
for (auto& d : dest)
{
int chip = static_cast<int>(code_phase);
auto chip = static_cast<int>(code_phase);
chip %= QZSS_L5_CODE_LENGTH;
d = {0.0F, code[static_cast<size_t>(chip)]};
+5 -2
View File
@@ -32,10 +32,13 @@ namespace own = gsl_lite;
/** \addtogroup Algorithm_libs algorithms_libs
* \{ */
//! Generates complex QZSS L1 C/A code for the desired SV ID, and sampled to specific sampling frequency
//! Returns true if the given QZSS PRN broadcasts the BOC(1,1)-modulated L1 C/B signal (IS-QZSS-PNT-006)
bool qzss_l1_code_is_boc(uint32_t prn);
//! Generates complex QZSS L1 C/A(B) code for the desired SV ID, and sampled to specific sampling frequency
void qzss_l1_code_gen_complex_sampled(own::span<std::complex<float>> dest, uint32_t prn, int32_t sampling_freq);
//! Generates real QZSS L1 C/A code for the desired SV ID
//! Generates real QZSS L1 C/A(B) code for the desired SV ID (QZSS_L1_SAMPLES_PER_CHIP samples per chip)
void qzss_l1_code_gen_float(own::span<float> dest, uint32_t prn);
//! Generates complex QZSS L5I code for the desired SV ID, and sampled to specific sampling frequency
+1 -1
View File
@@ -186,7 +186,7 @@ const int MAXPRNGAL = 36; //!< max satellite PRN numbe
const int NSATGAL = (MAXPRNGAL - MINPRNGAL + 1); //!< number of Galileo satellites
const int NSYSGAL = 1;
const int MAXPRNQZS = 202; //!< max satellite PRN number of QZSS
const int MAXPRNQZS = 206; //!< max satellite PRN number of QZSS
const int MINPRNQZS = 193; //!< min satellite PRN number of QZSS
#define ENAQZS
#ifdef ENAQZS
@@ -523,11 +523,14 @@ dll_pll_veml_tracking::dll_pll_veml_tracking(const Dll_Pll_Conf &conf_)
d_code_period = QZSS_L1_CA_CODE_PERIOD_S;
d_code_chip_rate = QZSS_L1_CHIP_RATE;
d_correlation_length_ms = 1;
d_code_samples_per_chip = 1;
// 2 samples per chip: L1 C/A chips duplicated, or L1 C/B sinBOC(1,1) subchips
d_code_samples_per_chip = QZSS_L1_SAMPLES_PER_CHIP;
d_code_length_chips = static_cast<int32_t>(QZSS_L1_CODE_LENGTH);
// QZSS L1 C/A does not have pilot component nor secondary code
d_secondary = false;
d_trk_parameters.track_pilot = false;
// BPSK (L1 C/A) correlation function; updated per PRN in start_tracking()
// when the satellite broadcasts the BOC(1,1)-modulated L1 C/B signal
d_trk_parameters.slope = 1.0;
d_trk_parameters.spc = d_trk_parameters.early_late_space_chips;
d_trk_parameters.y_intercept = 1.0;
@@ -1013,6 +1016,25 @@ void dll_pll_veml_tracking::start_tracking()
else if (d_systemName == "QZSS" && d_signal_type == "J1")
{
qzss_l1_code_gen_float(d_tracking_code, d_acquisition_gnss_synchro->PRN);
if (qzss_l1_code_is_boc(d_acquisition_gnss_synchro->PRN))
{
// L1 C/B is BOC(1,1)-modulated: normalize the DLL discriminator
// with the sinBOC(1,1) correlation function, as done for Galileo E1
d_trk_parameters.slope = static_cast<float>(-CalculateSlopeAbs(&SinBocCorrelationFunction<1, 1>, d_trk_parameters.spc));
d_trk_parameters.y_intercept = static_cast<float>(GetYInterceptAbs(&SinBocCorrelationFunction<1, 1>, d_trk_parameters.spc));
if (d_trk_parameters.spc > 0.33F)
{
LOG(WARNING) << "early_late_space_chips = " << d_trk_parameters.spc
<< " places the correlators outside the BOC(1,1) main peak while tracking QZSS L1 C/B PRN "
<< d_acquisition_gnss_synchro->PRN << "; consider a value below 0.33";
}
}
else
{
// L1 C/A is BPSK: triangular correlation function
d_trk_parameters.slope = 1.0;
d_trk_parameters.y_intercept = 1.0;
}
}
else if (d_systemName == "QZSS" && d_signal_type == "J5")
{
+16 -2
View File
@@ -1958,7 +1958,13 @@ void GNSSFlowgraph::priorize_satellites(const std::vector<std::pair<int, Gnss_Sa
}
for (const auto& signal_str : signal_str_vector)
{
auto& available_signals = available_signals_map_.at(signal_str);
const auto sig_it = available_signals_map_.find(signal_str);
if (sig_it == available_signals_map_.end())
{
// No channels are configured for this signal
continue;
}
auto& available_signals = sig_it->second;
gs = Gnss_Signal(visible_satellite.second, signal_str);
old_size = available_signals.size();
available_signals.remove(gs);
@@ -2096,7 +2102,10 @@ void GNSSFlowgraph::set_signals_list()
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
55, 56, 57, 58, 59, 60, 61, 62, 63}},
{"QZSS", {193, 194, 195, 196, 197, 199, 200, 201}},
// PRNs 198 and 202 are excluded: they are reserved but not assigned to any
// operational satellite (marked as non-standard codes in IS-QZSS-PNT).
// They can still be searched by setting the QZSS.prns configuration option.
{"QZSS", {193, 194, 195, 196, 197, 199, 200, 201, 203, 204, 205, 206}},
};
#if CXX_LESS_THAN_17
for (auto& entry : available_prn_map)
@@ -2170,6 +2179,11 @@ void GNSSFlowgraph::set_signals_list()
for (const auto& prn : available_prn_map.at(gnss_system_str))
{
if (signal_str == "J5" && prn > QZSS_L5_MAX_PRN)
{
// QZSS L1 C/B PRNs (203-206) do not transmit an L5 signal
continue;
}
available_signals.emplace_back(Gnss_Satellite(gnss_system_str, prn), signal_str);
}
}
+1 -1
View File
@@ -309,7 +309,7 @@ void Gnss_Satellite::set_PRN(uint32_t PRN_)
}
else if (system == "QZSS")
{
if (PRN_ < 193 || PRN_ > 201)
if (PRN_ < 193 || PRN_ > 206)
{
DLOG(INFO) << "This PRN is not defined";
PRN = 0;
@@ -36,7 +36,7 @@ Gps_Navigation_Message::Gps_Navigation_Message(LnavSystem system)
const std::string sys_str = (d_system == LnavSystem::GPS) ? "GPS" : "QZSS";
uint32_t prn_min = (d_system == LnavSystem::GPS) ? 1 : 193;
uint32_t prn_max = (d_system == LnavSystem::GPS) ? 32 : 202;
uint32_t prn_max = (d_system == LnavSystem::GPS) ? 32 : 206;
for (uint32_t i = prn_min; i <= prn_max; ++i)
{
@@ -623,7 +623,7 @@ Gps_Ephemeris Gps_Navigation_Message::get_ephemeris() const
if (d_system == LnavSystem::QZSS)
{
auto gnss_sat = Gnss_Satellite();
for (uint32_t i = 193; i < 203; i++)
for (uint32_t i = 193; i < 207; i++)
{
ephemeris.satelliteBlock[i] = gnss_sat.what_block("QZSS", i);
}
+17
View File
@@ -34,6 +34,10 @@ static constexpr double QZSS_L1_CA_CODE_PERIOD_S = 0.001;
static constexpr double QZSS_L5I_CODE_PERIOD_S = 0.001;
static constexpr int QZSS_L1_CODE_LENGTH = 1023;
// The L1 replica is generated with 2 samples per chip, so a single buffer format
// holds either the L1 C/A BPSK chips (each repeated twice) or the L1 C/B
// sinBOC(1,1) subchips, selected per PRN (IS-QZSS-PNT-006)
static constexpr int QZSS_L1_SAMPLES_PER_CHIP = 2;
static constexpr int QZSS_L5_CODE_LENGTH = 10230;
static constexpr int QZSS_L1_PERIOD_MS = 1;
static constexpr int QZSS_L5I_PERIOD_MS = 1;
@@ -57,9 +61,22 @@ static constexpr int32_t QZSS_ALMANAC_EPOCH_HEALTH_SV_ID = 51;
static constexpr int32_t QZSS_IONO_UTC_WIDE_AREA_SV_ID = 56;
static constexpr int32_t QZSS_IONO_UTC_JAPAN_AREA_SV_ID = 61;
static constexpr uint32_t QZSS_PRN_OFFSET = 192U;
static constexpr uint32_t QZSS_L5_MAX_PRN = 202U; //!< L5 PRN codes are only defined for PRNs 193-202 (IS-QZSS-PNT, Table 3.2.5-1); L1 C/B PRNs 203-206 have no L5 counterpart
static constexpr double QZSS_QZO_ECCENTRICITY_REF = 0.06;
static constexpr double QZSS_QZO_INCLINATION_REF = 0.25;
//! \brief Maps QZSS L1 C/B PRNs to the PRN carrying the satellite's nominal PNT signals.
//! L1 C/B is broadcast with a dedicated PRN (203-206), but the transmitting satellite is
//! identified by the PRN of its nominal signals (RINEX 4.00, Table 6: QZSS PRN to RINEX
//! Satellite Identifier): 203->196 (J04), 204->197 (J05), 205->200 (J08), 206->201 (J09).
//! Returns the input PRN unchanged if it is not an L1 C/B alias; in particular,
//! PRN 202 (J10) broadcasts L1 C/B under its own PRN.
constexpr uint32_t qzss_l1cb_prn_to_nominal_prn(uint32_t prn)
{
return prn == 203 ? 196 : (prn == 204 ? 197 : (prn == 205 ? 200 : (prn == 206 ? 201 : prn)));
}
/** \} */
/** \} */
@@ -15,6 +15,7 @@
* -----------------------------------------------------------------------------
*/
#include "qzss.h"
#include "qzss_signal_replica.h"
#include <gtest/gtest.h>
#include <vector>
@@ -23,8 +24,8 @@
TEST(QzssL1Code, Periodicity)
{
constexpr uint32_t prn = 193;
std::vector<float> code1(1023);
std::vector<float> code2(1023);
std::vector<float> code1(QZSS_L1_SAMPLES_PER_CHIP * QZSS_L1_CODE_LENGTH);
std::vector<float> code2(QZSS_L1_SAMPLES_PER_CHIP * QZSS_L1_CODE_LENGTH);
qzss_l1_code_gen_float(code1, prn);
qzss_l1_code_gen_float(code2, prn);
@@ -36,42 +37,54 @@ TEST(QzssL1Code, Periodicity)
TEST(QzssL1Code, GoldenFirst32Chips)
{
constexpr uint32_t prn = 193;
std::vector<float> code(1023);
std::vector<float> code(QZSS_L1_SAMPLES_PER_CHIP * QZSS_L1_CODE_LENGTH);
qzss_l1_code_gen_float(code, prn);
const float golden[32] = {
-1, 1, 1, 1, -1, 1, -1, 1,
1, 1, -1, -1, -1, -1, 1, 1,
-1, -1, -1, -1, -1, -1, 1, -1,
1, -1, -1, -1, -1, -1, -1, 1};
const float golden[64] = {
-1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1,
1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1,
1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1};
for (int i = 0; i < 32; ++i)
for (int i = 0; i < 64; ++i)
{
EXPECT_FLOAT_EQ(code[i], golden[i]);
}
constexpr uint32_t prn2 = 195;
qzss_l1_code_gen_float(code, prn2);
const float golden2[32] = {
-1, -1, -1, -1, -1, 1, 1, -1,
-1, -1, -1, 1, -1, 1, -1, 1,
1, 1, 1, 1, -1, -1, 1, 1,
-1, -1, 1, -1, 1, 1, -1, -1};
for (int i = 0; i < 32; ++i)
const float golden2[64] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1,
-1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1,
-1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1};
for (int i = 0; i < 64; ++i)
{
EXPECT_FLOAT_EQ(code[i], golden2[i]);
}
constexpr uint32_t prn3 = 199;
qzss_l1_code_gen_float(code, prn3);
const float golden3[32] = {
1, -1, -1, -1, 1, -1, 1, -1,
-1, -1, -1, -1, 1, -1, -1, -1,
-1, 1, -1, -1, 1, -1, -1, -1,
-1, 1, 1, 1, -1, 1, -1, -1};
for (int i = 0; i < 32; ++i)
const float golden3[64] = {
1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1,
-1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1,
-1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1};
for (int i = 0; i < 64; ++i)
{
EXPECT_FLOAT_EQ(code[i], golden3[i]);
}
constexpr uint32_t prn4 = 205;
qzss_l1_code_gen_float(code, prn4);
const float golden4[64] = {
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1,
1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1,
1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1,
-1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1};
for (int i = 0; i < 64; ++i)
{
EXPECT_FLOAT_EQ(code[i], golden4[i]);
}
}