1
0
mirror of https://github.com/gnss-sdr/gnss-sdr synced 2026-06-01 10:12:13 +00:00

Add code generation, acquisition and tracking adapters for QZSS L1 C/A and L5

This commit is contained in:
Carles Fernandez
2026-02-24 11:40:20 +01:00
parent a3bcbb5ab1
commit 8fc9276ff5
21 changed files with 1215 additions and 8 deletions
@@ -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))
{
}
@@ -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
@@ -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)
@@ -0,0 +1,46 @@
/*!
* \file qzss_l1_pcps_acquisition.cc
* \brief Adapts a PCPS acquisition block to an AcquisitionInterface for
* QZSS L1 signals
* \authors <ul>
* <li> Carles Fernandez, 2026. cfernandez(at)cttc.es
* </ul>
*
* -----------------------------------------------------------------------------
*
* 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<std::complex<float>> dest, uint32_t prn, int32_t sampling_freq)
{
qzss_l1_code_gen_complex_sampled(dest, prn, sampling_freq);
}
@@ -0,0 +1,62 @@
/*!
* \file qzss_l1_pcps_acquisition.h
* \brief Adapts a PCPS acquisition block to an AcquisitionInterface for
* QZSS L1 signals
* \authors <ul>
* <li> Carles Fernandez, 2026. cfernandez(at)cttc.es
* </ul>
*
* -----------------------------------------------------------------------------
*
* 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<std::complex<float>> dest, uint32_t prn, int32_t sampling_freq) override;
};
/** \} */
/** \} */
#endif // GNSS_SDR_QZSS_L1_PCPS_ACQUISITION_H
@@ -0,0 +1,46 @@
/*!
* \file qzss_l5i_pcps_acquisition.cc
* \brief Adapts a PCPS acquisition block to an AcquisitionInterface for
* QZSS L5I signals
* \authors <ul>
* <li> Carles Fernandez, 2026. cfernandez(at)cttc.es
* </ul>
*
* -----------------------------------------------------------------------------
*
* 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<std::complex<float>> dest, uint32_t prn, int32_t sampling_freq)
{
qzss_l5i_code_gen_complex_sampled(dest, prn, sampling_freq);
}
@@ -0,0 +1,62 @@
/*!
* \file qzss_l5i_pcps_acquisition.h
* \brief Adapts a PCPS acquisition block to an AcquisitionInterface for
* QZSS L5I signals
* \authors <ul>
* <li> Carles Fernandez, 2026. cfernandez(at)cttc.es
* </ul>
*
* -----------------------------------------------------------------------------
*
* 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<std::complex<float>> dest, uint32_t prn, int32_t sampling_freq) override;
};
/** \} */
/** \} */
#endif // GNSS_SDR_QZSS_L5I_PCPS_ACQUISITION_H
+2
View File
@@ -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}
+352
View File
@@ -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 <array>
#if USE_GLOG_AND_GFLAGS
#include <glog/logging.h>
#else
#include <absl/log/log.h>
#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<float> 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<uint8_t, 10> g1{};
std::array<uint8_t, 10> g2{};
g1.fill(1);
const uint16_t init = entry.init_g2_octal;
for (int s = 0; s < 10; ++s)
{
g2[s] = static_cast<uint8_t>((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<uint8_t>(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<uint8_t>(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<uint8_t>(
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<std::complex<float>> dest,
uint32_t prn,
int32_t sampling_freq)
{
if (sampling_freq <= 0)
{
LOG(WARNING) << "Invalid sampling frequency";
return;
}
std::array<float, 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);
double code_phase = 0.0;
for (size_t i = 0; i < dest.size(); ++i)
{
int chip = static_cast<int>(code_phase);
chip %= QZSS_L1_CODE_LENGTH;
dest[i] = {code[static_cast<size_t>(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<uint8_t>(state & 0x1);
const uint8_t fb = static_cast<uint8_t>(
((state >> 4) ^ (state >> 3) ^ (state >> 1) ^ (state >> 0)) & 0x1);
uint16_t next = static_cast<uint16_t>((state >> 1) | (static_cast<uint16_t>(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<uint8_t>(state & 0x1);
const uint8_t fb = static_cast<uint8_t>(
((state >> 12) ^ (state >> 10) ^ (state >> 9) ^ (state >> 7) ^
(state >> 6) ^ (state >> 5) ^ (state >> 1) ^ (state >> 0)) &
0x1);
state = static_cast<uint16_t>((state >> 1) | (static_cast<uint16_t>(fb) << 12));
return out;
}
void qzss_l5i_code_gen_float(own::span<float> 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<uint8_t>(xa ^ xb);
dest[i] = bit ? 1.0F : -1.0F;
}
}
void qzss_l5q_code_gen_float(own::span<float> 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<uint8_t>(xa ^ xb);
dest[i] = bit ? 1.0F : -1.0F;
}
}
void qzss_l5i_code_gen_complex_sampled(
own::span<std::complex<float>> dest,
uint32_t prn,
int32_t sampling_freq)
{
if (sampling_freq <= 0)
{
LOG(WARNING) << "Invalid sampling frequency";
return;
}
std::array<float, QZSS_L5_CODE_LENGTH> code{};
qzss_l5i_code_gen_float(own::span<float>(code.data(), code.size()), prn);
const double phase_step = QZSS_L5_CHIP_RATE / static_cast<double>(sampling_freq);
double code_phase = 0.0;
for (size_t i = 0; i < dest.size(); ++i)
{
int chip = static_cast<int>(code_phase);
chip %= QZSS_L5_CODE_LENGTH;
dest[i] = {code[static_cast<size_t>(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<std::complex<float>> dest,
uint32_t prn,
int32_t sampling_freq)
{
if (sampling_freq <= 0)
{
LOG(WARNING) << "Invalid sampling frequency";
return;
}
std::array<float, QZSS_L5_CODE_LENGTH> code{};
qzss_l5q_code_gen_float(own::span<float>(code.data(), code.size()), prn);
const double phase_step = QZSS_L5_CHIP_RATE / static_cast<double>(sampling_freq);
double code_phase = 0.0;
for (size_t i = 0; i < dest.size(); ++i)
{
int chip = static_cast<int>(code_phase);
chip %= QZSS_L5_CODE_LENGTH;
dest[i] = {0.0F, code[static_cast<size_t>(chip)]};
code_phase += phase_step;
if (code_phase >= QZSS_L5_CODE_LENGTH)
{
code_phase -= QZSS_L5_CODE_LENGTH;
}
}
}
+55
View File
@@ -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 <complex>
#include <cstdint>
#if HAS_STD_SPAN
#include <span>
namespace own = std;
#else
#include <gsl-lite/gsl-lite.hpp>
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<std::complex<float>> 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<float> 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<std::complex<float>> 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<float> 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<std::complex<float>> 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<float> dest, uint32_t prn);
/** \} */
/** \} */
#endif // GNSS_SDR_QZSS_SIGNAL_REPLICA_H
@@ -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}
)
@@ -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 <algorithm>
#include <array>
#include <cmath>
#include <iostream>
#if USE_GLOG_AND_GFLAGS
#include <glog/logging.h>
#else
#include <absl/log/log.h>
#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<char, 3> sig{'J', '1', '\0'};
std::copy_n(sig.data(), 3, config_params().signal);
const auto vector_length = static_cast<int>(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.";
}
}
@@ -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
@@ -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 <algorithm>
#include <array>
#if USE_GLOG_AND_GFLAGS
#include <glog/logging.h>
#else
#include <absl/log/log.h>
#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<int>(std::round(static_cast<double>(config_params().fs_in) / (static_cast<double>(QZSS_L5_CHIP_RATE) / static_cast<double>(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<char, 3> 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.";
}
}
@@ -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
@@ -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 <gnuradio/io_signature.h> // for io_signature
#include <gnuradio/thread/thread.h> // 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<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;
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<uint32_t>(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<int32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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));
@@ -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)
@@ -57,6 +57,8 @@ const std::unordered_map<std::string, double> SIGNAL_FREQ_MAP = {
{"B1", FREQ1_BDS},
{"B2", FREQ2_BDS},
{"B3", FREQ3_BDS},
{"J1", FREQ1},
{"J5", FREQ5},
};
+58
View File
@@ -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 <cstdint>
/** \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
+1
View File
@@ -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"
@@ -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 <gtest/gtest.h>
#include <vector>
TEST(QzssL1Code, Periodicity)
{
constexpr uint32_t prn = 193;
std::vector<float> code1(1023);
std::vector<float> 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<float> 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<float> 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<float> 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]);
}
}