diff --git a/include/gnss_utils/gnss_utils.h b/include/gnss_utils/gnss_utils.h index cf3c80b25b51685df6362d5ab9e6757cb3073782..146bdc584155a9b4a22cef831846e9e24ffe6e75 100644 --- a/include/gnss_utils/gnss_utils.h +++ b/include/gnss_utils/gnss_utils.h @@ -118,7 +118,7 @@ struct Options int tropopt; // troposphere option: TROPOPT_OFF(0):correction off, TROPOPT_SAAS(1):Saastamoinen model, TROPOPT_SBAS(2):SBAS model, TROPOPT_EST(3):troposphere option: ZTD estimation, TROPOPT_ESTG(4):ZTD+grad estimation, TROPOPT_ZTD(5):ZTD correction,6:ZTD+grad correction int sbascorr; // SBAS correction options (can be added): SBSOPT_LCORR(1): long term correction, SBSOPT_FCORR(2): fast correction, SBSOPT_ICORR(4): ionosphere correction, SBSOPT_RANGE(8): ranging int raim; // RAIM removed sats - double elmin; // min elevation (degrees) + double elmin; // min elevation (rad) double maxgdop; // maxgdop: reject threshold of gdop bool GPS,SBS,GLO,GAL,QZS,CMP,IRN,LEO; // constellations used TdcpOptions tdcp; // TDCP options @@ -173,7 +173,7 @@ class Range; // Typedefs typedef std::map<int,Satellite> Satellites; typedef std::map<int,Range> Ranges; -typedef std::map<int,Eigen::Vector2d> Azels; +typedef std::map<int,Eigen::Vector2d> Azels; // Azimuth and elevation (rad) // pointer typedefs typedef std::shared_ptr<Observations> ObservationsPtr; diff --git a/include/gnss_utils/observations.h b/include/gnss_utils/observations.h index 468c418d2e618647e423a76d42c428c361a4908a..fdf52af10071f8fcf68573f3226c1f2e6524a629 100644 --- a/include/gnss_utils/observations.h +++ b/include/gnss_utils/observations.h @@ -42,15 +42,10 @@ public: std::set<int> filterByCode(); std::set<int> filterByCarrierPhase(); std::set<int> filterByConstellations(const int& navsys); - std::set<int> filterByElevationSnr(const Eigen::Vector3d& x_r, - const Satellites& sats, - const snrmask_t& snrmask, - const double& elmin, - const bool& multi_freq); - std::set<int> filterByElevationSnr(const Azels& azels, - const snrmask_t& snrmask, - const double& elmin, - const bool& multi_freq); + std::set<int> filterByElevationSnr(const Azels& azels, + const snrmask_t& snrmask, + const double& elmin, + const bool& multi_freq); std::set<int> filter(const Satellites& sats, const std::set<int>& discarded_sats, const Eigen::Vector3d& x_r, diff --git a/include/gnss_utils/range.h b/include/gnss_utils/range.h index caf9e9db23c97692ba3e4bb1a438e8162b5c24ea..d3b1d4d9b1b78e8be24b6d0d7c548c564dc529f4 100644 --- a/include/gnss_utils/range.h +++ b/include/gnss_utils/range.h @@ -18,6 +18,7 @@ namespace GnssUtils class Range { public: + int sys = SYS_GPS; int sat = 0; double P = -1; double P_var = 1; @@ -28,6 +29,9 @@ class Range double L = -1; double L_corrected = -1; double L_var = 1; + double L2 = -1; + double L2_corrected = -1; + double L2_var = 1; public: Range(); @@ -39,6 +43,8 @@ class Range const Azels& azel, const Eigen::Vector3d& latlonalt, const Options& opt); + + static std::set<int> findCommonSatellites(const Ranges& ranges_1, const Ranges& ranges_2); }; } /* namespace GnssUtils */ diff --git a/include/gnss_utils/snapshot.h b/include/gnss_utils/snapshot.h index e2ef0cefc5d3144f0e916e92619edd2b50e4fd37..49a167503ad07aabeca7036337e2d57d9fb86ff0 100644 --- a/include/gnss_utils/snapshot.h +++ b/include/gnss_utils/snapshot.h @@ -27,16 +27,15 @@ public: // Public methods void loadFromRinex(const std::string& rnx_file, gtime_t t_start, gtime_t t_end, double dt = 0.0, const char* opt = ""); + double getGPST() const; - ObservationsPtr getObservations() const; + // Navigation NavigationPtr getNavigation() const; - const Satellites& getSatellites() const; - Satellites& getSatellites(); - void setObservations(ObservationsPtr obs); void setNavigation(NavigationPtr nav); - void computeSatellites(const int& eph_opt); // see rtklib.h L396); - bool satellitesComputed() const; + // Observations + ObservationsPtr getObservations() const; + void setObservations(ObservationsPtr obs); std::set<int> filterObservations(const std::set<int>& discarded_sats, const Eigen::Vector3d& x_r, @@ -49,14 +48,21 @@ public: const bool& check_carrier_phase, const Options& opt); - // Pseudo-ranges + // Satellites + const Satellites& getSatellites() const; + Satellites& getSatellites(); + void computeSatellites(const int& eph_opt); // see rtklib.h L396); + bool satellitesComputed() const; + + // Ranges Ranges& getRanges(); const Ranges& getRanges() const; bool rangesComputed() const; void computeRanges(const Azels& azel, const Eigen::Vector3d& latlonalt, const Options& opt); - + void computeRanges(const Eigen::Vector3d& x_ecef, + const Options& opt); void print(); private: @@ -81,6 +87,11 @@ private: namespace GnssUtils { +inline double Snapshot::getGPST() const +{ + return obs_->getObservations()[0].time.time + obs_->getObservations()[0].time.sec; +} + inline GnssUtils::ObservationsPtr Snapshot::getObservations() const { return obs_; @@ -122,7 +133,15 @@ inline std::set<int> Snapshot::filterObservations(const std::set<int>& disc const bool& check_carrier_phase, const Options& opt) { - return obs_->filter(sats_, discarded_sats, x_r, check_code, check_carrier_phase, opt); + std::set<int> filtered_sats = obs_->filter(sats_, discarded_sats, x_r, check_code, check_carrier_phase, opt); + + for (auto sat : filtered_sats) + { + sats_.erase(sat); + ranges_.erase(sat); + } + + return filtered_sats; } inline std::set<int> Snapshot::filterObservations(const std::set<int>& discarded_sats, @@ -131,7 +150,15 @@ inline std::set<int> Snapshot::filterObservations(const std::set<int>& discarde const bool& check_carrier_phase, const Options& opt) { - return obs_->filter(sats_, discarded_sats, azels, check_code, check_carrier_phase, opt); + std::set<int> filtered_sats = obs_->filter(sats_, discarded_sats, azels, check_code, check_carrier_phase, opt); + + for (auto sat : filtered_sats) + { + sats_.erase(sat); + ranges_.erase(sat); + } + + return filtered_sats; } inline const Ranges& Snapshot::getRanges() const diff --git a/include/gnss_utils/tdcp.h b/include/gnss_utils/tdcp.h index 143fa67b4d775ae123fafffbb43df65700445946..dadb9384f8786ac3d2f135e5016eceec55cfd0d2 100644 --- a/include/gnss_utils/tdcp.h +++ b/include/gnss_utils/tdcp.h @@ -14,40 +14,53 @@ struct TdcpBatchParams int raim_n; double raim_min_residual; bool relinearize_jacobian; - bool old_nav; int max_iterations; }; -bool Tdcp(SnapshotPtr snapshot_r, - SnapshotPtr snapshot_k, - Eigen::Vector4d& d, - Eigen::Matrix4d& cov_d, - double& residual, - const std::set<int>& discarded_sats, - std::set<int>& raim_discarded_sats, - const TdcpBatchParams& tdcp_params, - const Options& opt); - -bool Tdcp(SnapshotPtr snapshot_r, - SnapshotPtr snapshot_k, - const Eigen::Vector3d& x_r, - Eigen::Vector4d& d, - Eigen::Matrix4d& cov_d, - double& residual, - const std::set<int>& discarded_sats, - std::set<int>& raim_discarded_sats, - const TdcpBatchParams& tdcp_params, - const Options& opt); - -bool Tdcp(SnapshotPtr snapshot_r, - SnapshotPtr snapshot_k, - const Eigen::Vector3d& x_r, - const std::set<int> common_sats, - Eigen::Vector4d& d, - Eigen::Matrix4d& cov_d, - double& residual, - std::set<int> raim_discarded_sats, - const TdcpBatchParams& tdcp_params); +struct TdcpOutput +{ + bool success = false; + std::string msg = ""; + std::set<int> raim_discarded_sats; + std::set<int> used_sats; + Eigen::Vector4d d = Eigen::Vector4d::Zero(); + Eigen::Matrix4d cov_d = Eigen::Matrix4d::Zero(); + double dt = 0; + double residual = 0; +}; + +TdcpOutput Tdcp(SnapshotPtr snapshot_r, + SnapshotPtr snapshot_k, + const Eigen::Vector4d& d_0, + const std::set<int>& discarded_sats, + const TdcpBatchParams& tdcp_params, + const Options& opt); + +TdcpOutput Tdcp(SnapshotPtr snapshot_r, + SnapshotPtr snapshot_k, + const Eigen::Vector3d& x_r, + const Eigen::Vector4d& d_0, + const std::set<int>& discarded_sats, + const TdcpBatchParams& tdcp_params, + const Options& opt); + +TdcpOutput Tdcp(SnapshotPtr snapshot_r, + SnapshotPtr snapshot_k, + const Eigen::Vector3d& x_r, + const std::set<int>& common_sats, + const Eigen::Vector4d& d_0, + const TdcpBatchParams& tdcp_params); + +TdcpOutput Tdcp(const Eigen::Vector3d& x_r, + Eigen::MatrixXd& A, + Eigen::VectorXd& r, + Eigen::VectorXd& drho_m, + Eigen::MatrixXd& s_k, + Eigen::MatrixXd& s_r, + const Eigen::Vector4d& d_0, + std::set<int>& raim_discarded_rows, + const TdcpBatchParams& tdcp_params); + } // namespace GnssUtils #endif /* INCLUDE_GNSS_UTILS_Tdcp_H_ */ diff --git a/include/gnss_utils/utils/satellite.h b/include/gnss_utils/utils/satellite.h index 491f331cba40bf7de69909cc7a712909afae87c5..93d6681adc216524ec4cc5cb82fdaacc5258e4d5 100644 --- a/include/gnss_utils/utils/satellite.h +++ b/include/gnss_utils/utils/satellite.h @@ -19,12 +19,16 @@ namespace GnssUtils class Observations; class Navigation; - double computeSatElevation(const Eigen::Vector3d& receiver_ecef, const Eigen::Vector3d& sat_ecef); - Satellites computeSatellites(const Observations& obs, const Navigation& nav, const int& eph_opt); // see rtklib.h L396 + Eigen::Vector2d computeAzel(const Eigen::Vector3d& sat_ecef, const Eigen::Vector3d& receiver_ecef); + + Eigen::Vector2d computeAzel(const Satellite& sat, const Eigen::Vector3d& receiver_ecef); + + Azels computeAzels(const Satellites& sats, const Eigen::Vector3d& receiver_ecef); + struct Satellite { int sys; diff --git a/src/observations.cpp b/src/observations.cpp index bb4bb5e01e9d58e605950f4469a0059ffaee2b04..cb43c9113feded557ab7f33552a1ca1c26407d27 100644 --- a/src/observations.cpp +++ b/src/observations.cpp @@ -304,18 +304,19 @@ std::set<int> Observations::filterByConstellations(const int& navsys) return remove_sats; } -std::set<int> Observations::filterByElevationSnr(const std::map<int,Eigen::Vector2d>& azels, +std::set<int> Observations::filterByElevationSnr(const Azels& azels, const snrmask_t& snrmask, const double& elmin, const bool &multi_freq) { std::set<int> remove_sats; - for (int obs_i = 0; obs_i < obs_.size(); obs_i++) + for (auto&& sat_idx_pair : sat_2_idx_) { - auto&& obs_sat = getObservationByIdx(obs_i); - const int& sat_number = obs_sat.sat; + const int& sat_number = sat_idx_pair.first; + assert(azels.count(sat_number) && "azel missing for a satellite of this observation"); const double& elevation(azels.at(sat_number)(1)); + auto&& obs_sat = getObservationByIdx(sat_idx_pair.second); // check elevation if (elevation < elmin) @@ -354,27 +355,6 @@ std::set<int> Observations::filterByElevationSnr(const std::map<int,Eigen::Vecto return remove_sats; } -std::set<int> Observations::filterByElevationSnr(const Eigen::Vector3d& x_r, - const Satellites& sats, - const snrmask_t& snrmask, - const double& elmin, - const bool &multi_freq) -{ - std::set<int> remove_sats; - std::map<int,Eigen::Vector2d> azels; - - for (int obs_i = 0; obs_i < obs_.size(); obs_i++) - { - auto&& obs_sat = getObservationByIdx(obs_i); - const int& sat_number = obs_sat.sat; - - double elevation = computeSatElevation(x_r, sats.at(obs_sat.sat).pos); - azels.emplace(obs_sat.sat,Eigen::Vector2d(0.0,elevation)); - } - - return filterByElevationSnr(azels, snrmask, elmin, multi_freq); -} - std::set<int> Observations::filter(const Satellites& sats, const std::set<int>& discarded_sats, const Eigen::Vector3d& x_r, @@ -403,51 +383,19 @@ std::set<int> Observations::filter(const Satellites& sats, const double& elmin, const bool& multi_freq) { - //std::cout << "filter: initial size: " << obs_.size() << std::endl; - // Ephemeris - std::set<int> remove_sats = filterByEphemeris(sats); - - // Discarded sats - std::set<int> remove_sats_discarded = filterBySatellites(discarded_sats); - remove_sats.insert(remove_sats_discarded.begin(), remove_sats_discarded.end()); + assert(!x_r.isApprox(Eigen::Vector3d::Zero(), 1e-3)); - // Code - if (check_code) - { - std::set<int> remove_sats_code = filterByCode(); - remove_sats.insert(remove_sats_code.begin(), remove_sats_code.end()); - } + Azels azels = computeAzels(sats, x_r); - // Carrier phase - if (check_carrier_phase) - { - std::set<int> remove_sats_carrier = filterByCarrierPhase(); - remove_sats.insert(remove_sats_carrier.begin(), remove_sats_carrier.end()); - } - - // satellite flag and ephemeris variance - for (auto sat_pair : sats) - if (remove_sats.count(sat_pair.first) == 0) - if (satexclude(sat_pair.first,sat_pair.second.var, sat_pair.second.svh, NULL)) - { - //std::cout << "Discarding sat " << sat_pair.first << ": unhealthy or huge variance svh = " << sat_pair.second.svh << std::endl; - removeObservationBySat(sat_pair.first); - remove_sats.insert(sat_pair.first); - } - - // Constellations - std::set<int> remove_sats_constellations = filterByConstellations(navsys); - remove_sats.insert(remove_sats_constellations.begin(), remove_sats_constellations.end()); - - // Elevation and SNR - if (!x_r.isApprox(Eigen::Vector3d::Zero(), 1e-3)) - { - std::set<int> remove_sats_elevation = filterByElevationSnr(x_r, sats, snrmask, elmin, multi_freq); - remove_sats.insert(remove_sats_elevation.begin(), remove_sats_elevation.end()); - } - - return remove_sats; - // std::cout << "final size: " << obs_.size() << std::endl; + return filter(sats, + discarded_sats, + azels, + check_code, + check_carrier_phase, + navsys, + snrmask, + elmin, + multi_freq); } std::set<int> Observations::filter(const Satellites& sats, diff --git a/src/range.cpp b/src/range.cpp index a990cbfabdaaa8f07530cccf7dcc8976c4c3fbe7..86871b4dce6ac8294964dd23a29c148762b78ad0 100644 --- a/src/range.cpp +++ b/src/range.cpp @@ -103,9 +103,21 @@ Ranges Range::computeRanges(ObservationsPtr obs, ranges[sat].P_var = varerr(&prcopt,azel_i[1],prcopt.err[5],sat_i.sys)+sat_i.var+vmeas+vion+vtrp; /* ------------------- Carrier phase ------------------- */ + // L1 ranges[sat].L = obs_i.L[0]*nav->getNavigation().lam[sat-1][0]; ranges[sat].L_corrected = ranges[sat].L; + int sys = satsys(sat, NULL); + // L2 (GPS/GLO/QZS) + if (sys & (SYS_GPS | SYS_GLO | SYS_QZS)) + ranges[sat].L2 = obs_i.L[1]*nav->getNavigation().lam[sat-1][1]; + // E5 (GAL) + else if (sys & SYS_GAL) + ranges[sat].L2 = obs_i.L[2]*nav->getNavigation().lam[sat-1][2]; + else + ranges[sat].L2 = 0; + ranges[sat].L2_corrected = ranges[sat].L2; + /* ionospheric corrections */ if (opt.tdcp.corr_iono) { @@ -115,19 +127,29 @@ Ranges Range::computeRanges(ObservationsPtr obs, //ranges[sat].L_corrected = obs_i.L[0]*nav->getNavigation().lam[sat-1][0]; } else + { ranges[sat].L_corrected -= ranges[sat].iono_corr; + ranges[sat].L2_corrected -= ranges[sat].iono_corr; //FIXME: different? + } } /* tropospheric corrections */ if (opt.tdcp.corr_tropo) + { ranges[sat].L_corrected += ranges[sat].tropo_corr; + ranges[sat].L2_corrected += ranges[sat].tropo_corr; //FIXME: different? + } /* sat clock corrections */ if (opt.tdcp.corr_clock) + { ranges[sat].L_corrected += ranges[sat].sat_clock_corr; + ranges[sat].L2_corrected += ranges[sat].sat_clock_corr; //FIXME: different? + } /* carrier phase variance */ ranges[sat].L_var = opt.tdcp.sigma_carrier * opt.tdcp.sigma_carrier; + ranges[sat].L2_var = opt.tdcp.sigma_carrier * opt.tdcp.sigma_carrier; //std::cout << std::endl // << "\t\tprange: " << pranges[sat].prange << std::endl @@ -141,4 +163,31 @@ Ranges Range::computeRanges(ObservationsPtr obs, return ranges; } + + +std::set<int> Range::findCommonSatellites(const Ranges& ranges_1, const Ranges& ranges_2) +{ + std::set<int> common_sats; + + // std::cout << "ranges_1: "; + // for (auto&& range_pair : ranges_1) + // std::cout << range_pair.first << " "; + // std::cout << std::endl; + // std::cout << "ranges_2: "; + // for (auto&& range_pair : ranges_2) + // std::cout << range_pair.first << " "; + // std::cout << std::endl; + + for (auto&& range_pair : ranges_1) + if (ranges_2.count(range_pair.first)) + common_sats.insert(range_pair.first); + + // std::cout << "common sats: "; + // for (auto sat : common_sats) + // std::cout << sat << " "; + // std::cout << std::endl; + + return common_sats; +} + } /* namespace GnssUtils */ diff --git a/src/snapshot.cpp b/src/snapshot.cpp index 283cd3c593aa8dfe091212d6171beb658a378b30..49e13bb30152fb6393e995603bc5e6c46b9a65f4 100644 --- a/src/snapshot.cpp +++ b/src/snapshot.cpp @@ -1,5 +1,6 @@ #include "gnss_utils/snapshot.h" #include "gnss_utils/utils/satellite.h" +#include "gnss_utils/utils/transformations.h" using namespace GnssUtils; @@ -41,3 +42,12 @@ void Snapshot::computeRanges(const Azels& azel, ranges_ = Range::computeRanges(obs_, nav_, sats_, azel, latlonalt, opt); } + +void Snapshot::computeRanges(const Eigen::Vector3d& x_ecef, + const Options& opt) +{ + ranges_ = Range::computeRanges(obs_, nav_, sats_, + computeAzels(sats_, x_ecef), + ecefToLatLonAlt(x_ecef), + opt); +} diff --git a/src/tdcp.cpp b/src/tdcp.cpp index 1bea6dab33bd705a8c5fee41bfe052b8bade5953..e56c28506a70813966adeeba9f81b7e3c1b354f3 100644 --- a/src/tdcp.cpp +++ b/src/tdcp.cpp @@ -6,117 +6,100 @@ namespace GnssUtils { -bool Tdcp(SnapshotPtr snapshot_r, - SnapshotPtr snapshot_k, - Eigen::Vector4d& d, - Eigen::Matrix4d& cov_d, - double& residual, - const std::set<int>& discarded_sats, - std::set<int>& raim_discarded_sats, - const TdcpBatchParams& tdcp_params, - const Options& opt) +TdcpOutput Tdcp(SnapshotPtr snapshot_r, + SnapshotPtr snapshot_k, + const Eigen::Vector4d& d_0, + const std::set<int>& discarded_sats, + const TdcpBatchParams& tdcp_params, + const Options& opt) { return Tdcp(snapshot_r, snapshot_k, computePos(*snapshot_r->getObservations(), *snapshot_r->getNavigation(), opt).pos, - d, - cov_d, - residual, + d_0, discarded_sats, - raim_discarded_sats, tdcp_params, opt); } -bool Tdcp(SnapshotPtr snapshot_r, - SnapshotPtr snapshot_k, - const Eigen::Vector3d& x_r, - Eigen::Vector4d& d, - Eigen::Matrix4d& cov_d, - double& residual, - const std::set<int>& discarded_sats, - std::set<int>& raim_discarded_sats, - const TdcpBatchParams& tdcp_params, - const Options& opt) +TdcpOutput Tdcp(SnapshotPtr snapshot_r, + SnapshotPtr snapshot_k, + const Eigen::Vector3d& x_r, + const Eigen::Vector4d& d_0, + const std::set<int>& discarded_sats, + const TdcpBatchParams& tdcp_params, + const Options& opt) { // If use old nav temporary change navigation to (re)compute satellites positions auto nav_k = snapshot_k->getNavigation(); - if (tdcp_params.old_nav) + if (tdcp_params.tdcp.use_old_nav) { - snapshot_k->getSatellites().clear(); - snapshot_k->setNavigation(snapshot_r->getNavigation()); + auto new_snapshot_k = std::make_shared<Snapshot>(std::make_shared<Observations>(*snapshot_k->getObservations()), + std::make_shared<Navigation>(*snapshot_k->getNavigation())); + snapshot_k = new_snapshot_k; } // COMPUTE SATELLITES POSITION - if (!snapshot_r->satellitesComputed()) + if (not snapshot_r->satellitesComputed()) snapshot_r->computeSatellites(opt.sateph); - if (!snapshot_k->satellitesComputed()) + if (not snapshot_k->satellitesComputed()) snapshot_k->computeSatellites(opt.sateph); // FILTER SATELLITES (ALREADY DISCARDED, CORRUPTED DATA, WRONG POSITION, CONSTELLATION, ELEVATION and SNR) - snapshot_r->getObservations()->filter(snapshot_r->getSatellites(), - discarded_sats, - x_r, - false, - true, - opt); - snapshot_k->getObservations()->filter(snapshot_k->getSatellites(), - discarded_sats, - x_r, - false, - true, - opt); - - // FIND COMMON SATELLITES OBSERVATIONS - std::set<int> common_sats = Observations::findCommonObservations(*snapshot_r->getObservations(), - *snapshot_k->getObservations()); + snapshot_r->filterObservations(discarded_sats, x_r, false, true, opt); + snapshot_k->filterObservations(discarded_sats, x_r, false, true, opt); - // COMPUTE TDCP - bool tdcp_ok = Tdcp(snapshot_r, - snapshot_k, - x_r, - common_sats, - d, - cov_d, - residual, - raim_discarded_sats, - tdcp_params); - - // UNDO temporary change navigation - if (tdcp_params.old_nav) + // COMPUTE RANGES + if (not snapshot_r->rangesComputed()) + snapshot_r->computeRanges(x_r, opt); + if (not snapshot_k->rangesComputed()) + snapshot_k->computeRanges(x_r, opt); // the x_r is only used to compute Azels -> corrections + + // FIND COMMON SATELLITES + std::set<int> common_sats = Range::findCommonSatellites(snapshot_r->getRanges(), + snapshot_k->getRanges()); + + if (common_sats.empty()) { - snapshot_k->setNavigation(nav_k); - snapshot_k->computeSatellites(opt.sateph); + TdcpOutput output; + output.success = false; + output.msg = "No common satellites after filtering observations."; + return output; } - return tdcp_ok; + // COMPUTE TDCP + return Tdcp(snapshot_r, + snapshot_k, + x_r, + common_sats, + d_0, + tdcp_params); } -bool Tdcp(SnapshotPtr snapshot_r, - SnapshotPtr snapshot_k, - const Eigen::Vector3d& x_r, - const std::set<int> common_sats, - Eigen::Vector4d& d, - Eigen::Matrix4d& cov_d, - double& residual, - std::set<int> raim_discarded_sats, - const TdcpBatchParams& tdcp_params) +TdcpOutput Tdcp(SnapshotPtr snapshot_r, + SnapshotPtr snapshot_k, + const Eigen::Vector3d& x_r, + const std::set<int>& common_sats, + const Eigen::Vector4d& d_0, + const TdcpBatchParams& tdcp_params) { - // Checks + + TdcpOutput output; + assert(snapshot_r->satellitesComputed() && "this TDCP assumes satellites already computed"); assert(snapshot_k->satellitesComputed() && "this TDCP assumes satellites already computed"); - assert(snapshot_r->getSatellites().size() > snapshot_r->getObservations()->size() && "Satellites without position not filtered"); - assert(snapshot_k->getSatellites().size() > snapshot_k->getObservations()->size() && "Satellites without position not filtered"); - assert(snapshot_r->getSatellites().size() > common_sats.size() && "Too many common sats"); - assert(snapshot_k->getSatellites().size() > common_sats.size() && "Too many common sats"); - assert(raim_discarded_sats.empty() && "just output param"); - + assert(snapshot_r->rangesComputed() && "this TDCP assumes ranges already computed"); + assert(snapshot_k->rangesComputed() && "this TDCP assumes ranges already computed"); + assert(snapshot_r->getSatellites().size() >= common_sats.size() && "Too many common sats"); + assert(snapshot_k->getSatellites().size() >= common_sats.size() && "Too many common sats"); + assert(snapshot_r->getRanges().size() >= common_sats.size() && "Too many common sats"); + assert(snapshot_k->getRanges().size() >= common_sats.size() && "Too many common sats"); for (auto sat : common_sats) { - assert(snapshot_r->getObservations()->hasSatellite(sat) && "wrong common sat"); - assert(snapshot_k->getObservations()->hasSatellite(sat) && "wrong common sat"); - assert(snapshot_r->getSatellites().count(sat) && "common sat without position"); - assert(snapshot_k->getSatellites().count(sat) && "common sat without position"); + assert(snapshot_r->getSatellites().count(sat) && "common sat not stored in satellites"); + assert(snapshot_k->getSatellites().count(sat) && "common sat not stored in satellites"); + assert(snapshot_r->getRanges().count(sat) && "common sat not stored in ranges"); + assert(snapshot_k->getRanges().count(sat) && "common sat not stored in ranges"); } int n_common_sats = common_sats.size(); @@ -127,56 +110,43 @@ bool Tdcp(SnapshotPtr snapshot_r, printf("Tdcp: NOT ENOUGH COMMON SATS"); printf("Tdcp: %i common sats available, %i sats are REQUIRED. [ SKIPPING Tdcp ]", n_common_sats, required_n_sats); #endif - return false; + output.msg = "Not enough common sats"; + output.success = false; + return output; } // Times - double tr(-1), tk(-1); + double tr = snapshot_r->getGPST(); + double tk = snapshot_k->getGPST(); // MULTI-FREQUENCY std::map<int, std::pair<int, int>> row_2_sat_freq; int row = 0; for (auto sat : common_sats) { - assert(snapshot_r->getObservations()->hasSatellite(sat)); - assert(snapshot_k->getObservations()->hasSatellite(sat)); + assert(snapshot_r->getRanges().count(sat)); + assert(snapshot_k->getRanges().count(sat)); - auto&& obs_r = snapshot_r->getObservations()->getObservationBySat(sat); - auto&& obs_k = snapshot_k->getObservations()->getObservationBySat(sat); - - // fill times - if (tr < 0) - { - tr = obs_r.time.time + obs_r.time.sec; - tk = obs_k.time.time + obs_k.time.sec; - } + auto&& range_r = snapshot_r->getRanges().at(sat); + auto&& range_k = snapshot_k->getRanges().at(sat); // Carrier phase // L1/E1 - if (std::abs(obs_r.L[0]) > 1e-12 and std::abs(obs_k.L[0]) > 1e-12) + if (std::abs(range_r.L_corrected) > 1e-12 and std::abs(range_k.L_corrected) > 1e-12) row_2_sat_freq[row++] = std::make_pair(sat, 0); if (!tdcp_params.tdcp.multi_freq) continue; - // L2 (GPS/GLO/QZS) - int sys = satsys(sat, NULL); - if (NFREQ >= 2 and (sys & (SYS_GPS | SYS_GLO | SYS_QZS)) and std::abs(obs_r.L[1]) > 1e-12 and - std::abs(obs_k.L[1]) > 1e-12) + // L2 (GPS/GLO/QZS) / E5 (GAL) + if (std::abs(range_r.L2_corrected) > 1e-12 and std::abs(range_k.L2_corrected) > 1e-12) row_2_sat_freq[row++] = std::make_pair(sat, 1); - - // E5 (GAL) - else if (NFREQ >= 3 and (sys & SYS_GAL) and std::abs(obs_r.L[2]) > 1e-12 and std::abs(obs_k.L[2]) > 1e-12) - row_2_sat_freq[row++] = std::make_pair(sat, 2); } int n_differences = row_2_sat_freq.size(); - // Initial guess - Eigen::Vector4d d_0(d), delta_d(Eigen::Vector4d::Zero()); - #if GNSS_UTILS_TDCP_DEBUG == 1 std::cout << "Receiver at t_r: " << x_r.transpose() << std::endl; - std::cout << "d initial guess: " << d_0.transpose() << std::endl; + std::cout << "d initial guess: " << d.transpose() << std::endl; std::cout << "common sats: "; for (auto row_sat_freq_it : row_2_sat_freq) std::cout << row_sat_freq_it.second.first << " "; @@ -184,12 +154,11 @@ bool Tdcp(SnapshotPtr snapshot_r, #endif // FILL SATELLITES POSITIONS AND MEASUREMENTS ======================================================================= - Eigen::Matrix<double, Eigen::Dynamic, 4> A(Eigen::Matrix<double, Eigen::Dynamic, 4>::Zero(n_differences, 4)); - Eigen::VectorXd r(Eigen::VectorXd::Zero(n_differences)); - Eigen::VectorXd drho_m(Eigen::VectorXd::Zero(n_differences)); - Eigen::Matrix<double, 3, Eigen::Dynamic> s_k(Eigen::Matrix<double, 3, Eigen::Dynamic>::Zero(3, n_differences)); - Eigen::Matrix<double, 3, Eigen::Dynamic> s_r(Eigen::Matrix<double, 3, Eigen::Dynamic>::Zero(3, n_differences)); - int sat_i = 0; + Eigen::MatrixXd A(Eigen::MatrixXd::Zero(n_differences, 4)); + Eigen::VectorXd r(Eigen::VectorXd::Zero(n_differences)); + Eigen::VectorXd drho_m(Eigen::VectorXd::Zero(n_differences)); + Eigen::MatrixXd s_k(Eigen::MatrixXd::Zero(3, n_differences)); + Eigen::MatrixXd s_r(Eigen::MatrixXd::Zero(3, n_differences)); for (auto row_sat_freq_it : row_2_sat_freq) { @@ -197,39 +166,33 @@ bool Tdcp(SnapshotPtr snapshot_r, const int& sat_number = row_sat_freq_it.second.first; const int& freq = row_sat_freq_it.second.second; - auto obs_r = snapshot_r->getObservations()->getObservationBySat(sat_number); - auto obs_k = snapshot_k->getObservations()->getObservationBySat(sat_number); - auto nav_r = snapshot_r->getNavigation()->getNavigation(); - auto nav_k = snapshot_k->getNavigation()->getNavigation(); + auto&& range_r = snapshot_r->getRanges().at(sat_number); + auto&& range_k = snapshot_k->getRanges().at(sat_number); // Satellite's positions s_r.col(row) = snapshot_r->getSatellites().at(sat_number).pos; s_k.col(row) = snapshot_k->getSatellites().at(sat_number).pos; - //nav_k.ion_gps; - - drho_m(row) = (obs_k.L[freq] * nav_k.lam[sat_number - 1][freq]) - - (obs_r.L[freq] * nav_r.lam[sat_number - 1][freq]); + if (freq == 0) + drho_m(row) = range_k.L_corrected - range_r.L_corrected; + else + drho_m(row) = range_k.L2_corrected - range_r.L2_corrected; #if GNSS_UTILS_TDCP_DEBUG == 1 - std::cout << "\tsat " << sat_number - << " frequency " << freq - << " wavelength = " << nav_r.lam[sat_number-1][freq] << std::endl; - std::cout << std::setprecision(10) << "\tpositions:\n\t\ts_r: " << s_r.col(row).transpose() - << "\n\t\ts_k: " << s_k.col(row).transpose() << std::endl; - std::cout << "\tobs_r.L: " << obs_r.L[freq] << std::endl; - std::cout << "\tobs_k.L: " << obs_k.L[freq] << std::endl; - std::cout << "\tnav_r.getNavigation().lam[sat_number-1][freq]: " << nav_r.lam[sat_number-1][freq] << std::endl; - std::cout << "\tnav_k.getNavigation().lam[sat_number-1][freq]: " << nav_k.lam[sat_number-1][freq] << std::endl; - std::cout << "\trho_r: " << obs_r.L[freq] * nav_r.lam[sat_number-1][freq] << std::endl; - std::cout << "\trho_k: " << obs_k.L[freq] * nav_k.lam[sat_number-1][freq] << std::endl; + std::cout << "\tsat " << sat_number; + std::cout << std::setprecision(10) + << "\tpositions:" << std::endl + << "\t\ts_r: " << s_r.col(row).transpose() << std::endl + << "\t\ts_k: " << s_k.col(row).transpose() << std::endl; + std::cout << "\trange_r.L: " << (freq == 0 ? range_r.L_corrected : range_r.L2_corrected) << std::endl; + std::cout << "\trange_k.L: " << (freq == 0 ? range_k.L_corrected : range_k.L2_corrected) << std::endl; std::cout << "\tdrho_m: " << drho_m(row) << std::endl; #endif if (!tdcp_params.relinearize_jacobian) { // Unit vectors from receivers to satellites - Eigen::Vector3d u_k = (s_k.col(row) - x_r - d.head(3)).normalized(); + Eigen::Vector3d u_k = (s_k.col(row) - x_r - d_0.head(3)).normalized(); // Fill Jacobian matrix A(row, 0) = u_k(0); @@ -240,11 +203,58 @@ bool Tdcp(SnapshotPtr snapshot_r, } // LEAST SQUARES SOLUTION ======================================================================= + std::set<int> raim_discarded_rows; + output = Tdcp(x_r, A, r, drho_m, s_k, s_r, d_0, raim_discarded_rows, tdcp_params); + + // FILL OUTPUT + output.used_sats = common_sats; + for (auto disc_row : raim_discarded_rows) + { + output.raim_discarded_sats.insert(row_2_sat_freq[disc_row].first); + output.used_sats.erase(row_2_sat_freq[disc_row].first); + } + output.dt = tk - tr; + + // weight covariance with the measurement noise (proportional to time) + double sq_sigma_Tdcp = (tk - tr) * tdcp_params.tdcp.sigma_atm * tdcp_params.tdcp.sigma_atm + + 2 * tdcp_params.tdcp.sigma_carrier * tdcp_params.tdcp.sigma_carrier; + output.cov_d *= sq_sigma_Tdcp; + + return output; +} + +TdcpOutput Tdcp(const Eigen::Vector3d& x_r, + Eigen::MatrixXd& A, + Eigen::VectorXd& r, + Eigen::VectorXd& drho_m, + Eigen::MatrixXd& s_k, + Eigen::MatrixXd& s_r, + const Eigen::Vector4d& d_0, + std::set<int>& raim_discarded_rows, + const TdcpBatchParams& tdcp_params) +{ + assert(A.cols() == 4); + assert(s_k.rows() == 3); + assert(s_r.rows() == 3); + + TdcpOutput output; + Eigen::Vector4d& d(output.d); + Eigen::Matrix4d& cov_d(output.cov_d); + double& residual(output.residual); + + // Initial guess + Eigen::Vector4d delta_d(Eigen::Vector4d::Zero()); + d = d_0; + for (int j = 0; j < tdcp_params.max_iterations; j++) { // fill A and r for (int row = 0; row < A.rows(); row++) { + // skip discarded rows + if (raim_discarded_rows.count(row) != 0) + continue; + // Evaluate r r(row) = drho_m(row) - (s_k.col(row) - x_r - d.head(3)).norm() + (s_r.col(row) - x_r).norm() - d(3); @@ -271,7 +281,22 @@ bool Tdcp(SnapshotPtr snapshot_r, if (d.array().isNaN().any()) { std::cout << "Tdcp: NLS DID NOT WORK. NAN values appeared. ABORTING!!" << std::endl; - return false; + #if GNSS_UTILS_TDCP_DEBUG == 1 + std::cout << "Displacement vector =" << d.head<3>().transpose() << std::endl; + std::cout << "Displacement update =" << delta_d.head<3>().transpose() << std::endl; + printf("Ref distance = %7.3f m\n", d_0.norm()); + printf("Computed distance = %7.3f m\n", d.head<3>().norm()); + printf("Tdcp: rows = %lu\n", r.rows()); + std::cout << "Tdcp: r = " << r.transpose() << std::endl; + std::cout << "Tdcp: drho_m = " << drho_m.transpose() << std::endl; + std::cout << "Tdcp: A = \n" << A << std::endl; + std::cout << "Tdcp: H = \n" << A.transpose() * A << std::endl; + std::cout << "Tdcp: cov_d = \n" << cov_d << std::endl; + printf("Tdcp: dT = %8.3f secs\n", d(3)); + #endif + output.msg = "NaN values in NLS"; + output.success = false; + return output; } // residual @@ -286,7 +311,7 @@ bool Tdcp(SnapshotPtr snapshot_r, printf("Ref distance = %7.3f m\n", d_0.norm()); printf("Computed distance = %7.3f m\n", d.head<3>().norm()); printf("Tdcp: residual = %13.10f\n", residual); - printf("Tdcp: row = %lu\n", r.rows()); + printf("Tdcp: rows = %lu\n", r.rows()); std::cout << "Tdcp: r = " << r.transpose() << std::endl; std::cout << "Tdcp: drho_m = " << drho_m.transpose() << std::endl; std::cout << "Tdcp: A = \n" << A << std::endl; @@ -301,10 +326,9 @@ bool Tdcp(SnapshotPtr snapshot_r, int worst_sat_row = -1; double best_residual = 1e12; Eigen::Vector4d best_d; - int n_removed_rows = 1; // remove some satellites - while (raim_discarded_sats.size() < tdcp_params.raim_n) + while (raim_discarded_rows.size() < tdcp_params.raim_n) { auto A_raim = A; auto r_raim = r; @@ -312,21 +336,13 @@ bool Tdcp(SnapshotPtr snapshot_r, // solve removing each satellite for (int row_removed = 0; row_removed < A_raim.rows(); row_removed++) { - int sat_removed = row_2_sat_freq.at(row_removed).first; - - // Multi-freq: some rows for the same satellite - n_removed_rows = 1; - if (tdcp_params.tdcp.multi_freq) - while (row_removed + n_removed_rows < A_raim.rows() and - row_2_sat_freq.at(row_removed + n_removed_rows).first == sat_removed) - n_removed_rows++; + // skip already discarded rows + if (raim_discarded_rows.count(row_removed) != 0) + continue; - // remove satellite rows - for (auto r = 0; r < n_removed_rows; r++) - { - A_raim.row(row_removed + r) = Eigen::Vector4d::Zero().transpose(); - r_raim(row_removed + r) = 0; // not necessary - } + // remove satellite row + A_raim.row(row_removed) = Eigen::Vector4d::Zero().transpose(); + r_raim(row_removed) = 0; // not necessary // solve Eigen::Vector4d delta_d_raim = -(A_raim.transpose() * A_raim).inverse() * A_raim.transpose() * r_raim; @@ -340,17 +356,15 @@ bool Tdcp(SnapshotPtr snapshot_r, // residual = sqrt((r_raim + A_raim * delta_d_raim).squaredNorm() / A_raim.rows()); #if GNSS_UTILS_TDCP_DEBUG == 1 - std::cout << "RAIM excluding sat " << sat_removed << " - row " << row_removed << "(n_rows " - << n_removed_rows << ")" << std::endl; + std::cout << "RAIM excluding row " << row_removed << std::endl; std::cout << "Displacement vector =" << d_raim.head<3>().transpose() << std::endl; std::cout << "Displacement update =" << delta_d_raim.head<3>().transpose() << std::endl; printf("Ref distance = %7.3f m\n", d_0.norm()); printf("Computed distance = %7.3f m\n", d_raim.head<3>().norm()); printf("Tdcp: residual = %13.10f\n", residual); std::cout << "Tdcp: drho = " << r_raim.transpose() << std::endl; - std::cout << "Tdcp: A = \n" << A << std::endl; - std::cout << "Tdcp: H = \n" << A.transpose() * A << std::endl; - std::cout << "Tdcp: cov_d = \n" << cov_d << std::endl; + std::cout << "Tdcp: A = \n" << A_raim << std::endl; + std::cout << "Tdcp: H = \n" << A_raim.transpose() * A_raim << std::endl; printf("Tdcp: dT = %8.3f secs\n", d_raim(3)); #endif @@ -363,29 +377,27 @@ bool Tdcp(SnapshotPtr snapshot_r, } } // restore initial A and r - for (auto row = 0; row < n_removed_rows; row++) - { - A_raim.row(row_removed + row) = A.row(row_removed + row); - r_raim(row_removed + row) = r(row_removed + row); - } - row_removed += (n_removed_rows - 1); + A_raim.row(row_removed) = A.row(row_removed); + r_raim(row_removed) = r(row_removed); } // No successful RAIM solution if (worst_sat_row == -1) { printf("Tdcp: LS after RAIM DID NOT WORK. NAN values appeared. ABORTING!!"); - return false; + output.msg = "NaN values in NLS after RAIM"; + output.success = false; + return output; } // store removed sat - raim_discarded_sats.insert(row_2_sat_freq[worst_sat_row].first); + raim_discarded_rows.insert(worst_sat_row); - // decrease n_common_sats - n_differences -= n_removed_rows; - n_common_sats--; + // remove row + A.row(worst_sat_row) = Eigen::Vector4d::Zero().transpose(); + r(worst_sat_row) = 0; // not necessary - // Remove selected satellite from problem + /*// Remove selected satellite from problem std::cout << "resizing problem..." << std::endl; auto A_aux = A; auto r_aux = r; @@ -406,7 +418,7 @@ bool Tdcp(SnapshotPtr snapshot_r, s_k.rightCols(back_elements_changing) = s_k_aux.rightCols(back_elements_changing); r.tail(back_elements_changing) = r_aux.tail(back_elements_changing); drho_m.tail(back_elements_changing) = drho_m_aux.tail(back_elements_changing); - } + }*/ // apply the RAIM solution d = best_d; @@ -415,9 +427,10 @@ bool Tdcp(SnapshotPtr snapshot_r, #if GNSS_UTILS_TDCP_DEBUG == 1 std::cout << "Tdcp After RAIM " << std::endl; - std::cout << "\tExcluded sats : "; - std::cout << "\tCommon sats : " << n_common_sats << std::endl; - std::cout << "\tRows : " << n_differences << std::endl; + std::cout << "\tExcluded rows : "; + for (auto excl_row : raim_discarded_rows) + std::cout << excl_row << " "; + std::cout << std::endl; std::cout << "\tInitial guess = " << d_0.transpose() << " (" << d_0.head<3>().norm() << "m)" << std::endl; std::cout << "\tSolution = " << d.transpose() << " (" << d.head<3>().norm() << "m)" << std::endl; std::cout << "\tClock offset = " << d(3) << std::endl; @@ -430,9 +443,10 @@ bool Tdcp(SnapshotPtr snapshot_r, #if GNSS_UTILS_TDCP_DEBUG == 1 std::cout << "Tdcp iteration " << j << std::endl; - std::cout << "\tExcluded sats : "; - std::cout << "\tCommon sats : " << common_sats.size() << std::endl; - std::cout << "\tRows : " << n_differences << std::endl; + std::cout << "\tExcluded rows : "; + for (auto excl_row : raim_discarded_rows) + std::cout << excl_row << " "; + std::cout << std::endl; std::cout << "\tInitial guess = " << d_0.transpose() << " (" << d_0.head<3>().norm() << "m)" << std::endl; std::cout << "\tSolution = " << d.transpose() << " (" << d.head<3>().norm() << "m)" << std::endl; std::cout << "\tClock offset = " << d(3) << std::endl; @@ -443,19 +457,19 @@ bool Tdcp(SnapshotPtr snapshot_r, #endif } - // weight covariance with the measurement noise (proportional to time) - double sq_sigma_Tdcp = (tk - tr) * tdcp_params.tdcp.sigma_atm * tdcp_params.tdcp.sigma_atm + - 2 * tdcp_params.tdcp.sigma_carrier * tdcp_params.tdcp.sigma_carrier; - cov_d *= sq_sigma_Tdcp; - // residual = (r - A * d).norm() / A.rows(); - // Computing error on measurement space - for (int row = 0; row < n_differences; row++) - r(row) = drho_m(row) - (s_k.col(row) - x_r - d.head(3)).norm() + (s_r.col(row) - x_r).norm() - d(3); + r.setZero(); + for (int row = 0; row < r.size(); row++) + if (raim_discarded_rows.count(row) == 0) + r(row) = drho_m(row) - (s_k.col(row) - x_r - d.head(3)).norm() + (s_r.col(row) - x_r).norm() - d(3); - residual = sqrt(r.squaredNorm() / r.size()); + // residual = (r - A * d).norm() / A.rows(); + residual = sqrt(r.squaredNorm() / (r.size() - raim_discarded_rows.size())); + + output.success = true; - return true; + return output; } + } // namespace GnssUtils diff --git a/src/utils/satellite.cpp b/src/utils/satellite.cpp index 242f4aded99a222d654c93776510a0cdb5926c93..22e694828c40ace1d618a14f6b93e21415d70c01 100644 --- a/src/utils/satellite.cpp +++ b/src/utils/satellite.cpp @@ -14,24 +14,56 @@ namespace GnssUtils { -double computeSatElevation(const Eigen::Vector3d& receiver_ecef, const Eigen::Vector3d& sat_ecef) + +Eigen::Vector2d computeAzel(const Eigen::Vector3d& sat_ecef, const Eigen::Vector3d& receiver_ecef) { - // ecef 2 geodetic + // receiver position (geo) Eigen::Vector3d receiver_geo; ecef2pos(receiver_ecef.data(), receiver_geo.data()); - // receiver-sat vector ecef - Eigen::Vector3d receiver_sat_ecef = sat_ecef - receiver_ecef; + // receiver-sat unit vector (ecef) + Eigen::Vector3d e_ecef = (sat_ecef - receiver_ecef).normalized(); + + // receiver-sat unit vector (enu) + Eigen::Vector3d e_enu; + ecef2enu(receiver_geo.data(), // geodetic position {lat,lon} (rad) + e_ecef.data(), // vector in ecef coordinate {x,y,z} + e_enu.data()); // vector in local tangental coordinate {e,n,u} - // receiver-sat vector enu (receiver ecef as origin) - Eigen::Vector3d receiver_sat_enu; - ecef2enu(receiver_geo.data(), // geodetic position {lat,lon} (rad) - receiver_sat_ecef.data(), // vector in ecef coordinate {x,y,z} - receiver_sat_enu.data()); // vector in local tangental coordinate {e,n,u} + Eigen::Vector2d azel; + // azimuth + azel(0) = atan2(e_enu(0), e_enu(1)); // elevation - return atan2(receiver_sat_enu(2), - sqrt(receiver_sat_enu(0) * receiver_sat_enu(0) + receiver_sat_enu(1) * receiver_sat_enu(1))); + azel(1) = asin(e_enu(2)); + + return azel; +} + +Eigen::Vector2d computeAzel(const Satellite& sat, const Eigen::Vector3d& receiver_ecef) +{ + return computeAzel(sat.pos, receiver_ecef); + /*// compute receiver-to-satellilte unit vector (ecef) + Eigen::Vector3d e; + geodist(sat.pos.data(), receiver_ecef.data(), e.data()); + + // compute receiver position in geodetic + Eigen::Vector3d receiver_geo; + ecef2pos(receiver_ecef.data(), receiver_geo.data()); + + // compute azimut and elevation + Eigen::Vector2d azel; + satazel(receiver_geo.data(), e.data(), azel.data()); + + return azel;*/ +} + +Azels computeAzels(const Satellites& sats, const Eigen::Vector3d& receiver_ecef) +{ + Azels azels; + for (auto sat : sats) + azels.emplace(sat.first, computeAzel(sat.second, receiver_ecef)); + return azels; } Satellites computeSatellites(const Observations& obs, diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e7ef282189d9297ca55770cbad6e797b36cad125..4b38d2e10f58811e98ba3d282072327530cc0892 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -12,14 +12,18 @@ include_directories(${GTEST_INCLUDE_DIRS}) # # ############################################################## -# Transformations test -gnss_utils_add_gtest(gtest_transformations gtest_transformations.cpp) -target_link_libraries(gtest_transformations ${PROJECT_NAME}) +# Navigation test +gnss_utils_add_gtest(gtest_navigation gtest_navigation.cpp) +target_link_libraries(gtest_navigation libgtest ${PROJECT_NAME}) # Observations test gnss_utils_add_gtest(gtest_observations gtest_observations.cpp) target_link_libraries(gtest_observations libgtest ${PROJECT_NAME}) -# Navigation test -gnss_utils_add_gtest(gtest_navigation gtest_navigation.cpp) -target_link_libraries(gtest_navigation libgtest ${PROJECT_NAME}) \ No newline at end of file +# TDCP test +gnss_utils_add_gtest(gtest_tdcp gtest_tdcp.cpp) +target_link_libraries(gtest_tdcp ${PROJECT_NAME}) + +# Transformations test +gnss_utils_add_gtest(gtest_transformations gtest_transformations.cpp) +target_link_libraries(gtest_transformations ${PROJECT_NAME}) \ No newline at end of file diff --git a/test/gtest_tdcp.cpp b/test/gtest_tdcp.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1338b7e09879b6d6ae93c551a52f6165e34f0112 --- /dev/null +++ b/test/gtest_tdcp.cpp @@ -0,0 +1,330 @@ +#include "gtest/utils_gtest.h" +#include "gnss_utils/tdcp.h" +#include "gnss_utils/snapshot.h" +#include "gnss_utils/utils/satellite.h" +#include "gnss_utils/utils/transformations.h" + +using namespace GnssUtils; +using namespace Eigen; + +Vector3d computeRandomReceiverLatLonAlt() +{ + Vector3d receiver_LLA = Vector3d::Random(); // in [-1, 1] + receiver_LLA(0) *= M_PI / 2; // in [-PI/2, PI/2] + receiver_LLA(1) *= M_PI; // in [-PI, PI] + receiver_LLA(2) += 1; // ([0, 2]) + receiver_LLA(2) *= 5e2; // in [0, 1e3] + + return receiver_LLA; +} + +void computeRandomVisibleSatellite(const Vector3d& receiver_latlonalt, + Vector3d& sat_ENU, + Vector3d& sat_ECEF, + Vector2d& sat_azel, + double& range) +{ + Vector3d t_ECEF_ENU, t_ENU_ECEF; + Matrix3d R_ENU_ECEF; + + t_ECEF_ENU = latLonAltToEcef(receiver_latlonalt); + + computeEnuEcefFromLatLonAlt(receiver_latlonalt, R_ENU_ECEF, t_ENU_ECEF); + + // random elevation and azimuth + sat_azel = Vector2d::Random(); // in [-1, 1] + sat_azel(0) *= M_PI; // in [-pi, pi] + sat_azel(1) = (sat_azel(1)/2 + 0.5) * M_PI / 2; // in [0, pi/2] + range = VectorXd::Random(1)(0) * 5e2 + 1e3; // in [500, 1500] + + // ENU + sat_ENU << sin(sat_azel(0)) * cos(sat_azel(1)) * range, + cos(sat_azel(0)) * cos(sat_azel(1)) * range, + sin(sat_azel(1)) * range; + + // ECEF + sat_ECEF = t_ECEF_ENU + R_ENU_ECEF.transpose() * sat_ENU; + + // Range + range = (sat_ECEF - t_ECEF_ENU).norm(); +} + +TEST(Tdcp, Tdcp) +{ + int N_sats = 20; + + TdcpBatchParams tdcp_params; + tdcp_params.max_iterations = 10; + tdcp_params.min_common_sats = 6; + tdcp_params.raim_n = 0; + tdcp_params.raim_min_residual = 0; + tdcp_params.relinearize_jacobian = true; + tdcp_params.tdcp.multi_freq = false; + + Vector3d sat_ENU, sat_ECEF; + Vector3d x_r_LLA, x_r_ECEF, x_k_LLA, x_k_ECEF, d_ECEF; + Matrix3d R_ENU_ECEF; + Vector2d azel, azel2; + Vector4d d_gt; + Matrix4d cov_d; + double range, residual; + + // Snapshots + auto snapshot_r = std::make_shared<Snapshot>(); + auto snapshot_k = std::make_shared<Snapshot>(); + + // Observations (just needed for the GPST) + snapshot_r->setObservations(std::make_shared<Observations>()); + snapshot_k->setObservations(std::make_shared<Observations>()); + obsd_t obs_r; + obs_r.time.sec = 0; + obs_r.time.time = 0; + snapshot_r->getObservations()->addObservation(obs_r); + obsd_t obs_k; + obs_k.time.sec = 0; + obs_k.time.time = 1; + snapshot_k->getObservations()->addObservation(obs_k); + + // Random receiver position + for (auto i = 0; i<100; i++) + { + // clear satellites and ranges + snapshot_r->getSatellites().clear(); + snapshot_k->getSatellites().clear(); + snapshot_r->getRanges().clear(); + snapshot_k->getRanges().clear(); + + // random setup + x_r_LLA = computeRandomReceiverLatLonAlt(); + x_r_ECEF = latLonAltToEcef(x_r_LLA); + d_ECEF = Vector3d::Random() * 10; + x_k_ECEF = x_r_ECEF + d_ECEF; + x_k_LLA = ecefToLatLonAlt(x_k_ECEF); + + double clock_r = Vector2d::Random()(0) * 1e-6; + double clock_k = Vector2d::Random()(0) * 1e-6; + + // groundtruth + d_gt.head<3>() = d_ECEF; + d_gt(3) = (clock_k - clock_r) * CLIGHT; + + std::cout << "Iteration " << i << ":\n"; + std::cout << std::setprecision(10) << "\tx_r_ECEF " << x_r_ECEF.transpose() << ":\n"; + std::cout << "\tclock_r " << clock_r << ":\n"; + std::cout << std::setprecision(10) << "\tx_k_ECEF " << x_k_ECEF.transpose() << ":\n"; + std::cout << "\tclock_k " << clock_k << ":\n"; + std::cout << "\td_gt " << d_gt.transpose() << ":\n"; + + std::set<int> common_sats; + std::set<int> raim_discarded_sats; + + // random visible satellites + for (auto j = 0; j<N_sats; j++) + { + common_sats.insert(j); + + // Satellite r (random) + computeRandomVisibleSatellite(x_r_LLA, sat_ENU, sat_ECEF, azel, range); + ASSERT_MATRIX_APPROX(azel, computeAzel(sat_ECEF, x_r_ECEF), 1e-3); + + Satellite sat_r({0,j,sat_ECEF, Vector3d::Zero(), 1.0, 0, 0, 1}); + snapshot_r->getSatellites().emplace(j, sat_r); + + // Range r + Range range_r; + range_r.sat = j; + range_r.L_corrected = range + CLIGHT * clock_r; + range_r.L_var = 1; + range_r.L2_corrected = range_r.L_corrected; + range_r.L2_var = range_r.L_var; + + snapshot_r->getRanges().emplace(j, range_r); + + std::cout << "\tsat: " << j << "\n"; + std::cout << "\t\tsat_r_ECEF " << sat_ECEF.transpose() << ":\n"; + std::cout << "\t\trange_r.L_corrected " << range_r.L_corrected << ":\n"; + + // Satellite k (random) + computeRandomVisibleSatellite(x_k_LLA, sat_ENU, sat_ECEF, azel, range); + ASSERT_MATRIX_APPROX(azel, computeAzel(sat_ECEF, x_k_ECEF), 1e-3); + + Satellite sat_k({0,j,sat_ECEF, Vector3d::Zero(), 1.0, 0, 0, 1}); + snapshot_k->getSatellites().emplace(j, sat_k); + + // Range k + Range range_k; + range_k.sat = j; + range_k.L_corrected = range + CLIGHT * clock_k; + range_k.L_var = 1; + range_k.L2_corrected = range_k.L_corrected; + range_k.L2_var = range_k.L_var; + + snapshot_k->getRanges().emplace(j, range_k); + + std::cout << "\t\tsat_k_ECEF " << sat_ECEF.transpose() << ":\n"; + std::cout << "\t\trange_k.L_corrected " << range_k.L_corrected << ":\n"; + } + + // TDCP + auto tdcp_out = Tdcp(snapshot_r, + snapshot_k, + x_r_ECEF, + common_sats, + Vector4d::Zero(), + tdcp_params); + + // CHECKS + std::cout << "CHECKS iteration " << i << std::endl; + ASSERT_TRUE(tdcp_out.success); + ASSERT_LE(tdcp_out.residual, 1e-9); + ASSERT_MATRIX_APPROX(tdcp_out.d, d_gt, 1e-6); + } +} + +TEST(Tdcp, Tdcp_raim) +{ + int N_sats = 20; + + TdcpBatchParams tdcp_params; + tdcp_params.max_iterations = 10; + tdcp_params.min_common_sats = 6; + tdcp_params.raim_n = 2; + tdcp_params.raim_min_residual = 0; + tdcp_params.relinearize_jacobian = true; + tdcp_params.tdcp.multi_freq = false; + + Vector3d sat_ENU, sat_ECEF; + Vector3d x_r_LLA, x_r_ECEF, x_k_LLA, x_k_ECEF, d_ECEF; + Matrix3d R_ENU_ECEF; + Vector2d azel, azel2; + Vector4d d, d_gt; + Matrix4d cov_d; + double range, residual; + + // Snapshots + auto snapshot_r = std::make_shared<Snapshot>(); + auto snapshot_k = std::make_shared<Snapshot>(); + + // Observations (just needed for the GPST) + snapshot_r->setObservations(std::make_shared<Observations>()); + snapshot_k->setObservations(std::make_shared<Observations>()); + obsd_t obs_r; + obs_r.time.sec = 0; + obs_r.time.time = 0; + snapshot_r->getObservations()->addObservation(obs_r); + obsd_t obs_k; + obs_k.time.sec = 0; + obs_k.time.time = 1; + snapshot_k->getObservations()->addObservation(obs_k); + + // Random receiver position + for (auto i = 0; i<100; i++) + { + // clear satellites and ranges + snapshot_r->getSatellites().clear(); + snapshot_k->getSatellites().clear(); + snapshot_r->getRanges().clear(); + snapshot_k->getRanges().clear(); + + // random setup + x_r_LLA = computeRandomReceiverLatLonAlt(); + x_r_ECEF = latLonAltToEcef(x_r_LLA); + d_ECEF = Vector3d::Random() * 10; + x_k_ECEF = x_r_ECEF + d_ECEF; + x_k_LLA = ecefToLatLonAlt(x_k_ECEF); + + double clock_r = Vector2d::Random()(0) * 1e-6; + double clock_k = Vector2d::Random()(0) * 1e-6; + + // groundtruth + d_gt.head<3>() = d_ECEF; + d_gt(3) = (clock_k - clock_r) * CLIGHT; + + std::cout << "Iteration " << i << ":\n"; + std::cout << std::setprecision(10) << "\tx_r_ECEF " << x_r_ECEF.transpose() << ":\n"; + std::cout << "\tclock_r " << clock_r << ":\n"; + std::cout << std::setprecision(10) << "\tx_k_ECEF " << x_k_ECEF.transpose() << ":\n"; + std::cout << "\tclock_k " << clock_k << ":\n"; + std::cout << "\td_gt " << d_gt.transpose() << ":\n"; + + std::set<int> common_sats; + std::set<int> raim_discarded_sats; + + // random visible satellites + for (auto j = 0; j<N_sats; j++) + { + common_sats.insert(j); + + // Satellite r (random) + computeRandomVisibleSatellite(x_r_LLA, sat_ENU, sat_ECEF, azel, range); + ASSERT_MATRIX_APPROX(azel, computeAzel(sat_ECEF, x_r_ECEF), 1e-3); + + Satellite sat_r({0,j,sat_ECEF, Vector3d::Zero(), 1.0, 0, 0, 1}); + snapshot_r->getSatellites().emplace(j, sat_r); + + // Range r + Range range_r; + range_r.sat = j; + range_r.L_corrected = range + CLIGHT * clock_r; + range_r.L_var = 1; + range_r.L2_corrected = range_r.L_corrected; + range_r.L2_var = range_r.L_var; + + snapshot_r->getRanges().emplace(j, range_r); + + std::cout << "\tsat: " << j << "\n"; + std::cout << "\t\tsat_r_ECEF " << sat_ECEF.transpose() << ":\n"; + std::cout << "\t\trange_r.L_corrected " << range_r.L_corrected << ":\n"; + + // Satellite k (random) + computeRandomVisibleSatellite(x_k_LLA, sat_ENU, sat_ECEF, azel, range); + ASSERT_MATRIX_APPROX(azel, computeAzel(sat_ECEF, x_k_ECEF), 1e-3); + + Satellite sat_k({0,j,sat_ECEF, Vector3d::Zero(), 1.0, 0, 0, 1}); + snapshot_k->getSatellites().emplace(j, sat_k); + + // Range k + Range range_k; + range_k.sat = j; + range_k.L_corrected = range + CLIGHT * clock_k; + range_k.L_var = 1; + range_k.L2_corrected = range_k.L_corrected; + range_k.L2_var = range_k.L_var; + + snapshot_k->getRanges().emplace(j, range_k); + + std::cout << "\t\tsat_k_ECEF " << sat_ECEF.transpose() << ":\n"; + std::cout << "\t\trange_k.L_corrected " << range_k.L_corrected << ":\n"; + } + + // Distort 2 ranges -> to be detected by RAIM + int wrong_sat1 = i % N_sats; + snapshot_k->getRanges().at(wrong_sat1).L_corrected += 20; + int wrong_sat2 = (i + N_sats / 2) % N_sats; + snapshot_r->getRanges().at(wrong_sat2).L_corrected -= 10; + + + // TDCP + auto tdcp_out = Tdcp(snapshot_r, + snapshot_k, + x_r_ECEF, + common_sats, + Vector4d::Zero(), + tdcp_params); + + // CHECKS + std::cout << "CHECKS iteration " << i << std::endl; + ASSERT_EQ(tdcp_out.raim_discarded_sats.size(), 2); + ASSERT_TRUE(tdcp_out.raim_discarded_sats.count(wrong_sat1)); + ASSERT_TRUE(tdcp_out.raim_discarded_sats.count(wrong_sat2)); + ASSERT_TRUE(tdcp_out.success); + ASSERT_LE(tdcp_out.residual, 1e-9); + ASSERT_MATRIX_APPROX(tdcp_out.d, d_gt, 1e-6); + } +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/gtest_transformations.cpp b/test/gtest_transformations.cpp index cf5a3b05838cc18dff9fe057688c74bd2338104c..2becc26aa19f7703c8308e14fe93a4428a09d6fd 100644 --- a/test/gtest_transformations.cpp +++ b/test/gtest_transformations.cpp @@ -10,6 +10,46 @@ static double kSecondEccentricitySquared = 6.73949674228 * 0.001; using namespace GnssUtils; +Eigen::Vector3d computeRandomReceiverLatLonAlt() +{ + Eigen::Vector3d receiver_LLA = Eigen::Vector3d::Random(); // in [-1, 1] + receiver_LLA(0) *= M_PI / 2; // in [-PI/2, PI/2] + receiver_LLA(1) *= M_PI; // in [-PI, PI] + receiver_LLA(2) += 1; // ([0, 2]) + receiver_LLA(2) *= 5e2; // in [0, 1e3] + + return receiver_LLA; +} + +void computeRandomVisibleSatellite(const Eigen::Vector3d& receiver_latlonalt, + Eigen::Vector3d& sat_ENU, + Eigen::Vector3d& sat_ECEF, + Eigen::Vector2d& sat_azel, + double range) +{ + Eigen::Vector3d t_ECEF_ENU, t_ENU_ECEF; + Eigen::Matrix3d R_ENU_ECEF; + + t_ECEF_ENU = latLonAltToEcef(receiver_latlonalt); + + computeEnuEcefFromLatLonAlt(receiver_latlonalt, R_ENU_ECEF, t_ENU_ECEF); + + // random elevation and azimuth + sat_azel = Eigen::Vector2d::Random(); // in [-1, 1] + sat_azel(0) *= M_PI; // in [-pi, pi] + sat_azel(1) = (sat_azel(1)/2 + 0.5) * M_PI / 2; // in [0, pi/2] + range = Eigen::VectorXd::Random(1)(0) * 5e2 + 1e3; // in [500, 1500] + + // ENU + sat_ENU << sin(sat_azel(0)) * cos(sat_azel(1)) * range, + cos(sat_azel(0)) * cos(sat_azel(1)) * range, + sin(sat_azel(1)) * range; + + // ECEF + sat_ECEF = t_ECEF_ENU + R_ENU_ECEF.transpose() * sat_ENU; +} + + TEST(TransformationsTest, ecefToLatLonAlt) { Eigen::Vector3d ecef, latlonalt; @@ -78,10 +118,7 @@ TEST(TransformationsTest, latLonAltToEcef_ecefToLatLonAlt) // latlon -> ecef -> latlon for (auto i = 0; i<100; i++) { - latlonalt0 = Eigen::Vector3d::Random(); - latlonalt0(0) *= M_PI / 2; - latlonalt0(1) *= M_PI; - latlonalt0(2) *= 1e3; + latlonalt0 = computeRandomReceiverLatLonAlt(); ecef0 = latLonAltToEcef(latlonalt0); latlonalt1 = ecefToLatLonAlt(ecef0); @@ -98,10 +135,7 @@ TEST(TransformationsTest, computeEnuEcef) // random for (auto i = 0; i<100; i++) { - t_ENU_latlonalt = Eigen::Vector3d::Random(); - t_ENU_latlonalt(0) *= M_PI / 2; - t_ENU_latlonalt(1) *= M_PI; - t_ENU_latlonalt(2) *= 1e3; + t_ENU_latlonalt = computeRandomReceiverLatLonAlt(); t_ECEF_ENU = latLonAltToEcef(t_ENU_latlonalt); @@ -159,37 +193,27 @@ TEST(TransformationsTest, computeEnuEcef) } } -TEST(TransformationsTest, computeSatElevation) +TEST(TransformationsTest, computeAzel) { - Eigen::Vector3d t_ENU_ECEF,t_ECEF_ENU,t_ENU_latlonalt,rec_sat_ENU, sat_ECEF; + Eigen::Vector3d t_ENU_ECEF,t_ECEF_ENU,t_ENU_latlonalt, sat_ENU, sat_ECEF; Eigen::Matrix3d R_ENU_ECEF; + Eigen::Vector2d azel, azel2; + double range; // random receiver position for (auto i = 0; i<100; i++) { - t_ENU_latlonalt = Eigen::Vector3d::Random(); - t_ENU_latlonalt(0) *= M_PI / 2; - t_ENU_latlonalt(1) *= M_PI; - t_ENU_latlonalt(2) *= 1e3; - + t_ENU_latlonalt = computeRandomReceiverLatLonAlt(); t_ECEF_ENU = latLonAltToEcef(t_ENU_latlonalt); - computeEnuEcefFromLatLonAlt(t_ENU_latlonalt, R_ENU_ECEF, t_ENU_ECEF); - // random elevation and heading for (auto j = 0; j<100; j++) { - Eigen::Vector2d rand2 = Eigen::Vector2d::Random(); - double heading = rand2(0) * M_PI; - double elevation = rand2(0) * M_PI / 2; - - rec_sat_ENU << cos(heading) * 1000, sin(heading) * 1000, tan(elevation) * 1000; - - sat_ECEF = t_ECEF_ENU + R_ENU_ECEF.transpose() * rec_sat_ENU; + computeRandomVisibleSatellite(t_ENU_latlonalt, sat_ENU, sat_ECEF, azel, range); - double elevation2 = computeSatElevation(t_ECEF_ENU, sat_ECEF); + azel2 = computeAzel(sat_ECEF, t_ECEF_ENU); - ASSERT_NEAR(elevation, elevation2,1e-6); + ASSERT_MATRIX_APPROX(azel,azel2,1e-6); } } }