-
Joan Vallvé Navarro authoredJoan Vallvé Navarro authored
single_differences.cpp 23.27 KiB
/*
* single_differences.cpp
*
* Created on: Dec 4, 2019
* Author: jvallve
*/
#include "gnss_utils/single_differences.h"
namespace GNSSUtils
{
bool singleDifferences(const Observations& obs_r, Navigation& nav_r,
const Observations& obs_k, const Navigation& nav_k,
Eigen::Vector4d& d, Eigen::Matrix4d& cov_d,
double& residual, std::set<int>& discarded_sats,
const SingleDifferencesParams& sd_params, const prcopt_t& opt)
{
// COMPUTE SINGLE DIFFERENCES
return singleDifferences(obs_r, nav_r, computePos(obs_r,nav_r,opt).pos,
obs_k, nav_k,
d, cov_d,
residual, discarded_sats,
sd_params, opt);
}
bool singleDifferences(const Observations& obs_r, const Navigation& nav_r, const Eigen::Vector3d& x_r,
const Observations& obs_k, const Navigation& nav_k,
Eigen::Vector4d& d, Eigen::Matrix4d& cov_d,
double& residual, std::set<int>& discarded_sats,
const SingleDifferencesParams& sd_params, const prcopt_t& opt)
{
// FIND COMMON SATELLITES OBSERVATIONS
Observations common_obs_r, common_obs_k;
Observations::findCommonObservations(obs_r, obs_k, common_obs_r, common_obs_k);
int n_common_sats = common_obs_r.getObservations().size();
// COMPUTE COMMON SATELLITES POSITION
std::map<int, Eigen::Vector3d> common_sats_pos_r, common_sats_pos_k;
computeSatellitesPositions(common_obs_r, nav_r, opt, common_sats_pos_r);
computeSatellitesPositions(common_obs_k, sd_params.use_old_nav ? nav_r : nav_k, opt, common_sats_pos_k);
// FILTER SATELLITES (ALREADY DISCARDED, CORRUPTED DATA, WRONG POSITION, CONSTELLATION, ELEVATION and SNR)
filterCommonSatellites(common_obs_r, common_sats_pos_r,
common_obs_k, common_sats_pos_k,
discarded_sats, x_r,
sd_params, opt);
// COMPUTE SINGLE DIFFERENCES
return singleDifferences(common_obs_r, nav_r, common_sats_pos_r, x_r,
common_obs_k, nav_k, common_sats_pos_k,
d, cov_d,
residual, discarded_sats,
sd_params);
}
bool singleDifferences(const Observations& common_obs_r, const Navigation& nav_r, const std::map<int,Eigen::Vector3d>& common_sats_pos_r, const Eigen::Vector3d& x_r,
const Observations& common_obs_k, const Navigation& nav_k, const std::map<int,Eigen::Vector3d>& common_sats_pos_k,
Eigen::Vector4d& d, Eigen::Matrix4d& cov_d,
double& residual, std::set<int>& discarded_sats,
const SingleDifferencesParams& sd_params)
{
assert(common_obs_r.size() == common_obs_k.size());
assert(common_obs_r.size() == common_sats_pos_r.size());
assert(common_obs_k.size() == common_sats_pos_k.size());
double tr(common_obs_r.getObservations().front().time.time + common_obs_r.getObservations().front().time.sec);
double tk(common_obs_k.getObservations().front().time.time + common_obs_k.getObservations().front().time.sec);
int required_n_sats(std::max(sd_params.min_common_sats + sd_params.raim_n, 4 + sd_params.raim_n));
int n_common_sats = common_sats_pos_k.size();
if(n_common_sats < required_n_sats)
{
#if GNSS_UTILS_SD_DEBUG == 1
printf("SD: NOT ENOUGH COMMON SATS");
printf("SD: %i common sats available, %i sats are REQUIRED. [ SKIPPING SD ]",
n_common_sats,
required_n_sats);
#endif
return false;
}
// MULTI-FREQUENCY
std::map<int,std::pair<int,int>> row_2_sat_freq;
int row = 0;
for (auto obs_idx = 0; obs_idx < common_obs_k.size(); obs_idx++)
{
auto&& obs_r = common_obs_r.getObservationByIdx(obs_idx);
auto&& obs_k = common_obs_k.getObservationByIdx(obs_idx);
const int& sat_number = obs_k.sat;
if(sd_params.use_carrier_phase)
{
// L1/E1
if (std::abs(obs_r.L[0]) > 1e-12 and
std::abs(obs_k.L[0]) > 1e-12)
{
row_2_sat_freq[row] = std::make_pair(sat_number,0);
row++;
}
if (!sd_params.use_multi_freq)
continue;
// L2 (GPS/GLO/QZS)
int sys=satsys(sat_number,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)
{
row_2_sat_freq[row] = std::make_pair(sat_number,1);
row++;
}
// 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_number,2);
row++;
}
}
else
// L1/E1
if (std::abs(obs_r.P[0]) > 1e-12 and
std::abs(obs_k.P[0]) > 1e-12)
{
row_2_sat_freq[row] = std::make_pair(sat_number,0);
row++;
}
}
int n_differences = row_2_sat_freq.size();
// Initial guess
Eigen::Vector4d d_0(d);
#if GNSS_UTILS_SD_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 << "common sats: ";
for (auto row_sat_freq_it : row_2_sat_freq)
std::cout << row_sat_freq_it.second.first << " ";
std::cout << std::endl;
#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;
for (auto row_sat_freq_it : row_2_sat_freq)
{
const int& row = row_sat_freq_it.first;
const int& sat_number = row_sat_freq_it.second.first;
const int& freq = row_sat_freq_it.second.second;
auto obs_r = common_obs_r.getObservationBySat(sat_number);
auto obs_k = common_obs_k.getObservationBySat(sat_number);
// excluded satellite
if (discarded_sats.count(sat_number) != 0)
{
#if GNSS_UTILS_SD_DEBUG == 1
std::cout << "\tdiscarded sat" << std::endl;
#endif
continue;
}
// Satellite's positions
s_r.col(row) = common_sats_pos_r.at(sat_number);
s_k.col(row) = common_sats_pos_k.at(sat_number);
if(sd_params.use_carrier_phase)
drho_m(row) = (obs_k.L[freq] * nav_k.getNavigation().lam[sat_number-1][freq]) -
(obs_r.L[freq] * nav_r.getNavigation().lam[sat_number-1][freq]);
else
drho_m(row) = obs_k.P[freq] - obs_r.P[freq];
#if GNSS_UTILS_SD_DEBUG == 1
std::cout << "\tsat " << sat_number << " frequency " << freq << " wavelength = " << nav_r.getNavigation().lam[sat_number-1][freq] << std::endl;
//std::cout << "\tpositions:\n\t\tt_r: " << s_r.col(row).transpose() << "\n\t\tt_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.getNavigation().lam[sat_number-1][freq] << std::endl;
//std::cout << "\tnav_k.getNavigation().lam[sat_number-1][freq]: " << nav_k.getNavigation().lam[sat_number-1][freq] << std::endl;
//std::cout << "\trho_r: " << obs_r.L[freq] * nav_r.getNavigation().lam[sat_number-1][freq] << std::endl;
//std::cout << "\trho_k: " << obs_k.L[freq] * nav_k.getNavigation().lam[sat_number-1][freq] << std::endl;
//std::cout << "\tdrho_m: " << drho_m(row) << std::endl;
#endif
if(!sd_params.relinearize_jacobian)
{
// Unit vectors from recerivers to satellites
Eigen::Vector3d u_k = (s_k.col(row) - x_r - d.head(3)).normalized();
// Fill Jacobian matrix
A(row, 0) = u_k(0);
A(row, 1) = u_k(1);
A(row, 2) = u_k(2);
A(row, 3) = -1.0;
}
}
// LEAST SQUARES SOLUTION =======================================================================
for (int j = 0; j < sd_params.max_iterations; j++)
{
// fill A and r
for (int row = 0; row < A.rows(); row++)
{
// 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);
// Evaluate A
if (sd_params.relinearize_jacobian)
{
// Unit vectors from rcvrs to sats
Eigen::Vector3d u_k = (s_k.col(row) - x_r - d.head(3)).normalized();
// Fill Jacobian matrix
A(row, 0) = u_k(0);
A(row, 1) = u_k(1);
A(row, 2) = u_k(2);
A(row, 3) = -1.0;
}
}
// Solve
cov_d = (A.transpose()*A).inverse();
d -= cov_d*A.transpose()*r;
// wrong solution
if(d.array().isNaN().any())
{
std::cout << "SD: LSM DID NOT WORK. NAN values appeared. ABORTING!!" << std::endl;
return false;
}
// residual
//residual = sqrt((r - A * d).squaredNorm() / A.rows());
residual = (r + A * d).norm();
//std::cout << "residual = " << residual << std::endl;
//std::cout << "SD2: r-A*d = " << (r + A * d).transpose() << std::endl;
#if GNSS_UTILS_SD_DEBUG == 1
std::cout << "Displacement vector = %s" << 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("SD: residual = %13.10f\n", residual);
printf("SD: row = %i\n", r.rows());
std::cout << "SD: drho = " << r.transpose() << std::endl;
std::cout << "SD: drho_m = " << drho_m.transpose() << std::endl;
std::cout << "SD: H = \n" << A << std::endl;
printf("SD: dT = %8.3f secs\n", d(3));
#endif
// RAIM ====================================== (only at first iteration) RAIM WITH SATELLITES, not ROWS
int input_discarded_sats = discarded_sats.size();
if(j==0 and sd_params.raim_n > 0 and residual > sd_params.raim_min_residual)
{
int worst_sat_row = -1;
double best_residual = 1e12;
Eigen::Vector4d best_d;
int n_removed_rows = 1;
// remove some satellites
while(discarded_sats.size() - input_discarded_sats < sd_params.raim_n)
{
auto A_raim = A;
auto r_raim = r;
// solve removing each satellite
for (int row_removed = 0; row_removed < A_raim.rows(); row_removed++)
{
// Multi-freq: some rows for the same satellite
n_removed_rows = 1;
if (sd_params.use_multi_freq)
while (row_removed + n_removed_rows < A_raim.rows() and row_2_sat_freq.at(row_removed+n_removed_rows) == row_2_sat_freq.at(row_removed))
n_removed_rows++;
// 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
}
// solve
Eigen::Vector4d d_raim = d_0-(A_raim.transpose()*A_raim).inverse()*A_raim.transpose()*r_raim;
// no NaN
if (!d_raim.array().isNaN().any())
{
// residual
residual = (r - A * d_raim).norm() / A.rows(); //FIXME: evaluate with XX_raim or not? I think next line is the good one
//residual = sqrt((r_raim - A_raim * d_raim).squaredNorm() / A_raim.rows());
// store if best residual
if (residual < best_residual)
{
worst_sat_row = row_removed;
best_residual = residual;
best_d = d_raim;
}
}
// 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);
}
// No successful RAIM solution
if(worst_sat_row == -1)
{
printf("SD: LS after RAIM DID NOT WORK. NAN values appeared. ABORTING!!");
return false;
}
// store removed sat
discarded_sats.insert(row_2_sat_freq[worst_sat_row].first);
// decrease n_common_sats
n_differences=-n_removed_rows;
n_common_sats--;
// Remove selected satellite from problem
auto A_aux = A;
auto r_aux = r;
auto drho_m_aux = drho_m;
auto s_r_aux = s_r;
auto s_k_aux = s_k;
A.conservativeResize(n_differences, Eigen::NoChange);
r.conservativeResize(n_differences);
drho_m.conservativeResize(n_differences);
s_r.conservativeResize(Eigen::NoChange, n_differences);
s_k.conservativeResize(Eigen::NoChange, n_differences);
int back_elements_changing = A.rows() - worst_sat_row - n_removed_rows;
if (back_elements_changing != 0) //if last satelite removed, conservative resize did the job
{
A .bottomRows(back_elements_changing) = A_aux .bottomRows(back_elements_changing);
s_r .rightCols(back_elements_changing) = s_r_aux .rightCols(back_elements_changing);
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;
cov_d = (A.transpose()*A).inverse();
#if GNSS_UTILS_SD_DEBUG == 1
std::cout << "SD After RAIM iteration" << std::endl;
std::cout << "\tExcluded sats : ";
for (auto dsat : discarded_sats)
std::cout << (int)dsat << " ";
std::cout << std::endl;
std::cout << "\tCommon sats : " << n_common_sats << std::endl;
std::cout << "\tRows : " << n_differences << 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;
std::cout << "\tResidual = " << residual << std::endl;
#endif
}
#if GNSS_UTILS_SD_DEBUG == 1
std::cout << "SD After RAIM " << std::endl;
std::cout << "\tExcluded sats : ";
for (auto dsat : discarded_sats)
std::cout << (int)dsat << " ";
std::cout << std::endl;
std::cout << "\tCommon sats : " << n_common_sats << std::endl;
std::cout << "\tRows : " << n_differences << 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;
std::cout << "\tResidual = " << residual << std::endl;
#endif
}
#if GNSS_UTILS_SD_DEBUG == 1
std::cout << "SD iteration " << j << std::endl;
std::cout << "\tExcluded sats : ";
for (auto dsat : discarded_sats)
std::cout << (int)dsat << " ";
std::cout << std::endl;
std::cout << "\tCommon sats : " << n_common_sats << std::endl;
std::cout << "\tRows : " << n_differences << 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;
std::cout << "\tResidual = " << residual << std::endl;
#endif
}
// weight covariance with the measurement noise (proportional to time)
double sq_sigma_SD = (tk-tr) * sd_params.sigma_atm * sd_params.sigma_atm +
2 * (sd_params.use_carrier_phase ?
sd_params.sigma_carrier * sd_params.sigma_carrier :
sd_params.sigma_code * sd_params.sigma_code);
cov_d *= sq_sigma_SD;
//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);
}
residual = sqrt(r.squaredNorm() / r.size());
return true;
}
void filterCommonSatellites(Observations& common_obs_r, std::map<int,Eigen::Vector3d>& common_sats_pos_r,
Observations& common_obs_k, std::map<int,Eigen::Vector3d>& common_sats_pos_k,
std::set<int>& discarded_sats, const Eigen::Vector3d& x_r,
const SingleDifferencesParams& sd_params, const prcopt_t& opt)
{
assert(&common_obs_r != &common_obs_k);
assert(&common_sats_pos_r != &common_sats_pos_k);
assert(common_obs_r.size() == common_obs_k.size());
assert(common_obs_r.size() == common_sats_pos_r.size());
assert(common_obs_r.size() == common_sats_pos_k.size());
//std::cout << "filterCommonSatellites: initial size: " << common_obs_k.size() << std::endl;
std::set<int> remove_sats;
for (int obs_i = 0; obs_i < common_obs_r.size(); obs_i++)
{
auto&& obs_r = common_obs_r.getObservationByIdx(obs_i);
auto&& obs_k = common_obs_k.getObservationByIdx(obs_i);
assert(obs_r.sat == obs_k.sat && "common satellites observations have to be ordered equally");
const int& sat_number = obs_r.sat;
// already discarded sats
if (discarded_sats.count(sat_number) != 0)
{
remove_sats.insert(sat_number);
continue;
}
// wrong data (satellite is not included in the discarded list)
if (sd_params.use_carrier_phase and (std::abs(obs_r.L[0]) < 1e-12 or std::abs(obs_k.L[0]) < 1e-12))
{
// std::cout << "Discarding sat " << sat_number << ": wrong carrier phase data: " << obs_r.L[0] << " " << obs_k.L[0] << std::endl;
remove_sats.insert(sat_number);
continue;
}
if (!sd_params.use_carrier_phase and (std::abs(obs_r.P[0]) < 1e-12 or std::abs(obs_k.L[0]) < 1e-12))
{
// std::cout << "Discarding sat " << sat_number << ": wrong carrier phase data: " << obs_r.P[0] << " " << obs_k.P[0] << std::endl;
remove_sats.insert(sat_number);
continue;
}
// bad satellite position (satellite is not included in the discarded list)
if (common_sats_pos_r[sat_number].isApprox(Eigen::Vector3d::Zero(), 1e-3) or
common_sats_pos_r[sat_number].isApprox(Eigen::Vector3d::Zero(), 1e-3))
{
// std::cout << "Discarding sat " << sat_number << ": bad satellite position: \n\t" << common_sats_pos_r[sat_number].transpose() << "\n\t" << common_sats_pos_k[sat_number].transpose() << std::endl;
remove_sats.insert(sat_number);
continue;
}
// constellation
int sys=satsys(obs_r.sat,NULL);
if (!(sys & opt.navsys))
{
// std::cout << "Discarding sat " << sat_number << ": not selected constellation " << sys << " - mask: " << opt.navsys << std::endl;
discarded_sats.insert(sat_number);
remove_sats.insert(sat_number);
continue;
}
// check both elevations
double elevation = std::min(computeSatElevation(x_r, common_sats_pos_r[sat_number]),
computeSatElevation(x_r, common_sats_pos_k[sat_number]));
if (elevation < opt.elmin)
{
// std::cout << "Discarding sat " << sat_number << ": low elevation " << elevation << " - min: " << opt.elmin << std::endl;
discarded_sats.insert(sat_number);
remove_sats.insert(sat_number);
continue;
}
// snr TODO: multifrequency (2nd param and 3rd idx)
if (testsnr(0, 0, elevation, obs_r.SNR[0] * 0.25, &opt.snrmask) == 1)
{
// std::cout << "Discarding sat " << sat_number << ": snr test " << std::endl;
discarded_sats.insert(sat_number);
remove_sats.insert(sat_number);
continue;
}
}
// remove sats
//std::cout << "removing: " << remove_sats.size() << " satellites" << std::endl;
for (auto sat : remove_sats)
{
assert(common_obs_r.hasSatellite(sat));
assert(common_obs_k.hasSatellite(sat));
assert(common_sats_pos_r.count(sat));
assert(common_sats_pos_k.count(sat));
common_obs_r.removeObservationBySat(sat);
common_obs_k.removeObservationBySat(sat);
common_sats_pos_r.erase(sat);
common_sats_pos_k.erase(sat);
}
assert(common_obs_r.size() == common_obs_k.size());
assert(common_obs_r.size() == common_sats_pos_r.size());
assert(common_obs_r.size() == common_sats_pos_k.size());
//std::cout << "final size: " << common_obs_k.size() << std::endl;
}
}