From 8fc9276ff5bb423f30be5145d704418e675605f4 Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Tue, 24 Feb 2026 11:40:20 +0100 Subject: [PATCH] Add code generation, acquisition and tracking adapters for QZSS L1 C/A and L5 --- .../PVT/libs/signal_enabled_flags.cc | 14 +- .../PVT/libs/signal_enabled_flags.h | 6 +- .../acquisition/adapters/CMakeLists.txt | 4 + .../adapters/qzss_l1_pcps_acquisition.cc | 46 +++ .../adapters/qzss_l1_pcps_acquisition.h | 62 +++ .../adapters/qzss_l5i_pcps_acquisition.cc | 46 +++ .../adapters/qzss_l5i_pcps_acquisition.h | 62 +++ src/algorithms/libs/CMakeLists.txt | 2 + src/algorithms/libs/qzss_signal_replica.cc | 352 ++++++++++++++++++ src/algorithms/libs/qzss_signal_replica.h | 55 +++ .../tracking/adapters/CMakeLists.txt | 4 + .../adapters/qzss_l1_dll_pll_tracking.cc | 115 ++++++ .../adapters/qzss_l1_dll_pll_tracking.h | 62 +++ .../adapters/qzss_l5_dll_pll_tracking.cc | 87 +++++ .../adapters/qzss_l5_dll_pll_tracking.h | 62 +++ .../gnuradio_blocks/dll_pll_veml_tracking.cc | 94 +++++ src/core/system_parameters/CMakeLists.txt | 1 + src/core/system_parameters/gnss_frequencies.h | 2 + src/core/system_parameters/qzss.h | 58 +++ tests/test_main.cc | 1 + .../qzss_code_generation_test.cc | 88 +++++ 21 files changed, 1215 insertions(+), 8 deletions(-) create mode 100644 src/algorithms/acquisition/adapters/qzss_l1_pcps_acquisition.cc create mode 100644 src/algorithms/acquisition/adapters/qzss_l1_pcps_acquisition.h create mode 100644 src/algorithms/acquisition/adapters/qzss_l5i_pcps_acquisition.cc create mode 100644 src/algorithms/acquisition/adapters/qzss_l5i_pcps_acquisition.h create mode 100644 src/algorithms/libs/qzss_signal_replica.cc create mode 100644 src/algorithms/libs/qzss_signal_replica.h create mode 100644 src/algorithms/tracking/adapters/qzss_l1_dll_pll_tracking.cc create mode 100644 src/algorithms/tracking/adapters/qzss_l1_dll_pll_tracking.h create mode 100644 src/algorithms/tracking/adapters/qzss_l5_dll_pll_tracking.cc create mode 100644 src/algorithms/tracking/adapters/qzss_l5_dll_pll_tracking.h create mode 100644 src/core/system_parameters/qzss.h create mode 100644 tests/unit-tests/system-parameters/qzss_code_generation_test.cc diff --git a/src/algorithms/PVT/libs/signal_enabled_flags.cc b/src/algorithms/PVT/libs/signal_enabled_flags.cc index 031eacf13..9f984c083 100644 --- a/src/algorithms/PVT/libs/signal_enabled_flags.cc +++ b/src/algorithms/PVT/libs/signal_enabled_flags.cc @@ -36,8 +36,8 @@ uint32_t flags_from_config(const ConfigurationInterface* configuration) {GLO_2G, "Channels_2G.count"}, {BDS_B1, "Channels_B1.count"}, {BDS_B3, "Channels_B3.count"}, - {QZS_L1, "Channels_Q1.count"}, - {QZS_L5, "Channels_Q5.count"} + {QZS_J1, "Channels_J1.count"}, + {QZS_J5, "Channels_J5.count"} }; for (const auto& pair_aux : signal_flag_to_prop) @@ -67,9 +67,11 @@ Signal_Enabled_Flags::Signal_Enabled_Flags(uint32_t flags_) : flags(flags_), has_galileo(check_any_enabled(GAL_1B, GAL_E5a, GAL_E5b, GAL_E6)), has_glonass(check_any_enabled(GLO_1G, GLO_2G)), has_beidou(check_any_enabled(BDS_B1, BDS_B3)), - only_gps(has_gps && !(has_galileo || has_glonass || has_beidou)), - only_galileo(has_galileo && !(has_gps || has_glonass || has_beidou)), - only_glonass(has_glonass && !(has_gps || has_galileo || has_beidou)), - only_beidou(has_beidou && !(has_gps || has_galileo || has_glonass)) + has_qzss(check_any_enabled(QZS_J1, QZS_J5)), + only_gps(has_gps && !(has_galileo || has_glonass || has_beidou || has_qzss)), + only_galileo(has_galileo && !(has_gps || has_glonass || has_beidou || has_qzss)), + only_glonass(has_glonass && !(has_gps || has_galileo || has_beidou || has_qzss)), + only_beidou(has_beidou && !(has_gps || has_galileo || has_glonass || has_qzss)), + only_qzss(has_qzss && !(has_gps || has_galileo || has_glonass || has_beidou)) { } diff --git a/src/algorithms/PVT/libs/signal_enabled_flags.h b/src/algorithms/PVT/libs/signal_enabled_flags.h index 66dfd5a4c..5092eb620 100644 --- a/src/algorithms/PVT/libs/signal_enabled_flags.h +++ b/src/algorithms/PVT/libs/signal_enabled_flags.h @@ -34,8 +34,8 @@ enum signal_flag : uint32_t GLO_2G = 0x1 << 8, BDS_B1 = 0x1 << 9, BDS_B3 = 0x1 << 10, - QZS_L1 = 0x1 << 11, - QZS_L5 = 0x1 << 12 + QZS_J1 = 0x1 << 11, + QZS_J5 = 0x1 << 12 }; class Signal_Enabled_Flags @@ -74,11 +74,13 @@ public: const bool has_galileo; const bool has_glonass; const bool has_beidou; + const bool has_qzss; const bool only_gps; const bool only_galileo; const bool only_glonass; const bool only_beidou; + const bool only_qzss; }; #endif // GNSS_SDR_SIGNAL_ENABLED_FLAGS_H diff --git a/src/algorithms/acquisition/adapters/CMakeLists.txt b/src/algorithms/acquisition/adapters/CMakeLists.txt index 003ed544a..ede6b2cae 100644 --- a/src/algorithms/acquisition/adapters/CMakeLists.txt +++ b/src/algorithms/acquisition/adapters/CMakeLists.txt @@ -28,6 +28,8 @@ set(ACQ_ADAPTER_SOURCES glonass_l2_ca_pcps_acquisition.cc beidou_b1i_pcps_acquisition.cc beidou_b3i_pcps_acquisition.cc + qzss_l1_pcps_acquisition.cc + qzss_l5i_pcps_acquisition.cc ) set(ACQ_ADAPTER_HEADERS @@ -53,6 +55,8 @@ set(ACQ_ADAPTER_HEADERS glonass_l2_ca_pcps_acquisition.h beidou_b1i_pcps_acquisition.h beidou_b3i_pcps_acquisition.h + qzss_l1_pcps_acquisition.h + qzss_l5i_pcps_acquisition.h ) if(ENABLE_FPGA) diff --git a/src/algorithms/acquisition/adapters/qzss_l1_pcps_acquisition.cc b/src/algorithms/acquisition/adapters/qzss_l1_pcps_acquisition.cc new file mode 100644 index 000000000..14cfd04f2 --- /dev/null +++ b/src/algorithms/acquisition/adapters/qzss_l1_pcps_acquisition.cc @@ -0,0 +1,46 @@ +/*! + * \file qzss_l1_pcps_acquisition.cc + * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for + * QZSS L1 signals + * \authors + * + * ----------------------------------------------------------------------------- + * + * 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 "qzss_l1_pcps_acquisition.h" +#include "qzss.h" +#include "qzss_signal_replica.h" + + +QzssL1PcpsAcquisition::QzssL1PcpsAcquisition( + const ConfigurationInterface* configuration, + const std::string& role, + unsigned int in_streams, + unsigned int out_streams) + : BasePcpsAcquisition(configuration, + role, + in_streams, + out_streams, + QZSS_L1_CHIP_RATE, + QZSS_L1_OPT_ACQ_FS_SPS, + QZSS_L1_CODE_LENGTH, + QZSS_L1_PERIOD_MS) +{ +} + + +void QzssL1PcpsAcquisition::code_gen_complex_sampled(own::span> dest, uint32_t prn, int32_t sampling_freq) +{ + qzss_l1_code_gen_complex_sampled(dest, prn, sampling_freq); +} \ No newline at end of file diff --git a/src/algorithms/acquisition/adapters/qzss_l1_pcps_acquisition.h b/src/algorithms/acquisition/adapters/qzss_l1_pcps_acquisition.h new file mode 100644 index 000000000..957ce47e3 --- /dev/null +++ b/src/algorithms/acquisition/adapters/qzss_l1_pcps_acquisition.h @@ -0,0 +1,62 @@ +/*! + * \file qzss_l1_pcps_acquisition.h + * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for + * QZSS L1 signals + * \authors + * + * ----------------------------------------------------------------------------- + * + * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. + * This file is part of GNSS-SDR. + * + * Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ + +#ifndef GNSS_SDR_QZSS_L1_PCPS_ACQUISITION_H +#define GNSS_SDR_QZSS_L1_PCPS_ACQUISITION_H + +#include "base_pcps_acquisition.h" + +/** \addtogroup Acquisition + * \{ */ +/** \addtogroup Acq_adapters + * \{ */ + +/*! + * \brief This class adapts a PCPS acquisition block to an AcquisitionInterface + * for QZSS L1 signals + */ +class QzssL1PcpsAcquisition : public BasePcpsAcquisition +{ +public: + QzssL1PcpsAcquisition( + const ConfigurationInterface* configuration, + const std::string& role, + unsigned int in_streams, + unsigned int out_streams); + + ~QzssL1PcpsAcquisition() = default; + + /*! + * \brief Returns "QZSS_L1_PCPS_Acquisition" + */ + inline std::string implementation() override + { + return "QZSS_L1_PCPS_Acquisition"; + } + +private: + void code_gen_complex_sampled(own::span> dest, uint32_t prn, int32_t sampling_freq) override; +}; + + +/** \} */ +/** \} */ + + +#endif // GNSS_SDR_QZSS_L1_PCPS_ACQUISITION_H \ No newline at end of file diff --git a/src/algorithms/acquisition/adapters/qzss_l5i_pcps_acquisition.cc b/src/algorithms/acquisition/adapters/qzss_l5i_pcps_acquisition.cc new file mode 100644 index 000000000..6af23fe74 --- /dev/null +++ b/src/algorithms/acquisition/adapters/qzss_l5i_pcps_acquisition.cc @@ -0,0 +1,46 @@ +/*! + * \file qzss_l5i_pcps_acquisition.cc + * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for + * QZSS L5I signals + * \authors
    + *
  • Carles Fernandez, 2026. cfernandez(at)cttc.es + *
