INSTINCT Code Coverage Report


Directory: src/
File: NodeData/GNSS/SppSolution.hpp
Date: 2025-02-07 16:54:41
Exec Total Coverage
Lines: 43 178 24.2%
Functions: 4 12 33.3%
Branches: 36 261 13.8%

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 SppSolution.hpp
10 /// @brief SPP Algorithm output
11 /// @author T. Topp (topp@ins.uni-stuttgart.de)
12 /// @date 2022-05-28
13
14 #pragma once
15
16 #include <vector>
17 #include <optional>
18 #include <algorithm>
19
20 #include "Navigation/GNSS/Core/SatelliteIdentifier.hpp"
21 #include "Navigation/GNSS/Core/Code.hpp"
22 #include "Navigation/GNSS/Core/SatelliteSystem.hpp"
23 #include "Navigation/GNSS/Positioning/ReceiverClock.hpp"
24 #include "Navigation/GNSS/Positioning/SPP/Keys.hpp"
25
26 #include "NodeData/State/PosVel.hpp"
27
28 #include "util/Assert.h"
29 #include "util/Container/KeyedMatrix.hpp"
30 #include "util/Container/UncertainValue.hpp"
31
32 namespace NAV
33 {
34 /// SPP Algorithm output
35 class SppSolution : public PosVel
36 {
37 public:
38 /// @brief Returns the type of the data class
39 /// @return The data type
40 860 [[nodiscard]] static std::string type()
41 {
42
1/2
✓ Branch 1 taken 860 times.
✗ Branch 2 not taken.
1720 return "SppSolution";
43 }
44
45 /// @brief Returns the type of the data class
46 /// @return The data type
47 [[nodiscard]] std::string getType() const override { return type(); }
48
49 /// @brief Returns the parent types of the data class
50 /// @return The parent data types
51 112 [[nodiscard]] static std::vector<std::string> parentTypes()
52 {
53 112 auto parent = PosVel::parentTypes();
54
2/4
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 112 times.
✗ Branch 5 not taken.
112 parent.push_back(PosVel::type());
55 112 return parent;
56 }
57
58 /// @brief Returns a vector of data descriptors
59 3 [[nodiscard]] static std::vector<std::string> GetStaticDataDescriptors()
60 {
61 3 auto desc = PosVel::GetStaticDataDescriptors();
62
1/2
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 desc.reserve(GetStaticDescriptorCount());
63
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Number satellites");
64
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock bias GPS [s]");
65
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock drift GPS [s/s]");
66
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock bias StDev GPS [s]");
67
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock drift StDev GPS [s/s]");
68
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock bias GAL [s]");
69
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock drift GAL [s/s]");
70
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock bias StDev GAL [s]");
71
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock drift StDev GAL [s/s]");
72
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock bias GLO [s]");
73
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock drift GLO [s/s]");
74
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock bias StDev GLO [s]");
75
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock drift StDev GLO [s/s]");
76
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock bias BDS [s]");
77
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock drift BDS [s/s]");
78
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock bias StDev BDS [s]");
79
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock drift StDev BDS [s/s]");
80
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock bias QZSS [s]");
81
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock drift QZSS [s/s]");
82
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock bias StDev QZSS [s]");
83
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock drift StDev QZSS [s/s]");
84
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock bias IRNSS [s]");
85
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock drift IRNSS [s/s]");
86
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock bias StDev IRNSS [s]");
87
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock drift StDev IRNSS [s/s]");
88
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock bias SBAS [s]");
89
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock drift SBAS [s/s]");
90
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock bias StDev SBAS [s]");
91
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("Receiver clock drift StDev SBAS [s/s]");
92
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("HDOP");
93
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("VDOP");
94
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 desc.emplace_back("PDOP");
95
96 3 return desc;
97 }
98
99 /// @brief Get the amount of descriptors
100 3 [[nodiscard]] static constexpr size_t GetStaticDescriptorCount() { return PosVel::GetStaticDescriptorCount() + 32; }
101
102 /// @brief Returns a vector of data descriptors
103 [[nodiscard]] std::vector<std::string> staticDataDescriptors() const override { return GetStaticDataDescriptors(); }
104
105 /// @brief Get the amount of descriptors
106 [[nodiscard]] size_t staticDescriptorCount() const override { return GetStaticDescriptorCount(); }
107
108 /// @brief Get the value at the index
109 /// @param idx Index corresponding to data descriptor order
110 /// @return Value if in the observation
111 [[nodiscard]] std::optional<double> getValueAt(size_t idx) const override
112 {
113 INS_ASSERT(idx < GetStaticDescriptorCount());
114 if (idx < PosVel::GetStaticDescriptorCount()) { return PosVel::getValueAt(idx); }
115 switch (idx)
116 {
117 case PosVel::GetStaticDescriptorCount() + 0: // Number satellites
118 return static_cast<double>(nSatellites);
119
120 case PosVel::GetStaticDescriptorCount() + 1: // Receiver clock bias GPS [s]
121 if (const double* v = recvClk.biasFor(GPS)) { return *v; }
122 break;
123 case PosVel::GetStaticDescriptorCount() + 2: // Receiver clock drift GPS [s/s]
124 if (const double* v = recvClk.driftFor(GPS)) { return *v; }
125 break;
126 case PosVel::GetStaticDescriptorCount() + 3: // Receiver clock bias StDev GPS [s]
127 if (const double* v = recvClk.biasStdDevFor(GPS)) { return *v; }
128 break;
129 case PosVel::GetStaticDescriptorCount() + 4: // Receiver clock drift StDev GPS [s/s]
130 if (const double* v = recvClk.driftStdDevFor(GPS)) { return *v; }
131 break;
132
133 case PosVel::GetStaticDescriptorCount() + 5: // Receiver clock bias GAL [s]
134 if (const double* v = recvClk.biasFor(GAL)) { return *v; }
135 break;
136 case PosVel::GetStaticDescriptorCount() + 6: // Receiver clock drift GAL [s/s]
137 if (const double* v = recvClk.driftFor(GAL)) { return *v; }
138 break;
139 case PosVel::GetStaticDescriptorCount() + 7: // Receiver clock bias StDev GAL [s]
140 if (const double* v = recvClk.biasStdDevFor(GAL)) { return *v; }
141 break;
142 case PosVel::GetStaticDescriptorCount() + 8: // Receiver clock drift StDev GAL [s/s]
143 if (const double* v = recvClk.driftStdDevFor(GAL)) { return *v; }
144 break;
145
146 case PosVel::GetStaticDescriptorCount() + 9: // Receiver clock bias GLO [s]
147 if (const double* v = recvClk.biasFor(GLO)) { return *v; }
148 break;
149 case PosVel::GetStaticDescriptorCount() + 10: // Receiver clock drift GLO [s/s]
150 if (const double* v = recvClk.driftFor(GLO)) { return *v; }
151 break;
152 case PosVel::GetStaticDescriptorCount() + 11: // Receiver clock bias StDev GLO [s]
153 if (const double* v = recvClk.biasStdDevFor(GLO)) { return *v; }
154 break;
155 case PosVel::GetStaticDescriptorCount() + 12: // Receiver clock drift StDev GLO [s/s]
156 if (const double* v = recvClk.driftStdDevFor(GLO)) { return *v; }
157 break;
158
159 case PosVel::GetStaticDescriptorCount() + 13: // Receiver clock bias BDS [s]
160 if (const double* v = recvClk.biasFor(BDS)) { return *v; }
161 break;
162 case PosVel::GetStaticDescriptorCount() + 14: // Receiver clock drift BDS [s/s]
163 if (const double* v = recvClk.driftFor(BDS)) { return *v; }
164 break;
165 case PosVel::GetStaticDescriptorCount() + 15: // Receiver clock bias StDev BDS [s]
166 if (const double* v = recvClk.biasStdDevFor(BDS)) { return *v; }
167 break;
168 case PosVel::GetStaticDescriptorCount() + 16: // Receiver clock drift StDev BDS [s/s]
169 if (const double* v = recvClk.driftStdDevFor(BDS)) { return *v; }
170 break;
171
172 case PosVel::GetStaticDescriptorCount() + 17: // Receiver clock bias QZSS [s]
173 if (const double* v = recvClk.biasFor(QZSS)) { return *v; }
174 break;
175 case PosVel::GetStaticDescriptorCount() + 18: // Receiver clock drift QZSS [s/s]
176 if (const double* v = recvClk.driftFor(QZSS)) { return *v; }
177 break;
178 case PosVel::GetStaticDescriptorCount() + 19: // Receiver clock bias StDev QZSS [s]
179 if (const double* v = recvClk.biasStdDevFor(QZSS)) { return *v; }
180 break;
181 case PosVel::GetStaticDescriptorCount() + 20: // Receiver clock drift StDev QZSS [s/s]
182 if (const double* v = recvClk.driftStdDevFor(QZSS)) { return *v; }
183 break;
184
185 case PosVel::GetStaticDescriptorCount() + 21: // Receiver clock bias IRNSS [s]
186 if (const double* v = recvClk.biasFor(IRNSS)) { return *v; }
187 break;
188 case PosVel::GetStaticDescriptorCount() + 22: // Receiver clock drift IRNSS [s/s]
189 if (const double* v = recvClk.driftFor(IRNSS)) { return *v; }
190 break;
191 case PosVel::GetStaticDescriptorCount() + 23: // Receiver clock bias StDev IRNSS [s]
192 if (const double* v = recvClk.biasStdDevFor(IRNSS)) { return *v; }
193 break;
194 case PosVel::GetStaticDescriptorCount() + 24: // Receiver clock drift StDev IRNSS [s/s]
195 if (const double* v = recvClk.driftStdDevFor(IRNSS)) { return *v; }
196 break;
197
198 case PosVel::GetStaticDescriptorCount() + 25: // Receiver clock bias SBAS [s]
199 if (const double* v = recvClk.biasFor(SBAS)) { return *v; }
200 break;
201 case PosVel::GetStaticDescriptorCount() + 26: // Receiver clock drift SBAS [s/s]
202 if (const double* v = recvClk.driftFor(SBAS)) { return *v; }
203 break;
204 case PosVel::GetStaticDescriptorCount() + 27: // Receiver clock bias StDev SBAS [s]
205 if (const double* v = recvClk.biasStdDevFor(SBAS)) { return *v; }
206 break;
207 case PosVel::GetStaticDescriptorCount() + 28: // Receiver clock drift StDev SBAS [s/s]
208 if (const double* v = recvClk.driftStdDevFor(SBAS)) { return *v; }
209 break;
210 case PosVel::GetStaticDescriptorCount() + 29: // HDOP
211 return HDOP;
212 case PosVel::GetStaticDescriptorCount() + 30: // VDOP
213 return VDOP;
214 case PosVel::GetStaticDescriptorCount() + 31: // PDOP
215 return PDOP;
216 default:
217 return std::nullopt;
218 }
219 return std::nullopt;
220 }
221
222 /// @brief Returns a vector of data descriptors for the dynamic data
223 [[nodiscard]] std::vector<std::string> dynamicDataDescriptors() const override
224 {
225 std::vector<std::string> descriptors;
226 descriptors.reserve(interFrequencyBias.size() * 2 + satData.size() * 2);
227
228 for (const auto& bias : interFrequencyBias)
229 {
230 descriptors.push_back(fmt::format("{} Inter-freq bias [s]", bias.first));
231 descriptors.push_back(fmt::format("{} Inter-freq bias StDev [s]", bias.first));
232 }
233 for (const auto& [satId, satData] : satData)
234 {
235 descriptors.push_back(fmt::format("{} Elevation [deg]", satId));
236 descriptors.push_back(fmt::format("{} Azimuth [deg]", satId));
237 // descriptors.push_back(fmt::format("{} Satellite clock bias [s]", satData.first));
238 // descriptors.push_back(fmt::format("{} Satellite clock drift [s/s]", satData.first));
239 // descriptors.push_back(fmt::format("{} SatPos ECEF X [m]", satData.first));
240 // descriptors.push_back(fmt::format("{} SatPos ECEF Y [m]", satData.first));
241 // descriptors.push_back(fmt::format("{} SatPos ECEF Z [m]", satData.first));
242 // descriptors.push_back(fmt::format("{} SatPos Latitude [deg]", satData.first));
243 // descriptors.push_back(fmt::format("{} SatPos Longitude [deg]", satData.first));
244 // descriptors.push_back(fmt::format("{} SatPos Altitude [m]", satData.first));
245 // descriptors.push_back(fmt::format("{} SatVel ECEF X [m/s]", satData.first));
246 // descriptors.push_back(fmt::format("{} SatVel ECEF Y [m/s]", satData.first));
247 // descriptors.push_back(fmt::format("{} SatVel ECEF Z [m/s]", satData.first));
248 }
249
250 return descriptors;
251 }
252
253 /// @brief Get the value for the descriptor
254 /// @return Value if in the observation
255 [[nodiscard]] std::optional<double> getDynamicDataAt(const std::string& descriptor) const override
256 {
257 for (const auto& bias : interFrequencyBias)
258 {
259 if (descriptor == fmt::format("{} Inter-freq bias [s]", bias.first)) { return bias.second.value; }
260 if (descriptor == fmt::format("{} Inter-freq bias StDev [s]", bias.first)) { return bias.second.stdDev; }
261 }
262 for (const auto& [satId, satData] : satData)
263 {
264 if (descriptor == fmt::format("{} Elevation [deg]", satId)) { return rad2deg(satData.satElevation); }
265 if (descriptor == fmt::format("{} Azimuth [deg]", satId)) { return rad2deg(satData.satAzimuth); }
266 // if (descriptor == fmt::format("{} Satellite clock bias [s]", satData.first)) { return satData.second.satClock.bias; }
267 // if (descriptor == fmt::format("{} Satellite clock drift [s/s]", satData.first)) { return satData.second.satClock.drift; }
268 // if (descriptor == fmt::format("{} SatPos ECEF X [m]", satData.first)) { return satData.second.e_satPos.x(); }
269 // if (descriptor == fmt::format("{} SatPos ECEF Y [m]", satData.first)) { return satData.second.e_satPos.y(); }
270 // if (descriptor == fmt::format("{} SatPos ECEF Z [m]", satData.first)) { return satData.second.e_satPos.z(); }
271 // if (descriptor == fmt::format("{} SatPos Latitude [deg]", satData.first)) { return rad2deg(satData.second.lla_satPos.x()); }
272 // if (descriptor == fmt::format("{} SatPos Longitude [deg]", satData.first)) { return rad2deg(satData.second.lla_satPos.y()); }
273 // if (descriptor == fmt::format("{} SatPos Altitude [m]", satData.first)) { return satData.second.lla_satPos.z(); }
274 // if (descriptor == fmt::format("{} SatVel ECEF X [m/s]", satData.first)) { return satData.second.e_satVel.x(); }
275 // if (descriptor == fmt::format("{} SatVel ECEF Y [m/s]", satData.first)) { return satData.second.e_satVel.y(); }
276 // if (descriptor == fmt::format("{} SatVel ECEF Z [m/s]", satData.first)) { return satData.second.e_satVel.z(); }
277 }
278 return std::nullopt;
279 }
280
281 /// @brief Returns a vector of data descriptors and values for the dynamic data
282 [[nodiscard]] std::vector<std::pair<std::string, double>> getDynamicData() const override
283 {
284 std::vector<std::pair<std::string, double>> dynData;
285 dynData.reserve(interFrequencyBias.size() * 2 + satData.size() * 2);
286
287 for (const auto& bias : interFrequencyBias)
288 {
289 dynData.emplace_back(fmt::format("{} Inter-freq bias [s]", bias.first), bias.second.value);
290 dynData.emplace_back(fmt::format("{} Inter-freq bias StDev [s]", bias.first), bias.second.stdDev);
291 }
292 for (const auto& [satId, satData] : satData)
293 {
294 dynData.emplace_back(fmt::format("{} Elevation [deg]", satId), rad2deg(satData.satElevation));
295 dynData.emplace_back(fmt::format("{} Azimuth [deg]", satId), rad2deg(satData.satAzimuth));
296 // dynData.emplace_back(fmt::format("{} Satellite clock bias [s]", satData.first), satData.second.satClock.bias);
297 // dynData.emplace_back(fmt::format("{} Satellite clock drift [s/s]", satData.first), satData.second.satClock.drift);
298 // dynData.emplace_back(fmt::format("{} SatPos ECEF X [m]", satData.first), satData.second.e_satPos.x());
299 // dynData.emplace_back(fmt::format("{} SatPos ECEF Y [m]", satData.first), satData.second.e_satPos.y());
300 // dynData.emplace_back(fmt::format("{} SatPos ECEF Z [m]", satData.first), satData.second.e_satPos.z());
301 // dynData.emplace_back(fmt::format("{} SatPos Latitude [deg]", satData.first), rad2deg(satData.second.lla_satPos.x()));
302 // dynData.emplace_back(fmt::format("{} SatPos Longitude [deg]", satData.first), rad2deg(satData.second.lla_satPos.y()));
303 // dynData.emplace_back(fmt::format("{} SatPos Altitude [m]", satData.first), satData.second.lla_satPos.z());
304 // dynData.emplace_back(fmt::format("{} SatVel ECEF X [m/s]", satData.first), satData.second.e_satVel.x());
305 // dynData.emplace_back(fmt::format("{} SatVel ECEF Y [m/s]", satData.first), satData.second.e_satVel.y());
306 // dynData.emplace_back(fmt::format("{} SatVel ECEF Z [m/s]", satData.first), satData.second.e_satVel.z());
307 }
308 return dynData;
309 }
310
311 // --------------------------------------------------------- Public Members ------------------------------------------------------------
312
313 /// Amount of satellites used for the calculation
314 size_t nSatellites = 0;
315 /// Amount of pseudorange measurements used to calculate the position solution
316 size_t nMeasPsr = 0;
317 /// Amount of doppler measurements used to calculate the velocity solution
318 size_t nMeasDopp = 0;
319 /// Amount of Parameters estimated in this epoch
320 size_t nParam = 0;
321
322 /// HDOP value
323 double HDOP = std::nan("");
324 /// VDOP value
325 double VDOP = std::nan("");
326 /// PDOP value
327 double PDOP = std::nan("");
328
329 /// Estimated receiver clock parameter
330 ReceiverClock recvClk{ {} };
331
332 /// Inter-frequency biases
333 std::unordered_map<Frequency, UncertainValue<double>> interFrequencyBias;
334
335 /// Satellite specific data
336 struct SatData
337 {
338 double satElevation = 0.0; ///< Satellite Elevation [rad]
339 double satAzimuth = 0.0; ///< Satellite Azimuth [rad]
340 };
341
342 /// Extended data for each satellite
343 std::vector<std::pair<SatId, SatData>> satData;
344
345 /// @brief Adds an event to the event list
346 /// @param event Event string
347 void addEvent(const std::string& event) { _events.push_back(event); }
348
349 private:
350 /// Standard deviation of Position in ECEF coordinates [m]
351 Eigen::Vector3d _e_positionStdev = Eigen::Vector3d::Zero() * std::nan("");
352 /// Standard deviation of Position in local navigation frame coordinates [m]
353 Eigen::Vector3d _n_positionStdev = Eigen::Vector3d::Zero() * std::nan("");
354
355 /// Standard deviation of Velocity in earth coordinates [m/s]
356 Eigen::Vector3d _e_velocityStdev = Eigen::Vector3d::Zero() * std::nan("");
357 /// Standard deviation of Velocity in navigation coordinates [m/s]
358 Eigen::Vector3d _n_velocityStdev = Eigen::Vector3d::Zero() * std::nan("");
359
360 /// Covariance matrix in ECEF coordinates
361 std::optional<KeyedMatrixXd<SPP::States::StateKeyType, SPP::States::StateKeyType>> _e_covarianceMatrix;
362
363 /// Covariance matrix in local navigation coordinates
364 std::optional<KeyedMatrixXd<SPP::States::StateKeyType, SPP::States::StateKeyType>> _n_covarianceMatrix;
365 };
366
367 } // namespace NAV
368