/*! * \file rinex_printer.h * \brief Interface of a RINEX 2.11 / 3.01 printer * See http://igscb.jpl.nasa.gov/igscb/data/format/rinex301.pdf * * Receiver Independent EXchange Format (RINEX): * The first proposal for the Receiver Independent Exchange Format RINEX * was developed by the Astronomical Institute of the University of Berne * for the easy exchange of the GPS data to be collected during the large * European GPS campaign EUREF 89, which involved more than 60 GPS receivers * of 4 different manufacturers. * The governing aspect during the development was the fact that most geodetic * processing software for GPS data use a well-defined set of observables: * 1) The carrier-phase measurement at one or both carriers (actually being a * measurement on the beat frequency between the received carrier of the * satellite signal and a receiver-generated reference frequency). * 2) The pseudorange (code) measurement , equivalent to the difference * of the time of reception (expressed in the time frame of the receiver) * and the time of transmission (expressed in the time frame of the satellite) * of a distinct satellite signal. * 3) The observation time being the reading of the receiver clock at the * instant of validity of the carrier-phase and/or the code measurements. * Note: A collection of the formats currently used by the IGS can be found * here: http://igscb.jpl.nasa.gov/components/formats.html * \author Carles Fernandez Prades, 2011. cfernandez(at)cttc.es * ------------------------------------------------------------------------- * * Copyright (C) 2010-2018 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNSS-SDR is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #ifndef GNSS_SDR_RINEX_PRINTER_H_ #define GNSS_SDR_RINEX_PRINTER_H_ #include "gps_navigation_message.h" #include "gps_cnav_navigation_message.h" #include "galileo_navigation_message.h" #include "glonass_gnav_navigation_message.h" #include "GPS_L1_CA.h" #include "Galileo_E1.h" #include "GLONASS_L1_L2_CA.h" #include "gnss_synchro.h" #include #include #include #include // for stringstream #include // for setprecision #include class Sbas_Raw_Msg; /*! * \brief Class that handles the generation of Receiver * INdependent EXchange format (RINEX) files */ class Rinex_Printer { public: /*! * \brief Default constructor. Creates GPS Navigation and Observables RINEX files and their headers */ Rinex_Printer(int version = 0); /*! * \brief Default destructor. Closes GPS Navigation and Observables RINEX files */ ~Rinex_Printer(); std::fstream obsFile; //& eph_map); /*! * \brief Writes data from the GPS L2 navigation message into the RINEX file */ void log_rinex_nav(std::fstream& out, const std::map& eph_map); /*! * \brief Writes data from the Galileo navigation message into the RINEX file */ void log_rinex_nav(std::fstream& out, const std::map& eph_map); /*! * \brief Writes data from the Mixed (GPS/Galileo) navigation message into the RINEX file */ void log_rinex_nav(std::fstream& out, const std::map& gps_eph_map, const std::map& galileo_eph_map); /*! * \brief Writes data from the GLONASS GNAV navigation message into the RINEX file */ void log_rinex_nav(std::fstream& out, const std::map& eph_map); /*! * \brief Writes data from the Mixed (GPS/GLONASS GNAV) navigation message into the RINEX file */ void log_rinex_nav(std::fstream& out, const std::map& gps_eph_map, const std::map& glonass_gnav_eph_map); /*! * \brief Writes data from the Mixed (GPS/GLONASS GNAV) navigation message into the RINEX file */ void log_rinex_nav(std::fstream& out, const std::map& gps_cnav_eph_map, const std::map& glonass_gnav_eph_map); /*! * \brief Writes data from the Mixed (Galileo/ GLONASS GNAV) navigation message into the RINEX file */ void log_rinex_nav(std::fstream& out, const std::map& galileo_eph_map, const std::map& glonass_gnav_eph_map); /*! * \brief Writes GPS L1 observables into the RINEX file */ void log_rinex_obs(std::fstream& out, const Gps_Ephemeris& eph, double obs_time, const std::map& observables); /*! * \brief Writes GPS L2 observables into the RINEX file */ void log_rinex_obs(std::fstream& out, const Gps_CNAV_Ephemeris& eph, double obs_time, const std::map& observables); /*! * \brief Writes dual frequency GPS L1 and L2 observables into the RINEX file */ void log_rinex_obs(std::fstream& out, const Gps_Ephemeris& eph, const Gps_CNAV_Ephemeris& eph_cnav, double obs_time, const std::map& observables); /*! * \brief Writes Galileo observables into the RINEX file. Example: galileo_bands("1B"), galileo_bands("1B 5X"), galileo_bands("5X"), ... Default: "1B". */ void log_rinex_obs(std::fstream& out, const Galileo_Ephemeris& eph, double obs_time, const std::map& observables, const std::string galileo_bands = "1B"); /*! * \brief Writes Mixed GPS / Galileo observables into the RINEX file */ void log_rinex_obs(std::fstream& out, const Gps_Ephemeris& gps_eph, const Galileo_Ephemeris& galileo_eph, const double gps_obs_time, const std::map& observables); /*! * \brief Writes GLONASS GNAV observables into the RINEX file. Example: glonass_bands("1C"), galileo_bands("1B 5X"), galileo_bands("5X"), ... Default: "1B". */ void log_rinex_obs(std::fstream& out, const Glonass_Gnav_Ephemeris& eph, double obs_time, const std::map& observables, const std::string glonass_bands = "1C"); /*! * \brief Writes Mixed GPS L1 C/A - GLONASS observables into the RINEX file */ void log_rinex_obs(std::fstream& out, const Gps_Ephemeris& gps_eph, const Glonass_Gnav_Ephemeris& glonass_gnav_eph, const double gps_obs_time, const std::map& observables); /*! * \brief Writes Mixed GPS L2C - GLONASS observables into the RINEX file */ void log_rinex_obs(std::fstream& out, const Gps_CNAV_Ephemeris& gps_cnav_eph, const Glonass_Gnav_Ephemeris& glonass_gnav_eph, const double gps_obs_time, const std::map& observables); /*! * \brief Writes Mixed Galileo/GLONASS observables into the RINEX file */ void log_rinex_obs(std::fstream& out, const Galileo_Ephemeris& galileo_eph, const Glonass_Gnav_Ephemeris& glonass_gnav_eph, const double gps_obs_time, const std::map& observables); /*! * \brief Represents GPS time in the date time format. Leap years are considered, but leap seconds are not. */ void to_date_time(int gps_week, int gps_tow, int& year, int& month, int& day, int& hour, int& minute, int& second); /*! * \brief Writes raw SBAS messages into the RINEX file */ //void log_rinex_sbs(std::fstream & out, const Sbas_Raw_Msg & sbs_message); void update_nav_header(std::fstream& out, const Gps_Utc_Model& gps_utc, const Gps_Iono& gps_iono); void update_nav_header(std::fstream& out, const Gps_CNAV_Utc_Model& utc_model, const Gps_CNAV_Iono& iono); void update_nav_header(std::fstream& out, const Gps_Iono& gps_iono, const Gps_Utc_Model& gps_utc_model, const Galileo_Iono& galileo_iono, const Galileo_Utc_Model& galileo_utc_model, const Galileo_Almanac& galileo_almanac); void update_nav_header(std::fstream& out, const Galileo_Iono& galileo_iono, const Galileo_Utc_Model& utc_model, const Galileo_Almanac& galileo_almanac); void update_nav_header(std::fstream& out, const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model, const Glonass_Gnav_Almanac& glonass_gnav_almanac); void update_nav_header(std::fstream& out, const Gps_Iono& gps_iono, const Gps_Utc_Model& gps_utc, const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model, const Glonass_Gnav_Almanac& glonass_gnav_almanac); void update_nav_header(std::fstream& out, const Gps_CNAV_Iono& gps_cnav_iono, const Gps_CNAV_Utc_Model& gps_cnav_utc, const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model, const Glonass_Gnav_Almanac& glonass_gnav_almanac); void update_nav_header(std::fstream& out, const Galileo_Iono& galileo_iono, const Galileo_Utc_Model& galileo_utc_model, const Galileo_Almanac& galileo_almanac, const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model, const Glonass_Gnav_Almanac& glonass_gnav_almanac); void update_obs_header(std::fstream& out, const Gps_Utc_Model& utc_model); void update_obs_header(std::fstream& out, const Gps_CNAV_Utc_Model& utc_model); void update_obs_header(std::fstream& out, const Galileo_Utc_Model& galileo_utc_model); void update_obs_header(std::fstream& out, const Glonass_Gnav_Utc_Model& glonass_gnav_utc_model); std::map satelliteSystem; // observationType; // observationCode; // 0.87654E-0004 or -0.1234E00005. */ inline std::string& sci2for(std::string& aStr, const std::string::size_type startPos = 0, const std::string::size_type length = std::string::npos, const std::string::size_type expLen = 3, const bool checkSwitch = true); /* * Convert double precision floating point to a string * containing the number in FORTRAN notation. * As an example, the number 156360 becomes ".15636D6". * @param d number to convert. * @param length length (in characters) of number, including exponent. * @param expLen length (in characters of exponent, including sign. * @param checkSwitch if true, keeps the exponential sanity check for * exponentials above three characters in length. If false, it removes * that check. * @return a string containing \a d in FORTRAN notation. */ inline std::string doub2for(const double& d, const std::string::size_type length, const std::string::size_type expLen, const bool checkSwitch = true); /* * Convert a string to a double precision floating point number. * @param s string containing a number. * @return double representation of string. */ inline double asDouble(const std::string& s) { return strtod(s.c_str(), 0); } inline int toInt(std::string bitString, int sLength); /* * Convert a string to an integer. * @param s string containing a number. * @return long integer representation of string. */ inline long asInt(const std::string& s) { return strtol(s.c_str(), 0, 10); } /* * Convert a double to a string in fixed notation. * @param x double. * @param precision the number of decimal places you want displayed. * @return string representation of \a x. */ inline std::string asString(const double x, const std::string::size_type precision = 17); /* * Convert a long double to a string in fixed notation. * @param x long double. * @param precision the number of decimal places you want displayed. * @return string representation of \a x. */ inline std::string asString(const long double x, const std::string::size_type precision = 21); /* * Convert any old object to a string. * The class must have stream operators defined. * @param x object to turn into a string. * @return string representation of \a x. */ template inline std::string asString(const X x); inline std::string asFixWidthString(const int x, const int width, char fill_digit); }; // Implementation of inline functions (modified versions from GPSTk http://www.gpstk.org) inline std::string& Rinex_Printer::leftJustify(std::string& s, const std::string::size_type length, const char pad) { if (length < s.length()) { s = s.substr(0, length); } else { s.append(length - s.length(), pad); } return s; } // if the string is bigger than length, truncate it from the left. // otherwise, add pad characters to its left. inline std::string& Rinex_Printer::rightJustify(std::string& s, const std::string::size_type length, const char pad) { if (length < s.length()) { s = s.substr(s.length() - length, std::string::npos); } else { s.insert(static_cast(0), length - s.length(), pad); } return s; } inline std::string Rinex_Printer::doub2for(const double& d, const std::string::size_type length, const std::string::size_type expLen, const bool checkSwitch) { short exponentLength = expLen; /* Validate the assumptions regarding the input arguments */ if (exponentLength < 0) exponentLength = 1; if (exponentLength > 3 && checkSwitch) exponentLength = 3; std::string toReturn = doub2sci(d, length, exponentLength, true, checkSwitch); sci2for(toReturn, 0, length, exponentLength, checkSwitch); return toReturn; } inline std::string Rinex_Printer::doub2sci(const double& d, const std::string::size_type length, const std::string::size_type expLen, const bool showSign, const bool checkSwitch) { std::string toReturn; short exponentLength = expLen; /* Validate the assumptions regarding the input arguments */ if (exponentLength < 0) exponentLength = 1; if (exponentLength > 3 && checkSwitch) exponentLength = 3; std::stringstream c; c.setf(std::ios::scientific, std::ios::floatfield); // length - 3 for special characters ('.', 'e', '+' or '-') // - exponentlength (e04) // - 1 for the digit before the decimal (2.) // and if showSign == true, // an extra -1 for '-' or ' ' if it's positive or negative int expSize = 0; if (showSign) expSize = 1; c.precision(length - 3 - exponentLength - 1 - expSize); c << d; c >> toReturn; return toReturn; } inline std::string& Rinex_Printer::sci2for(std::string& aStr, const std::string::size_type startPos, const std::string::size_type length, const std::string::size_type expLen, const bool checkSwitch) { std::string::size_type idx = aStr.find('.', startPos); int expAdd = 0; std::string exp; long iexp; //If checkSwitch is false, always redo the exponential. Otherwise, //set it to false. bool redoexp = !checkSwitch; // Check for decimal place within specified boundaries if ((idx <= 0) || (idx >= (startPos + length - expLen - 1))) { // Error: no decimal point in string return aStr; } // Here, account for the possibility that there are // no numbers to the left of the decimal, but do not // account for the possibility of non-scientific // notation (more than one digit to the left of the // decimal) if (idx > startPos) { redoexp = true; // Swap digit and decimal. aStr[idx] = aStr[idx - 1]; aStr[idx - 1] = '.'; // Only add one to the exponent if the number is non-zero if (asDouble(aStr.substr(startPos, length)) != 0.0) expAdd = 1; } idx = aStr.find('e', startPos); if (idx == std::string::npos) { idx = aStr.find('E', startPos); if (idx == std::string::npos) { // Error: no 'e' or 'E' in string"; } } // Change the exponent character to D normally, or E of checkSwitch is false. if (checkSwitch) aStr[idx] = 'D'; else aStr[idx] = 'E'; // Change the exponent itself if (redoexp) { exp = aStr.substr(idx + 1, std::string::npos); iexp = asInt(exp); iexp += expAdd; aStr.erase(idx + 1); if (iexp < 0) { aStr += "-"; iexp -= iexp * 2; } else aStr += "+"; aStr += Rinex_Printer::rightJustify(asString(iexp), expLen, '0'); } // if the number is positive, append a space // (if it's negative, there's a leading '-' if (aStr[0] == '.') { aStr.insert(static_cast(0), 1, ' '); } //If checkSwitch is false, add on one leading zero to the string if (!checkSwitch) { aStr.insert(static_cast(1), 1, '0'); } return aStr; } // end sci2for inline std::string asString(const long double x, const std::string::size_type precision) { std::ostringstream ss; ss << std::fixed << std::setprecision(precision) << x; return ss.str(); } inline std::string Rinex_Printer::asString(const double x, const std::string::size_type precision) { std::ostringstream ss; ss << std::fixed << std::setprecision(precision) << x; return ss.str(); } inline std::string Rinex_Printer::asFixWidthString(const int x, const int width, char fill_digit) { std::ostringstream ss; ss << std::setfill(fill_digit) << std::setw(width) << x; return ss.str().substr(ss.str().size() - width); } inline long asInt(const std::string& s) { return strtol(s.c_str(), 0, 10); } inline int Rinex_Printer::toInt(std::string bitString, int sLength) { int tempInt; int num = 0; for (int i = 0; i < sLength; i++) { tempInt = bitString[i] - '0'; num |= (1 << (sLength - 1 - i)) * tempInt; } return num; } template inline std::string Rinex_Printer::asString(const X x) { std::ostringstream ss; ss << x; return ss.str(); } #endif