+ * + * ----------------------------------------------------------------------------- + * + * 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 "qzss_l5i_pcps_acquisition.h" +#include "qzss.h" +#include "qzss_signal_replica.h" + + +QzssL5iPcpsAcquisition::QzssL5iPcpsAcquisition( + const ConfigurationInterface* configuration, + const std::string& role, + unsigned int in_streams, + unsigned int out_streams) + : BasePcpsAcquisition(configuration, + role, + in_streams, + out_streams, + QZSS_L5_CHIP_RATE, + QZSS_L5_OPT_ACQ_FS_SPS, + QZSS_L5_CODE_LENGTH, + QZSS_L5I_PERIOD_MS) +{ +} + + +void QzssL5iPcpsAcquisition::code_gen_complex_sampled(own::span> dest, uint32_t prn, int32_t sampling_freq) +{ + qzss_l5i_code_gen_complex_sampled(dest, prn, sampling_freq); +} \ No newline at end of file diff --git a/src/algorithms/acquisition/adapters/qzss_l5i_pcps_acquisition.h b/src/algorithms/acquisition/adapters/qzss_l5i_pcps_acquisition.h new file mode 100644 index 000000000..bdc033cd5 --- /dev/null +++ b/src/algorithms/acquisition/adapters/qzss_l5i_pcps_acquisition.h @@ -0,0 +1,62 @@ +/*! + * \file qzss_l5i_pcps_acquisition.h + * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for + * QZSS L5I signals + * \authors
    + *
  • Carles Fernandez, 2026. cfernandez(at)cttc.es + *
