/*! * \file front_end_cal.cc * \brief Implementation of the Front-end calibration program. * \author Javier Arribas, 2013. jarribas(at)cttc.es * * * ----------------------------------------------------------------------------- * * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. * This file is part of GNSS-SDR. * * Copyright (C) 2010-2020 (see AUTHORS file for a list of contributors) * SPDX-License-Identifier: GPL-3.0-or-later * * ----------------------------------------------------------------------------- */ #include "front_end_cal.h" #include "GPS_L1_CA.h" // for GPS_L1_FREQ_HZ #include "concurrent_map.h" #include "configuration_interface.h" #include "gnss_sdr_supl_client.h" #include "gps_acq_assist.h" // for Gps_Acq_Assist #include "gps_almanac.h" #include "gps_ephemeris.h" #include "gps_iono.h" #include "gps_utc_model.h" #include #include // for min #include #include // for operator<< #include #include #include #if USE_GLOG_AND_GFLAGS #include #else #include #endif extern Concurrent_Map global_gps_ephemeris_map; extern Concurrent_Map global_gps_iono_map; extern Concurrent_Map global_gps_utc_model_map; extern Concurrent_Map global_gps_almanac_map; extern Concurrent_Map global_gps_acq_assist_map; bool FrontEndCal::read_assistance_from_XML() { Gnss_Sdr_Supl_Client supl_client_ephemeris_; std::string eph_xml_filename = "gps_ephemeris.xml"; std::cout << "SUPL: Trying to read GPS ephemeris from XML file " << eph_xml_filename << '\n'; LOG(INFO) << "SUPL: Trying to read GPS ephemeris from XML file " << eph_xml_filename; if (supl_client_ephemeris_.load_ephemeris_xml(eph_xml_filename) == true) { std::map::iterator gps_eph_iter; for (gps_eph_iter = supl_client_ephemeris_.gps_ephemeris_map.begin(); gps_eph_iter != supl_client_ephemeris_.gps_ephemeris_map.end(); gps_eph_iter++) { std::cout << "SUPL: Read XML Ephemeris for GPS SV " << gps_eph_iter->first << '\n'; LOG(INFO) << "SUPL: Read XML Ephemeris for GPS SV " << gps_eph_iter->first; LOG(INFO) << "New Ephemeris record inserted with Toe=" << gps_eph_iter->second.toe << " and GPS Week=" << gps_eph_iter->second.WN; global_gps_ephemeris_map.write(gps_eph_iter->second.PRN, gps_eph_iter->second); } return true; } std::cout << "ERROR: SUPL client error reading XML\n"; LOG(WARNING) << "ERROR: SUPL client error reading XML"; return false; } int FrontEndCal::Get_SUPL_Assist() { // ######### GNSS Assistance ################################# Gnss_Sdr_Supl_Client supl_client_acquisition_; Gnss_Sdr_Supl_Client supl_client_ephemeris_; int supl_mcc; // Current network MCC (Mobile country code), 3 digits. int supl_mns; // Current network MNC (Mobile Network code), 2 or 3 digits. int supl_lac; // Current network LAC (Location area code),16 bits, 1-65520 are valid values. int supl_ci; // Cell Identity (16 bits, 0-65535 are valid values). // GNSS Assistance configuration int error = 0; bool enable_gps_supl_assistance = configuration_->property("GNSS-SDR.SUPL_gps_enabled", false); if (enable_gps_supl_assistance == true) // SUPL SERVER TEST. Not operational yet! { LOG(INFO) << "SUPL RRLP GPS assistance enabled!"; std::string default_acq_server = "supl.nokia.com"; std::string default_eph_server = "supl.google.com"; supl_client_ephemeris_.server_name = configuration_->property("GNSS-SDR.SUPL_gps_ephemeris_server", std::move(default_acq_server)); supl_client_acquisition_.server_name = configuration_->property("GNSS-SDR.SUPL_gps_acquisition_server", std::move(default_eph_server)); supl_client_ephemeris_.server_port = configuration_->property("GNSS-SDR.SUPL_gps_ephemeris_port", 7275); supl_client_acquisition_.server_port = configuration_->property("GNSS-SDR.SUPL_gps_acquisition_port", 7275); supl_mcc = configuration_->property("GNSS-SDR.SUPL_MCC", 244); supl_mns = configuration_->property("GNSS-SDR.SUPL_MNC", 5); std::string default_lac = "0x59e2"; std::string default_ci = "0x31b0"; try { supl_lac = boost::lexical_cast(configuration_->property("GNSS-SDR.SUPL_LAC", std::move(default_lac))); } catch (boost::bad_lexical_cast &) { supl_lac = 0x59e2; } try { supl_ci = boost::lexical_cast(configuration_->property("GNSS-SDR.SUPL_CI", std::move(default_ci))); } catch (boost::bad_lexical_cast &) { supl_ci = 0x31b0; } bool SUPL_read_gps_assistance_xml = configuration_->property("GNSS-SDR.SUPL_read_gps_assistance_xml", false); if (SUPL_read_gps_assistance_xml == true) { // read assistance from file read_assistance_from_XML(); } else { // Request ephemeris from SUPL server supl_client_ephemeris_.request = 1; LOG(INFO) << "SUPL: Trying to read GPS ephemeris from SUPL server..."; std::cout << "SUPL: Trying to read GPS ephemeris from SUPL server...\n"; error = supl_client_ephemeris_.get_assistance(supl_mcc, supl_mns, supl_lac, supl_ci); if (error == 0) { std::map::iterator gps_eph_iter; for (gps_eph_iter = supl_client_ephemeris_.gps_ephemeris_map.begin(); gps_eph_iter != supl_client_ephemeris_.gps_ephemeris_map.end(); gps_eph_iter++) { LOG(INFO) << "SUPL: Received Ephemeris for GPS SV " << gps_eph_iter->first; std::cout << "SUPL: Received Ephemeris for GPS SV " << gps_eph_iter->first << '\n'; LOG(INFO) << "New Ephemeris record inserted with Toe=" << gps_eph_iter->second.toe << " and GPS Week=" << gps_eph_iter->second.WN; global_gps_ephemeris_map.write(gps_eph_iter->second.PRN, gps_eph_iter->second); } // Save ephemeris to XML file std::string eph_xml_filename = configuration_->property("GNSS-SDR.SUPL_gps_ephemeris_xml", eph_default_xml_filename); if (supl_client_ephemeris_.save_ephemeris_map_xml(eph_xml_filename, supl_client_ephemeris_.gps_ephemeris_map) == true) { LOG(INFO) << "SUPL: XML Ephemeris file created."; } } else { LOG(WARNING) << "ERROR: SUPL client for Ephemeris returned " << error; std::cout << "ERROR in SUPL client. Please check your Internet connection and SUPL server configuration\n"; } // Request almanac , IONO and UTC Model supl_client_ephemeris_.request = 0; LOG(INFO) << "SUPL: Try read Almanac, Iono, Utc Model, Ref Time and Ref Location from SUPL server..."; error = supl_client_ephemeris_.get_assistance(supl_mcc, supl_mns, supl_lac, supl_ci); if (error == 0) { std::map::iterator gps_alm_iter; for (gps_alm_iter = supl_client_ephemeris_.gps_almanac_map.begin(); gps_alm_iter != supl_client_ephemeris_.gps_almanac_map.end(); gps_alm_iter++) { LOG(INFO) << "SUPL: Received Almanac for GPS SV " << gps_alm_iter->first; std::cout << "SUPL: Received Almanac for GPS SV " << gps_alm_iter->first << '\n'; global_gps_almanac_map.write(gps_alm_iter->first, gps_alm_iter->second); } if (supl_client_ephemeris_.gps_iono.valid == true) { LOG(INFO) << "SUPL: Received GPS Iono"; std::cout << "SUPL: Received GPS Iono\n"; global_gps_iono_map.write(0, supl_client_ephemeris_.gps_iono); } if (supl_client_ephemeris_.gps_utc.valid == true) { LOG(INFO) << "SUPL: Received GPS UTC Model"; std::cout << "SUPL: Received GPS UTC Model\n"; global_gps_utc_model_map.write(0, supl_client_ephemeris_.gps_utc); } } else { LOG(WARNING) << "ERROR: SUPL client for Almanac returned " << error; std::cout << "ERROR in SUPL client. Please check your Internet connection and SUPL server configuration\n"; } // Request acquisition assistance supl_client_acquisition_.request = 2; LOG(INFO) << "SUPL: Trying to read Acquisition assistance from SUPL server..."; std::cout << "SUPL: Trying to read Acquisition assistance from SUPL server...\n"; error = supl_client_acquisition_.get_assistance(supl_mcc, supl_mns, supl_lac, supl_ci); if (error == 0) { std::map::iterator gps_acq_iter; for (gps_acq_iter = supl_client_acquisition_.gps_acq_map.begin(); gps_acq_iter != supl_client_acquisition_.gps_acq_map.end(); gps_acq_iter++) { LOG(INFO) << "SUPL: Received Acquisition assistance for GPS SV " << gps_acq_iter->first; std::cout << "SUPL: Received Acquisition assistance for GPS SV " << gps_acq_iter->first << '\n'; LOG(INFO) << "New acq assist record inserted"; global_gps_acq_assist_map.write(gps_acq_iter->second.PRN, gps_acq_iter->second); } } else { LOG(WARNING) << "ERROR: SUPL client for Acquisition assistance returned " << error; std::cout << "ERROR in SUPL client. Please check your Internet connection and SUPL server configuration\n"; } } } return error; } void FrontEndCal::set_configuration(std::shared_ptr configuration) { configuration_ = std::move(configuration); } bool FrontEndCal::get_ephemeris() { bool read_ephemeris_from_xml = configuration_->property("GNSS-SDR.read_eph_from_xml", false); if (read_ephemeris_from_xml == true) { std::cout << "Trying to read ephemeris from XML file...\n"; LOG(INFO) << "Trying to read ephemeris from XML file..."; if (read_assistance_from_XML() == false) { std::cout << "ERROR: Could not read Ephemeris file: Trying to get ephemeris from SUPL server...\n"; LOG(INFO) << "ERROR: Could not read Ephemeris file: Trying to get ephemeris from SUPL server..."; if (Get_SUPL_Assist() == 1) { return true; } return false; } return true; } std::cout << "Trying to read ephemeris from SUPL server...\n"; LOG(INFO) << "Trying to read ephemeris from SUPL server..."; if (Get_SUPL_Assist() == 0) { return true; } return false; } arma::vec FrontEndCal::lla2ecef(const arma::vec &lla) { // WGS84 flattening double f = 1.0 / 298.257223563; // WGS84 equatorial radius double R = 6378137.0; arma::vec ellipsoid = "0.0 0.0"; double phi = (lla(0) / 360.0) * TWO_PI; double lambda = (lla(1) / 360.0) * TWO_PI; ellipsoid(0) = R; ellipsoid(1) = sqrt(1.0 - (1.0 - f) * (1.0 - f)); arma::vec ecef = "0.0 0.0 0.0 0.0"; ecef = geodetic2ecef(phi, lambda, lla(3), ellipsoid); return ecef; } arma::vec FrontEndCal::geodetic2ecef(double phi, double lambda, double h, const arma::vec &ellipsoid) { double a = ellipsoid(0); double e2 = ellipsoid(1) * ellipsoid(1); double sinphi = sin(phi); double cosphi = cos(phi); double N = a / sqrt(1.0 - e2 * sinphi * sinphi); arma::vec ecef = "0.0 0.0 0.0 0.0"; ecef(0) = (N + h) * cosphi * cos(lambda); ecef(1) = (N + h) * cosphi * sin(lambda); ecef(2) = (N * (1.0 - e2) + h) * sinphi; return ecef; } double FrontEndCal::estimate_doppler_from_eph(unsigned int PRN, double tow, double lat, double lon, double height) noexcept(false) { int num_secs = 10; double step_secs = 0.5; // Observer position ECEF arma::vec obs_ecef = "0.0 0.0 0.0 0.0"; arma::vec lla = "0.0 0.0 0.0 0.0"; lla(0) = lat; lla(1) = lon; lla(2) = height; obs_ecef = lla2ecef(lla); // Satellite positions ECEF std::map eph_map; eph_map = global_gps_ephemeris_map.get_map_copy(); std::map::iterator eph_it; eph_it = eph_map.find(PRN); if (eph_it != eph_map.end()) { arma::vec SV_pos_ecef = "0.0 0.0 0.0 0.0"; double obs_time_start; double obs_time_stop; obs_time_start = tow - static_cast(num_secs) / 2.0; obs_time_stop = tow + static_cast(num_secs) / 2.0; int n_points = round((obs_time_stop - obs_time_start) / step_secs); arma::vec ranges = arma::zeros(n_points, 1); double obs_time = obs_time_start; for (int i = 0; i < n_points; i++) { eph_it->second.satellitePosition(obs_time); SV_pos_ecef(0) = eph_it->second.satpos_X; SV_pos_ecef(1) = eph_it->second.satpos_Y; SV_pos_ecef(2) = eph_it->second.satpos_Z; // SV distances to observer (true range) ranges(i) = arma::norm(SV_pos_ecef - obs_ecef, 2); obs_time += step_secs; } // Observer to satellite radial velocity // Numeric derivative: Positive slope means that the distance from obs to // satellite is increasing arma::vec obs_to_sat_velocity; obs_to_sat_velocity = (ranges.subvec(1, (n_points - 1)) - ranges.subvec(0, (n_points - 2))) / step_secs; // Doppler equations are formulated accounting for positive velocities if the // tx and rx are approaching to each other. So, the satellite velocity must // be redefined as: obs_to_sat_velocity = -obs_to_sat_velocity; // Doppler estimation arma::vec Doppler_Hz; Doppler_Hz = (obs_to_sat_velocity / SPEED_OF_LIGHT_M_S) * GPS_L1_FREQ_HZ; double mean_Doppler_Hz; mean_Doppler_Hz = arma::mean(Doppler_Hz); return mean_Doppler_Hz; } throw std::runtime_error("1"); } void FrontEndCal::GPS_L1_front_end_model_E4000(double f_bb_true_Hz, double f_bb_meas_Hz, double fs_nominal_hz, double *estimated_fs_Hz, double *estimated_f_if_Hz, double *f_osc_err_ppm) { const double f_osc_n = 28.8e6; // PLL registers settings (according to E4000 datasheet) const double N = 109.0; const double Y = 65536.0; const double X = 26487.0; const double R = 2.0; // Obtained RF center frequency double f_rf_pll = (f_osc_n * (N + X / Y)) / R; // RF frequency error caused by fractional PLL roundings double f_bb_err_pll = GPS_L1_FREQ_HZ - f_rf_pll; // Measured F_rf error double f_rf_err = (f_bb_meas_Hz - f_bb_true_Hz) - f_bb_err_pll; double f_osc_err_hz = (f_rf_err * R) / (N + X / Y); f_osc_err_hz = -f_osc_err_hz; *f_osc_err_ppm = f_osc_err_hz / (f_osc_n / 1e6); double frac = fs_nominal_hz / f_osc_n; *estimated_fs_Hz = frac * (f_osc_n + f_osc_err_hz); *estimated_f_if_Hz = f_rf_err; }