mirror of
https://github.com/gnss-sdr/gnss-sdr
synced 2026-10-13 19:21:50 +00:00
Merge branch 'vladisslav2011-qzss_l1cb' into next
This commit is contained in:
@@ -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:
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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)]};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user