+ * + * ----------------------------------------------------------------------------- + * + * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. + * This file is part of GNSS-SDR. + * + * Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ + +#ifndef GNSS_SDR_QZSS_L5I_PCPS_ACQUISITION_H +#define GNSS_SDR_QZSS_L5I_PCPS_ACQUISITION_H + +#include "base_pcps_acquisition.h" + +/** \addtogroup Acquisition + * \{ */ +/** \addtogroup Acq_adapters + * \{ */ + +/*! + * \brief This class adapts a PCPS acquisition block to an AcquisitionInterface + * for QZSS L5I signals + */ +class QzssL5iPcpsAcquisition : public BasePcpsAcquisition +{ +public: + QzssL5iPcpsAcquisition( + const ConfigurationInterface* configuration, + const std::string& role, + unsigned int in_streams, + unsigned int out_streams); + + ~QzssL5iPcpsAcquisition() = default; + + /*! + * \brief Returns "QZSS_L5I_PCPS_Acquisition" + */ + inline std::string implementation() override + { + return "QZSS_L5I_PCPS_Acquisition"; + } + +private: + void code_gen_complex_sampled(own::span> dest, uint32_t prn, int32_t sampling_freq) override; +}; + + +/** \} */ +/** \} */ + + +#endif // GNSS_SDR_QZSS_L5I_PCPS_ACQUISITION_H \ No newline at end of file diff --git a/src/algorithms/libs/CMakeLists.txt b/src/algorithms/libs/CMakeLists.txt index 898aa8194..dc564821a 100644 --- a/src/algorithms/libs/CMakeLists.txt +++ b/src/algorithms/libs/CMakeLists.txt @@ -32,6 +32,7 @@ set(GNSS_SPLIBS_SOURCES pass_through.cc short_x2_to_cshort.cc gnss_sdr_string_literals.cc + qzss_signal_replica.cc ) set(GNSS_SPLIBS_HEADERS @@ -66,6 +67,7 @@ set(GNSS_SPLIBS_HEADERS gnss_sdr_string_literals.h gnss_time.h matlab_writter_helper.h + qzss_signal_replica.h ) set(GNSS_SPLIBS_HEADERS ${GNSS_SPLIBS_HEADERS} diff --git a/src/algorithms/libs/qzss_signal_replica.cc b/src/algorithms/libs/qzss_signal_replica.cc new file mode 100644 index 000000000..9317d3e8f --- /dev/null +++ b/src/algorithms/libs/qzss_signal_replica.cc @@ -0,0 +1,352 @@ +/*! + * \file qzss_signal_replica.cc + * \brief This file implements signal generators for QZSS signals + * \author Carles Fernández-Prades, 2026. cfernandez (at) cttc.es + * + * ----------------------------------------------------------------------------- + * + * 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 "qzss_signal_replica.h" +#include "qzss.h" +#include +#if USE_GLOG_AND_GFLAGS +#include +#else +#include +#endif + +// ----------------------------- +// QZSS L1 C/A utils +// ----------------------------- + +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 +}; + + +// IS-QZSS-PNT-006 Table 3.2.2-1 (PRN 193..206) +static QzssL1Entry qzss_l1_table(uint32_t prn) +{ + switch (prn) + { + case 193: + return {339, 01050}; + case 194: + return {208, 01607}; + case 195: + return {711, 01747}; + case 196: + return {189, 01305}; + case 197: + return {263, 00540}; + case 198: + return {537, 01363}; // non-standard + case 199: + return {663, 00727}; + case 200: + return {942, 00147}; + case 201: + return {173, 01206}; + case 202: + return {900, 01045}; // non-standard + case 203: + return {30, 00476}; // L1C/B + case 204: + return {500, 00604}; // L1C/B + case 205: + return {935, 01757}; // L1C/B + case 206: + return {556, 01330}; // L1C/B + default: + LOG(WARNING) << "Unsupported QZSS L1 PRN"; + return {0, 0}; + } +} + +// ----------------------------- +// QZSS L5 utils +// ----------------------------- + +struct QzssL5Entry +{ + uint16_t init_i5; + uint16_t init_q5; + int advance_i5; + int advance_q5; +}; + + +// IS-QZSS-PNT-006 Table 3.2.5-1 +static QzssL5Entry qzss_l5_table(uint32_t prn) +{ + switch (prn) + { + case 193: + return {0b0110000101110, 0b1001110000111, 5836, 4757}; + case 194: + return {0b0110010011111, 0b0110100111010, 926, 427}; + case 195: + return {0b1000111001100, 0b0110001100110, 6086, 5452}; + case 196: + return {0b111101110001, 0b0000100001100, 950, 5182}; + case 197: + return {0b0011111100001, 0b0101000101101, 5905, 6606}; + case 198: + return {0b0000001110001, 0b1000001010111, 3240, 6531}; + case 199: + return {0b1010110100100, 0b0011001110001, 6675, 4268}; + case 200: + return {0b0100001110110, 0b0100011100110, 3197, 3115}; + case 201: + return {0b0111110100011, 0b0100101100101, 1555, 6835}; + case 202: + return {0b0001111001011, 0b1110001010111, 3589, 862}; + default: + LOG(WARNING) << "Unsupported QZSS L5 PRN"; + return {0, 0, 0, 0}; + } +} + + +// ----------------------------------- +// QZSS L1 C/A code generation +// ----------------------------------- + +// Generates real QZSS L1 C/A code (1023 chips, +/-1) +void qzss_l1_code_gen_float(own::span dest, uint32_t prn) +{ + if (dest.size() != QZSS_L1_CODE_LENGTH) + { + LOG(WARNING) << "QZSS L1 code must be 1023 chips"; + return; + } + + const auto entry = qzss_l1_table(prn); + + std::array g1{}; + std::array g2{}; + + g1.fill(1); + const uint16_t init = entry.init_g2_octal; + for (int s = 0; s < 10; ++s) + { + g2[s] = static_cast((init >> s) & 0x1); + } + + // Generate 1023 chips + for (int i = 0; i < QZSS_L1_CODE_LENGTH; ++i) + { + const uint8_t g1_out = g1[9]; + const uint8_t g2_out = g2[9]; + const uint8_t prn_bit = static_cast(g1_out ^ g2_out); + + // Map 0 -> -1, 1 -> +1 + dest[i] = prn_bit ? 1.0F : -1.0F; + + // Step G1 + const uint8_t g1_fb = static_cast(g1[2] ^ g1[9]); + for (int k = 9; k > 0; --k) + { + g1[k] = g1[k - 1]; + } + g1[0] = g1_fb; + + // Step G2 + const uint8_t g2_fb = static_cast( + g2[1] ^ g2[2] ^ g2[5] ^ g2[7] ^ g2[8] ^ g2[9]); + for (int k = 9; k > 0; --k) + { + g2[k] = g2[k - 1]; + } + g2[0] = g2_fb; + } +} + + +void qzss_l1_code_gen_complex_sampled( + own::span> dest, + uint32_t prn, + int32_t sampling_freq) +{ + if (sampling_freq <= 0) + { + LOG(WARNING) << "Invalid sampling frequency"; + return; + } + + std::array code{}; + qzss_l1_code_gen_float(own::span(code.data(), code.size()), prn); + + const double phase_step = QZSS_L1_CHIP_RATE / static_cast(sampling_freq); + double code_phase = 0.0; + + for (size_t i = 0; i < dest.size(); ++i) + { + int chip = static_cast(code_phase); + chip %= QZSS_L1_CODE_LENGTH; + dest[i] = {code[static_cast(chip)], 0.0F}; + + code_phase += phase_step; + if (code_phase >= QZSS_L1_CODE_LENGTH) + { + code_phase -= QZSS_L1_CODE_LENGTH; + } + } +} + + +// ----------------------------------- +// QZSS L5 (I&Q) code generation +// ----------------------------------- + +static uint8_t xa_step(uint16_t& state) +{ + const uint8_t out = static_cast(state & 0x1); + const uint8_t fb = static_cast( + ((state >> 4) ^ (state >> 3) ^ (state >> 1) ^ (state >> 0)) & 0x1); + + uint16_t next = static_cast((state >> 1) | (static_cast(fb) << 12)); + + if (state == XA_SHORT_DECODE) + { + next = XA_ALL_ONES; + } + + state = next; + return out; +} + + +static uint8_t xb_step(uint16_t& state) +{ + const uint8_t out = static_cast(state & 0x1); + const uint8_t fb = static_cast( + ((state >> 12) ^ (state >> 10) ^ (state >> 9) ^ (state >> 7) ^ + (state >> 6) ^ (state >> 5) ^ (state >> 1) ^ (state >> 0)) & + 0x1); + + state = static_cast((state >> 1) | (static_cast(fb) << 12)); + return out; +} + + +void qzss_l5i_code_gen_float(own::span dest, uint32_t prn) +{ + if (dest.size() != QZSS_L5_CODE_LENGTH) + { + LOG(WARNING) << "L5I code must be 10230 chips"; + return; + } + + const auto entry = qzss_l5_table(prn); + uint16_t xa_state = XA_ALL_ONES; + uint16_t xb_state = entry.init_i5; + + for (int i = 0; i < QZSS_L5_CODE_LENGTH; ++i) + { + const uint8_t xa = xa_step(xa_state); + const uint8_t xb = xb_step(xb_state); + const uint8_t bit = static_cast(xa ^ xb); + + dest[i] = bit ? 1.0F : -1.0F; + } +} + + +void qzss_l5q_code_gen_float(own::span dest, uint32_t prn) +{ + if (dest.size() != QZSS_L5_CODE_LENGTH) + { + LOG(WARNING) << "L5Q code must be 10230 chips"; + return; + } + + const auto entry = qzss_l5_table(prn); + uint16_t xa_state = XA_ALL_ONES; + uint16_t xb_state = entry.init_q5; + + for (int i = 0; i < QZSS_L5_CODE_LENGTH; ++i) + { + const uint8_t xa = xa_step(xa_state); + const uint8_t xb = xb_step(xb_state); + const uint8_t bit = static_cast(xa ^ xb); + + dest[i] = bit ? 1.0F : -1.0F; + } +} + + +void qzss_l5i_code_gen_complex_sampled( + own::span> dest, + uint32_t prn, + int32_t sampling_freq) +{ + if (sampling_freq <= 0) + { + LOG(WARNING) << "Invalid sampling frequency"; + return; + } + + std::array code{}; + qzss_l5i_code_gen_float(own::span(code.data(), code.size()), prn); + + const double phase_step = QZSS_L5_CHIP_RATE / static_cast(sampling_freq); + double code_phase = 0.0; + + for (size_t i = 0; i < dest.size(); ++i) + { + int chip = static_cast(code_phase); + chip %= QZSS_L5_CODE_LENGTH; + + dest[i] = {code[static_cast(chip)], 0.0F}; + + code_phase += phase_step; + if (code_phase >= QZSS_L5_CODE_LENGTH) + { + code_phase -= QZSS_L5_CODE_LENGTH; + } + } +} + + +void qzss_l5q_code_gen_complex_sampled( + own::span> dest, + uint32_t prn, + int32_t sampling_freq) +{ + if (sampling_freq <= 0) + { + LOG(WARNING) << "Invalid sampling frequency"; + return; + } + + std::array code{}; + qzss_l5q_code_gen_float(own::span(code.data(), code.size()), prn); + + const double phase_step = QZSS_L5_CHIP_RATE / static_cast(sampling_freq); + double code_phase = 0.0; + + for (size_t i = 0; i < dest.size(); ++i) + { + int chip = static_cast(code_phase); + chip %= QZSS_L5_CODE_LENGTH; + + dest[i] = {0.0F, code[static_cast(chip)]}; + + code_phase += phase_step; + if (code_phase >= QZSS_L5_CODE_LENGTH) + { + code_phase -= QZSS_L5_CODE_LENGTH; + } + } +} \ No newline at end of file diff --git a/src/algorithms/libs/qzss_signal_replica.h b/src/algorithms/libs/qzss_signal_replica.h new file mode 100644 index 000000000..c0f511a3f --- /dev/null +++ b/src/algorithms/libs/qzss_signal_replica.h @@ -0,0 +1,55 @@ +/*! + * \file qzss_signal_replica.h + * \brief This file implements signal generators for QZSS signals + * \author Carles Fernández-Prades, 2026. cfernandez (at) cttc.es + * + * ----------------------------------------------------------------------------- + * + * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. + * This file is part of GNSS-SDR. + * + * Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ + +#ifndef GNSS_SDR_QZSS_SIGNAL_REPLICA_H +#define GNSS_SDR_QZSS_SIGNAL_REPLICA_H + +#include +#include +#if HAS_STD_SPAN +#include +namespace own = std; +#else +#include +namespace own = gsl_lite; +#endif + +/** \addtogroup Algorithms_Library + * \{ */ +/** \addtogroup Algorithm_libs algorithms_libs + * \{ */ + +//! Generates complex QZSS L1 C/A code for the desired SV ID, and sampled to specific sampling frequency +void qzss_l1_code_gen_complex_sampled(own::span> dest, uint32_t prn, int32_t sampling_freq); + +//! Generates real QZSS L1 C/A code for the desired SV ID +void qzss_l1_code_gen_float(own::span dest, uint32_t prn); + +//! Generates complex QZSS L5I code for the desired SV ID, and sampled to specific sampling frequency +void qzss_l5i_code_gen_complex_sampled(own::span> dest, uint32_t prn, int32_t sampling_freq); + +//! Generates real QZSS L5I code for the desired SV ID +void qzss_l5i_code_gen_float(own::span dest, uint32_t prn); + +//! Generates complex QZSS L5Q code for the desired SV ID, and sampled to specific sampling frequency +void qzss_l5q_code_gen_complex_sampled(own::span> dest, uint32_t prn, int32_t sampling_freq); + +//! Generates real QZSS L5I code for the desired SV ID +void qzss_l5q_code_gen_float(own::span dest, uint32_t prn); + +/** \} */ +/** \} */ +#endif // GNSS_SDR_QZSS_SIGNAL_REPLICA_H \ No newline at end of file diff --git a/src/algorithms/tracking/adapters/CMakeLists.txt b/src/algorithms/tracking/adapters/CMakeLists.txt index 1bc8ab106..4806fdbd1 100644 --- a/src/algorithms/tracking/adapters/CMakeLists.txt +++ b/src/algorithms/tracking/adapters/CMakeLists.txt @@ -53,6 +53,8 @@ set(TRACKING_ADAPTER_SOURCES gps_l1_ca_tcp_connector_tracking.cc gps_l1_ca_gaussian_tracking.cc gps_l1_ca_kf_tracking.cc + qzss_l1_dll_pll_tracking.cc + qzss_l5_dll_pll_tracking.cc ${OPT_TRACKING_ADAPTERS_SOURCES} ) @@ -73,6 +75,8 @@ set(TRACKING_ADAPTER_HEADERS gps_l1_ca_tcp_connector_tracking.h gps_l1_ca_gaussian_tracking.h gps_l1_ca_kf_tracking.h + qzss_l1_dll_pll_tracking.h + qzss_l5_dll_pll_tracking.h ${OPT_TRACKING_ADAPTERS_HEADERS} ) diff --git a/src/algorithms/tracking/adapters/qzss_l1_dll_pll_tracking.cc b/src/algorithms/tracking/adapters/qzss_l1_dll_pll_tracking.cc new file mode 100644 index 000000000..ff33768ab --- /dev/null +++ b/src/algorithms/tracking/adapters/qzss_l1_dll_pll_tracking.cc @@ -0,0 +1,115 @@ +/*! + * \file qzss_l1_dll_pll_tracking.cc + * \brief Interface of an adapter of a DLL+PLL tracking loop block + * for QZSS L1 signals to a TrackingInterface + * \author Carles Fernandez, 2026. cfernandez(at)cttc.es + * + * Code DLL + carrier PLL according to the algorithms described in: + * K.Borre, D.M.Akos, N.Bertelsen, P.Rinder, and S.H.Jensen, + * A Software-Defined GPS and Galileo Receiver. A Single-Frequency + * Approach, Birkhauser, 2007 + * + * ----------------------------------------------------------------------------- + * + * 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 "qzss_l1_dll_pll_tracking.h" +#include "configuration_interface.h" +#include "display.h" +#include "qzss.h" +#include +#include +#include +#include + +#if USE_GLOG_AND_GFLAGS +#include +#else +#include +#endif + +QzssL1DllPllTracking::QzssL1DllPllTracking( + const ConfigurationInterface* configuration, + const std::string& role, + unsigned int in_streams, + unsigned int out_streams) + : BaseDllPllTracking(configuration, role, in_streams, out_streams) +{ + configure_tracking_parameters(configuration); + create_tracking_block(); +} + + +void QzssL1DllPllTracking::configure_tracking_parameters( + const ConfigurationInterface* configuration __attribute__((unused))) +{ + // Set basic signal identifiers + config_params().system = 'Q'; + const std::array sig{'J', '1', '\0'}; + std::copy_n(sig.data(), 3, config_params().signal); + + const auto vector_length = static_cast(std::round(config_params().fs_in / (QZSS_L1_CHIP_RATE / QZSS_L1_CODE_LENGTH))); + config_params().vector_length = vector_length; + + // Sanity checks and warnings + if (config_params().extend_correlation_symbols < 1) + { + config_params().extend_correlation_symbols = 1; + std::cout << TEXT_RED + << "WARNING: QZSS L1 C/A: extend_correlation_symbols must be > 0. " + << "Coherent integration set to 1 ms." + << TEXT_RESET << std::endl; + } + else if (config_params().extend_correlation_symbols > 20) + { + config_params().extend_correlation_symbols = 20; + std::cout << TEXT_RED + << "WARNING: QZSS L1 C/A: extend_correlation_symbols limited to 20 (20 ms)." + << TEXT_RESET << std::endl; + } + + // QZSS L1 C/A does not have a pilot component + config_params().track_pilot = configuration->property(this->role() + ".track_pilot", false); + if (config_params().track_pilot) + { + config_params().track_pilot = false; + std::cout << TEXT_RED + << "WARNING: QZSS L1 C/A does not have pilot signal. " + << "Data tracking enabled instead." + << TEXT_RESET << std::endl; + } + + // Ensure bandwidth sanity when narrow-band is enabled + if ((config_params().extend_correlation_symbols > 1) && + (config_params().pll_bw_narrow_hz > config_params().pll_bw_hz || + config_params().dll_bw_narrow_hz > config_params().dll_bw_hz)) + { + std::cout << TEXT_RED + << "WARNING: QZSS L1 C/A: Narrow tracking bandwidth is higher than wide bandwidth." + << TEXT_RESET << std::endl; + } +} + + +void QzssL1DllPllTracking::create_tracking_block() +{ + // Create GNU Radio block + if (config_params().item_type == "gr_complex") + { + tracking_sptr_ = dll_pll_veml_make_tracking(config_params()); + DLOG(INFO) << "Tracking block (" << tracking_sptr_->unique_id() << ")"; + } + else + { + set_item_size(0); + tracking_sptr_ = nullptr; + LOG(WARNING) << config_params().item_type << " unknown tracking item type."; + } +} diff --git a/src/algorithms/tracking/adapters/qzss_l1_dll_pll_tracking.h b/src/algorithms/tracking/adapters/qzss_l1_dll_pll_tracking.h new file mode 100644 index 000000000..6f3106f43 --- /dev/null +++ b/src/algorithms/tracking/adapters/qzss_l1_dll_pll_tracking.h @@ -0,0 +1,62 @@ +/*! + * \file qzss_l1_dll_pll_tracking.h + * \brief Interface of an adapter of a DLL+PLL tracking loop block + * for QZSS L1 signals to a TrackingInterface + * \author Carles Fernandez, 2026. cfernandez(at)cttc.es + * + * Code DLL + carrier PLL according to the algorithms described in: + * K.Borre, D.M.Akos, N.Bertelsen, P.Rinder, and S.H.Jensen, + * A Software-Defined GPS and Galileo Receiver. A Single-Frequency + * Approach, Birkhauser, 2007 + * + * ----------------------------------------------------------------------------- + * + * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. + * This file is part of GNSS-SDR. + * + * Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ + +#ifndef GNSS_SDR_QZSS_L1_DLL_PLL_TRACKING_H +#define GNSS_SDR_QZSS_L1_DLL_PLL_TRACKING_H + +#include "base_dll_pll_tracking.h" + +/** \addtogroup Tracking + * Classes for GNSS signal tracking. + * \{ */ +/** \addtogroup Tracking_adapters tracking_adapters + * Wrap GNU Radio blocks for GNSS signal tracking with a TrackingInterface + * \{ */ + + +/*! + * \brief This class implements a code DLL + carrier PLL tracking loop + * block adapter for QZSS L1 signals + */ +class QzssL1DllPllTracking : public BaseDllPllTracking +{ +public: + //! Constructor + QzssL1DllPllTracking(const ConfigurationInterface* configuration, + const std::string& role, + unsigned int in_streams, + unsigned int out_streams); + + //! Returns "QZSS_L1_CA_DLL_PLL_Tracking" + inline std::string implementation() override + { + return "QZSS_L1_CA_DLL_PLL_Tracking"; + } + +private: + void configure_tracking_parameters(const ConfigurationInterface* configuration) override; + void create_tracking_block() override; +}; + +/** \} */ +/** \} */ +#endif // GNSS_SDR_QZSS_L1_CA_DLL_PLL_TRACKING_H diff --git a/src/algorithms/tracking/adapters/qzss_l5_dll_pll_tracking.cc b/src/algorithms/tracking/adapters/qzss_l5_dll_pll_tracking.cc new file mode 100644 index 000000000..fe96a71b7 --- /dev/null +++ b/src/algorithms/tracking/adapters/qzss_l5_dll_pll_tracking.cc @@ -0,0 +1,87 @@ +/*! + * \file qzss_l5_dll_pll_tracking.cc + * \brief Interface of an adapter of a DLL+PLL tracking loop block + * for QZSS L5 signals to a TrackingInterface + * \author Carles Fernandez, 2026. cfernandez(at)cttc.es + * + * Code DLL + carrier PLL according to the algorithms described in: + * K.Borre, D.M.Akos, N.Bertelsen, P.Rinder, and S.H.Jensen, + * A Software-Defined GPS and Galileo Receiver. A Single-Frequency + * Approach, Birkhauser, 2007 + * + * ----------------------------------------------------------------------------- + * + * 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 "qzss_l5_dll_pll_tracking.h" +#include "qzss.h" +#include "configuration_interface.h" +#include "display.h" +#include +#include + +#if USE_GLOG_AND_GFLAGS +#include +#else +#include +#endif + +QzssL5DllPllTracking::QzssL5DllPllTracking( + const ConfigurationInterface* configuration, + const std::string& role, + unsigned int in_streams, + unsigned int out_streams) + : BaseDllPllTracking(configuration, role, in_streams, out_streams) +{ + configure_tracking_parameters(configuration); + create_tracking_block(); +} + + +void QzssL5DllPllTracking::configure_tracking_parameters( + const ConfigurationInterface* configuration __attribute__((unused))) +{ + const auto vector_length = static_cast(std::round(static_cast(config_params().fs_in) / (static_cast(QZSS_L5_CHIP_RATE) / static_cast(QZSS_L5_CODE_LENGTH)))); + config_params().vector_length = vector_length; + if (config_params().extend_correlation_symbols < 1) + { + config_params().extend_correlation_symbols = 1; + std::cout << TEXT_RED << "WARNING: QZSS L5. extend_correlation_symbols must be bigger than 0. Coherent integration has been set to 1 symbol (1 ms)" << TEXT_RESET << '\n'; + } + else if (!config_params().track_pilot and config_params().extend_correlation_symbols > QZSS_L5I_NH_CODE_LENGTH) + { + config_params().extend_correlation_symbols = QZSS_L5I_NH_CODE_LENGTH; + std::cout << TEXT_RED << "WARNING: QZSS L5. extend_correlation_symbols must be lower than 11 when tracking the data component. Coherent integration has been set to 10 symbols (10 ms)" << TEXT_RESET << '\n'; + } + if ((config_params().extend_correlation_symbols > 1) and (config_params().pll_bw_narrow_hz > config_params().pll_bw_hz or config_params().dll_bw_narrow_hz > config_params().dll_bw_hz)) + { + std::cout << TEXT_RED << "WARNING: QZSS L5. PLL or DLL narrow tracking bandwidth is higher than wide tracking one" << TEXT_RESET << '\n'; + } + config_params().system = 'Q'; + const std::array sig{'J', '5', '\0'}; + std::copy_n(sig.data(), 3, config_params().signal); +} + + +void QzssL5DllPllTracking::create_tracking_block() +{ + // ################# Make a GNU Radio Tracking block object ################ + if (config_params().item_type == "gr_complex") + { + tracking_sptr_ = dll_pll_veml_make_tracking(config_params()); + DLOG(INFO) << "tracking(" << tracking_sptr_->unique_id() << ")"; + } + else + { + set_item_size(0); + tracking_sptr_ = nullptr; + LOG(WARNING) << config_params().item_type << " unknown tracking item type."; + } +} diff --git a/src/algorithms/tracking/adapters/qzss_l5_dll_pll_tracking.h b/src/algorithms/tracking/adapters/qzss_l5_dll_pll_tracking.h new file mode 100644 index 000000000..4e56791e4 --- /dev/null +++ b/src/algorithms/tracking/adapters/qzss_l5_dll_pll_tracking.h @@ -0,0 +1,62 @@ +/*! + * \file qzss_l5_dll_pll_tracking.h + * \brief Interface of an adapter of a DLL+PLL tracking loop block + * for QZSS L5 signals to a TrackingInterface + * \author Carles Fernandez, 2026. cfernandez(at)cttc.es + * + * Code DLL + carrier PLL according to the algorithms described in: + * K.Borre, D.M.Akos, N.Bertelsen, P.Rinder, and S.H.Jensen, + * A Software-Defined GPS and Galileo Receiver. A Single-Frequency + * Approach, Birkhauser, 2007 + * + * ----------------------------------------------------------------------------- + * + * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. + * This file is part of GNSS-SDR. + * + * Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ + +#ifndef GNSS_SDR_QZSS_L5_DLL_PLL_TRACKING_H +#define GNSS_SDR_QZSS_L5_DLL_PLL_TRACKING_H + +#include "base_dll_pll_tracking.h" + +/** \addtogroup Tracking + * Classes for GNSS signal tracking. + * \{ */ +/** \addtogroup Tracking_adapters tracking_adapters + * Wrap GNU Radio blocks for GNSS signal tracking with a TrackingInterface + * \{ */ + + +/*! + * \brief This class implements a code DLL + carrier PLL tracking loop + * block adapter for QZSS L5 signals + */ +class QzssL5DllPllTracking : public BaseDllPllTracking +{ +public: + //! Constructor + QzssL5DllPllTracking(const ConfigurationInterface* configuration, + const std::string& role, + unsigned int in_streams, + unsigned int out_streams); + + //! Returns "QZSS_L5_DLL_PLL_Tracking" + inline std::string implementation() override + { + return "QZSS_L5_DLL_PLL_Tracking"; + } + +private: + void configure_tracking_parameters(const ConfigurationInterface* configuration) override; + void create_tracking_block() override; +}; + +/** \} */ +/** \} */ +#endif // GNSS_SDR_QZSS_L5_DLL_PLL_TRACKING_H diff --git a/src/algorithms/tracking/gnuradio_blocks/dll_pll_veml_tracking.cc b/src/algorithms/tracking/gnuradio_blocks/dll_pll_veml_tracking.cc index 5e7fe327f..375ceaf9b 100644 --- a/src/algorithms/tracking/gnuradio_blocks/dll_pll_veml_tracking.cc +++ b/src/algorithms/tracking/gnuradio_blocks/dll_pll_veml_tracking.cc @@ -49,6 +49,8 @@ #include "gps_sdr_signal_replica.h" #include "lock_detectors.h" #include "matlab_writter_helper.h" +#include "qzss.h" +#include "qzss_signal_replica.h" #include "tracking_discriminators.h" #include // for io_signature #include // for scoped_lock @@ -179,6 +181,8 @@ dll_pll_veml_tracking::dll_pll_veml_tracking(const Dll_Pll_Conf &conf_) map_signal_pretty_name["B1"] = "B1I"; map_signal_pretty_name["B3"] = "B3I"; map_signal_pretty_name["E6"] = "E6"; + map_signal_pretty_name["J1"] = "L1 C/A"; + map_signal_pretty_name["J5"] = "L5"; d_signal_pretty_name = map_signal_pretty_name[d_signal_type]; @@ -509,6 +513,77 @@ dll_pll_veml_tracking::dll_pll_veml_tracking(const Dll_Pll_Conf &conf_) d_symbols_per_bit = 0; } } + else if (d_trk_parameters.system == 'Q') + { + d_systemName = "QZSS"; + if (d_signal_type == "J1") + { + d_signal_carrier_freq = QZSS_L1_FREQ_HZ; + 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; + d_code_length_chips = static_cast(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; + d_trk_parameters.slope = 1.0; + d_trk_parameters.spc = d_trk_parameters.early_late_space_chips; + d_trk_parameters.y_intercept = 1.0; + // symbol integration: 20 trk symbols (20 ms) = 1 tlm bit + // set the bit transition pattern in secondary code to obtain bit synchronization + d_secondary_code_length = static_cast(QZSS_CA_PREAMBLE_LENGTH_SYMBOLS); + d_secondary_code_string = QZSS_CA_PREAMBLE_SYMBOLS_STR; + d_symbols_per_bit = QZSS_CA_TELEMETRY_SYMBOLS_PER_BIT; + } + else if (d_signal_type == "J5") + { + d_signal_carrier_freq = QZSS_L5_FREQ_HZ; + d_code_period = QZSS_L5I_CODE_PERIOD_S; + d_code_chip_rate = QZSS_L5_CHIP_RATE; + // symbol integration: 10 trk symbols (10 ms) = 1 tlm bit + d_symbols_per_bit = QZSS_L5_SAMPLES_PER_SYMBOL; + d_correlation_length_ms = 1; + d_code_samples_per_chip = 1; + d_code_length_chips = static_cast(QZSS_L5_CODE_LENGTH); + d_secondary = true; + d_trk_parameters.slope = 1.0; + d_trk_parameters.spc = d_trk_parameters.early_late_space_chips; + d_trk_parameters.y_intercept = 1.0; + if (d_trk_parameters.track_pilot) + { + // synchronize pilot secondary code + d_secondary_code_length = static_cast(QZSS_L5Q_NH_CODE_LENGTH); + d_secondary_code_string = QZSS_L5Q_NH_CODE_STR; + // remove data secondary code + // remove Neuman-Hofman Code + d_data_secondary_code_length = static_cast(QZSS_L5I_NH_CODE_LENGTH); + d_data_secondary_code_string = QZSS_L5I_NH_CODE_STR; + d_signal_pretty_name = d_signal_pretty_name + "Q"; + } + else + { + // synchronize and remove data secondary code + // remove Neuman-Hofman Code + d_secondary_code_length = static_cast(QZSS_L5I_NH_CODE_LENGTH); + d_secondary_code_string = QZSS_L5I_NH_CODE_STR; + d_signal_pretty_name = d_signal_pretty_name + "I"; + d_interchange_iq = true; + } + } + else + { + LOG(WARNING) << "Invalid Signal argument when instantiating tracking blocks"; + std::cerr << "Invalid Signal argument when instantiating tracking blocks\n"; + d_correlation_length_ms = 1; + d_secondary = false; + d_signal_carrier_freq = 0.0; + d_code_period = 0.0; + d_code_length_chips = 0; + d_code_samples_per_chip = 0U; + d_symbols_per_bit = 0; + } + } else { LOG(WARNING) << "Invalid System argument when instantiating tracking blocks"; @@ -933,6 +1008,25 @@ void dll_pll_veml_tracking::start_tracking() d_extend_correlation_symbols = GLONASS_GNAV_TELEMETRY_SYMBOLS_PER_BIT; } } + else if (d_systemName == "QZSS" && d_signal_type == "J1") + { + qzss_l1_code_gen_float(d_tracking_code, d_acquisition_gnss_synchro->PRN); + } + else if (d_systemName == "QZSS" && d_signal_type == "J5") + { + if (d_trk_parameters.track_pilot) + { + qzss_l5q_code_gen_float(d_tracking_code, d_acquisition_gnss_synchro->PRN); + qzss_l5i_code_gen_float(d_data_code, d_acquisition_gnss_synchro->PRN); + d_Prompt_Data[0] = gr_complex(0.0, 0.0); + d_correlator_data_cpu.set_local_code_and_taps(d_code_length_chips, d_data_code.data(), d_prompt_data_shift); + } + else + { + qzss_l5i_code_gen_float(d_tracking_code, d_acquisition_gnss_synchro->PRN); + } + } + d_multicorrelator_cpu.set_local_code_and_taps(d_code_samples_per_chip * d_code_length_chips, d_tracking_code.data(), d_local_code_shift_chips.data()); std::fill_n(d_correlator_outs.begin(), d_n_correlator_taps, gr_complex(0.0, 0.0)); diff --git a/src/core/system_parameters/CMakeLists.txt b/src/core/system_parameters/CMakeLists.txt index 252295207..cc757901b 100644 --- a/src/core/system_parameters/CMakeLists.txt +++ b/src/core/system_parameters/CMakeLists.txt @@ -97,6 +97,7 @@ set(SYSTEM_PARAMETERS_HEADERS osnma_data.h osnma_dsm_reader.h tow_to_trk.h + qzss.h ) list(SORT SYSTEM_PARAMETERS_HEADERS) diff --git a/src/core/system_parameters/gnss_frequencies.h b/src/core/system_parameters/gnss_frequencies.h index 3099bae68..67664bdd5 100644 --- a/src/core/system_parameters/gnss_frequencies.h +++ b/src/core/system_parameters/gnss_frequencies.h @@ -57,6 +57,8 @@ const std::unordered_map SIGNAL_FREQ_MAP = { {"B1", FREQ1_BDS}, {"B2", FREQ2_BDS}, {"B3", FREQ3_BDS}, + {"J1", FREQ1}, + {"J5", FREQ5}, }; diff --git a/src/core/system_parameters/qzss.h b/src/core/system_parameters/qzss.h new file mode 100644 index 000000000..adb376f67 --- /dev/null +++ b/src/core/system_parameters/qzss.h @@ -0,0 +1,58 @@ +/*! + * \file qzss.h + * \brief Defines system parameters for QZSS signals + * \author Carles Fernández-Prades, 2026. cfernandez (at) cttc.es + * + * ----------------------------------------------------------------------------- + * + * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. + * This file is part of GNSS-SDR. + * + * Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ + + +#ifndef GNSS_SDR_QZSS_H +#define GNSS_SDR_QZSS_H + +#include "gnss_frequencies.h" +#include + +/** \addtogroup Core + * \{ */ +/** \addtogroup System_Parameters + * \{ */ + +static constexpr double QZSS_L1_FREQ_HZ = FREQ1; +static constexpr double QZSS_L5_FREQ_HZ = FREQ5; +static constexpr double QZSS_L1_CHIP_RATE = 1.023e6; +static constexpr double QZSS_L5_CHIP_RATE = 10.23e6; +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; +static constexpr int QZSS_L5_CODE_LENGTH = 10230; +static constexpr int QZSS_L1_PERIOD_MS = 1; +static constexpr int QZSS_L5I_PERIOD_MS = 1; +static constexpr int32_t QZSS_CA_PREAMBLE_LENGTH_SYMBOLS = 160; +static constexpr int32_t QZSS_CA_TELEMETRY_SYMBOLS_PER_BIT = 20; +static constexpr int32_t QZSS_L5_SAMPLES_PER_SYMBOL = 10; +static constexpr int32_t QZSS_L5Q_NH_CODE_LENGTH = 20; +static constexpr int32_t QZSS_L5I_NH_CODE_LENGTH = 10; +static constexpr uint32_t QZSS_L1_OPT_ACQ_FS_SPS = 2000000; +static constexpr uint32_t QZSS_L5_OPT_ACQ_FS_SPS = 10000000; + +static constexpr uint16_t XA_ALL_ONES = 0x1FFF; // 13 bits all ones +static constexpr uint16_t XA_SHORT_DECODE = 0x1FFD; // 1111111111101 (ICD) + +constexpr const char QZSS_CA_PREAMBLE_SYMBOLS_STR[161] = "1111111111111111111100000000000000000000000000000000000000000000000000000000000011111111111111111111000000000000000000001111111111111111111111111111111111111111"; +constexpr const char QZSS_L5Q_NH_CODE_STR[21] = "00000100110101001110"; +constexpr const char QZSS_L5I_NH_CODE_STR[11] = "0000110101"; + +/** \} */ +/** \} */ + +#endif // GNSS_SDR_QZSS_H diff --git a/tests/test_main.cc b/tests/test_main.cc index 399bb6762..c0462f7fa 100644 --- a/tests/test_main.cc +++ b/tests/test_main.cc @@ -137,6 +137,7 @@ private: #include "unit-tests/system-parameters/glonass_gnav_ephemeris_test.cc" #include "unit-tests/system-parameters/glonass_gnav_nav_message_test.cc" #include "unit-tests/system-parameters/has_decoding_test.cc" +#include "unit-tests/system-parameters/qzss_code_generation_test.cc" #ifndef EXCLUDE_TESTS_REQUIRING_BINARIES #include "unit-tests/control-plane/control_thread_test.cc" diff --git a/tests/unit-tests/system-parameters/qzss_code_generation_test.cc b/tests/unit-tests/system-parameters/qzss_code_generation_test.cc new file mode 100644 index 000000000..7e5e79492 --- /dev/null +++ b/tests/unit-tests/system-parameters/qzss_code_generation_test.cc @@ -0,0 +1,88 @@ +/*! + * \file qzss_code_generation_test.cc + * \brief This file implements unit tests for the QZSS code generation + * \author Carles Fernández-Prades, 2026. cfernandez (at)) cttc.es + * + * + * ----------------------------------------------------------------------------- + * + * 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 "qzss_signal_replica.h" +#include +#include + + +TEST(QzssL1Code, Periodicity) +{ + constexpr uint32_t prn = 193; + std::vector code1(1023); + std::vector code2(1023); + + qzss_l1_code_gen_float(code1, prn); + qzss_l1_code_gen_float(code2, prn); + + EXPECT_EQ(code1, code2); +} + + +TEST(QzssL1Code, GoldenFirst32Chips) +{ + constexpr uint32_t prn = 193; + std::vector code(1023); + 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}; + + for (int i = 0; i < 32; ++i) + { + EXPECT_FLOAT_EQ(code[i], golden[i]); + } +} + + +TEST(QzssL5Code, L5IGoldenFirst32Chips) +{ + std::vector code(10230); + qzss_l5i_code_gen_float(code, 193); + + 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}; + + for (int i = 0; i < 32; ++i) + { + EXPECT_FLOAT_EQ(code[i], golden[i]); + } +} + + +TEST(QzssL5Code, L5QGoldenFirst32Chips) +{ + std::vector code(10230); + qzss_l5q_code_gen_float(code, 193); + + 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}; + + for (int i = 0; i < 32; ++i) + { + EXPECT_FLOAT_EQ(code[i], golden[i]); + } +} \ No newline at end of file