Line |
Branch |
Exec |
Source |
1 |
|
|
// This file is part of INSTINCT, the INS Toolkit for Integrated |
2 |
|
|
// Navigation Concepts and Training by the Institute of Navigation of |
3 |
|
|
// the University of Stuttgart, Germany. |
4 |
|
|
// |
5 |
|
|
// This Source Code Form is subject to the terms of the Mozilla Public |
6 |
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this |
7 |
|
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/. |
8 |
|
|
|
9 |
|
|
/// @file Algorithm.hpp |
10 |
|
|
/// @brief Single Point Positioning Algorithm |
11 |
|
|
/// @author T. Topp (topp@ins.uni-stuttgart.de) |
12 |
|
|
/// @date 2023-12-20 |
13 |
|
|
|
14 |
|
|
#pragma once |
15 |
|
|
|
16 |
|
|
#include <fmt/format.h> |
17 |
|
|
#include <cstdint> |
18 |
|
|
#include <set> |
19 |
|
|
|
20 |
|
|
#include "Navigation/GNSS/Positioning/Observation.hpp" |
21 |
|
|
#include "Navigation/GNSS/Positioning/ObservationEstimator.hpp" |
22 |
|
|
#include "Navigation/GNSS/Positioning/ObservationFilter.hpp" |
23 |
|
|
#include "Navigation/GNSS/Positioning/Receiver.hpp" |
24 |
|
|
#include "Navigation/GNSS/Positioning/SPP/Keys.hpp" |
25 |
|
|
#include "Navigation/GNSS/Positioning/SPP/KalmanFilter.hpp" |
26 |
|
|
|
27 |
|
|
#include "NodeData/GNSS/GnssObs.hpp" |
28 |
|
|
#include "NodeData/GNSS/SppSolution.hpp" |
29 |
|
|
|
30 |
|
|
namespace NAV |
31 |
|
|
{ |
32 |
|
|
|
33 |
|
|
namespace SPP |
34 |
|
|
{ |
35 |
|
|
|
36 |
|
|
/// Single Point Positioning Algorithm |
37 |
|
|
class Algorithm |
38 |
|
|
{ |
39 |
|
|
public: |
40 |
|
|
/// Possible SPP estimation algorithms |
41 |
|
|
enum class EstimatorType : uint8_t |
42 |
|
|
{ |
43 |
|
|
LeastSquares, ///< Linear Least Squares |
44 |
|
|
WeightedLeastSquares, ///< Weighted Linear Least Squares |
45 |
|
|
KalmanFilter, ///< Kalman Filter |
46 |
|
|
COUNT, ///< Amount of items in the enum |
47 |
|
|
}; |
48 |
|
|
|
49 |
|
|
/// @brief Receiver Types |
50 |
|
|
enum ReceiverType : uint8_t |
51 |
|
|
{ |
52 |
|
|
Rover, ///< Rover |
53 |
|
|
ReceiverType_COUNT, ///< Amount of receiver types |
54 |
|
|
}; |
55 |
|
|
|
56 |
|
|
/// @brief Shows the GUI input to select the options |
57 |
|
|
/// @param[in] id Unique id for ImGui. |
58 |
|
|
/// @param[in] itemWidth Width of the widgets |
59 |
|
|
/// @param[in] unitWidth Width on unit inputs |
60 |
|
|
bool ShowGuiWidgets(const char* id, float itemWidth, float unitWidth); |
61 |
|
|
|
62 |
|
|
/// Reset the algorithm |
63 |
|
|
void reset(); |
64 |
|
|
|
65 |
|
|
/// @brief Get the Estimator Type |
66 |
|
|
[[nodiscard]] EstimatorType getEstimatorType() const { return _estimatorType; } |
67 |
|
|
|
68 |
|
|
/// @brief Get the last update time |
69 |
|
|
const InsTime& getLastUpdateTime() const { return _lastUpdate; } |
70 |
|
|
|
71 |
|
|
/// @brief Calculate the SPP solution |
72 |
|
|
/// @param[in] gnssObs GNSS observation |
73 |
|
|
/// @param[in] gnssNavInfos Collection of GNSS Nav information |
74 |
|
|
/// @param[in] nameId Name and id of the node calling this (only used for logging purposes) |
75 |
|
|
/// @return The SPP Solution if it could be calculated, otherwise nullptr |
76 |
|
|
std::shared_ptr<SppSolution> calcSppSolution(const std::shared_ptr<const GnssObs>& gnssObs, |
77 |
|
|
const std::vector<const GnssNavInfo*>& gnssNavInfos, |
78 |
|
|
const std::string& nameId); |
79 |
|
|
|
80 |
|
|
/// Observation Filter |
81 |
|
|
ObservationFilter _obsFilter{ ReceiverType_COUNT, |
82 |
|
|
/* available */ std::unordered_set{ GnssObs::Pseudorange, GnssObs::Doppler }, |
83 |
|
|
/* needed */ std::unordered_set{ GnssObs::Pseudorange } }; |
84 |
|
|
|
85 |
|
|
/// Observation Estimator |
86 |
|
|
ObservationEstimator _obsEstimator{ ReceiverType_COUNT }; |
87 |
|
|
|
88 |
|
|
/// Estimate Inter-frequency biases |
89 |
|
|
bool _estimateInterFreqBiases = true; |
90 |
|
|
|
91 |
|
|
private: |
92 |
|
|
using Receiver = NAV::Receiver<ReceiverType>; ///< Receiver |
93 |
|
|
|
94 |
|
|
/// @brief All position keys |
95 |
|
|
const std::vector<SPP::States::StateKeyType>& PosKey = Keys::Pos<SPP::States::StateKeyType>; |
96 |
|
|
/// @brief All velocity keys |
97 |
|
|
const std::vector<SPP::States::StateKeyType>& VelKey = Keys::Vel<SPP::States::StateKeyType>; |
98 |
|
|
/// @brief All position and velocity keys |
99 |
|
|
const std::vector<SPP::States::StateKeyType>& PosVelKey = Keys::PosVel<SPP::States::StateKeyType>; |
100 |
|
|
|
101 |
|
|
/// @brief Checks if the SPP algorithm can calculate the position (always true for Kalman filter) |
102 |
|
|
/// @param[in] nDoppMeas Amount of Doppler measurements |
103 |
|
|
[[nodiscard]] bool canCalculateVelocity(size_t nDoppMeas) const; |
104 |
|
|
|
105 |
|
|
/// @brief Checks if the SPP algorithm can estimate inter-frequency biases |
106 |
|
|
[[nodiscard]] bool canEstimateInterFrequencyBias() const; |
107 |
|
|
|
108 |
|
|
/// @brief Updates the inter frequency biases |
109 |
|
|
/// @param[in] observations List of GNSS observation data used for the calculation |
110 |
|
|
/// @param[in] nameId Name and id of the node calling this (only used for logging purposes) |
111 |
|
|
void updateInterFrequencyBiases(const Observations& observations, const std::string& nameId); |
112 |
|
|
|
113 |
|
|
/// @brief Returns a list of state keys |
114 |
|
|
/// @param[in] usedSatSystems Used Satellite systems this epoch |
115 |
|
|
/// @param[in] nDoppMeas Amount of Doppler measurements |
116 |
|
|
/// @param[in] nameId Name and id of the node calling this (only used for logging purposes) |
117 |
|
|
std::vector<States::StateKeyType> determineStateKeys(const std::set<SatelliteSystem>& usedSatSystems, size_t nDoppMeas, const std::string& nameId) const; |
118 |
|
|
|
119 |
|
|
/// @brief Returns a list of measurement keys |
120 |
|
|
/// @param[in] observations List of GNSS observation data used for the calculation |
121 |
|
|
/// @param[in] nPsrMeas Amount of pseudo-range measurements |
122 |
|
|
/// @param[in] nDoppMeas Amount of doppler measurements |
123 |
|
|
/// @param[in] nameId Name and id of the node calling this (only used for logging purposes) |
124 |
|
|
std::vector<Meas::MeasKeyTypes> determineMeasKeys(const Observations& observations, size_t nPsrMeas, size_t nDoppMeas, const std::string& nameId) const; |
125 |
|
|
|
126 |
|
|
/// @brief Calculates the measurement sensitivity matrix 𝐇 |
127 |
|
|
/// @param[in] stateKeys State Keys |
128 |
|
|
/// @param[in] measKeys Measurement Keys |
129 |
|
|
/// @param[in] observations List of GNSS observation data used for the calculation |
130 |
|
|
/// @param[in] nameId Name and id of the node calling this (only used for logging purposes) |
131 |
|
|
/// @return The 𝐇 matrix |
132 |
|
|
[[nodiscard]] KeyedMatrixXd<Meas::MeasKeyTypes, States::StateKeyType> calcMatrixH(const std::vector<States::StateKeyType>& stateKeys, |
133 |
|
|
const std::vector<Meas::MeasKeyTypes>& measKeys, |
134 |
|
|
const Observations& observations, |
135 |
|
|
const std::string& nameId) const; |
136 |
|
|
|
137 |
|
|
/// @brief Calculates the measurement noise covariance matrix 𝐑 |
138 |
|
|
/// @param[in] measKeys Measurement Keys |
139 |
|
|
/// @param[in] observations List of GNSS observation data used for the calculation |
140 |
|
|
/// @param[in] nameId Name and id of the node calling this (only used for logging purposes) |
141 |
|
|
/// @return The 𝐑 matrix |
142 |
|
|
[[nodiscard]] static KeyedMatrixXd<Meas::MeasKeyTypes, Meas::MeasKeyTypes> calcMatrixR(const std::vector<Meas::MeasKeyTypes>& measKeys, |
143 |
|
|
const Observations& observations, |
144 |
|
|
const std::string& nameId); |
145 |
|
|
|
146 |
|
|
/// @brief Calculates the measurement innovation vector 𝜹𝐳 |
147 |
|
|
/// @param[in] measKeys Measurement Keys |
148 |
|
|
/// @param[in] observations List of GNSS observation data used for the calculation |
149 |
|
|
/// @param[in] nameId Name and id of the node calling this (only used for logging purposes) |
150 |
|
|
/// @return The measurement innovation 𝜹𝐳 vector |
151 |
|
|
[[nodiscard]] static KeyedVectorXd<Meas::MeasKeyTypes> calcMeasInnovation(const std::vector<Meas::MeasKeyTypes>& measKeys, |
152 |
|
|
const Observations& observations, |
153 |
|
|
const std::string& nameId); |
154 |
|
|
|
155 |
|
|
/// @brief Assigns the result to the receiver variable |
156 |
|
|
/// @param[in] state Delta state |
157 |
|
|
/// @param[in] variance Variance of the state |
158 |
|
|
/// @param[in] e_oldPos Old position in ECEF coordinates in [m] |
159 |
|
|
/// @param[in] nParams Number of parameters to estimate the position |
160 |
|
|
/// @param[in] nUniqueDopplerMeas Number of available doppler measurements (unique per satellite) |
161 |
|
|
/// @param[in] dt Time step size in [s] |
162 |
|
|
/// @param[in] nameId Name and id of the node calling this (only used for logging purposes) |
163 |
|
|
void assignLeastSquaresResult(const KeyedVectorXd<States::StateKeyType>& state, |
164 |
|
|
const KeyedMatrixXd<States::StateKeyType, States::StateKeyType>& variance, |
165 |
|
|
const Eigen::Vector3d& e_oldPos, |
166 |
|
|
size_t nParams, size_t nUniqueDopplerMeas, double dt, const std::string& nameId); |
167 |
|
|
|
168 |
|
|
/// @brief Assigns the result to the receiver variable |
169 |
|
|
/// @param state Total state |
170 |
|
|
/// @param variance Variance of the state |
171 |
|
|
/// @param[in] nameId Name and id of the node calling this (only used for logging purposes) |
172 |
|
|
void assignKalmanFilterResult(const KeyedVectorXd<States::StateKeyType>& state, |
173 |
|
|
const KeyedMatrixXd<States::StateKeyType, States::StateKeyType>& variance, |
174 |
|
|
const std::string& nameId); |
175 |
|
|
|
176 |
|
|
/// @brief Computes all DOP values (by reference) |
177 |
|
|
/// @param[in, out] sppSol SppSol to fill with DOP values |
178 |
|
|
/// @param[in] H Measurement sensitivity matrix 𝐇 |
179 |
|
|
/// @param[in] nameId Name and id of the node calling this (only used for logging purposes) |
180 |
|
|
void computeDOPs(const std::shared_ptr<SppSolution>& sppSol, |
181 |
|
|
const KeyedMatrixXd<Meas::MeasKeyTypes, States::StateKeyType>& H, |
182 |
|
|
const std::string& nameId); |
183 |
|
|
|
184 |
|
|
/// Receiver |
185 |
|
|
Receiver _receiver{ Rover, _obsFilter.getSystemFilter().toVector() }; |
186 |
|
|
|
187 |
|
|
/// Estimator type used for the calculations |
188 |
|
|
EstimatorType _estimatorType = EstimatorType::WeightedLeastSquares; |
189 |
|
|
|
190 |
|
|
/// SPP specific Kalman filter |
191 |
|
|
SPP::KalmanFilter _kalmanFilter; |
192 |
|
|
|
193 |
|
|
/// Time of last update |
194 |
|
|
InsTime _lastUpdate; |
195 |
|
|
|
196 |
|
|
Eigen::Matrix3d _e_lastPositionCovarianceMatrix; ///< Last position covariance matrix |
197 |
|
|
Eigen::Matrix3d _e_lastVelocityCovarianceMatrix; ///< Last velocity covariance matrix |
198 |
|
|
|
199 |
|
|
friend void to_json(json& j, const Algorithm& obj); |
200 |
|
|
friend void from_json(const json& j, Algorithm& obj); |
201 |
|
|
}; |
202 |
|
|
|
203 |
|
|
/// @brief Converts the provided object into json |
204 |
|
|
/// @param[out] j Json object which gets filled with the info |
205 |
|
|
/// @param[in] obj Object to convert into json |
206 |
|
|
void to_json(json& j, const Algorithm& obj); |
207 |
|
|
/// @brief Converts the provided json object into a node object |
208 |
|
|
/// @param[in] j Json object with the needed values |
209 |
|
|
/// @param[out] obj Object to fill from the json |
210 |
|
|
void from_json(const json& j, Algorithm& obj); |
211 |
|
|
|
212 |
|
|
} // namespace SPP |
213 |
|
|
|
214 |
|
|
/// @brief Converts the enum to a string |
215 |
|
|
/// @param[in] estimatorType Enum value to convert into text |
216 |
|
|
/// @return String representation of the enum |
217 |
|
|
[[nodiscard]] const char* to_string(SPP::Algorithm::EstimatorType estimatorType); |
218 |
|
|
|
219 |
|
|
/// @brief Converts the enum to a string |
220 |
|
|
/// @param[in] receiver Enum value to convert into text |
221 |
|
|
/// @return String representation of the enum |
222 |
|
|
[[nodiscard]] const char* to_string(SPP::Algorithm::ReceiverType receiver); |
223 |
|
|
|
224 |
|
|
} // namespace NAV |
225 |
|
|
|
226 |
|
|
/// @brief Stream insertion operator overload |
227 |
|
|
/// @param[in, out] os Output stream object to stream the time into |
228 |
|
|
/// @param[in] obj Object to print |
229 |
|
|
/// @return Returns the output stream object in order to chain stream insertions |
230 |
|
|
std::ostream& operator<<(std::ostream& os, const NAV::SPP::Algorithm::EstimatorType& obj); |
231 |
|
|
|
232 |
|
|
/// @brief Stream insertion operator overload |
233 |
|
|
/// @param[in, out] os Output stream object to stream the time into |
234 |
|
|
/// @param[in] obj Object to print |
235 |
|
|
/// @return Returns the output stream object in order to chain stream insertions |
236 |
|
|
std::ostream& operator<<(std::ostream& os, const NAV::SPP::Algorithm::ReceiverType& obj); |
237 |
|
|
|
238 |
|
|
#ifndef DOXYGEN_IGNORE |
239 |
|
|
|
240 |
|
|
/// @brief Formatter |
241 |
|
|
template<> |
242 |
|
|
struct fmt::formatter<NAV::SPP::Algorithm::EstimatorType> : fmt::formatter<const char*> |
243 |
|
|
{ |
244 |
|
|
/// @brief Defines how to format structs |
245 |
|
|
/// @param[in] type Struct to format |
246 |
|
|
/// @param[in, out] ctx Format context |
247 |
|
|
/// @return Output iterator |
248 |
|
|
template<typename FormatContext> |
249 |
|
✗ |
auto format(const NAV::SPP::Algorithm::EstimatorType& type, FormatContext& ctx) const |
250 |
|
|
{ |
251 |
|
✗ |
return fmt::formatter<const char*>::format(NAV::to_string(type), ctx); |
252 |
|
|
} |
253 |
|
|
}; |
254 |
|
|
|
255 |
|
|
/// @brief Formatter |
256 |
|
|
template<> |
257 |
|
|
struct fmt::formatter<NAV::SPP::Algorithm::ReceiverType> : fmt::formatter<const char*> |
258 |
|
|
{ |
259 |
|
|
/// @brief Defines how to format structs |
260 |
|
|
/// @param[in] type Struct to format |
261 |
|
|
/// @param[in, out] ctx Format context |
262 |
|
|
/// @return Output iterator |
263 |
|
|
template<typename FormatContext> |
264 |
|
✗ |
auto format(const NAV::SPP::Algorithm::ReceiverType& type, FormatContext& ctx) const |
265 |
|
|
{ |
266 |
|
✗ |
return fmt::formatter<const char*>::format(NAV::to_string(type), ctx); |
267 |
|
|
} |
268 |
|
|
}; |
269 |
|
|
|
270 |
|
|
#endif |
271 |
|
|
|