mirror of
https://github.com/gnss-sdr/gnss-sdr
synced 2026-10-13 19:21:50 +00:00
Merge remote-tracking branch 'cf/rinex4' into next
This commit is contained in:
@@ -58,6 +58,22 @@ All notable changes to GNSS-SDR will be documented in this file.
|
||||
|
||||
### Improvements in Interoperability:
|
||||
|
||||
- Added support for RINEX 4.02 output, activated by setting
|
||||
`PVT.rinex_version=4` in the configuration file (or with the
|
||||
`-RINEX_version=4.02` command-line flag). Observation files are generated in
|
||||
the 4.02 version format, and navigation files make use of the data record
|
||||
structure introduced in RINEX 4.00. The default behavior when
|
||||
`PVT.rinex_version` is not set remains unchanged (RINEX 3.02).
|
||||
- The ionospheric Klobuchar coefficients and the UTC(NICT) offset parameters
|
||||
broadcast by QZSS satellites, both in the L1 C/A LNAV message and in the L5
|
||||
CNAV message, are now stored separately from the GPS ones instead of
|
||||
overwriting them. This enables the QZUT / QZSS ION RINEX 4 data records (with
|
||||
the compulsory `WIDE` subtype for the CNVX Klobuchar set, broadcast in CNAV
|
||||
Message Type 30), prevents QZSS-sourced parameters from being mislabeled as
|
||||
GPS corrections in mixed GPS + QZSS configurations, feeds the QZSS slots of
|
||||
the RTKLIB navigation structure, and adds `qzss_utc_model.xml`,
|
||||
`qzss_iono.xml`, `qzss_cnav_utc_model.xml`, and `qzss_cnav_iono.xml` to the
|
||||
XML storage output.
|
||||
- Added a new Signal Source implementation `Pocket_SDR_Signal_Source`, which
|
||||
supports
|
||||
[Pocket SDR FE](https://www.datagnss.com/products/pocketsdr-gnss-receiver)
|
||||
|
||||
@@ -102,7 +102,11 @@ Rtklib_Pvt::Rtklib_Pvt(const ConfigurationInterface* configuration,
|
||||
// RINEX version
|
||||
pvt_output_parameters.rinex_version = configuration->property(role + ".rinex_version", 3);
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
if (FLAGS_RINEX_version == "3.01" || FLAGS_RINEX_version == "3.02" || FLAGS_RINEX_version == "3")
|
||||
if (FLAGS_RINEX_version == "4.00" || FLAGS_RINEX_version == "4.01" || FLAGS_RINEX_version == "4.02" || FLAGS_RINEX_version == "4")
|
||||
{
|
||||
pvt_output_parameters.rinex_version = 4;
|
||||
}
|
||||
else if (FLAGS_RINEX_version == "3.01" || FLAGS_RINEX_version == "3.02" || FLAGS_RINEX_version == "3")
|
||||
{
|
||||
pvt_output_parameters.rinex_version = 3;
|
||||
}
|
||||
@@ -111,7 +115,11 @@ Rtklib_Pvt::Rtklib_Pvt(const ConfigurationInterface* configuration,
|
||||
pvt_output_parameters.rinex_version = 2;
|
||||
}
|
||||
#else
|
||||
if (absl::GetFlag(FLAGS_RINEX_version) == "3.01" || absl::GetFlag(FLAGS_RINEX_version) == "3.02" || absl::GetFlag(FLAGS_RINEX_version) == "3")
|
||||
if (absl::GetFlag(FLAGS_RINEX_version) == "4.00" || absl::GetFlag(FLAGS_RINEX_version) == "4.01" || absl::GetFlag(FLAGS_RINEX_version) == "4.02" || absl::GetFlag(FLAGS_RINEX_version) == "4")
|
||||
{
|
||||
pvt_output_parameters.rinex_version = 4;
|
||||
}
|
||||
else if (absl::GetFlag(FLAGS_RINEX_version) == "3.01" || absl::GetFlag(FLAGS_RINEX_version) == "3.02" || absl::GetFlag(FLAGS_RINEX_version) == "3")
|
||||
{
|
||||
pvt_output_parameters.rinex_version = 3;
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ class Gps_Ephemeris;
|
||||
* .nmea_dump_filename - ("./nmea_pvt.nmea")
|
||||
* .nmea_dump_devname - ("/dev/tty1")
|
||||
*
|
||||
* .rinex_version - (3) overridden by -RINEX_version=n.nn command line argument
|
||||
* .rinex_version - (3) values 2 (2.11), 3 (3.02) or 4 (4.02), overridden by -RINEX_version=n.nn command line argument
|
||||
* .rinexobs_rate_ms - rate at which RINEX observations are written (1000). Note that
|
||||
* the actual rate is the least common multiple of this value and
|
||||
* .output_rate_ms
|
||||
|
||||
@@ -57,6 +57,10 @@
|
||||
#include "osnma_data.h"
|
||||
#include "pvt_conf.h"
|
||||
#include "qzss.h"
|
||||
#include "qzss_cnav_iono.h"
|
||||
#include "qzss_cnav_utc_model.h"
|
||||
#include "qzss_iono.h"
|
||||
#include "qzss_utc_model.h"
|
||||
#include "rinex_printer.h"
|
||||
#include "rtcm_printer.h"
|
||||
#include "rtklib_rtkcmn.h"
|
||||
@@ -145,6 +149,10 @@ rtklib_pvt_gs::rtklib_pvt_gs(uint32_t nchannels,
|
||||
d_gps_cnav_iono_sptr_type_hash_code(typeid(std::shared_ptr<Gps_CNAV_Iono>).hash_code()),
|
||||
d_gps_cnav_utc_model_sptr_type_hash_code(typeid(std::shared_ptr<Gps_CNAV_Utc_Model>).hash_code()),
|
||||
d_gps_almanac_sptr_type_hash_code(typeid(std::shared_ptr<Gps_Almanac>).hash_code()),
|
||||
d_qzss_iono_sptr_type_hash_code(typeid(std::shared_ptr<Qzss_Iono>).hash_code()),
|
||||
d_qzss_utc_model_sptr_type_hash_code(typeid(std::shared_ptr<Qzss_Utc_Model>).hash_code()),
|
||||
d_qzss_cnav_iono_sptr_type_hash_code(typeid(std::shared_ptr<Qzss_CNAV_Iono>).hash_code()),
|
||||
d_qzss_cnav_utc_model_sptr_type_hash_code(typeid(std::shared_ptr<Qzss_CNAV_Utc_Model>).hash_code()),
|
||||
d_galileo_ephemeris_sptr_type_hash_code(typeid(std::shared_ptr<Galileo_Ephemeris>).hash_code()),
|
||||
d_galileo_reduced_ced_sptr_type_hash_code(typeid(std::shared_ptr<Galileo_Reduced_CED>).hash_code()),
|
||||
d_galileo_iono_sptr_type_hash_code(typeid(std::shared_ptr<Galileo_Iono>).hash_code()),
|
||||
@@ -874,6 +882,126 @@ rtklib_pvt_gs::~rtklib_pvt_gs()
|
||||
LOG(INFO) << "Failed to save GPS CNAV ionospheric model parameters, not valid data";
|
||||
}
|
||||
|
||||
// Save QZSS UTC model parameters
|
||||
file_name = d_xml_base_path + "qzss_utc_model.xml";
|
||||
if (d_internal_pvt_solver->qzss_utc_model.valid)
|
||||
{
|
||||
std::ofstream ofs;
|
||||
try
|
||||
{
|
||||
ofs.open(file_name.c_str(), std::ofstream::trunc | std::ofstream::out);
|
||||
boost::archive::xml_oarchive xml(ofs);
|
||||
xml << boost::serialization::make_nvp("GNSS-SDR_qzss_utc_model", d_internal_pvt_solver->qzss_utc_model);
|
||||
LOG(INFO) << "Saved QZSS UTC model parameters";
|
||||
}
|
||||
catch (const boost::archive::archive_exception& e)
|
||||
{
|
||||
LOG(WARNING) << e.what();
|
||||
}
|
||||
catch (const std::ofstream::failure& e)
|
||||
{
|
||||
LOG(WARNING) << "Problem opening output XML file";
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG(WARNING) << e.what();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(INFO) << "Failed to save QZSS UTC model parameters, not valid data";
|
||||
}
|
||||
|
||||
// Save QZSS iono parameters
|
||||
file_name = d_xml_base_path + "qzss_iono.xml";
|
||||
if (d_internal_pvt_solver->qzss_iono.valid == true)
|
||||
{
|
||||
std::ofstream ofs;
|
||||
try
|
||||
{
|
||||
ofs.open(file_name.c_str(), std::ofstream::trunc | std::ofstream::out);
|
||||
boost::archive::xml_oarchive xml(ofs);
|
||||
xml << boost::serialization::make_nvp("GNSS-SDR_qzss_iono_model", d_internal_pvt_solver->qzss_iono);
|
||||
LOG(INFO) << "Saved QZSS ionospheric model parameters";
|
||||
}
|
||||
catch (const boost::archive::archive_exception& e)
|
||||
{
|
||||
LOG(WARNING) << e.what();
|
||||
}
|
||||
catch (const std::ofstream::failure& e)
|
||||
{
|
||||
LOG(WARNING) << "Problem opening output XML file";
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG(WARNING) << e.what();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(INFO) << "Failed to save QZSS ionospheric model parameters, not valid data";
|
||||
}
|
||||
|
||||
// Save QZSS CNAV UTC model parameters
|
||||
file_name = d_xml_base_path + "qzss_cnav_utc_model.xml";
|
||||
if (d_internal_pvt_solver->qzss_cnav_utc_model.valid)
|
||||
{
|
||||
std::ofstream ofs;
|
||||
try
|
||||
{
|
||||
ofs.open(file_name.c_str(), std::ofstream::trunc | std::ofstream::out);
|
||||
boost::archive::xml_oarchive xml(ofs);
|
||||
xml << boost::serialization::make_nvp("GNSS-SDR_qzss_cnav_utc_model", d_internal_pvt_solver->qzss_cnav_utc_model);
|
||||
LOG(INFO) << "Saved QZSS CNAV UTC model parameters";
|
||||
}
|
||||
catch (const boost::archive::archive_exception& e)
|
||||
{
|
||||
LOG(WARNING) << e.what();
|
||||
}
|
||||
catch (const std::ofstream::failure& e)
|
||||
{
|
||||
LOG(WARNING) << "Problem opening output XML file";
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG(WARNING) << e.what();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(INFO) << "Failed to save QZSS CNAV UTC model parameters, not valid data";
|
||||
}
|
||||
|
||||
// Save QZSS CNAV iono parameters
|
||||
file_name = d_xml_base_path + "qzss_cnav_iono.xml";
|
||||
if (d_internal_pvt_solver->qzss_cnav_iono.valid == true)
|
||||
{
|
||||
std::ofstream ofs;
|
||||
try
|
||||
{
|
||||
ofs.open(file_name.c_str(), std::ofstream::trunc | std::ofstream::out);
|
||||
boost::archive::xml_oarchive xml(ofs);
|
||||
xml << boost::serialization::make_nvp("GNSS-SDR_qzss_cnav_iono_model", d_internal_pvt_solver->qzss_cnav_iono);
|
||||
LOG(INFO) << "Saved QZSS CNAV ionospheric model parameters";
|
||||
}
|
||||
catch (const boost::archive::archive_exception& e)
|
||||
{
|
||||
LOG(WARNING) << e.what();
|
||||
}
|
||||
catch (const std::ofstream::failure& e)
|
||||
{
|
||||
LOG(WARNING) << "Problem opening output XML file";
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG(WARNING) << e.what();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(INFO) << "Failed to save QZSS CNAV ionospheric model parameters, not valid data";
|
||||
}
|
||||
|
||||
// Save Galileo iono parameters
|
||||
file_name = d_xml_base_path + "gal_iono.xml";
|
||||
if (d_internal_pvt_solver->galileo_iono.ai0 != 0.0)
|
||||
@@ -1271,6 +1399,50 @@ void rtklib_pvt_gs::msg_handler_telemetry(const pmt::pmt_t& msg)
|
||||
}
|
||||
DLOG(INFO) << "New UTC record has arrived";
|
||||
}
|
||||
else if (msg_type_hash_code == d_qzss_iono_sptr_type_hash_code)
|
||||
{
|
||||
// ### QZSS IONO ###
|
||||
const auto qzss_iono = wht::any_cast<std::shared_ptr<Qzss_Iono>>(pmt::any_ref(msg));
|
||||
d_internal_pvt_solver->qzss_iono = *qzss_iono;
|
||||
if (d_enable_rx_clock_correction == true)
|
||||
{
|
||||
d_user_pvt_solver->qzss_iono = *qzss_iono;
|
||||
}
|
||||
DLOG(INFO) << "New QZSS IONO record has arrived";
|
||||
}
|
||||
else if (msg_type_hash_code == d_qzss_utc_model_sptr_type_hash_code)
|
||||
{
|
||||
// ### QZSS UTC MODEL ###
|
||||
const auto qzss_utc_model = wht::any_cast<std::shared_ptr<Qzss_Utc_Model>>(pmt::any_ref(msg));
|
||||
d_internal_pvt_solver->qzss_utc_model = *qzss_utc_model;
|
||||
if (d_enable_rx_clock_correction == true)
|
||||
{
|
||||
d_user_pvt_solver->qzss_utc_model = *qzss_utc_model;
|
||||
}
|
||||
DLOG(INFO) << "New QZSS UTC record has arrived";
|
||||
}
|
||||
else if (msg_type_hash_code == d_qzss_cnav_iono_sptr_type_hash_code)
|
||||
{
|
||||
// ### QZSS CNAV IONO ###
|
||||
const auto qzss_cnav_iono = wht::any_cast<std::shared_ptr<Qzss_CNAV_Iono>>(pmt::any_ref(msg));
|
||||
d_internal_pvt_solver->qzss_cnav_iono = *qzss_cnav_iono;
|
||||
if (d_enable_rx_clock_correction == true)
|
||||
{
|
||||
d_user_pvt_solver->qzss_cnav_iono = *qzss_cnav_iono;
|
||||
}
|
||||
DLOG(INFO) << "New QZSS CNAV IONO record has arrived";
|
||||
}
|
||||
else if (msg_type_hash_code == d_qzss_cnav_utc_model_sptr_type_hash_code)
|
||||
{
|
||||
// ### QZSS CNAV UTC MODEL ###
|
||||
const auto qzss_cnav_utc_model = wht::any_cast<std::shared_ptr<Qzss_CNAV_Utc_Model>>(pmt::any_ref(msg));
|
||||
d_internal_pvt_solver->qzss_cnav_utc_model = *qzss_cnav_utc_model;
|
||||
if (d_enable_rx_clock_correction == true)
|
||||
{
|
||||
d_user_pvt_solver->qzss_cnav_utc_model = *qzss_cnav_utc_model;
|
||||
}
|
||||
DLOG(INFO) << "New QZSS CNAV UTC record has arrived";
|
||||
}
|
||||
else if (msg_type_hash_code == d_gps_cnav_ephemeris_sptr_type_hash_code)
|
||||
{
|
||||
// ### GPS CNAV message ###
|
||||
|
||||
@@ -224,6 +224,10 @@ private:
|
||||
const size_t d_gps_cnav_iono_sptr_type_hash_code;
|
||||
const size_t d_gps_cnav_utc_model_sptr_type_hash_code;
|
||||
const size_t d_gps_almanac_sptr_type_hash_code;
|
||||
const size_t d_qzss_iono_sptr_type_hash_code;
|
||||
const size_t d_qzss_utc_model_sptr_type_hash_code;
|
||||
const size_t d_qzss_cnav_iono_sptr_type_hash_code;
|
||||
const size_t d_qzss_cnav_utc_model_sptr_type_hash_code;
|
||||
const size_t d_galileo_ephemeris_sptr_type_hash_code;
|
||||
const size_t d_galileo_reduced_ced_sptr_type_hash_code;
|
||||
const size_t d_galileo_iono_sptr_type_hash_code;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*!
|
||||
* \file rinex_printer.cc
|
||||
* \brief Implementation of a RINEX 2.11 / 3.02 printer
|
||||
* See ftp://igs.org/pub/data/format/rinex302.pdf
|
||||
* \brief Implementation of a RINEX 2.11 / 3.02 / 4.02 printer
|
||||
* See https://files.igs.org/pub/data/format/rinex_4.02.pdf
|
||||
* \author Carles Fernandez Prades, 2011-2026. cfernandez(at)cttc.es
|
||||
* \author Mathieu Favreau, 2025-2026. favreau.mathieu(at)hotmail.com
|
||||
*
|
||||
@@ -203,6 +203,33 @@ uint32_t to_rinex_qzss_prn(uint32_t prn)
|
||||
}
|
||||
|
||||
|
||||
bool is_beidou_geo_prn(uint32_t prn)
|
||||
{
|
||||
// BeiDou-2/3 GEO satellites (D2 navigation message) use PRNs 1-5 and 59-63
|
||||
return prn <= 5 || prn >= 59;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Resolves the ambiguity of a truncated week number broadcast modulo
|
||||
* 2^num_bits by choosing the candidate closest to a full reference week
|
||||
*/
|
||||
int32_t expand_truncated_week(int32_t week, int32_t reference_week, int32_t modulus)
|
||||
{
|
||||
int32_t expanded_week = week;
|
||||
while ((expanded_week - reference_week) < -modulus / 2)
|
||||
{
|
||||
expanded_week += modulus;
|
||||
}
|
||||
while ((expanded_week - reference_week) > modulus / 2 - 1)
|
||||
{
|
||||
expanded_week -= modulus;
|
||||
}
|
||||
|
||||
return expanded_week;
|
||||
}
|
||||
|
||||
|
||||
Constellation_Observables_Map get_constellation_observables_map(
|
||||
const std::map<char, std::set<signal_flag>>& constel_signal_flags,
|
||||
const std::map<int32_t, Gnss_Synchro>& observables)
|
||||
@@ -844,6 +871,72 @@ std::string doub2for(double d,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Convert a double to the E19.12 field used in RINEX 4 navigation data
|
||||
* records (e.g., " 1.330170780420e-04"). RINEX 4 recommends the 'e'
|
||||
* exponent indicator and deprecates the FORTRAN 'D' notation
|
||||
* (see RINEX 4.02 specification, Section 6.8).
|
||||
*/
|
||||
std::string doub2e(double d)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << std::scientific << std::setprecision(12) << d;
|
||||
return rightJustify(ss.str(), 19);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Formats an epoch as used in RINEX 4 navigation data records:
|
||||
* yyyy mm dd hh mm ss (I4, 5(1X,I2.2))
|
||||
*/
|
||||
std::string get_v4_epoch_string(const boost::posix_time::ptime& p_time)
|
||||
{
|
||||
const std::string timestring = boost::posix_time::to_iso_string(p_time);
|
||||
std::string line;
|
||||
line += timestring.substr(0, 4); // year
|
||||
line += std::string(1, ' ');
|
||||
line += timestring.substr(4, 2); // month
|
||||
line += std::string(1, ' ');
|
||||
line += timestring.substr(6, 2); // day
|
||||
line += std::string(1, ' ');
|
||||
line += timestring.substr(9, 2); // hour
|
||||
line += std::string(1, ' ');
|
||||
line += timestring.substr(11, 2); // minutes
|
||||
line += std::string(1, ' ');
|
||||
line += timestring.substr(13, 2); // seconds
|
||||
return line;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* RINEX 4 navigation data record header line, e.g. "> EPH G01 LNAV".
|
||||
* A blank prn (0) identifies system-wide records such as STO or ION
|
||||
*/
|
||||
std::string get_nav_record_header_line(const std::string& record_type, char sys_char, uint32_t prn, const std::string& msg_type)
|
||||
{
|
||||
std::string line;
|
||||
line += std::string("> ");
|
||||
line += record_type;
|
||||
line += std::string(1, ' ');
|
||||
line += sys_char;
|
||||
if (prn > 0)
|
||||
{
|
||||
if (prn < 10)
|
||||
{
|
||||
line += std::string("0");
|
||||
}
|
||||
line += std::to_string(prn);
|
||||
}
|
||||
else
|
||||
{
|
||||
line += std::string(2, ' ');
|
||||
}
|
||||
line += std::string(1, ' ');
|
||||
line += msg_type;
|
||||
return line;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Checks that the line is 80 characters length
|
||||
*/
|
||||
@@ -1115,7 +1208,7 @@ std::string get_local_time(int version)
|
||||
line += std::string(5, ' ');
|
||||
}
|
||||
|
||||
if (version == 3)
|
||||
if (version >= 3)
|
||||
{
|
||||
line += boost::gregorian::to_iso_string(today);
|
||||
line += std::string(1, ' ');
|
||||
@@ -1334,6 +1427,18 @@ std::string get_leap_second_line(int32_t Delta_tLS, int32_t Delta_tLSF, int32_t
|
||||
}
|
||||
|
||||
|
||||
std::string get_blank_leap_second_line()
|
||||
{
|
||||
std::string line;
|
||||
|
||||
line += std::string(60, ' ');
|
||||
line += leftJustify("LEAP SECONDS", 20);
|
||||
lengthCheck(line);
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
|
||||
std::string get_end_of_header_line()
|
||||
{
|
||||
std::string line;
|
||||
@@ -1599,7 +1704,7 @@ void add_constellation_obs_sat_record_lines(std::fstream& out, const std::string
|
||||
{
|
||||
std::string line;
|
||||
|
||||
if (version == 3)
|
||||
if (version >= 3)
|
||||
{
|
||||
auto prn = it.first;
|
||||
if (system_char == satelliteSystem.at("QZSS"))
|
||||
@@ -1654,7 +1759,7 @@ void add_constellation_obs_sat_record_lines(std::fstream& out, const Signal_Enab
|
||||
}
|
||||
|
||||
|
||||
std::string get_nav_sv_epoch_svclk_line(const boost::posix_time::ptime& p_utc_time, char sys_char, uint32_t prn, double value0, double value1, double value2)
|
||||
std::string get_nav_sv_epoch_svclk_line(const boost::posix_time::ptime& p_utc_time, char sys_char, uint32_t prn, double value0, double value1, double value2, int version = 3)
|
||||
{
|
||||
std::string line;
|
||||
|
||||
@@ -1667,6 +1772,19 @@ std::string get_nav_sv_epoch_svclk_line(const boost::posix_time::ptime& p_utc_ti
|
||||
|
||||
line += std::to_string(prn);
|
||||
|
||||
if (version == 4)
|
||||
{
|
||||
line += std::string(1, ' ');
|
||||
line += get_v4_epoch_string(p_utc_time);
|
||||
line += doub2e(value0);
|
||||
line += doub2e(value1);
|
||||
line += doub2e(value2);
|
||||
|
||||
lengthCheck(line);
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
add_svclk_to_line(p_utc_time, true, 3, line);
|
||||
|
||||
line += doub2for(value0, 18, 2);
|
||||
@@ -1685,6 +1803,20 @@ std::string get_nav_broadcast_orbit(const double* value0, const double* value1,
|
||||
{
|
||||
std::string line;
|
||||
|
||||
if (version == 4)
|
||||
{
|
||||
// 4X,4E19.12 with blank fields for data not known / not defined
|
||||
line += std::string(4, ' ');
|
||||
line += value0 ? doub2e(*value0) : std::string(19, ' ');
|
||||
line += value1 ? doub2e(*value1) : std::string(19, ' ');
|
||||
line += value2 ? doub2e(*value2) : std::string(19, ' ');
|
||||
line += value3 ? doub2e(*value3) : std::string(19, ' ');
|
||||
|
||||
lengthCheck(line);
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
line += std::string(version + 2, ' ');
|
||||
line += value0 ? doub2for(*value0, 18, 2) : std::string(18, ' ');
|
||||
line += std::string(1, ' ');
|
||||
@@ -1705,6 +1837,96 @@ std::string get_nav_broadcast_orbit(const double* value0, const double* value1,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* RINEX 4 System Time Offset (STO) data record
|
||||
* (see RINEX 4.02 specification, Table A33)
|
||||
*/
|
||||
void add_sto_record(std::fstream& out, char sys_char, const std::string& msg_type,
|
||||
const boost::posix_time::ptime& ref_epoch, const std::string& sto_label,
|
||||
const std::string& utc_id, double t_tm, double a0, double a1, double a2)
|
||||
{
|
||||
out << get_nav_record_header_line("STO", sys_char, 0, msg_type) << '\n';
|
||||
|
||||
std::string line;
|
||||
line += std::string(4, ' ');
|
||||
line += get_v4_epoch_string(ref_epoch);
|
||||
line += std::string(1, ' ');
|
||||
line += leftJustify(sto_label, 18);
|
||||
line += std::string(1, ' ');
|
||||
line += leftJustify("", 18); // SBAS ID, blank if not SBUT
|
||||
line += std::string(1, ' ');
|
||||
line += leftJustify(utc_id, 18);
|
||||
lengthCheck(line);
|
||||
out << line << '\n';
|
||||
|
||||
line.clear();
|
||||
line += std::string(4, ' ');
|
||||
line += doub2e(t_tm);
|
||||
line += doub2e(a0);
|
||||
line += doub2e(a1);
|
||||
line += doub2e(a2);
|
||||
lengthCheck(line);
|
||||
out << line << '\n';
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* RINEX 4 Ionosphere (ION) Klobuchar model data record
|
||||
* (see RINEX 4.02 specification, Table A35). The message subtype is
|
||||
* compulsory for the QZSS CNVX message type (WIDE or JAPN) and shall be
|
||||
* left blank otherwise
|
||||
*/
|
||||
void add_ion_klobuchar_record(std::fstream& out, char sys_char, const std::string& msg_type,
|
||||
const boost::posix_time::ptime& transmit_time,
|
||||
double alpha0, double alpha1, double alpha2, double alpha3,
|
||||
double beta0, double beta1, double beta2, double beta3,
|
||||
const std::string& subtype = "")
|
||||
{
|
||||
std::string header_line = get_nav_record_header_line("ION", sys_char, 0, msg_type);
|
||||
if (!subtype.empty())
|
||||
{
|
||||
header_line += std::string(1, ' ');
|
||||
header_line += subtype;
|
||||
}
|
||||
out << header_line << '\n';
|
||||
|
||||
std::string line;
|
||||
line += std::string(4, ' ');
|
||||
line += get_v4_epoch_string(transmit_time);
|
||||
line += doub2e(alpha0);
|
||||
line += doub2e(alpha1);
|
||||
line += doub2e(alpha2);
|
||||
lengthCheck(line);
|
||||
out << line << '\n';
|
||||
|
||||
out << get_nav_broadcast_orbit(&alpha3, &beta0, &beta1, &beta2, 4) << '\n';
|
||||
out << get_nav_broadcast_orbit(&beta3, nullptr, nullptr, nullptr, 4) << '\n';
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* RINEX 4 Ionosphere (ION) NeQuick-G model data record
|
||||
* (see RINEX 4.02 specification, Table A36)
|
||||
*/
|
||||
void add_ion_nequick_g_record(std::fstream& out,
|
||||
const boost::posix_time::ptime& transmit_time,
|
||||
double ai0, double ai1, double ai2, double disturbance_flags)
|
||||
{
|
||||
out << get_nav_record_header_line("ION", satelliteSystem.at("Galileo"), 0, "IFNV") << '\n';
|
||||
|
||||
std::string line;
|
||||
line += std::string(4, ' ');
|
||||
line += get_v4_epoch_string(transmit_time);
|
||||
line += doub2e(ai0);
|
||||
line += doub2e(ai1);
|
||||
line += doub2e(ai2);
|
||||
lengthCheck(line);
|
||||
out << line << '\n';
|
||||
|
||||
out << get_nav_broadcast_orbit(&disturbance_flags, nullptr, nullptr, nullptr, 4) << '\n';
|
||||
}
|
||||
|
||||
|
||||
void add_obs_signal_strength(std::fstream& out)
|
||||
{
|
||||
std::string line;
|
||||
@@ -2141,7 +2363,12 @@ int get_version(const Signal_Enabled_Flags& flags, int version)
|
||||
std::cout << "Changing RINEX version to 3.02, because version 2.11 is incompatible with signal selection.\n";
|
||||
}
|
||||
|
||||
return 3; // Only support version 2.11 and 3.02
|
||||
if (version == 4)
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
return 3; // Only support versions 2.11, 3.02 and 4.02
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -2164,13 +2391,14 @@ Rinex_Printer::Rinex_Printer(uint32_t signal_enabled_flags,
|
||||
observationCode(getObservationCodes()),
|
||||
d_flags(signal_enabled_flags),
|
||||
d_version(get_version(d_flags, version)),
|
||||
d_stringVersion(d_version == 2 ? "2.11" : "3.02"), // Only version 2.11 and 3.02
|
||||
d_stringVersion(d_version == 2 ? "2.11" : (d_version == 4 ? "4.02" : "3.02")), // Only versions 2.11, 3.02 and 4.02
|
||||
d_fake_cnav_iode(1),
|
||||
d_rinex_header_updated(false),
|
||||
d_rinex_header_gps_updated(!d_flags.has_gps),
|
||||
d_rinex_header_galileo_updated(!d_flags.has_galileo),
|
||||
d_rinex_header_glonass_updated(!d_flags.has_glonass),
|
||||
d_rinex_header_beidou_updated(!d_flags.has_beidou),
|
||||
d_rinex_header_qzss_updated(!d_flags.has_qzss),
|
||||
d_rinex_header_written(false),
|
||||
d_ref_gps_week(ref_gps_week),
|
||||
navfilename(getNavFilePath(d_flags, d_version, base_name, base_rinex_path)),
|
||||
@@ -2358,6 +2586,9 @@ void Rinex_Printer::print_rinex_annotation(const Rtklib_Solver* pvt_solver,
|
||||
std::vector<std::string> time_corr_lines;
|
||||
std::string leap_second_line;
|
||||
|
||||
// From RINEX 4 on, the ionospheric and time system corrections are no
|
||||
// longer header lines: they are logged as ION and STO data records once
|
||||
// the corresponding data are received (see log_rinex_nav_v4_ion_sto_records)
|
||||
if (d_flags.check_any_enabled(GPS_1C))
|
||||
{
|
||||
if (d_version == 2)
|
||||
@@ -2369,27 +2600,36 @@ void Rinex_Printer::print_rinex_annotation(const Rtklib_Solver* pvt_solver,
|
||||
}
|
||||
else
|
||||
{
|
||||
iono_lines.emplace_back(get_gps_iono_alpha_line(pvt_solver->gps_iono));
|
||||
iono_lines.emplace_back(get_gps_iono_beta_line(pvt_solver->gps_iono));
|
||||
time_corr_lines.emplace_back(get_gps_time_corr_line(pvt_solver->gps_utc_model, gps_ephemeris_iter->second, d_ref_gps_week));
|
||||
if (d_version == 3)
|
||||
{
|
||||
iono_lines.emplace_back(get_gps_iono_alpha_line(pvt_solver->gps_iono));
|
||||
iono_lines.emplace_back(get_gps_iono_beta_line(pvt_solver->gps_iono));
|
||||
time_corr_lines.emplace_back(get_gps_time_corr_line(pvt_solver->gps_utc_model, gps_ephemeris_iter->second, d_ref_gps_week));
|
||||
}
|
||||
leap_second_line = get_leap_second_line(pvt_solver->gps_utc_model, gps_ephemeris_iter->second, d_ref_gps_week);
|
||||
}
|
||||
}
|
||||
else if (d_flags.check_any_enabled(GPS_2S, GPS_L5))
|
||||
{
|
||||
iono_lines.emplace_back(get_gps_iono_alpha_line(pvt_solver->gps_cnav_iono));
|
||||
iono_lines.emplace_back(get_gps_iono_beta_line(pvt_solver->gps_cnav_iono));
|
||||
time_corr_lines.emplace_back(get_gps_time_corr_line(pvt_solver->gps_cnav_utc_model));
|
||||
if (d_version == 3)
|
||||
{
|
||||
iono_lines.emplace_back(get_gps_iono_alpha_line(pvt_solver->gps_cnav_iono));
|
||||
iono_lines.emplace_back(get_gps_iono_beta_line(pvt_solver->gps_cnav_iono));
|
||||
time_corr_lines.emplace_back(get_gps_time_corr_line(pvt_solver->gps_cnav_utc_model));
|
||||
}
|
||||
leap_second_line = get_leap_second_line(pvt_solver->gps_cnav_utc_model);
|
||||
}
|
||||
|
||||
if (d_flags.has_galileo)
|
||||
{
|
||||
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));
|
||||
if (pvt_solver->galileo_utc_model.flag_GGTO)
|
||||
if (d_version == 3)
|
||||
{
|
||||
time_corr_lines.emplace_back(get_gps_to_galileo_time_corr_line(pvt_solver->galileo_utc_model));
|
||||
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));
|
||||
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")
|
||||
@@ -2413,7 +2653,7 @@ void Rinex_Printer::print_rinex_annotation(const Rtklib_Solver* pvt_solver,
|
||||
time_corr_lines.emplace_back(std::move(time_corr_line));
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (d_version == 3)
|
||||
{
|
||||
time_corr_lines.emplace_back(get_glonass_time_corr_line(pvt_solver->glonass_gnav_utc_model));
|
||||
time_corr_lines.emplace_back(get_glonass_to_gps_time_corr_line(pvt_solver->glonass_gnav_utc_model));
|
||||
@@ -2421,9 +2661,12 @@ void Rinex_Printer::print_rinex_annotation(const Rtklib_Solver* pvt_solver,
|
||||
}
|
||||
if (d_flags.has_beidou)
|
||||
{
|
||||
iono_lines.emplace_back(get_beidou_iono_alpha_line(pvt_solver->beidou_dnav_iono));
|
||||
iono_lines.emplace_back(get_beidou_iono_beta_line(pvt_solver->beidou_dnav_iono));
|
||||
time_corr_lines.emplace_back(get_beidou_time_corr_line(pvt_solver->beidou_dnav_utc_model));
|
||||
if (d_version == 3)
|
||||
{
|
||||
iono_lines.emplace_back(get_beidou_iono_alpha_line(pvt_solver->beidou_dnav_iono));
|
||||
iono_lines.emplace_back(get_beidou_iono_beta_line(pvt_solver->beidou_dnav_iono));
|
||||
time_corr_lines.emplace_back(get_beidou_time_corr_line(pvt_solver->beidou_dnav_utc_model));
|
||||
}
|
||||
|
||||
if (system_time_str == "BDS")
|
||||
{
|
||||
@@ -2474,7 +2717,13 @@ void Rinex_Printer::print_rinex_annotation(const Rtklib_Solver* pvt_solver,
|
||||
add_obs_epoch_record(obsFile, system_time, seconds, d_version, constel_observables);
|
||||
add_constellation_obs_sat_record_lines(obsFile, d_flags, constel_observables, d_version);
|
||||
|
||||
if (!d_rinex_header_updated)
|
||||
if (!d_rinex_header_updated && d_version == 4)
|
||||
{
|
||||
// From RINEX 4 on, the iono and time system corrections are logged
|
||||
// as ION and STO data records instead of header lines
|
||||
log_rinex_nav_v4_ion_sto_records(pvt_solver, rx_time, system_time_str);
|
||||
}
|
||||
else if (!d_rinex_header_updated)
|
||||
{
|
||||
std::string leap_second_line;
|
||||
std::vector<NavHeaderInfo> nav_header_info;
|
||||
@@ -2564,6 +2813,176 @@ void Rinex_Printer::print_rinex_annotation(const Rtklib_Solver* pvt_solver,
|
||||
}
|
||||
|
||||
|
||||
void Rinex_Printer::log_rinex_nav_v4_ion_sto_records(const Rtklib_Solver* pvt_solver, double rx_time, const std::string& system_time_str)
|
||||
{
|
||||
auto& out = navFile;
|
||||
bool records_written = false;
|
||||
std::string leap_second_line;
|
||||
const double tow = fmod(rx_time, 604800.0);
|
||||
|
||||
if (!d_rinex_header_gps_updated)
|
||||
{
|
||||
if (d_flags.check_any_enabled(GPS_1C) && pvt_solver->gps_utc_model.A0 != 0)
|
||||
{
|
||||
const auto& eph = pvt_solver->gps_ephemeris_map.cbegin()->second;
|
||||
const auto& utc_model = pvt_solver->gps_utc_model;
|
||||
const auto& iono = pvt_solver->gps_iono;
|
||||
add_ion_klobuchar_record(out, satelliteSystem.at("GPS"), "LNAV", Rinex_Printer::compute_GPS_time(eph, rx_time),
|
||||
iono.alpha0, iono.alpha1, iono.alpha2, iono.alpha3,
|
||||
iono.beta0, iono.beta1, iono.beta2, iono.beta3);
|
||||
const int32_t wn_t = expand_lnav_utc_week(utc_model.WN_T, eph.WN, d_ref_gps_week);
|
||||
add_sto_record(out, satelliteSystem.at("GPS"), "LNAV", gps_time_to_ptime(wn_t, utc_model.tot),
|
||||
"GPUT", "UTC(USNO)", tow, utc_model.A0, utc_model.A1, utc_model.A2);
|
||||
leap_second_line = get_leap_second_line(utc_model, eph, d_ref_gps_week);
|
||||
d_rinex_header_gps_updated = true;
|
||||
records_written = true;
|
||||
}
|
||||
else if (d_flags.check_any_enabled(GPS_2S, GPS_L5) && pvt_solver->gps_cnav_utc_model.A0 != 0)
|
||||
{
|
||||
const auto& eph = pvt_solver->gps_cnav_ephemeris_map.cbegin()->second;
|
||||
const auto& utc_model = pvt_solver->gps_cnav_utc_model;
|
||||
const auto& iono = pvt_solver->gps_cnav_iono;
|
||||
add_ion_klobuchar_record(out, satelliteSystem.at("GPS"), "CNVX", Rinex_Printer::compute_GPS_time(eph, rx_time),
|
||||
iono.alpha0, iono.alpha1, iono.alpha2, iono.alpha3,
|
||||
iono.beta0, iono.beta1, iono.beta2, iono.beta3);
|
||||
add_sto_record(out, satelliteSystem.at("GPS"), "CNVX", gps_time_to_ptime(utc_model.WN_T, utc_model.tot),
|
||||
"GPUT", "UTC(USNO)", tow, utc_model.A0, utc_model.A1, utc_model.A2);
|
||||
leap_second_line = get_leap_second_line(utc_model);
|
||||
d_rinex_header_gps_updated = true;
|
||||
records_written = true;
|
||||
}
|
||||
}
|
||||
if (!d_rinex_header_qzss_updated)
|
||||
{
|
||||
if (d_flags.check_any_enabled(QZS_J1) && pvt_solver->qzss_utc_model.A0 != 0)
|
||||
{
|
||||
// QZSS weeks are aligned with GPS weeks, so any ephemeris of the
|
||||
// shared GPS/QZSS LNAV map works as reference to resolve the
|
||||
// 8-bit UTC week number
|
||||
const auto& eph = pvt_solver->gps_ephemeris_map.cbegin()->second;
|
||||
const auto& utc_model = pvt_solver->qzss_utc_model;
|
||||
const auto& iono = pvt_solver->qzss_iono;
|
||||
add_ion_klobuchar_record(out, satelliteSystem.at("QZSS"), "LNAV", Rinex_Printer::compute_GPS_time(eph, rx_time),
|
||||
iono.alpha0, iono.alpha1, iono.alpha2, iono.alpha3,
|
||||
iono.beta0, iono.beta1, iono.beta2, iono.beta3);
|
||||
const int32_t wn_t = expand_lnav_utc_week(utc_model.WN_T, eph.WN, d_ref_gps_week);
|
||||
add_sto_record(out, satelliteSystem.at("QZSS"), "LNAV", gps_time_to_ptime(wn_t, utc_model.tot),
|
||||
"QZUT", "UTC(NICT)", tow, utc_model.A0, utc_model.A1, utc_model.A2);
|
||||
|
||||
if (system_time_str == "QZS")
|
||||
{
|
||||
leap_second_line = get_leap_second_line(utc_model, eph, d_ref_gps_week);
|
||||
}
|
||||
d_rinex_header_qzss_updated = true;
|
||||
records_written = true;
|
||||
}
|
||||
else if (d_flags.check_any_enabled(QZS_J5) && pvt_solver->qzss_cnav_utc_model.A0 != 0)
|
||||
{
|
||||
const auto& eph = pvt_solver->gps_cnav_ephemeris_map.cbegin()->second;
|
||||
const auto& utc_model = pvt_solver->qzss_cnav_utc_model;
|
||||
const auto& iono = pvt_solver->qzss_cnav_iono;
|
||||
// QZSS CNAV Message Type 30 broadcasts the Wide Area coefficient
|
||||
// set (the Japan area set of Message Type 61 is not decoded), so
|
||||
// the compulsory CNVX message subtype is WIDE (see Table A35)
|
||||
add_ion_klobuchar_record(out, satelliteSystem.at("QZSS"), "CNVX", Rinex_Printer::compute_GPS_time(eph, rx_time),
|
||||
iono.alpha0, iono.alpha1, iono.alpha2, iono.alpha3,
|
||||
iono.beta0, iono.beta1, iono.beta2, iono.beta3, "WIDE");
|
||||
add_sto_record(out, satelliteSystem.at("QZSS"), "CNVX", gps_time_to_ptime(utc_model.WN_T, utc_model.tot),
|
||||
"QZUT", "UTC(NICT)", tow, utc_model.A0, utc_model.A1, utc_model.A2);
|
||||
|
||||
if (system_time_str == "QZS")
|
||||
{
|
||||
leap_second_line = get_leap_second_line(utc_model);
|
||||
}
|
||||
d_rinex_header_qzss_updated = true;
|
||||
records_written = true;
|
||||
}
|
||||
}
|
||||
if (!d_rinex_header_galileo_updated && pvt_solver->galileo_utc_model.A0 != 0)
|
||||
{
|
||||
const auto& eph = pvt_solver->galileo_ephemeris_map.cbegin()->second;
|
||||
const auto& utc_model = pvt_solver->galileo_utc_model;
|
||||
const auto& iono = pvt_solver->galileo_iono;
|
||||
const auto disturbance_flags = static_cast<double>(((iono.Region1_flag ? 1U : 0U) << 4U) |
|
||||
((iono.Region2_flag ? 1U : 0U) << 3U) |
|
||||
((iono.Region3_flag ? 1U : 0U) << 2U) |
|
||||
((iono.Region4_flag ? 1U : 0U) << 1U) |
|
||||
(iono.Region5_flag ? 1U : 0U));
|
||||
add_ion_nequick_g_record(out, Rinex_Printer::compute_Galileo_time(eph, rx_time), iono.ai0, iono.ai1, iono.ai2, disturbance_flags);
|
||||
|
||||
// The continuous GAL week number is aligned to the continuous GPS week number
|
||||
const int32_t gal_continuous_week = eph.WN + 1024 + 4096 * ((eph.WN + 1024) / 4096);
|
||||
const int32_t wn_ot = expand_truncated_week(utc_model.WNot, gal_continuous_week, 256);
|
||||
add_sto_record(out, satelliteSystem.at("Galileo"), "IFNV", gps_time_to_ptime(wn_ot, utc_model.tot),
|
||||
"GAUT", "UTCGAL", tow, utc_model.A0, utc_model.A1, 0.0);
|
||||
if (utc_model.flag_GGTO)
|
||||
{
|
||||
const int32_t wn_0g = expand_truncated_week(utc_model.WN_0G, gal_continuous_week, 64);
|
||||
add_sto_record(out, satelliteSystem.at("Galileo"), "IFNV", gps_time_to_ptime(wn_0g, utc_model.t_0G),
|
||||
"GAGP", "", tow, utc_model.A_0G, utc_model.A_1G, 0.0);
|
||||
}
|
||||
|
||||
if (system_time_str == "GAL")
|
||||
{
|
||||
leap_second_line = get_leap_second_line(utc_model);
|
||||
}
|
||||
d_rinex_header_galileo_updated = true;
|
||||
records_written = true;
|
||||
}
|
||||
if (!d_rinex_header_glonass_updated && pvt_solver->glonass_gnav_utc_model.d_tau_c != 0)
|
||||
{
|
||||
const auto& eph = pvt_solver->glonass_gnav_ephemeris_map.cbegin()->second;
|
||||
const auto& utc_model = pvt_solver->glonass_gnav_utc_model;
|
||||
// The reference epoch is the start of the GLONASS day (t_0d), aligned to UTC
|
||||
const boost::posix_time::ptime day_start(eph.glot_to_utc(eph.d_t_b, 0.0).date());
|
||||
const boost::posix_time::ptime p_utc_time = Rinex_Printer::compute_UTC_time(eph, rx_time);
|
||||
const double utc_tow = static_cast<double>(p_utc_time.date().day_of_week()) * 86400.0 +
|
||||
static_cast<double>(p_utc_time.time_of_day().total_seconds());
|
||||
// The broadcast GLONASS time offsets are stored as -TauC and -TauGPS
|
||||
// (see RINEX 4.02 specification, Section 5.4.4 and Table 28)
|
||||
add_sto_record(out, satelliteSystem.at("GLONASS"), "FDMA", day_start,
|
||||
"GLUT", "UTC(SU)", utc_tow, -utc_model.d_tau_c, 0.0, 0.0);
|
||||
add_sto_record(out, satelliteSystem.at("GLONASS"), "FDMA", day_start,
|
||||
"GLGP", "", utc_tow, -utc_model.d_tau_gps, 0.0, 0.0);
|
||||
d_rinex_header_glonass_updated = true;
|
||||
records_written = true;
|
||||
}
|
||||
if (!d_rinex_header_beidou_updated && pvt_solver->beidou_dnav_utc_model.A0_UTC != 0)
|
||||
{
|
||||
const auto& eph = pvt_solver->beidou_dnav_ephemeris_map.cbegin()->second;
|
||||
const auto& utc_model = pvt_solver->beidou_dnav_utc_model;
|
||||
const auto& iono = pvt_solver->beidou_dnav_iono;
|
||||
add_ion_klobuchar_record(out, satelliteSystem.at("Beidou"), "D1D2", Rinex_Printer::compute_BDS_time(eph, rx_time),
|
||||
iono.alpha0, iono.alpha1, iono.alpha2, iono.alpha3,
|
||||
iono.beta0, iono.beta1, iono.beta2, iono.beta3);
|
||||
// The reference epoch is the start of the BDT week (t_0w)
|
||||
add_sto_record(out, satelliteSystem.at("Beidou"), "D1D2", Rinex_Printer::compute_BDS_time(eph, 0.0),
|
||||
"BDUT", "UTC(NTSC)", tow, utc_model.A0_UTC, utc_model.A1_UTC, 0.0);
|
||||
|
||||
if (system_time_str == "BDS")
|
||||
{
|
||||
leap_second_line = get_leap_second_line(utc_model);
|
||||
}
|
||||
d_rinex_header_beidou_updated = true;
|
||||
records_written = true;
|
||||
}
|
||||
|
||||
if (!records_written)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!leap_second_line.empty())
|
||||
{
|
||||
update_obs_header(obsFile, leap_second_line);
|
||||
update_nav_header_from_info(navFile, navfilename, {NavHeaderInfo("", "LEAP SECONDS", leap_second_line)});
|
||||
}
|
||||
|
||||
std::cout << "The RINEX Navigation file has been updated with UTC and IONO data records.\n";
|
||||
d_rinex_header_updated = d_rinex_header_gps_updated && d_rinex_header_qzss_updated && d_rinex_header_galileo_updated && d_rinex_header_glonass_updated && d_rinex_header_beidou_updated;
|
||||
}
|
||||
|
||||
|
||||
void Rinex_Printer::log_rinex_nav_gps_nav(const std::map<int32_t, Gps_Ephemeris>& new_eph)
|
||||
{
|
||||
std::string line;
|
||||
@@ -2618,11 +3037,15 @@ void Rinex_Printer::log_rinex_nav_gps_nav(const std::map<int32_t, Gps_Ephemeris>
|
||||
out << line << '\n';
|
||||
line.clear();
|
||||
}
|
||||
if (d_version == 3)
|
||||
if (d_version >= 3)
|
||||
{
|
||||
const auto sat_system_char = is_qzss_prn(eph.PRN) ? satelliteSystem.at("QZSS") : satelliteSystem.at("GPS");
|
||||
const auto rinex_prn = is_qzss_prn(eph.PRN) ? to_rinex_qzss_prn(eph.PRN) : eph.PRN;
|
||||
out << get_nav_sv_epoch_svclk_line(p_utc_time, sat_system_char, rinex_prn, eph.af0, eph.af1, eph.af2) << '\n';
|
||||
if (d_version == 4)
|
||||
{
|
||||
out << get_nav_record_header_line("EPH", sat_system_char, rinex_prn, "LNAV") << '\n';
|
||||
}
|
||||
out << get_nav_sv_epoch_svclk_line(p_utc_time, sat_system_char, rinex_prn, eph.af0, eph.af1, eph.af2, d_version) << '\n';
|
||||
}
|
||||
|
||||
// -------- BROADCAST ORBIT - 1
|
||||
@@ -2746,6 +3169,51 @@ void Rinex_Printer::log_rinex_nav_gps_cnav(const std::map<int32_t, Gps_CNAV_Ephe
|
||||
const boost::posix_time::ptime p_utc_time = Rinex_Printer::compute_GPS_time(eph, eph.toc);
|
||||
const auto sat_system_char = is_qzss_prn(eph.PRN) ? satelliteSystem.at("QZSS") : satelliteSystem.at("GPS");
|
||||
const auto rinex_prn = is_qzss_prn(eph.PRN) ? to_rinex_qzss_prn(eph.PRN) : eph.PRN;
|
||||
|
||||
if (d_version == 4)
|
||||
{
|
||||
// CNAV navigation message record, see RINEX 4.02 specification, Table A10
|
||||
out << get_nav_record_header_line("EPH", sat_system_char, rinex_prn, "CNAV") << '\n';
|
||||
out << get_nav_sv_epoch_svclk_line(p_utc_time, sat_system_char, rinex_prn, eph.af0, eph.af1, eph.af2, d_version) << '\n';
|
||||
|
||||
// -------- BROADCAST ORBIT - 1
|
||||
out << get_nav_broadcast_orbit(&eph.Adot, &eph.Crs, &eph.delta_n, &eph.M_0, d_version) << '\n';
|
||||
|
||||
// -------- BROADCAST ORBIT - 2
|
||||
out << get_nav_broadcast_orbit(&eph.Cuc, &eph.ecc, &eph.Cus, &eph.sqrtA, d_version) << '\n';
|
||||
|
||||
// -------- BROADCAST ORBIT - 3
|
||||
const auto top_d = static_cast<double>(eph.top);
|
||||
out << get_nav_broadcast_orbit(&top_d, &eph.Cic, &eph.OMEGA_0, &eph.Cis, d_version) << '\n';
|
||||
|
||||
// -------- BROADCAST ORBIT - 4
|
||||
out << get_nav_broadcast_orbit(&eph.i_0, &eph.Crc, &eph.omega, &eph.OMEGAdot, d_version) << '\n';
|
||||
|
||||
// -------- BROADCAST ORBIT - 5
|
||||
const auto uraned0_d = static_cast<double>(eph.URANED0);
|
||||
const auto uraned1_d = static_cast<double>(eph.URANED1);
|
||||
out << get_nav_broadcast_orbit(&eph.idot, &eph.delta_ndot, &uraned0_d, &uraned1_d, d_version) << '\n';
|
||||
|
||||
// -------- BROADCAST ORBIT - 6
|
||||
const auto uraed_d = static_cast<double>(eph.URAED);
|
||||
const auto health_d = static_cast<double>(eph.signal_health);
|
||||
const auto uraned2_d = static_cast<double>(eph.URANED2);
|
||||
out << get_nav_broadcast_orbit(&uraed_d, &health_d, &eph.TGD, &uraned2_d, d_version) << '\n';
|
||||
|
||||
// -------- BROADCAST ORBIT - 7
|
||||
out << get_nav_broadcast_orbit(&eph.ISCL1, &eph.ISCL2, &eph.ISCL5I, &eph.ISCL5Q, d_version) << '\n';
|
||||
|
||||
// -------- BROADCAST ORBIT - 8
|
||||
const auto tow_d = static_cast<double>(eph.tow);
|
||||
// wn_op: GPS continuous week number with the 8-bit MT30 ambiguity resolved
|
||||
const auto wn_op_d = static_cast<double>(eph.WNop >= 0 ? expand_truncated_week(eph.WNop, eph.WN, 256) : eph.WN);
|
||||
const auto flags_d = static_cast<double>((eph.integrity_status_flag ? 1 : 0) |
|
||||
(eph.l2c_phasing_flag ? 2 : 0) |
|
||||
(eph.alert_flag ? 4 : 0));
|
||||
out << get_nav_broadcast_orbit(&tow_d, &wn_op_d, &flags_d, nullptr, d_version) << '\n';
|
||||
continue;
|
||||
}
|
||||
|
||||
out << get_nav_sv_epoch_svclk_line(p_utc_time, sat_system_char, rinex_prn, eph.af0, eph.af1, eph.af2) << '\n';
|
||||
|
||||
// -------- BROADCAST ORBIT - 1
|
||||
@@ -2809,21 +3277,26 @@ void Rinex_Printer::log_rinex_nav_gal_nav(const std::map<int32_t, Galileo_Epheme
|
||||
|
||||
// -------- SV / EPOCH / SV CLK
|
||||
const boost::posix_time::ptime p_utc_time = Rinex_Printer::compute_Galileo_time(eph, eph.toe);
|
||||
out << get_nav_sv_epoch_svclk_line(p_utc_time, sys_char, eph.PRN, eph.af0, eph.af1, eph.af2) << '\n';
|
||||
if (d_version == 4)
|
||||
{
|
||||
const std::string msg_type = eph.nav_message_type == Galileo_Nav_Message_Type::FNAV ? "FNAV" : "INAV";
|
||||
out << get_nav_record_header_line("EPH", sys_char, eph.PRN, msg_type) << '\n';
|
||||
}
|
||||
out << get_nav_sv_epoch_svclk_line(p_utc_time, sys_char, eph.PRN, eph.af0, eph.af1, eph.af2, d_version) << '\n';
|
||||
|
||||
// -------- BROADCAST ORBIT - 1
|
||||
const auto iod_d = static_cast<double>(eph.IOD_ephemeris);
|
||||
out << get_nav_broadcast_orbit(&iod_d, &eph.Crs, &eph.delta_n, &eph.M_0) << '\n';
|
||||
out << get_nav_broadcast_orbit(&iod_d, &eph.Crs, &eph.delta_n, &eph.M_0, d_version) << '\n';
|
||||
|
||||
// -------- BROADCAST ORBIT - 2
|
||||
out << get_nav_broadcast_orbit(&eph.Cuc, &eph.ecc, &eph.Cus, &eph.sqrtA) << '\n';
|
||||
out << get_nav_broadcast_orbit(&eph.Cuc, &eph.ecc, &eph.Cus, &eph.sqrtA, d_version) << '\n';
|
||||
|
||||
// -------- BROADCAST ORBIT - 3
|
||||
const auto toe_d = static_cast<double>(eph.toe);
|
||||
out << get_nav_broadcast_orbit(&toe_d, &eph.Cic, &eph.OMEGA_0, &eph.Cis) << '\n';
|
||||
out << get_nav_broadcast_orbit(&toe_d, &eph.Cic, &eph.OMEGA_0, &eph.Cis, d_version) << '\n';
|
||||
|
||||
// -------- BROADCAST ORBIT - 4
|
||||
out << get_nav_broadcast_orbit(&eph.i_0, &eph.Crc, &eph.omega, &eph.OMEGAdot) << '\n';
|
||||
out << get_nav_broadcast_orbit(&eph.i_0, &eph.Crc, &eph.omega, &eph.OMEGAdot, d_version) << '\n';
|
||||
|
||||
// -------- BROADCAST ORBIT - 5
|
||||
// RINEX Galileo data-source bits: bit 0 is I/NAV E1-B, bit 1 is
|
||||
@@ -2836,7 +3309,7 @@ void Rinex_Printer::log_rinex_nav_gal_nav(const std::map<int32_t, Galileo_Epheme
|
||||
const double num_GST_rollovers = floor((GST_week + 1024.0) / 4096.0);
|
||||
const double Galileo_week_continuous_number = GST_week + 1024.0 + num_GST_rollovers * 4096.0;
|
||||
const double zero = 0.0;
|
||||
out << get_nav_broadcast_orbit(&eph.idot, &data_source_rinex, &Galileo_week_continuous_number, &zero) << '\n';
|
||||
out << get_nav_broadcast_orbit(&eph.idot, &data_source_rinex, &Galileo_week_continuous_number, d_version == 4 ? nullptr : &zero, d_version) << '\n';
|
||||
|
||||
// -------- BROADCAST ORBIT - 6
|
||||
const uint32_t unavailable_hs = 3U;
|
||||
@@ -2857,11 +3330,19 @@ void Rinex_Printer::log_rinex_nav_gal_nav(const std::map<int32_t, Galileo_Epheme
|
||||
((e5b_dvs & 0x1U) << 8U);
|
||||
const auto SVhealth = static_cast<double>(health_bits);
|
||||
const auto SISA_d = static_cast<double>(eph.SISA);
|
||||
out << get_nav_broadcast_orbit(&SISA_d, &SVhealth, &eph.BGD_E1E5a, &eph.BGD_E1E5b) << '\n';
|
||||
out << get_nav_broadcast_orbit(&SISA_d, &SVhealth, &eph.BGD_E1E5a, &eph.BGD_E1E5b, d_version) << '\n';
|
||||
|
||||
// -------- BROADCAST ORBIT - 7
|
||||
const auto tow_d = static_cast<double>(eph.tow);
|
||||
out << get_nav_broadcast_orbit(&tow_d, &zero, nullptr, nullptr) << '\n';
|
||||
if (d_version == 4)
|
||||
{
|
||||
// Only the transmission time of message (4X,1E19.12), see Table A13
|
||||
out << get_nav_broadcast_orbit(&tow_d, nullptr, nullptr, nullptr, d_version) << '\n';
|
||||
}
|
||||
else
|
||||
{
|
||||
out << get_nav_broadcast_orbit(&tow_d, &zero, nullptr, nullptr) << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2894,7 +3375,7 @@ void Rinex_Printer::log_rinex_nav_glo_gnav(const std::map<int32_t, Glonass_Gnav_
|
||||
lengthCheck(line);
|
||||
out << line << '\n';
|
||||
}
|
||||
if (d_version == 3)
|
||||
if (d_version >= 3)
|
||||
{
|
||||
// Message frame time in seconds of the UTC week (tk is given in
|
||||
// seconds of the GLONASS day, which is offset 3 h from UTC)
|
||||
@@ -2902,7 +3383,11 @@ void Rinex_Printer::log_rinex_nav_glo_gnav(const std::map<int32_t, Glonass_Gnav_
|
||||
const auto frame_day_of_week = static_cast<double>(p_frame_time_utc.date().day_of_week());
|
||||
const auto frame_seconds_of_day = static_cast<double>(p_frame_time_utc.time_of_day().total_seconds());
|
||||
const double message_frame_time = frame_day_of_week * 86400.0 + frame_seconds_of_day;
|
||||
out << get_nav_sv_epoch_svclk_line(p_utc_time, sys_char, eph.PRN, -eph.d_tau_n, +eph.d_gamma_n, message_frame_time) << '\n';
|
||||
if (d_version == 4)
|
||||
{
|
||||
out << get_nav_record_header_line("EPH", sys_char, eph.PRN, "FDMA") << '\n';
|
||||
}
|
||||
out << get_nav_sv_epoch_svclk_line(p_utc_time, sys_char, eph.PRN, -eph.d_tau_n, +eph.d_gamma_n, message_frame_time, d_version) << '\n';
|
||||
}
|
||||
line.clear();
|
||||
|
||||
@@ -2917,6 +3402,36 @@ void Rinex_Printer::log_rinex_nav_glo_gnav(const std::map<int32_t, Glonass_Gnav_
|
||||
|
||||
// -------- BROADCAST ORBIT - 3
|
||||
out << get_nav_broadcast_orbit(&eph.d_Zn, &eph.d_VZn, &eph.d_AZn, &eph.d_E_n, d_version) << '\n';
|
||||
|
||||
// -------- BROADCAST ORBIT - 4 (only from RINEX 4 on, see Table A15)
|
||||
if (d_version == 4)
|
||||
{
|
||||
// P1 is stored in minutes (0/30/45/60); map it back to the 2-bit broadcast code
|
||||
const auto p1_minutes = static_cast<int32_t>(eph.d_P_1);
|
||||
int32_t p1_code = 0;
|
||||
if (p1_minutes == 30)
|
||||
{
|
||||
p1_code = 1;
|
||||
}
|
||||
else if (p1_minutes == 45)
|
||||
{
|
||||
p1_code = 2;
|
||||
}
|
||||
else if (p1_minutes == 60)
|
||||
{
|
||||
p1_code = 3;
|
||||
}
|
||||
const auto status_flags = static_cast<double>((static_cast<uint32_t>(eph.d_M) << 7U) |
|
||||
((eph.d_P_4 ? 1U : 0U) << 6U) |
|
||||
((eph.d_P_3 ? 1U : 0U) << 5U) |
|
||||
((eph.d_P_2 ? 1U : 0U) << 4U) |
|
||||
(static_cast<uint32_t>(p1_code) << 2U) |
|
||||
static_cast<uint32_t>(eph.d_P));
|
||||
// Health flags: bit 2 holds the string 3 health bit l(3); the almanac
|
||||
// health bit (bit 0) is not decoded, and bit 1 = 0 marks it as not reported
|
||||
const auto health_flags = static_cast<double>((eph.d_l3rd_n ? 1U : 0U) << 2U);
|
||||
out << get_nav_broadcast_orbit(&status_flags, &eph.d_Delta_tau_n, &eph.d_F_T, &health_flags, d_version) << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2932,33 +3447,38 @@ void Rinex_Printer::log_rinex_nav_bds_dnav(const std::map<int32_t, Beidou_Dnav_E
|
||||
|
||||
// -------- SV / EPOCH / SV CLK
|
||||
const boost::posix_time::ptime p_utc_time = Rinex_Printer::compute_BDS_time(eph, eph.toc);
|
||||
out << get_nav_sv_epoch_svclk_line(p_utc_time, sys_char, eph.PRN, eph.af0, eph.af1, eph.af2) << '\n';
|
||||
if (d_version == 4)
|
||||
{
|
||||
const std::string msg_type = is_beidou_geo_prn(eph.PRN) ? "D2" : "D1";
|
||||
out << get_nav_record_header_line("EPH", sys_char, eph.PRN, msg_type) << '\n';
|
||||
}
|
||||
out << get_nav_sv_epoch_svclk_line(p_utc_time, sys_char, eph.PRN, eph.af0, eph.af1, eph.af2, d_version) << '\n';
|
||||
|
||||
// -------- BROADCAST ORBIT - 1
|
||||
out << get_nav_broadcast_orbit(&eph.AODE, &eph.Crs, &eph.delta_n, &eph.M_0) << '\n';
|
||||
out << get_nav_broadcast_orbit(&eph.AODE, &eph.Crs, &eph.delta_n, &eph.M_0, d_version) << '\n';
|
||||
|
||||
// -------- BROADCAST ORBIT - 2
|
||||
out << get_nav_broadcast_orbit(&eph.Cuc, &eph.ecc, &eph.Cus, &eph.sqrtA) << '\n';
|
||||
out << get_nav_broadcast_orbit(&eph.Cuc, &eph.ecc, &eph.Cus, &eph.sqrtA, d_version) << '\n';
|
||||
|
||||
// -------- BROADCAST ORBIT - 3
|
||||
const auto toe_d = static_cast<double>(eph.toe);
|
||||
out << get_nav_broadcast_orbit(&toe_d, &eph.Cic, &eph.OMEGA_0, &eph.Cis) << '\n';
|
||||
out << get_nav_broadcast_orbit(&toe_d, &eph.Cic, &eph.OMEGA_0, &eph.Cis, d_version) << '\n';
|
||||
|
||||
// -------- BROADCAST ORBIT - 4
|
||||
out << get_nav_broadcast_orbit(&eph.i_0, &eph.Crc, &eph.omega, &eph.OMEGAdot) << '\n';
|
||||
out << get_nav_broadcast_orbit(&eph.i_0, &eph.Crc, &eph.omega, &eph.OMEGAdot, d_version) << '\n';
|
||||
|
||||
// -------- BROADCAST ORBIT - 5
|
||||
const auto wn_d = static_cast<double>(eph.WN);
|
||||
out << get_nav_broadcast_orbit(&eph.idot, nullptr, &wn_d, nullptr) << '\n';
|
||||
out << get_nav_broadcast_orbit(&eph.idot, nullptr, &wn_d, nullptr, d_version) << '\n';
|
||||
|
||||
// -------- BROADCAST ORBIT - 6
|
||||
const auto acc_d = static_cast<double>(eph.SV_accuracy);
|
||||
const auto health_d = static_cast<double>(eph.SV_health);
|
||||
out << get_nav_broadcast_orbit(&acc_d, &health_d, &eph.TGD1, &eph.TGD2) << '\n';
|
||||
out << get_nav_broadcast_orbit(&acc_d, &health_d, &eph.TGD1, &eph.TGD2, d_version) << '\n';
|
||||
|
||||
// -------- BROADCAST ORBIT - 7
|
||||
const auto tow_d = static_cast<double>(eph.tow);
|
||||
out << get_nav_broadcast_orbit(&tow_d, &eph.AODC, nullptr, nullptr) << '\n';
|
||||
out << get_nav_broadcast_orbit(&tow_d, &eph.AODC, nullptr, nullptr, d_version) << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3004,6 +3524,13 @@ void Rinex_Printer::rinex_nav_header(std::fstream& out,
|
||||
|
||||
add_navigation_header_start(out, type, constellation, d_version, d_stringVersion);
|
||||
|
||||
if (d_version == 4)
|
||||
{
|
||||
// From RINEX 4 on, station navigation files are to include the
|
||||
// receiver line (see Table A7)
|
||||
add_obs_rec_type(out);
|
||||
}
|
||||
|
||||
for (const auto& iono_line : iono_lines)
|
||||
{
|
||||
out << iono_line << '\n';
|
||||
@@ -3018,6 +3545,12 @@ void Rinex_Printer::rinex_nav_header(std::fstream& out,
|
||||
{
|
||||
out << leap_second_line << '\n';
|
||||
}
|
||||
else if (d_version == 4)
|
||||
{
|
||||
// LEAP SECONDS is a compulsory header record in RINEX 4 (see Table A7);
|
||||
// write a blank record to be updated once the data are received
|
||||
out << get_blank_leap_second_line() << '\n';
|
||||
}
|
||||
|
||||
out << get_end_of_header_line() << '\n';
|
||||
}
|
||||
@@ -3161,7 +3694,7 @@ void Rinex_Printer::rinex_obs_header(std::fstream& out,
|
||||
|
||||
// -------- SYS /PHASE SHIFTS
|
||||
|
||||
if (d_version == 3 && d_flags.has_glonass)
|
||||
if (d_version >= 3 && d_flags.has_glonass)
|
||||
{
|
||||
// -------- GLONASS SLOT / FRQ #
|
||||
add_obs_glonass_slot_freq(out);
|
||||
@@ -3177,6 +3710,9 @@ void Rinex_Printer::rinex_obs_header(std::fstream& out,
|
||||
|
||||
void Rinex_Printer::update_obs_header(std::fstream& out, const std::string& leap_second_line) const
|
||||
{
|
||||
// Keep the end-of-data position, so that the file is not mistaken
|
||||
// for an empty one (and thus deleted) when the printer is destroyed
|
||||
const int64_t pos = out.tellp();
|
||||
out.seekp(0);
|
||||
|
||||
std::vector<std::string> data;
|
||||
@@ -3196,7 +3732,7 @@ void Rinex_Printer::update_obs_header(std::fstream& out, const std::string& leap
|
||||
data.push_back(line_str);
|
||||
}
|
||||
|
||||
override_stream_with_new_data(out, obsfilename, data, 0);
|
||||
override_stream_with_new_data(out, obsfilename, data, pos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*!
|
||||
* \file rinex_printer.h
|
||||
* \brief Interface of a RINEX 2.11 / 3.01 printer
|
||||
* See ftp://igs.org/pub/data/format/rinex301.pdf
|
||||
* \brief Interface of a RINEX 2.11 / 3.02 / 4.02 printer
|
||||
* See https://files.igs.org/pub/data/format/rinex_4.02.pdf
|
||||
*
|
||||
* Receiver Independent EXchange Format (RINEX):
|
||||
* The first proposal for the Receiver Independent Exchange Format RINEX
|
||||
@@ -183,6 +183,15 @@ private:
|
||||
const std::vector<std::string>& time_corr_lines,
|
||||
const std::string& leap_second_line) const;
|
||||
|
||||
/*
|
||||
* Logs RINEX 4 ION and STO data records for the enabled constellations as
|
||||
* soon as the iono model and UTC data become available, and updates the
|
||||
* LEAP SECONDS header line of the observation and navigation files
|
||||
*/
|
||||
void log_rinex_nav_v4_ion_sto_records(const Rtklib_Solver* pvt_solver,
|
||||
double rx_time,
|
||||
const std::string& system_time_str);
|
||||
|
||||
/*
|
||||
* Computes the BDS Time and returns a boost::posix_time::ptime object
|
||||
* \details Function used to convert the observation time into BDT time which is used
|
||||
@@ -240,8 +249,8 @@ private:
|
||||
|
||||
const Signal_Enabled_Flags d_flags;
|
||||
|
||||
const int d_version; // RINEX version (2 for 2.10/2.11 and 3 for 3.01)
|
||||
const std::string d_stringVersion; // RINEX version (2.10/2.11 or 3.01/3.02)
|
||||
const int d_version; // RINEX version (2 for 2.11, 3 for 3.02, 4 for 4.02)
|
||||
const std::string d_stringVersion; // RINEX version (2.11, 3.02 or 4.02)
|
||||
|
||||
double d_fake_cnav_iode;
|
||||
bool d_rinex_header_updated;
|
||||
@@ -249,6 +258,7 @@ private:
|
||||
bool d_rinex_header_galileo_updated;
|
||||
bool d_rinex_header_glonass_updated;
|
||||
bool d_rinex_header_beidou_updated;
|
||||
bool d_rinex_header_qzss_updated;
|
||||
bool d_rinex_header_written;
|
||||
const int32_t d_ref_gps_week;
|
||||
|
||||
|
||||
@@ -1876,6 +1876,31 @@ bool Rtklib_Solver::get_PVT(const std::map<int, Gnss_Synchro> &gnss_observables_
|
||||
d_nav_data.ion_gps[6] = gps_cnav_iono.beta2;
|
||||
d_nav_data.ion_gps[7] = gps_cnav_iono.beta3;
|
||||
}
|
||||
if (qzss_iono.valid || qzss_cnav_iono.valid)
|
||||
{
|
||||
const Gps_Iono &qzss_iono_ref = qzss_iono.valid ? static_cast<const Gps_Iono &>(qzss_iono) : static_cast<const Gps_Iono &>(qzss_cnav_iono);
|
||||
d_nav_data.ion_qzs[0] = qzss_iono_ref.alpha0;
|
||||
d_nav_data.ion_qzs[1] = qzss_iono_ref.alpha1;
|
||||
d_nav_data.ion_qzs[2] = qzss_iono_ref.alpha2;
|
||||
d_nav_data.ion_qzs[3] = qzss_iono_ref.alpha3;
|
||||
d_nav_data.ion_qzs[4] = qzss_iono_ref.beta0;
|
||||
d_nav_data.ion_qzs[5] = qzss_iono_ref.beta1;
|
||||
d_nav_data.ion_qzs[6] = qzss_iono_ref.beta2;
|
||||
d_nav_data.ion_qzs[7] = qzss_iono_ref.beta3;
|
||||
if (!(gps_iono.valid) && !(gps_cnav_iono.valid))
|
||||
{
|
||||
// Keep the GPS-interoperable QZSS Klobuchar coefficients available
|
||||
// to the broadcast ionospheric model in QZSS-only configurations
|
||||
d_nav_data.ion_gps[0] = qzss_iono_ref.alpha0;
|
||||
d_nav_data.ion_gps[1] = qzss_iono_ref.alpha1;
|
||||
d_nav_data.ion_gps[2] = qzss_iono_ref.alpha2;
|
||||
d_nav_data.ion_gps[3] = qzss_iono_ref.alpha3;
|
||||
d_nav_data.ion_gps[4] = qzss_iono_ref.beta0;
|
||||
d_nav_data.ion_gps[5] = qzss_iono_ref.beta1;
|
||||
d_nav_data.ion_gps[6] = qzss_iono_ref.beta2;
|
||||
d_nav_data.ion_gps[7] = qzss_iono_ref.beta3;
|
||||
}
|
||||
}
|
||||
if (galileo_iono.ai0 != 0.0)
|
||||
{
|
||||
d_nav_data.ion_gal[0] = galileo_iono.ai0;
|
||||
@@ -1910,6 +1935,20 @@ bool Rtklib_Solver::get_PVT(const std::map<int, Gnss_Synchro> &gnss_observables_
|
||||
d_nav_data.utc_gps[3] = gps_cnav_utc_model.WN_T;
|
||||
d_nav_data.leaps = gps_cnav_utc_model.DeltaT_LS;
|
||||
}
|
||||
if (qzss_utc_model.valid || qzss_cnav_utc_model.valid)
|
||||
{
|
||||
const Gps_Utc_Model &qzss_utc_ref = qzss_utc_model.valid ? static_cast<const Gps_Utc_Model &>(qzss_utc_model) : static_cast<const Gps_Utc_Model &>(qzss_cnav_utc_model);
|
||||
d_nav_data.utc_qzs[0] = qzss_utc_ref.A0;
|
||||
d_nav_data.utc_qzs[1] = qzss_utc_ref.A1;
|
||||
d_nav_data.utc_qzs[2] = qzss_utc_ref.tot;
|
||||
d_nav_data.utc_qzs[3] = qzss_utc_ref.WN_T;
|
||||
if (!(gps_utc_model.valid) && !(gps_cnav_utc_model.valid))
|
||||
{
|
||||
// QZSS time is aligned with GPS time, so its leap seconds apply
|
||||
// in QZSS-only configurations
|
||||
d_nav_data.leaps = qzss_utc_ref.DeltaT_LS;
|
||||
}
|
||||
}
|
||||
if (glonass_gnav_utc_model.valid)
|
||||
{
|
||||
d_nav_data.utc_glo[0] = glonass_gnav_utc_model.d_tau_c; // ??
|
||||
|
||||
@@ -61,6 +61,10 @@
|
||||
#include "pvt_conf.h"
|
||||
#include "pvt_kf.h"
|
||||
#include "pvt_solution.h"
|
||||
#include "qzss_cnav_iono.h"
|
||||
#include "qzss_cnav_utc_model.h"
|
||||
#include "qzss_iono.h"
|
||||
#include "qzss_utc_model.h"
|
||||
#include "rtklib.h"
|
||||
#include "rtklib_conversions.h"
|
||||
#include "sensor_data/sensor_data_aggregator.h"
|
||||
@@ -133,6 +137,12 @@ public:
|
||||
Gps_CNAV_Iono gps_cnav_iono;
|
||||
Gps_CNAV_Utc_Model gps_cnav_utc_model;
|
||||
|
||||
Qzss_Utc_Model qzss_utc_model;
|
||||
Qzss_Iono qzss_iono;
|
||||
|
||||
Qzss_CNAV_Utc_Model qzss_cnav_utc_model;
|
||||
Qzss_CNAV_Iono qzss_cnav_iono;
|
||||
|
||||
Glonass_Gnav_Utc_Model glonass_gnav_utc_model; //!< Map storing GLONASS GNAV UTC Model
|
||||
Glonass_Gnav_Almanac glonass_gnav_almanac; //!< Map storing GLONASS GNAV Almanac Model
|
||||
|
||||
|
||||
+52
-12
@@ -23,10 +23,12 @@
|
||||
#include "dump_logger_helper.h"
|
||||
#include "gnss_sdr_make_unique.h" // for std::make_unique in C++11
|
||||
#include "gnss_synchro.h"
|
||||
#include "gnss_time.h" // for timetags produced by Tracking
|
||||
#include "gps_ephemeris.h" // for Gps_Ephemeris
|
||||
#include "gps_iono.h" // for Gps_Iono
|
||||
#include "gps_utc_model.h" // for Gps_Utc_Model
|
||||
#include "gnss_time.h" // for timetags produced by Tracking
|
||||
#include "gps_ephemeris.h" // for Gps_Ephemeris
|
||||
#include "gps_iono.h" // for Gps_Iono
|
||||
#include "gps_utc_model.h" // for Gps_Utc_Model
|
||||
#include "qzss_iono.h" // for Qzss_Iono
|
||||
#include "qzss_utc_model.h" // for Qzss_Utc_Model
|
||||
#include "tlm_conf.h"
|
||||
#include "tlm_crc_stats.h"
|
||||
#include "tlm_utils.h"
|
||||
@@ -373,13 +375,32 @@ bool gps_l1_ca_telemetry_decoder_gs::decode_subframe(double cn0, bool flag_inver
|
||||
case 4: // Possible IONOSPHERE and UTC model update (page 18)
|
||||
if (d_nav->get_flag_iono_valid() == true)
|
||||
{
|
||||
const std::shared_ptr<Gps_Iono> tmp_obj = std::make_shared<Gps_Iono>(d_nav->get_iono());
|
||||
this->message_port_pub(pmt::mp("telemetry"), pmt::make_any(tmp_obj));
|
||||
if (d_system == L1LnavSystem::QZSS)
|
||||
{
|
||||
// QZSS broadcasts its own Klobuchar coefficients; keep them
|
||||
// separate from the GPS ones
|
||||
const std::shared_ptr<Qzss_Iono> tmp_obj = std::make_shared<Qzss_Iono>(d_nav->get_iono());
|
||||
this->message_port_pub(pmt::mp("telemetry"), pmt::make_any(tmp_obj));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::shared_ptr<Gps_Iono> tmp_obj = std::make_shared<Gps_Iono>(d_nav->get_iono());
|
||||
this->message_port_pub(pmt::mp("telemetry"), pmt::make_any(tmp_obj));
|
||||
}
|
||||
}
|
||||
if (d_nav->get_flag_utc_model_valid() == true)
|
||||
{
|
||||
const std::shared_ptr<Gps_Utc_Model> tmp_obj = std::make_shared<Gps_Utc_Model>(d_nav->get_utc_model());
|
||||
this->message_port_pub(pmt::mp("telemetry"), pmt::make_any(tmp_obj));
|
||||
if (d_system == L1LnavSystem::QZSS)
|
||||
{
|
||||
// The QZSS UTC offset refers to UTC(NICT), not to UTC(USNO)
|
||||
const std::shared_ptr<Qzss_Utc_Model> tmp_obj = std::make_shared<Qzss_Utc_Model>(d_nav->get_utc_model());
|
||||
this->message_port_pub(pmt::mp("telemetry"), pmt::make_any(tmp_obj));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::shared_ptr<Gps_Utc_Model> tmp_obj = std::make_shared<Gps_Utc_Model>(d_nav->get_utc_model());
|
||||
this->message_port_pub(pmt::mp("telemetry"), pmt::make_any(tmp_obj));
|
||||
}
|
||||
}
|
||||
if (d_nav->almanac_validation() == true)
|
||||
{
|
||||
@@ -390,13 +411,32 @@ bool gps_l1_ca_telemetry_decoder_gs::decode_subframe(double cn0, bool flag_inver
|
||||
case 5:
|
||||
if (d_nav->get_flag_iono_valid() == true)
|
||||
{
|
||||
const std::shared_ptr<Gps_Iono> tmp_obj = std::make_shared<Gps_Iono>(d_nav->get_iono());
|
||||
this->message_port_pub(pmt::mp("telemetry"), pmt::make_any(tmp_obj));
|
||||
if (d_system == L1LnavSystem::QZSS)
|
||||
{
|
||||
// QZSS broadcasts its own Klobuchar coefficients; keep them
|
||||
// separate from the GPS ones
|
||||
const std::shared_ptr<Qzss_Iono> tmp_obj = std::make_shared<Qzss_Iono>(d_nav->get_iono());
|
||||
this->message_port_pub(pmt::mp("telemetry"), pmt::make_any(tmp_obj));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::shared_ptr<Gps_Iono> tmp_obj = std::make_shared<Gps_Iono>(d_nav->get_iono());
|
||||
this->message_port_pub(pmt::mp("telemetry"), pmt::make_any(tmp_obj));
|
||||
}
|
||||
}
|
||||
if (d_nav->get_flag_utc_model_valid() == true)
|
||||
{
|
||||
const std::shared_ptr<Gps_Utc_Model> tmp_obj = std::make_shared<Gps_Utc_Model>(d_nav->get_utc_model());
|
||||
this->message_port_pub(pmt::mp("telemetry"), pmt::make_any(tmp_obj));
|
||||
if (d_system == L1LnavSystem::QZSS)
|
||||
{
|
||||
// The QZSS UTC offset refers to UTC(NICT), not to UTC(USNO)
|
||||
const std::shared_ptr<Qzss_Utc_Model> tmp_obj = std::make_shared<Qzss_Utc_Model>(d_nav->get_utc_model());
|
||||
this->message_port_pub(pmt::mp("telemetry"), pmt::make_any(tmp_obj));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::shared_ptr<Gps_Utc_Model> tmp_obj = std::make_shared<Gps_Utc_Model>(d_nav->get_utc_model());
|
||||
this->message_port_pub(pmt::mp("telemetry"), pmt::make_any(tmp_obj));
|
||||
}
|
||||
}
|
||||
if (d_nav->almanac_validation() == true)
|
||||
{
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
#include "gnss_synchro.h"
|
||||
#include "gps_cnav_ephemeris.h"
|
||||
#include "gps_cnav_iono.h"
|
||||
#include "gps_cnav_utc_model.h" // for Gps_CNAV_Utc_Model
|
||||
#include "gps_cnav_utc_model.h" // for Gps_CNAV_Utc_Model
|
||||
#include "qzss_cnav_iono.h" // for Qzss_CNAV_Iono
|
||||
#include "qzss_cnav_utc_model.h" // for Qzss_CNAV_Utc_Model
|
||||
#include "tlm_conf.h"
|
||||
#include "tlm_crc_stats.h"
|
||||
#include "tlm_utils.h"
|
||||
@@ -229,8 +231,18 @@ int gps_l5_telemetry_decoder_gs::general_work(int noutput_items __attribute__((u
|
||||
}
|
||||
if (d_CNAV_Message->have_new_iono() == true)
|
||||
{
|
||||
const std::shared_ptr<Gps_CNAV_Iono> tmp_obj = std::make_shared<Gps_CNAV_Iono>(d_CNAV_Message->get_iono());
|
||||
this->message_port_pub(pmt::mp("telemetry"), pmt::make_any(tmp_obj));
|
||||
if (d_system == CnavSystem::QZSS)
|
||||
{
|
||||
// QZSS broadcasts its own Klobuchar coefficients (the Wide Area
|
||||
// set in Message Type 30); keep them separate from the GPS ones
|
||||
const std::shared_ptr<Qzss_CNAV_Iono> tmp_obj = std::make_shared<Qzss_CNAV_Iono>(d_CNAV_Message->get_iono());
|
||||
this->message_port_pub(pmt::mp("telemetry"), pmt::make_any(tmp_obj));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::shared_ptr<Gps_CNAV_Iono> tmp_obj = std::make_shared<Gps_CNAV_Iono>(d_CNAV_Message->get_iono());
|
||||
this->message_port_pub(pmt::mp("telemetry"), pmt::make_any(tmp_obj));
|
||||
}
|
||||
const auto default_precision = std::cout.precision();
|
||||
std::cout << TEXT_MAGENTA << "New " << ((d_system == CnavSystem::GPS) ? "GPS" : "QZSS")
|
||||
<< " L5 CNAV message received in channel " << d_channel
|
||||
@@ -241,8 +253,17 @@ int gps_l5_telemetry_decoder_gs::general_work(int noutput_items __attribute__((u
|
||||
|
||||
if (d_CNAV_Message->have_new_utc_model() == true)
|
||||
{
|
||||
const std::shared_ptr<Gps_CNAV_Utc_Model> tmp_obj = std::make_shared<Gps_CNAV_Utc_Model>(d_CNAV_Message->get_utc_model());
|
||||
this->message_port_pub(pmt::mp("telemetry"), pmt::make_any(tmp_obj));
|
||||
if (d_system == CnavSystem::QZSS)
|
||||
{
|
||||
// The QZSS UTC offset refers to UTC(NICT), not to UTC(USNO)
|
||||
const std::shared_ptr<Qzss_CNAV_Utc_Model> tmp_obj = std::make_shared<Qzss_CNAV_Utc_Model>(d_CNAV_Message->get_utc_model());
|
||||
this->message_port_pub(pmt::mp("telemetry"), pmt::make_any(tmp_obj));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::shared_ptr<Gps_CNAV_Utc_Model> tmp_obj = std::make_shared<Gps_CNAV_Utc_Model>(d_CNAV_Message->get_utc_model());
|
||||
this->message_port_pub(pmt::mp("telemetry"), pmt::make_any(tmp_obj));
|
||||
}
|
||||
const auto default_precision = std::cout.precision();
|
||||
std::cout << TEXT_MAGENTA << "New " << ((d_system == CnavSystem::GPS) ? "GPS" : "QZSS")
|
||||
<< " L5 CNAV message received in channel " << d_channel
|
||||
|
||||
@@ -100,6 +100,10 @@ set(SYSTEM_PARAMETERS_HEADERS
|
||||
osnma_dsm_reader.h
|
||||
tow_to_trk.h
|
||||
qzss.h
|
||||
qzss_cnav_iono.h
|
||||
qzss_cnav_utc_model.h
|
||||
qzss_iono.h
|
||||
qzss_utc_model.h
|
||||
signal_flag.h
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*!
|
||||
* \file qzss_cnav_iono.h
|
||||
* \brief Interface of a QZSS CNAV ionospheric model storage
|
||||
* \author Carles Fernandez-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_CNAV_IONO_H
|
||||
#define GNSS_SDR_QZSS_CNAV_IONO_H
|
||||
|
||||
#include "gps_cnav_iono.h"
|
||||
|
||||
/** \addtogroup Core
|
||||
* \{ */
|
||||
/** \addtogroup System_Parameters
|
||||
* \{ */
|
||||
|
||||
|
||||
/*!
|
||||
* \brief This class is a storage for the QZSS CNAV Klobuchar ionospheric
|
||||
* model data as described in IS-QZSS-PNT-006
|
||||
*
|
||||
* Message Type 30 broadcasts the Wide Area coefficient set (the Japan area
|
||||
* set is broadcast in Message Type 61). See https://qzss.go.jp/en/technical/ps-is-qzss/
|
||||
*/
|
||||
class Qzss_CNAV_Iono : public Gps_CNAV_Iono
|
||||
{
|
||||
public:
|
||||
Qzss_CNAV_Iono() = default; //!< Default constructor
|
||||
explicit Qzss_CNAV_Iono(const Gps_CNAV_Iono& iono) : Gps_CNAV_Iono(iono) {}
|
||||
};
|
||||
|
||||
|
||||
/** \} */
|
||||
/** \} */
|
||||
#endif // GNSS_SDR_QZSS_CNAV_IONO_H
|
||||
@@ -0,0 +1,45 @@
|
||||
/*!
|
||||
* \file qzss_cnav_utc_model.h
|
||||
* \brief Interface of a QZSS CNAV UTC model storage
|
||||
* \author Carles Fernandez-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_CNAV_UTC_MODEL_H
|
||||
#define GNSS_SDR_QZSS_CNAV_UTC_MODEL_H
|
||||
|
||||
#include "gps_cnav_utc_model.h"
|
||||
|
||||
/** \addtogroup Core
|
||||
* \{ */
|
||||
/** \addtogroup System_Parameters
|
||||
* \{ */
|
||||
|
||||
|
||||
/*!
|
||||
* \brief This class is a storage for the QZSS CNAV UTC model data as
|
||||
* described in IS-QZSS-PNT-006
|
||||
*
|
||||
* The parameters share the GPS CNAV layout, but the broadcast offset refers
|
||||
* to UTC(NICT) instead of UTC(USNO). See https://qzss.go.jp/en/technical/ps-is-qzss/
|
||||
*/
|
||||
class Qzss_CNAV_Utc_Model : public Gps_CNAV_Utc_Model
|
||||
{
|
||||
public:
|
||||
Qzss_CNAV_Utc_Model() = default; //!< Default constructor
|
||||
explicit Qzss_CNAV_Utc_Model(const Gps_CNAV_Utc_Model& utc_model) : Gps_CNAV_Utc_Model(utc_model) {}
|
||||
};
|
||||
|
||||
|
||||
/** \} */
|
||||
/** \} */
|
||||
#endif // GNSS_SDR_QZSS_CNAV_UTC_MODEL_H
|
||||
@@ -0,0 +1,45 @@
|
||||
/*!
|
||||
* \file qzss_iono.h
|
||||
* \brief Interface of a QZSS ionospheric model storage
|
||||
* \author Carles Fernandez-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_IONO_H
|
||||
#define GNSS_SDR_QZSS_IONO_H
|
||||
|
||||
#include "gps_iono.h"
|
||||
|
||||
/** \addtogroup Core
|
||||
* \{ */
|
||||
/** \addtogroup System_Parameters
|
||||
* \{ */
|
||||
|
||||
|
||||
/*!
|
||||
* \brief This class is a storage for the QZSS Klobuchar ionospheric model
|
||||
* data as described in IS-QZSS-PNT-006
|
||||
*
|
||||
* The coefficients share the GPS LNAV layout, but they are independently
|
||||
* determined by QZSS. See https://qzss.go.jp/en/technical/ps-is-qzss/
|
||||
*/
|
||||
class Qzss_Iono : public Gps_Iono
|
||||
{
|
||||
public:
|
||||
Qzss_Iono() = default; //!< Default constructor
|
||||
explicit Qzss_Iono(const Gps_Iono& iono) : Gps_Iono(iono) {}
|
||||
};
|
||||
|
||||
|
||||
/** \} */
|
||||
/** \} */
|
||||
#endif // GNSS_SDR_QZSS_IONO_H
|
||||
@@ -0,0 +1,45 @@
|
||||
/*!
|
||||
* \file qzss_utc_model.h
|
||||
* \brief Interface of a QZSS UTC model storage
|
||||
* \author Carles Fernandez-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_UTC_MODEL_H
|
||||
#define GNSS_SDR_QZSS_UTC_MODEL_H
|
||||
|
||||
#include "gps_utc_model.h"
|
||||
|
||||
/** \addtogroup Core
|
||||
* \{ */
|
||||
/** \addtogroup System_Parameters
|
||||
* \{ */
|
||||
|
||||
|
||||
/*!
|
||||
* \brief This class is a storage for the QZSS UTC model data as described in
|
||||
* IS-QZSS-PNT-006
|
||||
*
|
||||
* The parameters share the GPS LNAV layout, but the broadcast offset refers
|
||||
* to UTC(NICT) instead of UTC(USNO). See https://qzss.go.jp/en/technical/ps-is-qzss/
|
||||
*/
|
||||
class Qzss_Utc_Model : public Gps_Utc_Model
|
||||
{
|
||||
public:
|
||||
Qzss_Utc_Model() = default; //!< Default constructor
|
||||
explicit Qzss_Utc_Model(const Gps_Utc_Model& utc_model) : Gps_Utc_Model(utc_model) {}
|
||||
};
|
||||
|
||||
|
||||
/** \} */
|
||||
/** \} */
|
||||
#endif // GNSS_SDR_QZSS_UTC_MODEL_H
|
||||
@@ -203,6 +203,88 @@ std::string find_rinex_header_line(const std::string& filename, const std::strin
|
||||
}
|
||||
|
||||
|
||||
std::string find_line_starting_with(const std::string& filename, const std::string& prefix)
|
||||
{
|
||||
std::fstream fstr(filename.c_str(), std::fstream::in);
|
||||
if (!fstr.is_open())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
fstr.seekg(0);
|
||||
|
||||
std::string line_str;
|
||||
while (!fstr.eof())
|
||||
{
|
||||
std::getline(fstr, line_str);
|
||||
if (line_str.compare(0, prefix.length(), prefix) == 0)
|
||||
{
|
||||
return line_str;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
std::string find_line_containing(const std::string& filename, const std::string& text)
|
||||
{
|
||||
std::fstream fstr(filename.c_str(), std::fstream::in);
|
||||
if (!fstr.is_open())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
fstr.seekg(0);
|
||||
|
||||
std::string line_str;
|
||||
while (!fstr.eof())
|
||||
{
|
||||
std::getline(fstr, line_str);
|
||||
if (line_str.find(text) != std::string::npos)
|
||||
{
|
||||
return line_str;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
int32_t count_record_body_lines(const std::string& filename, const std::string& record_header)
|
||||
{
|
||||
std::fstream fstr(filename.c_str(), std::fstream::in);
|
||||
if (!fstr.is_open())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
fstr.seekg(0);
|
||||
|
||||
std::string line_str;
|
||||
bool found = false;
|
||||
int32_t body_lines = 0;
|
||||
while (!fstr.eof())
|
||||
{
|
||||
std::getline(fstr, line_str);
|
||||
if (!found)
|
||||
{
|
||||
if (line_str.compare(0, record_header.length(), record_header) == 0)
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (line_str.empty() || line_str[0] == '>')
|
||||
{
|
||||
break;
|
||||
}
|
||||
body_lines++;
|
||||
}
|
||||
}
|
||||
|
||||
return found ? body_lines : -1;
|
||||
}
|
||||
|
||||
|
||||
int32_t expand_test_lnav_utc_week(int32_t utc_week, int32_t reference_week)
|
||||
{
|
||||
int32_t expanded_week = utc_week;
|
||||
@@ -273,6 +355,324 @@ TEST_F(RinexPrinterTest, GpsUtcHeaderExpandsEightBitWeekFields)
|
||||
}
|
||||
|
||||
|
||||
TEST_F(RinexPrinterTest, Rinex4GpsNavAndObs)
|
||||
{
|
||||
Pvt_Conf conf;
|
||||
conf.use_e6_for_pvt = false;
|
||||
const auto signal_enabled_flags = GPS_1C;
|
||||
auto pvt_solution = std::make_shared<Rtklib_Solver>(rtk, conf, "filename", signal_enabled_flags, false, false);
|
||||
|
||||
Gps_Ephemeris eph;
|
||||
eph.PRN = 1;
|
||||
eph.WN = 512;
|
||||
pvt_solution->gps_ephemeris_map[1] = eph;
|
||||
pvt_solution->gps_iono.alpha0 = 1.1175870895385742e-08;
|
||||
pvt_solution->gps_iono.alpha1 = -7.4505805969238281e-09;
|
||||
pvt_solution->gps_iono.alpha2 = -5.9604644775390625e-08;
|
||||
pvt_solution->gps_iono.alpha3 = 1.1920928955078125e-07;
|
||||
pvt_solution->gps_iono.beta0 = 90112.0;
|
||||
pvt_solution->gps_iono.beta1 = -16384.0;
|
||||
pvt_solution->gps_iono.beta2 = -131072.0;
|
||||
pvt_solution->gps_iono.beta3 = 589824.0;
|
||||
pvt_solution->gps_utc_model.A0 = 9.3132257461547852e-10;
|
||||
pvt_solution->gps_utc_model.A1 = 8.8817841970012523e-16;
|
||||
pvt_solution->gps_utc_model.tot = 233472;
|
||||
pvt_solution->gps_utc_model.WN_T = 5;
|
||||
pvt_solution->gps_utc_model.DeltaT_LS = 18;
|
||||
pvt_solution->gps_utc_model.WN_LSF = 7;
|
||||
pvt_solution->gps_utc_model.DN = 7;
|
||||
pvt_solution->gps_utc_model.DeltaT_LSF = 18;
|
||||
|
||||
Gnss_Synchro gs{};
|
||||
gs.System = 'G';
|
||||
gs.PRN = 1;
|
||||
std::memcpy(static_cast<void*>(gs.Signal), "1C", 3);
|
||||
gs.Pseudorange_m = 22000000.0;
|
||||
gs.Carrier_phase_rads = 23.4;
|
||||
gs.Carrier_Doppler_hz = 1534.0;
|
||||
gs.CN0_dB_hz = 42.0;
|
||||
gs.Flag_valid_pseudorange = true;
|
||||
std::map<int, Gnss_Synchro> gnss_observables_map;
|
||||
gnss_observables_map[1] = gs;
|
||||
|
||||
auto rp = std::make_shared<Rinex_Printer>(signal_enabled_flags, 4);
|
||||
rp->print_rinex_annotation(pvt_solution.get(), gnss_observables_map, 42.0, true);
|
||||
|
||||
const std::string obsfile = rp->get_obsfilename();
|
||||
const std::string navfile = rp->get_navfilename()[0];
|
||||
rp = nullptr; // close the RINEX files so we can inspect them
|
||||
|
||||
const std::string obs_version_line = find_rinex_header_line(obsfile, "RINEX VERSION / TYPE");
|
||||
EXPECT_NE(std::string::npos, obs_version_line.find("4.02"));
|
||||
EXPECT_NE(std::string::npos, obs_version_line.find("OBSERVATION DATA"));
|
||||
|
||||
const std::string nav_version_line = find_rinex_header_line(navfile, "RINEX VERSION / TYPE");
|
||||
EXPECT_NE(std::string::npos, nav_version_line.find("4.02"));
|
||||
EXPECT_NE(std::string::npos, nav_version_line.find("N: GNSS NAV DATA"));
|
||||
|
||||
// From RINEX 4 on, the iono and time system corrections are ION / STO data
|
||||
// records instead of header lines
|
||||
EXPECT_TRUE(find_rinex_header_line(navfile, "IONOSPHERIC CORR").empty());
|
||||
EXPECT_TRUE(find_rinex_header_line(navfile, "TIME SYSTEM CORR").empty());
|
||||
|
||||
const std::string leap_second_line = find_rinex_header_line(navfile, "LEAP SECONDS");
|
||||
EXPECT_NE(std::string::npos, leap_second_line.find("18"));
|
||||
|
||||
EXPECT_FALSE(find_line_starting_with(navfile, "> EPH G01 LNAV").empty());
|
||||
EXPECT_FALSE(find_line_starting_with(navfile, "> ION G LNAV").empty());
|
||||
EXPECT_FALSE(find_line_starting_with(navfile, "> STO G LNAV").empty());
|
||||
|
||||
const std::string sto_line = find_line_containing(navfile, "GPUT");
|
||||
EXPECT_NE(std::string::npos, sto_line.find("UTC(USNO)"));
|
||||
|
||||
// RINEX 4 navigation records use the 'e' exponent indicator instead of 'D'
|
||||
EXPECT_TRUE(find_line_containing(navfile, "D+0").empty());
|
||||
EXPECT_TRUE(find_line_containing(navfile, "D-0").empty());
|
||||
EXPECT_FALSE(find_line_containing(navfile, "e+0").empty());
|
||||
|
||||
fs::remove(obsfile);
|
||||
fs::remove(navfile);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(RinexPrinterTest, Rinex4GalileoNav)
|
||||
{
|
||||
Pvt_Conf conf;
|
||||
conf.use_e6_for_pvt = false;
|
||||
const auto signal_enabled_flags = GAL_1B;
|
||||
auto pvt_solution = std::make_shared<Rtklib_Solver>(rtk, conf, "filename", signal_enabled_flags, false, false);
|
||||
|
||||
auto eph = Galileo_Ephemeris();
|
||||
eph.PRN = 11;
|
||||
eph.WN = 120;
|
||||
pvt_solution->galileo_ephemeris_map[11] = std::move(eph);
|
||||
pvt_solution->galileo_iono.ai0 = 45.75;
|
||||
pvt_solution->galileo_iono.ai1 = 0.1875;
|
||||
pvt_solution->galileo_iono.ai2 = 0.0135;
|
||||
pvt_solution->galileo_utc_model.A0 = -9.3132257461547852e-10;
|
||||
pvt_solution->galileo_utc_model.A1 = -8.8817841970012523e-16;
|
||||
pvt_solution->galileo_utc_model.tot = 432000;
|
||||
pvt_solution->galileo_utc_model.WNot = 41;
|
||||
pvt_solution->galileo_utc_model.Delta_tLS = 18;
|
||||
pvt_solution->galileo_utc_model.WN_LSF = 137;
|
||||
pvt_solution->galileo_utc_model.DN = 7;
|
||||
pvt_solution->galileo_utc_model.Delta_tLSF = 18;
|
||||
|
||||
std::map<int, Gnss_Synchro> gnss_observables_map;
|
||||
Gnss_Synchro gs{};
|
||||
gs.PRN = 11;
|
||||
gnss_observables_map[11] = std::move(gs);
|
||||
|
||||
auto rp = std::make_shared<Rinex_Printer>(signal_enabled_flags, 4);
|
||||
rp->print_rinex_annotation(pvt_solution.get(), gnss_observables_map, 0.0, true);
|
||||
|
||||
const std::string obsfile = rp->get_obsfilename();
|
||||
const std::string navfile = rp->get_navfilename()[0];
|
||||
rp = nullptr; // close the RINEX files so we can inspect them
|
||||
|
||||
EXPECT_FALSE(find_line_starting_with(navfile, "> EPH E11 INAV").empty());
|
||||
EXPECT_FALSE(find_line_starting_with(navfile, "> ION E IFNV").empty());
|
||||
EXPECT_FALSE(find_line_starting_with(navfile, "> STO E IFNV").empty());
|
||||
|
||||
const std::string sto_line = find_line_containing(navfile, "GAUT");
|
||||
EXPECT_NE(std::string::npos, sto_line.find("UTCGAL"));
|
||||
|
||||
fs::remove(obsfile);
|
||||
fs::remove(navfile);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(RinexPrinterTest, Rinex4GlonassNav)
|
||||
{
|
||||
Pvt_Conf conf;
|
||||
conf.use_e6_for_pvt = false;
|
||||
const auto signal_enabled_flags = GLO_1G;
|
||||
auto pvt_solution = std::make_shared<Rtklib_Solver>(rtk, conf, "filename", signal_enabled_flags, false, false);
|
||||
|
||||
auto eph = Glonass_Gnav_Ephemeris();
|
||||
eph.PRN = 1;
|
||||
eph.d_yr = 2020;
|
||||
eph.d_N_T = 260;
|
||||
pvt_solution->glonass_gnav_ephemeris_map[1] = std::move(eph);
|
||||
pvt_solution->glonass_gnav_utc_model.d_tau_c = -9.3132257461547852e-09;
|
||||
pvt_solution->glonass_gnav_utc_model.d_tau_gps = 1.8626451492309570e-09;
|
||||
|
||||
std::map<int, Gnss_Synchro> gnss_observables_map;
|
||||
Gnss_Synchro gs{};
|
||||
gs.PRN = 1;
|
||||
gnss_observables_map[1] = std::move(gs);
|
||||
|
||||
auto rp = std::make_shared<Rinex_Printer>(signal_enabled_flags, 4);
|
||||
rp->print_rinex_annotation(pvt_solution.get(), gnss_observables_map, 0.0, true);
|
||||
|
||||
const std::string obsfile = rp->get_obsfilename();
|
||||
const std::string navfile = rp->get_navfilename()[0];
|
||||
rp = nullptr; // close the RINEX files so we can inspect them
|
||||
|
||||
EXPECT_FALSE(find_line_starting_with(navfile, "> EPH R01 FDMA").empty());
|
||||
EXPECT_FALSE(find_line_starting_with(navfile, "> STO R FDMA").empty());
|
||||
|
||||
const std::string sto_line = find_line_containing(navfile, "GLUT");
|
||||
EXPECT_NE(std::string::npos, sto_line.find("UTC(SU)"));
|
||||
EXPECT_FALSE(find_line_containing(navfile, "GLGP").empty());
|
||||
|
||||
// The FDMA record has the SV / EPOCH / SV CLK line plus four broadcast
|
||||
// orbit lines (see RINEX 4.02 specification, Table A15)
|
||||
EXPECT_EQ(5, count_record_body_lines(navfile, "> EPH R01 FDMA"));
|
||||
|
||||
fs::remove(obsfile);
|
||||
fs::remove(navfile);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(RinexPrinterTest, Rinex4GpsCnavNav)
|
||||
{
|
||||
Pvt_Conf conf;
|
||||
conf.use_e6_for_pvt = false;
|
||||
const auto signal_enabled_flags = GPS_2S;
|
||||
auto pvt_solution = std::make_shared<Rtklib_Solver>(rtk, conf, "filename", signal_enabled_flags, false, false);
|
||||
|
||||
Gps_CNAV_Ephemeris eph;
|
||||
eph.PRN = 1;
|
||||
eph.WN = 2338;
|
||||
eph.WNop = 34;
|
||||
pvt_solution->gps_cnav_ephemeris_map[1] = eph;
|
||||
pvt_solution->gps_cnav_iono.alpha0 = 1.1175870895385742e-08;
|
||||
pvt_solution->gps_cnav_iono.beta0 = 90112.0;
|
||||
pvt_solution->gps_cnav_utc_model.A0 = 9.3132257461547852e-10;
|
||||
pvt_solution->gps_cnav_utc_model.A1 = 8.8817841970012523e-16;
|
||||
pvt_solution->gps_cnav_utc_model.tot = 233472;
|
||||
pvt_solution->gps_cnav_utc_model.WN_T = 2338;
|
||||
pvt_solution->gps_cnav_utc_model.DeltaT_LS = 18;
|
||||
|
||||
std::map<int, Gnss_Synchro> gnss_observables_map;
|
||||
Gnss_Synchro gs{};
|
||||
gs.System = 'G';
|
||||
gs.PRN = 1;
|
||||
std::memcpy(static_cast<void*>(gs.Signal), "2S", 3);
|
||||
gnss_observables_map[1] = std::move(gs);
|
||||
|
||||
auto rp = std::make_shared<Rinex_Printer>(signal_enabled_flags, 4);
|
||||
rp->print_rinex_annotation(pvt_solution.get(), gnss_observables_map, 42.0, true);
|
||||
|
||||
const std::string obsfile = rp->get_obsfilename();
|
||||
const std::string navfile = rp->get_navfilename()[0];
|
||||
rp = nullptr; // close the RINEX files so we can inspect them
|
||||
|
||||
EXPECT_FALSE(find_line_starting_with(navfile, "> EPH G01 CNAV").empty());
|
||||
EXPECT_FALSE(find_line_starting_with(navfile, "> ION G CNVX").empty());
|
||||
EXPECT_FALSE(find_line_starting_with(navfile, "> STO G CNVX").empty());
|
||||
|
||||
// The CNAV record has the SV / EPOCH / SV CLK line plus eight broadcast
|
||||
// orbit lines (see RINEX 4.02 specification, Table A10)
|
||||
EXPECT_EQ(9, count_record_body_lines(navfile, "> EPH G01 CNAV"));
|
||||
|
||||
fs::remove(obsfile);
|
||||
fs::remove(navfile);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(RinexPrinterTest, Rinex4QzssNav)
|
||||
{
|
||||
Pvt_Conf conf;
|
||||
conf.use_e6_for_pvt = false;
|
||||
const auto signal_enabled_flags = QZS_J1;
|
||||
auto pvt_solution = std::make_shared<Rtklib_Solver>(rtk, conf, "filename", signal_enabled_flags, false, false);
|
||||
|
||||
Gps_Ephemeris eph;
|
||||
eph.PRN = 193; // QZSS J01
|
||||
eph.WN = 512;
|
||||
pvt_solution->gps_ephemeris_map[193] = eph;
|
||||
pvt_solution->qzss_iono.alpha0 = 2.6077032089233398e-08;
|
||||
pvt_solution->qzss_iono.alpha1 = -7.4505805969238281e-09;
|
||||
pvt_solution->qzss_iono.beta0 = 118784.0;
|
||||
pvt_solution->qzss_iono.beta1 = -16384.0;
|
||||
pvt_solution->qzss_utc_model.A0 = -9.3132257461547852e-10;
|
||||
pvt_solution->qzss_utc_model.A1 = -8.8817841970012523e-16;
|
||||
pvt_solution->qzss_utc_model.tot = 233472;
|
||||
pvt_solution->qzss_utc_model.WN_T = 5;
|
||||
pvt_solution->qzss_utc_model.DeltaT_LS = 18;
|
||||
pvt_solution->qzss_utc_model.WN_LSF = 7;
|
||||
pvt_solution->qzss_utc_model.DN = 7;
|
||||
pvt_solution->qzss_utc_model.DeltaT_LSF = 18;
|
||||
|
||||
std::map<int, Gnss_Synchro> gnss_observables_map;
|
||||
Gnss_Synchro gs{};
|
||||
gs.System = 'J';
|
||||
gs.PRN = 193;
|
||||
std::memcpy(static_cast<void*>(gs.Signal), "J1", 3);
|
||||
gnss_observables_map[193] = std::move(gs);
|
||||
|
||||
auto rp = std::make_shared<Rinex_Printer>(signal_enabled_flags, 4);
|
||||
rp->print_rinex_annotation(pvt_solution.get(), gnss_observables_map, 42.0, true);
|
||||
|
||||
const std::string obsfile = rp->get_obsfilename();
|
||||
const std::string navfile = rp->get_navfilename()[0];
|
||||
rp = nullptr; // close the RINEX files so we can inspect them
|
||||
|
||||
EXPECT_FALSE(find_line_starting_with(navfile, "> EPH J01 LNAV").empty());
|
||||
EXPECT_FALSE(find_line_starting_with(navfile, "> ION J LNAV").empty());
|
||||
EXPECT_FALSE(find_line_starting_with(navfile, "> STO J LNAV").empty());
|
||||
|
||||
const std::string sto_line = find_line_containing(navfile, "QZUT");
|
||||
EXPECT_NE(std::string::npos, sto_line.find("UTC(NICT)"));
|
||||
|
||||
const std::string leap_second_line = find_rinex_header_line(navfile, "LEAP SECONDS");
|
||||
EXPECT_NE(std::string::npos, leap_second_line.find("18"));
|
||||
|
||||
fs::remove(obsfile);
|
||||
fs::remove(navfile);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(RinexPrinterTest, Rinex4QzssCnavNav)
|
||||
{
|
||||
Pvt_Conf conf;
|
||||
conf.use_e6_for_pvt = false;
|
||||
const auto signal_enabled_flags = QZS_J5;
|
||||
auto pvt_solution = std::make_shared<Rtklib_Solver>(rtk, conf, "filename", signal_enabled_flags, false, false);
|
||||
|
||||
Gps_CNAV_Ephemeris eph;
|
||||
eph.PRN = 193; // QZSS J01
|
||||
eph.WN = 2338;
|
||||
pvt_solution->gps_cnav_ephemeris_map[193] = eph;
|
||||
pvt_solution->qzss_cnav_iono.alpha0 = 2.6077032089233398e-08;
|
||||
pvt_solution->qzss_cnav_iono.beta0 = 118784.0;
|
||||
pvt_solution->qzss_cnav_utc_model.A0 = -9.3132257461547852e-10;
|
||||
pvt_solution->qzss_cnav_utc_model.A1 = -8.8817841970012523e-16;
|
||||
pvt_solution->qzss_cnav_utc_model.tot = 233472;
|
||||
pvt_solution->qzss_cnav_utc_model.WN_T = 2338;
|
||||
pvt_solution->qzss_cnav_utc_model.DeltaT_LS = 18;
|
||||
|
||||
std::map<int, Gnss_Synchro> gnss_observables_map;
|
||||
Gnss_Synchro gs{};
|
||||
gs.System = 'J';
|
||||
gs.PRN = 193;
|
||||
std::memcpy(static_cast<void*>(gs.Signal), "J5", 3);
|
||||
gnss_observables_map[193] = std::move(gs);
|
||||
|
||||
auto rp = std::make_shared<Rinex_Printer>(signal_enabled_flags, 4);
|
||||
rp->print_rinex_annotation(pvt_solution.get(), gnss_observables_map, 42.0, true);
|
||||
|
||||
const std::string obsfile = rp->get_obsfilename();
|
||||
const std::string navfile = rp->get_navfilename()[0];
|
||||
rp = nullptr; // close the RINEX files so we can inspect them
|
||||
|
||||
EXPECT_FALSE(find_line_starting_with(navfile, "> EPH J01 CNAV").empty());
|
||||
EXPECT_FALSE(find_line_starting_with(navfile, "> ION J CNVX WIDE").empty());
|
||||
EXPECT_FALSE(find_line_starting_with(navfile, "> STO J CNVX").empty());
|
||||
|
||||
const std::string sto_line = find_line_containing(navfile, "QZUT");
|
||||
EXPECT_NE(std::string::npos, sto_line.find("UTC(NICT)"));
|
||||
|
||||
const std::string leap_second_line = find_rinex_header_line(navfile, "LEAP SECONDS");
|
||||
EXPECT_NE(std::string::npos, leap_second_line.find("18"));
|
||||
|
||||
fs::remove(obsfile);
|
||||
fs::remove(navfile);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(RinexPrinterTest, GalileoObsHeader)
|
||||
{
|
||||
Pvt_Conf conf;
|
||||
|
||||
Reference in New Issue
Block a user