Fix GGTO handling (see ICD 5.1.8)

This commit is contained in:
Carles Fernandez
2026-07-17 21:29:38 +02:00
parent 289096ae8e
commit 07fb80e132
6 changed files with 115 additions and 14 deletions
+8 -2
View File
@@ -2393,7 +2393,10 @@ void Rinex_Printer::print_rinex_annotation(const Rtklib_Solver* pvt_solver,
{
iono_lines.emplace_back(get_galileo_iono_alpha_line(pvt_solver->galileo_iono));
time_corr_lines.emplace_back(get_galileo_time_corr_line(pvt_solver->galileo_utc_model));
time_corr_lines.emplace_back(get_gps_to_galileo_time_corr_line(pvt_solver->galileo_utc_model));
if (pvt_solver->galileo_utc_model.flag_GGTO)
{
time_corr_lines.emplace_back(get_gps_to_galileo_time_corr_line(pvt_solver->galileo_utc_model));
}
if (system_time_str == "GAL")
{
@@ -2507,7 +2510,10 @@ void Rinex_Printer::print_rinex_annotation(const Rtklib_Solver* pvt_solver,
{
nav_header_info.emplace_back("GAL", "IONOSPHERIC CORR", get_galileo_iono_alpha_line(pvt_solver->galileo_iono));
nav_header_info.emplace_back("GAUT", "TIME SYSTEM CORR", get_galileo_time_corr_line(pvt_solver->galileo_utc_model));
nav_header_info.emplace_back("GPGA", "TIME SYSTEM CORR", get_gps_to_galileo_time_corr_line(pvt_solver->galileo_utc_model));
if (pvt_solver->galileo_utc_model.flag_GGTO)
{
nav_header_info.emplace_back("GPGA", "TIME SYSTEM CORR", get_gps_to_galileo_time_corr_line(pvt_solver->galileo_utc_model));
}
d_rinex_header_galileo_updated = true;
if (system_time_str == "GAL")
@@ -649,8 +649,11 @@ void galileo_telemetry_decoder_gs::decode_INAV_word(float *page_part_symbols, in
<< " dB-Hz" << TEXT_RESET << std::endl;
}
d_delta_t = tmp_obj->A_0G + tmp_obj->A_1G * (static_cast<double>(d_TOW_at_current_symbol_ms) / 1000.0 - tmp_obj->t_0G + 604800 * (std::fmod(static_cast<float>(d_inav_nav.get_Galileo_week() - tmp_obj->WN_0G), 64.0)));
DLOG(INFO) << "delta_t=" << d_delta_t << "[s]";
if (tmp_obj->flag_GGTO)
{
d_delta_t = tmp_obj->A_0G + tmp_obj->A_1G * (static_cast<double>(d_TOW_at_current_symbol_ms) / 1000.0 - tmp_obj->t_0G + 604800 * (std::fmod(static_cast<float>(d_inav_nav.get_Galileo_week() - tmp_obj->WN_0G), 64.0)));
DLOG(INFO) << "delta_t=" << d_delta_t << "[s]";
}
}
if (d_inav_nav.have_new_almanac() == true) // flag_almanac_4 tells if W10 available.
@@ -524,6 +524,7 @@ Galileo_Utc_Model Galileo_Inav_Message::get_utc_model() const
utc_model.A_1G = A_1G_10;
utc_model.t_0G = t_0G_10;
utc_model.WN_0G = WN_0G_10;
utc_model.flag_GGTO = get_flag_GGTO();
return utc_model;
}
@@ -1243,6 +1244,12 @@ int32_t Galileo_Inav_Message::page_jk_decoder(const char* data_jk)
WN_0G_10 = static_cast<int32_t>(read_navigation_unsigned(data_jk_bits, WN_0_G_10_BIT));
flag_GGTO_4 = true;
DLOG(INFO) << "WN_0G_10= " << WN_0G_10;
// OS SIS ICD: GGTO is unavailable only when all four raw fields
// are simultaneously encoded as all ones.
flag_GGTO_valid = !(read_navigation_unsigned(data_jk_bits, A_0_G_10_BIT) == 0xFFFFU &&
read_navigation_unsigned(data_jk_bits, A_1_G_10_BIT) == 0x0FFFU &&
read_navigation_unsigned(data_jk_bits, T_0_G_10_BIT) == 0x00FFU &&
read_navigation_unsigned(data_jk_bits, WN_0_G_10_BIT) == 0x003FU);
flag_almanac_4 = true;
DLOG(INFO) << "flag_tow_set" << flag_TOW_set;
nav_bits_word_10 = data_jk_bits.to_string().substr(86, 42);
@@ -230,7 +230,7 @@ public:
inline bool get_flag_GGTO() const
{
return (flag_GGTO_1 == true && flag_GGTO_2 == true && flag_GGTO_3 == true && flag_GGTO_4 == true);
return (flag_GGTO_1 == true && flag_GGTO_2 == true && flag_GGTO_3 == true && flag_GGTO_4 == true && flag_GGTO_valid == true);
}
inline double get_A0G() const
@@ -499,6 +499,7 @@ private:
bool flag_GGTO_2{};
bool flag_GGTO_3{};
bool flag_GGTO_4{};
bool flag_GGTO_valid{};
bool flag_CED{};
bool enable_rs{};
+21 -3
View File
@@ -20,6 +20,7 @@
#define GNSS_SDR_GALILEO_UTC_MODEL_H
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/version.hpp>
#include <cstdint>
/** \addtogroup Core
@@ -61,6 +62,7 @@ public:
int32_t WN_0G{};
bool flag_utc_model{};
bool flag_GGTO{};
template <class Archive>
@@ -70,9 +72,6 @@ public:
*/
inline void serialize(Archive& archive, const unsigned int version)
{
if (version)
{
};
archive& BOOST_SERIALIZATION_NVP(A0);
archive& BOOST_SERIALIZATION_NVP(A1);
archive& BOOST_SERIALIZATION_NVP(Delta_tLS);
@@ -82,9 +81,28 @@ public:
archive& BOOST_SERIALIZATION_NVP(DN);
archive& BOOST_SERIALIZATION_NVP(Delta_tLSF);
archive& BOOST_SERIALIZATION_NVP(flag_utc_model);
if (version > 0)
{
archive& BOOST_SERIALIZATION_NVP(A_0G);
archive& BOOST_SERIALIZATION_NVP(A_1G);
archive& BOOST_SERIALIZATION_NVP(t_0G);
archive& BOOST_SERIALIZATION_NVP(WN_0G);
archive& BOOST_SERIALIZATION_NVP(flag_GGTO);
}
else
{
// Version 0 archives predate GGTO persistence.
A_0G = 0.0;
A_1G = 0.0;
t_0G = 0;
WN_0G = 0;
flag_GGTO = false;
}
}
};
BOOST_CLASS_VERSION(Galileo_Utc_Model, 1)
/** \} */
/** \} */
@@ -70,22 +70,19 @@ uint32_t compute_galileo_inav_crc(const std::string& crc_data)
}
std::pair<std::string, std::string> build_inav_page(uint8_t word_type, char page_type, const std::string& osnma_sis)
std::pair<std::string, std::string> build_inav_page_from_data(const std::string& data_jk, char page_type, const std::string& osnma_sis)
{
std::string data_k = to_bit_string(word_type, 6);
data_k.append(106, '0');
std::string even_page;
even_page.reserve(114);
even_page.push_back('0');
even_page.push_back(page_type);
even_page += data_k;
even_page += data_jk.substr(0, 112);
std::string odd_page_without_crc;
odd_page_without_crc.reserve(82);
odd_page_without_crc.push_back('1');
odd_page_without_crc.push_back(page_type);
odd_page_without_crc.append(16, '0');
odd_page_without_crc += data_jk.substr(112, 16);
odd_page_without_crc += osnma_sis;
odd_page_without_crc.append(22, '0'); // SAR
odd_page_without_crc.append(2, '0'); // Spare
@@ -101,6 +98,33 @@ std::pair<std::string, std::string> build_inav_page(uint8_t word_type, char page
}
std::pair<std::string, std::string> build_inav_page(uint8_t word_type, char page_type, const std::string& osnma_sis)
{
std::string data_jk = to_bit_string(word_type, 6);
data_jk.append(122, '0');
return build_inav_page_from_data(data_jk, page_type, osnma_sis);
}
void set_inav_field(std::string& data_jk, const std::vector<std::pair<int32_t, int32_t>>& field, uint32_t value)
{
const auto& range = field.front();
data_jk.replace(static_cast<size_t>(range.first - 1), static_cast<size_t>(range.second), to_bit_string(value, static_cast<size_t>(range.second)));
}
std::string build_ggto_word(uint32_t a0g, uint32_t a1g, uint32_t t0g, uint32_t wn0g)
{
std::string data_jk(128, '0');
set_inav_field(data_jk, PAGE_TYPE_BIT, 10U);
set_inav_field(data_jk, A_0_G_10_BIT, a0g);
set_inav_field(data_jk, A_1_G_10_BIT, a1g);
set_inav_field(data_jk, T_0_G_10_BIT, t0g);
set_inav_field(data_jk, WN_0_G_10_BIT, wn0g);
return data_jk;
}
std::string build_osnma_sis(uint8_t hkroot, uint32_t mack)
{
return to_bit_string(hkroot, 8) + to_bit_string(mack, 32);
@@ -115,6 +139,14 @@ void feed_inav_page(Galileo_Inav_Message& decoder, uint8_t word_type, char page_
}
void feed_inav_data_page(Galileo_Inav_Message& decoder, const std::string& data_jk)
{
const auto page = build_inav_page_from_data(data_jk, '0', std::string(40, '0'));
decoder.split_page(page.first, 0);
decoder.split_page(page.second, 1);
}
OSNMA_msg decode_osnma_page(uint8_t word_type, char page_type, const std::string& osnma_sis)
{
Galileo_Inav_Message decoder;
@@ -124,6 +156,40 @@ OSNMA_msg decode_osnma_page(uint8_t word_type, char page_type, const std::string
} // namespace
TEST(Galileo_INAV_Message_Test, GgtoAllOnesEncodingIsUnavailable)
{
Galileo_Inav_Message decoder;
feed_inav_data_page(decoder, build_ggto_word(0xFFFFU, 0x0FFFU, 0x00FFU, 0x003FU));
ASSERT_TRUE(decoder.get_flag_CRC_test());
EXPECT_FALSE(decoder.get_flag_GGTO());
EXPECT_FALSE(decoder.get_utc_model().flag_GGTO);
}
TEST(Galileo_INAV_Message_Test, GgtoUnavailableRequiresAllFourAllOnesFields)
{
const std::array<std::array<uint32_t, 4>, 4> available_encodings{{
{{0xFFFEU, 0x0FFFU, 0x00FFU, 0x003FU}},
{{0xFFFFU, 0x0FFEU, 0x00FFU, 0x003FU}},
{{0xFFFFU, 0x0FFFU, 0x00FEU, 0x003FU}},
{{0xFFFFU, 0x0FFFU, 0x00FFU, 0x003EU}},
}};
for (size_t i = 0; i < available_encodings.size(); ++i)
{
SCOPED_TRACE(i);
Galileo_Inav_Message decoder;
const auto& fields = available_encodings[i];
feed_inav_data_page(decoder, build_ggto_word(fields[0], fields[1], fields[2], fields[3]));
ASSERT_TRUE(decoder.get_flag_CRC_test());
EXPECT_TRUE(decoder.get_flag_GGTO());
EXPECT_TRUE(decoder.get_utc_model().flag_GGTO);
}
}
class Galileo_FNAV_INAV_test : public ::testing::Test
{
public: