0.5.0
Loading...
Searching...
No Matches
RtkSolution.cpp
Go to the documentation of this file.
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 RtkSolution.cpp
10/// @brief RTK Solution data
11/// @author T. Topp (topp@ins.uni-stuttgart.de)
12/// @date 2024-07-17
13
14#include "RtkSolution.hpp"
15#include <algorithm>
16#include <cstddef>
17#include <imgui.h>
18#include <numeric>
19#include <set>
20#include <string>
21#include <unordered_set>
22#include <utility>
23#include <variant>
32#include <fmt/core.h>
33
34void NAV::RtkSolution::guiTooltipSatellites(const std::map<SatelliteSystem, std::unordered_set<SatId>>& satsReceived, const char* id) const
35{
36 if (ImGui::BeginTable(fmt::format("Satellites {}", id).c_str(), 2,
37 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit
38 | ImGuiTableFlags_NoHostExtendX))
39 {
40 for (const auto& [satSys, satsRecv] : satsReceived)
41 {
42 ImGui::TableNextColumn();
43 ImGui::TextUnformatted(fmt::format("{:<4}", satSys).c_str());
44 ImGui::SameLine();
45 ImGui::TextUnformatted(fmt::format("(# {:2})", satsRecv.size()).c_str());
46
47 ImGui::TableNextColumn();
48 size_t printed = 0;
49 for (const SatId& satId : satsRecv)
50 {
51 if (satId.satSys != satSys) { continue; }
52
53 bool sameLine = printed != 0 && (printed % 7) != 0;
54 if (sameLine)
55 {
56 ImGui::SameLine();
57 ImGui::SetCursorPosX(ImGui::GetCursorPosX() - ImGui::GetStyle().ItemSpacing.x);
58 ImGui::TextUnformatted(", ");
59 ImGui::SameLine();
60 ImGui::SetCursorPosX(ImGui::GetCursorPosX() - ImGui::GetStyle().ItemSpacing.x);
61 }
62 auto sat = std::ranges::find_if(satData, [&](const std::pair<SatId, RtkSolution::SatData>& satData) {
63 return satData.first == satId;
64 });
65 bool isUnused = std::ranges::find_if(observableUsed, [&](const Observable& used) {
66 return satId == used.satSigId.toSatId();
67 })
68 == observableUsed.end();
69 bool isFiltered = std::ranges::find_if(observableFiltered, [&](const Observable& used) {
70 return satId == used.satSigId.toSatId();
71 })
72 == observableFiltered.end();
73
74 if (isFiltered) { ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled)); } // Gray
75 else if (isUnused) { ImGui::PushStyleColor(ImGuiCol_Text, ImColor(115, 147, 179).Value); } // Blue Gray
76
77 ImGui::TextUnformatted(fmt::format("{} ({:2.0f}°)", satId, rad2deg(sat->second.satElevation)).c_str());
78
79 if (isFiltered || isUnused) { ImGui::PopStyleColor(); }
80
81 if (ImGui::IsItemHovered())
82 {
83 if (isFiltered)
84 {
85 ImGui::BeginTooltip();
86 if (std::ranges::any_of(filtered.frequencyFilter,
87 [&satId](const SatSigId& satSigId) {
88 return satSigId.toSatId() == satId;
89 })) { ImGui::TextUnformatted("Signals excluded due to Frequency filter"); }
90 if (std::ranges::any_of(filtered.codeFilter,
91 [&satId](const SatSigId& satSigId) {
92 return satSigId.toSatId() == satId;
93 })) { ImGui::TextUnformatted("Signals excluded due to Code filter"); }
94 if (std::ranges::any_of(filtered.excludedSatellites,
95 [&satId](const SatSigId& satSigId) {
96 return satSigId.toSatId() == satId;
97 })) { ImGui::TextUnformatted("Satellite excluded due to satellite exclusion list"); }
98 if (std::ranges::any_of(filtered.tempExcludedSignal,
99 [&satId](const SatSigId& satSigId) {
100 return satSigId.toSatId() == satId;
101 })) { ImGui::TextUnformatted("Signals temporarily excluded"); }
102 if (std::ranges::any_of(filtered.notAllReceiversObserved,
103 [&satId](const SatSigId& satSigId) {
104 return satSigId.toSatId() == satId;
105 })) { ImGui::TextUnformatted("Signals not observed by all receivers"); }
106 if (std::ranges::any_of(filtered.singleObservation,
107 [&satId](const SatSigId& satSigId) {
108 return satSigId.toSatId() == satId;
109 })) { ImGui::TextUnformatted("No second signal for double difference."); }
110 if (std::ranges::any_of(filtered.noPseudorangeMeasurement,
111 [&satId](const SatSigId& satSigId) {
112 return satSigId.toSatId() == satId;
113 })) { ImGui::TextUnformatted("Signals without pseudorange measurement"); }
114 if (std::ranges::any_of(filtered.navigationDataMissing,
115 [&satId](const SatSigId& satSigId) {
116 return satSigId.toSatId() == satId;
117 })) { ImGui::TextUnformatted("Satellite without navigation data"); }
118 if (std::ranges::any_of(filtered.elevationMaskTriggered,
119 [&satId](const std::pair<SatSigId, double>& satSigId) {
120 return satSigId.first.toSatId() == satId;
121 })) { ImGui::TextUnformatted("Satellite triggered elevation mask"); }
122 if (std::ranges::any_of(filtered.snrMaskTriggered,
123 [&satId](const std::pair<SatSigId, double>& satSigId) {
124 return satSigId.first.toSatId() == satId;
125 })) { ImGui::TextUnformatted("Signals triggered SNR mask"); }
126 ImGui::EndTooltip();
127 }
128 else if (isUnused) { ImGui::SetTooltip("Removed due to outlier check"); }
129 }
130
131 printed++;
132 }
133 }
134
135 ImGui::EndTable();
136 }
137}
138
139void NAV::RtkSolution::guiTooltipObservationTable(const std::multiset<RtkSolution::Observable>& observables,
140 bool showSatCounts,
141 bool colorPivots,
142 bool colorNotUsed,
143 bool colorCycleSlips,
144 bool colorPivotChanges,
145 const char* id) const
146{
147 std::array<bool, GnssObs::ObservationType_COUNT> hasObsType{};
148 std::map<SatelliteSystem, size_t> obsCount;
149 std::map<SatelliteSystem, std::set<SatId>> satellites;
150 for (const RtkSolution::Observable& obs : observables)
151 {
152 hasObsType.at(obs.obsType) = true;
153 auto satId = obs.satSigId.toSatId();
154 obsCount[satId.satSys]++;
155 satellites[satId.satSys].insert(satId);
156 }
157 auto obsTypeCount = static_cast<int>(std::ranges::count(hasObsType, true));
158
159 if (ImGui::BeginTable(fmt::format("Pivot sats {}", id).c_str(), obsTypeCount + 1,
160 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit
161 | ImGuiTableFlags_NoHostExtendX))
162 {
163 ImGui::TableSetupColumn("");
164 auto headerColumn = [&](const char* desc, const GnssObs::ObservationType& obsType) {
165 if (hasObsType.at(obsType))
166 {
167 if (showSatCounts)
168 {
169 ImGui::TableSetupColumn(fmt::format("{} #{} ({} sats)", desc, nObservations.at(obsType),
171 .c_str());
172 }
173 else { ImGui::TableSetupColumn(desc); }
174 }
175 };
176 headerColumn("Pseudorange", GnssObs::Pseudorange);
177 headerColumn("Carrier", GnssObs::Carrier);
178 headerColumn("Doppler", GnssObs::Doppler);
179
180 ImGui::TableHeadersRow();
181
182 for (const auto& [satSys, sats] : satellites)
183 {
184 ImGui::TableNextRow();
185 ImGui::TableNextColumn();
186 ImGui::TextUnformatted(fmt::format("{}", satSys).c_str());
187
188 if (showSatCounts)
189 {
190 ImGui::TextUnformatted(fmt::format("#{:3}", obsCount.at(satSys)).c_str());
191 if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Number of signals"); }
192
193 ImGui::TextUnformatted(fmt::format("S{:3}", sats.size()).c_str());
194 if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Number of satellites"); }
195 }
196
197 for (size_t o = 0; o < GnssObs::ObservationType_COUNT; o++)
198 {
199 if (!hasObsType.at(o)) { continue; }
200 ImGui::TableSetColumnIndex(static_cast<int>(o + 1));
201
202 size_t printed = 0;
203 Frequency lastFreq = Freq_None;
204 for (const auto& code : Code::GetAll())
205 {
206 if (code.getFrequency().getSatSys() != satSys) { continue; }
207
208 for (const RtkSolution::Observable& obs : observables)
209 {
210 if (obs.satSigId.code != code || obs.obsType != o) { continue; }
211
212 bool sameLine = printed != 0 && (printed % 2) != 0;
213 if (sameLine)
214 {
215 ImGui::SameLine();
216 ImGui::SetCursorPosX(ImGui::GetCursorPosX() - ImGui::GetStyle().ItemSpacing.x);
217 if (lastFreq != code.getFrequency()) { ImGui::PushStyleColor(ImGuiCol_Text, ImColor(243, 156, 18).Value); } // Orange
218 ImGui::TextUnformatted(", ");
219 if (lastFreq != code.getFrequency()) { ImGui::PopStyleColor(); }
220 ImGui::SameLine();
221 ImGui::SetCursorPosX(ImGui::GetCursorPosX() - ImGui::GetStyle().ItemSpacing.x);
222 }
223 auto sat = std::ranges::find_if(satData, [&](const std::pair<SatId, RtkSolution::SatData>& satData) {
224 return satData.first == obs.satSigId.toSatId();
225 });
226
227 bool isPivot = std::ranges::find_if(pivots, [&](const Observable& piv) {
228 return piv.satSigId == obs.satSigId && piv.obsType == obs.obsType;
229 })
230 != pivots.end();
231 bool isFiltered = std::ranges::find_if(observableFiltered, [&](const Observable& used) {
232 return obs.satSigId == used.satSigId && obs.obsType == used.obsType;
233 })
234 == observableFiltered.end();
235 bool isUnused = std::ranges::find_if(observableUsed, [&](const Observable& used) {
236 return obs.satSigId == used.satSigId && obs.obsType == used.obsType;
237 })
238 == observableUsed.end();
239 bool isNewlyEstimated = std::ranges::find_if(newEstimatedAmbiguity, [&](const SatSigId& satSigId) {
240 return obs.satSigId == satSigId && obs.obsType == GnssObs::Carrier;
241 })
242 != newEstimatedAmbiguity.end();
243
244 auto cycleSlip = std::ranges::find_if(cycleSlipDetectorResult,
245 [&obs](const std::pair<CycleSlipDetector::Result, std::string>& cycleSlip) {
246 if (const auto* s = std::get_if<CycleSlipDetector::CycleSlipLossOfLockIndicator>(&cycleSlip.first))
247 {
248 return s->signal == obs.satSigId;
249 }
250 if (const auto* s = std::get_if<CycleSlipDetector::CycleSlipSingleFrequency>(&cycleSlip.first))
251 {
252 return s->signal == obs.satSigId;
253 }
254 if (const auto* s = std::get_if<CycleSlipDetector::CycleSlipDualFrequency>(&cycleSlip.first))
255 {
256 return s->signals.front() == obs.satSigId || s->signals.back() == obs.satSigId;
257 }
258 return false;
259 });
260 bool isCycleSlip = obs.obsType == GnssObs::Carrier && cycleSlip != cycleSlipDetectorResult.end();
261
262 bool pivChanged = isPivot && changedPivotSatellites.contains(std::make_pair(code, obs.obsType));
263
264 if (colorNotUsed && isFiltered) { ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled)); } // Gray
265 else if (colorNotUsed && isUnused) { ImGui::PushStyleColor(ImGuiCol_Text, ImColor(115, 147, 179).Value); } // Blue Gray
266 else if (colorCycleSlips && isCycleSlip) { ImGui::PushStyleColor(ImGuiCol_Text, ImColor(255, 191, 0).Value); } // Yellow
267 else if (colorCycleSlips && isNewlyEstimated) { ImGui::PushStyleColor(ImGuiCol_Text, ImColor(245, 245, 220).Value); } // Beige
268 else if (colorPivotChanges && pivChanged) { ImGui::PushStyleColor(ImGuiCol_Text, ImColor(255, 191, 0).Value); } // Yellow
269 else if (colorPivots && isPivot) { ImGui::PushStyleColor(ImGuiCol_Text, ImColor(80, 200, 120).Value); } // Green
270
271 ImGui::TextUnformatted(fmt::format("{} ({:2.0f}°)", obs.satSigId, rad2deg(sat->second.satElevation)).c_str());
272
273 if ((colorPivots && isPivot)
274 || (colorNotUsed && isFiltered)
275 || (colorNotUsed && isUnused)
276 || (colorCycleSlips && (isCycleSlip || isNewlyEstimated))
277 || (colorPivotChanges && pivChanged)) { ImGui::PopStyleColor(); }
278
279 if (ImGui::IsItemHovered())
280 {
281 if (isPivot && !colorPivotChanges)
282 {
283 ImGui::BeginTooltip();
284 ImGui::TextUnformatted("Pivot");
285 ImGui::EndTooltip();
286 }
287 if (isFiltered)
288 {
289 ImGui::BeginTooltip();
290 if (std::ranges::any_of(filtered.frequencyFilter,
291 [&obs](const SatSigId& satSigId) {
292 return satSigId == obs.satSigId;
293 })) { ImGui::TextUnformatted("Signal excluded due to Frequency filter"); }
294 else if (std::ranges::any_of(filtered.codeFilter,
295 [&obs](const SatSigId& satSigId) {
296 return satSigId == obs.satSigId;
297 })) { ImGui::TextUnformatted("Signal excluded due to Code filter"); }
298 else if (std::ranges::any_of(filtered.excludedSatellites,
299 [&obs](const SatSigId& satSigId) {
300 return satSigId == obs.satSigId;
301 })) { ImGui::TextUnformatted("Signal excluded due to satellite exclusion list"); }
302 else if (std::ranges::any_of(filtered.tempExcludedSignal,
303 [&obs](const SatSigId& satSigId) {
304 return satSigId == obs.satSigId;
305 })) { ImGui::TextUnformatted("Signal temporarily excluded"); }
306 else if (std::ranges::any_of(filtered.notAllReceiversObserved,
307 [&obs](const SatSigId& satSigId) {
308 return satSigId == obs.satSigId;
309 })) { ImGui::TextUnformatted("Signal not observed by all receivers"); }
310 else if (std::ranges::any_of(filtered.singleObservation,
311 [&obs](const SatSigId& satSigId) {
312 return satSigId == obs.satSigId;
313 })) { ImGui::TextUnformatted("No second signal for double difference."); }
314 else if (std::ranges::any_of(filtered.noPseudorangeMeasurement,
315 [&obs](const SatSigId& satSigId) {
316 return satSigId == obs.satSigId;
317 })) { ImGui::TextUnformatted("Signal excluded because no pseudorange measurement to calculate satellite position"); }
318 else if (std::ranges::any_of(filtered.navigationDataMissing,
319 [&obs](const SatSigId& satSigId) {
320 return satSigId == obs.satSigId;
321 })) { ImGui::TextUnformatted("Signal excluded because no navigation data"); }
322 else if (auto sig = std::ranges::find_if(filtered.elevationMaskTriggered,
323 [&obs](const std::pair<SatSigId, double>& satSigId) {
324 return satSigId.first == obs.satSigId;
325 });
326 sig != filtered.elevationMaskTriggered.end())
327 {
328 ImGui::TextUnformatted(fmt::format("Satellite triggered elevation mask (elevation {:2.0f}°)", rad2deg(sig->second)).c_str());
329 }
330 else if (auto sig = std::ranges::find_if(filtered.snrMaskTriggered,
331 [&obs](const std::pair<SatSigId, double>& satSigId) {
332 return satSigId.first == obs.satSigId;
333 });
334 sig != filtered.snrMaskTriggered.end())
335 {
336 ImGui::TextUnformatted(fmt::format("Signal triggered SNR mask (SNR {:.0f} [dBHz])", sig->second).c_str());
337 }
338 ImGui::EndTooltip();
339 }
340 else if (isUnused)
341 {
342 if (auto outlier = std::ranges::find_if(outliers, [&obs](const Outlier& outlier) {
343 return outlier.satSigId == obs.satSigId && outlier.obsType == obs.obsType;
344 });
345 outlier != outliers.end())
346 {
347 ImGui::BeginTooltip();
348 ImGui::TextUnformatted(fmt::format("Outlier: {}", outlier->type).c_str());
349 ImGui::EndTooltip();
350 }
351 else
352 {
353 ImGui::BeginTooltip();
354 ImGui::TextUnformatted("Cannot calc double difference with single observation");
355 ImGui::EndTooltip();
356 }
357 }
358 if (isCycleSlip)
359 {
360 ImGui::BeginTooltip();
361 ImGui::TextUnformatted(fmt::format("{}: {}", cycleSlip->second, cycleSlip->first).c_str());
362 ImGui::EndTooltip();
363 }
364 else if (isNewlyEstimated)
365 {
366 ImGui::BeginTooltip();
367 ImGui::TextUnformatted("Signal is newly estimated this epoch");
368 ImGui::EndTooltip();
369 }
370 if (pivChanged)
371 {
372 ImGui::BeginTooltip();
373 ImGui::TextUnformatted(fmt::format("{}", changedPivotSatellites.at(std::make_pair(code, obs.obsType))).c_str());
374 ImGui::EndTooltip();
375 }
377 switch (obs.obsType)
378 {
380 key = RTK::Meas::PsrDD{ obs.satSigId };
381 break;
382 case GnssObs::Carrier:
383 key = RTK::Meas::CarrierDD{ obs.satSigId };
384 break;
385 case GnssObs::Doppler:
386 key = RTK::Meas::DopplerDD{ obs.satSigId };
387 break;
389 break;
390 }
391 if (measInnovation.hasRow(key))
392 {
393 ImGui::BeginTooltip();
394 ImGui::TextUnformatted(fmt::format("Meas. innovation: {:.2g}", measInnovation(key)).c_str());
395 ImGui::EndTooltip();
396 }
397 }
398 printed++;
399 lastFreq = code.getFrequency();
400 }
401 }
402 }
403 }
404
405 ImGui::EndTable();
406 }
407}
408
410{
411 std::set<SatSigId> pivotSats;
412 for (const auto& amb : ambiguityDD_br)
413 {
414 pivotSats.insert(amb.pivotSatSigId);
415 }
416
417 if (ImGui::BeginTable(fmt::format("Ambiguities {}", id).c_str(), static_cast<int>(pivotSats.size()),
418 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit
419 | ImGuiTableFlags_NoHostExtendX))
420 {
421 for (const auto& pivotSatSigId : pivotSats)
422 {
423 ImGui::TableSetupColumn(fmt::format("{}", pivotSatSigId).c_str());
424 }
425 ImGui::TableHeadersRow();
426
427 bool addedEntry = true;
428 for (size_t row = 0; addedEntry; row++)
429 {
430 addedEntry = false;
431 int p = 0;
432 for (const auto& pivotSatSigId : pivotSats)
433 {
434 size_t i = 0;
435 for (const auto& amb : ambiguityDD_br)
436 {
437 if (amb.pivotSatSigId != pivotSatSigId) { continue; }
438 if (i++ < row) { continue; } // Skip entries to be in the correct row
439
440 if (!addedEntry) { ImGui::TableNextRow(); }
441 ImGui::TableSetColumnIndex(p);
442 ImGui::TextUnformatted(fmt::format("{}", amb.satSigId).c_str());
443
444 ImGui::TextUnformatted(fmt::format("V {:7.1f}", amb.value.value).c_str());
445 if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Value [cycles]"); }
446
447 ImGui::TextUnformatted(fmt::format("S {:7.1e}", amb.value.stdDev).c_str());
448 if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Standard deviation [cycles]"); }
449
450 addedEntry = true;
451 break;
452 }
453 p++;
454 }
455 }
456 ImGui::EndTable();
457 }
458}
459
460void NAV::RtkSolution::guiTooltip(bool detailView, bool firstOpen, const char* /* displayName */, const char* id, int* /* rootWindow */) const
461{
463 {
464 ImGui::BulletText("%s", fmt::format("Solution Type: {}Partial Fix {} / {}",
465 solType == SolutionType::RTK_Float ? "" : fmt::format("{} - ", solType),
467 ambiguityDD_br.size())
468 .c_str());
469 }
470 else
471 {
472 ImGui::BulletText("%s", fmt::format("Solution Type: {}", solType).c_str());
473 }
474
475 ImGui::BulletText("Rover obs time: %s", fmt::format("{}", insTime.toYMDHMS(GPST)).c_str());
476 ImGui::BulletText("Base obs time: %s", fmt::format("{}", baseTime.toYMDHMS(GPST)).c_str());
477
478 std::map<SatelliteSystem, std::unordered_set<SatId>> satsReceived;
479 for (const auto& obs : observableReceived)
480 {
481 satsReceived[obs.satSigId.toSatId().satSys].insert(obs.satSigId.toSatId());
482 }
483 size_t nSatellitesReceived = 0;
484 for (const auto& satRecv : satsReceived) { nSatellitesReceived += satRecv.second.size(); }
485
486 ImGui::SetNextItemOpen(detailView, firstOpen ? ImGuiCond_Always : ImGuiCond_Once);
487 if (ImGui::TreeNode(fmt::format("Satellites: used {:3d} / {:3d} received", nSatellites, nSatellitesReceived).c_str()))
488 {
489 guiTooltipSatellites(satsReceived, id);
490 ImGui::TreePop();
491 }
492
493 ImGui::SetNextItemOpen(detailView, firstOpen ? ImGuiCond_Always : ImGuiCond_Once);
494 if (ImGui::TreeNode(fmt::format("Observables: used {:3d} / {:3d} received", observableUsed.size(), observableReceived.size()).c_str()))
495 {
496 guiTooltipObservationTable(observableReceived, true, true, true, true, false, id);
497 ImGui::TreePop();
498 }
499
500 if (ImGui::TreeNode(fmt::format("Pivots: {} ({} changed)", pivots.size(), changedPivotSatellites.size()).c_str()))
501 {
502 guiTooltipObservationTable(pivots, false, false, true, false, true, id);
503 ImGui::TreePop();
504 }
505
507 {
508 if (nisResultInitial->triggered)
509 {
510 ImGui::SetNextItemOpen(detailView, firstOpen ? ImGuiCond_Always : ImGuiCond_Once);
511 if (ImGui::TreeNode(fmt::format("NIS Outlier check: {} observables removed", nisRemovedCnt).c_str()))
512 {
513 ImGui::BulletText("%s", fmt::format("Initial NIS {:.2g} > {:.2g} r2", nisResultInitial->NIS, nisResultInitial->r2).c_str());
514 ImGui::BulletText("%s", fmt::format("Final NIS {:.2g} {} {:.2g} r2",
515 nisResultFinal->NIS, nisResultFinal->NIS < nisResultFinal->r2 ? "<" : ">", nisResultFinal->r2)
516 .c_str());
517 ImGui::TreePop();
518 }
519 }
520 else
521 {
522 ImGui::BulletText("NIS Outlier check not triggered");
523 }
524 }
525
526 ImGui::SetNextItemOpen(detailView, firstOpen ? ImGuiCond_Always : ImGuiCond_Once);
527 if (ImGui::TreeNode(fmt::format("Measurement innovation: Largest {:.2g}, Mean {:.2g}",
528 measInnovation.rows() ? measInnovation(all).cwiseAbs().maxCoeff() : std::nan(""),
529 measInnovation.rows() ? measInnovation(all).mean() : std::nan(""))
530 .c_str()))
531 {
532 auto showLargestAndMean = [&]<typename T>(const char* desc, const char* unit) {
533 if (std::any_of(measInnovation.rowKeys().begin(), measInnovation.rowKeys().end(), [](const auto& key) {
534 return std::holds_alternative<T>(key);
535 }))
536 {
537 std::vector<RTK::Meas::MeasKeyTypes> keys;
538 for (const auto& key : measInnovation.rowKeys())
539 {
540 if (std::holds_alternative<T>(key)) { keys.push_back(key); }
541 }
542 ImGui::BulletText("%s", fmt::format("{} Largest {:.2g}, Mean {:.2g} [{}]",
543 desc,
544 measInnovation(keys).rows() ? measInnovation(keys).cwiseAbs().maxCoeff() : std::nan(""),
545 measInnovation(keys).rows() ? measInnovation(keys).mean() : std::nan(""),
546 unit)
547 .c_str());
548 }
549 };
550 showLargestAndMean.operator()<RTK::Meas::PsrDD>("Pseudorange:", "m");
551 showLargestAndMean.operator()<RTK::Meas::CarrierDD>("Carrier: ", "m");
552 showLargestAndMean.operator()<RTK::Meas::DopplerDD>("Doppler: ", "m/s");
553
554 if (ImGui::TreeNode("Vector"))
555 {
556 gui::widgets::KeyedVectorView(fmt::format("Measurement innovation##{}", id).c_str(), &measInnovation, 300.0F);
557 ImGui::TreePop();
558 }
559 ImGui::TreePop();
560 }
561
562 if (ImGui::TreeNode(fmt::format("Ambiguities: {}", ambiguityDD_br.size()).c_str()))
563 {
565 ImGui::TreePop();
566 }
567}
Code definitions.
Combination of different cycle-slip detection algorithms.
Frequency definition for different satellite systems.
Keys for the RTK algorithm for use inside the KeyedMatrices.
GNSS Observation messages.
RTK Node/Algorithm output.
Structs identifying a unique satellite.
GNSS Satellite System.
static std::vector< Code > GetAll()
Returns a list with all possible codes.
Definition Code.cpp:757
Frequency definition for different satellite systems.
Definition Frequency.hpp:59
double getFrequency(int8_t num) const
Get the frequency in [Hz].
ObservationType
Observation types.
Definition GnssObs.hpp:37
@ Doppler
Doppler (Pseudorange rate)
Definition GnssObs.hpp:40
@ ObservationType_COUNT
Count.
Definition GnssObs.hpp:41
@ Carrier
Carrier-Phase.
Definition GnssObs.hpp:39
@ Pseudorange
Pseudorange.
Definition GnssObs.hpp:38
InsTime insTime
Time at which the message was received.
Definition NodeData.hpp:123
std::multiset< Observable > observableReceived
Observables available from receivers (only if double diff possible)
std::vector< AmbiguityDD > ambiguityDD_br
Double differenced ambiguities.
void guiTooltipSatellites(const std::map< SatelliteSystem, std::unordered_set< SatId > > &satsReceived, const char *id) const
Print a table for the satellites.
ObservationFilter::Filtered filtered
Signals filtered by the observation filter.
std::unordered_map< GnssObs::ObservationType, size_t > nObservations
Number of utilized observations (including pivot)
void guiTooltipAmbiguities(const char *id) const
Print a table for the ambiguities.
std::vector< std::pair< SatId, SatData > > satData
Extended data for each satellite.
std::unordered_map< GnssObs::ObservationType, size_t > nObservationsUniqueSatellite
Number of utilized observations (counted once for each satellite)
std::vector< SatSigId > newEstimatedAmbiguity
Newly estimated ambiguities.
InsTime baseTime
Time of the base observation used.
std::optional< size_t > nAmbiguitiesFixed
Number of Ambiguities fixed.
@ RTK_Float
RTK solution with floating point ambiguities.
size_t nisRemovedCnt
Amount of observations removed by NIS.
void guiTooltipObservationTable(const std::multiset< RtkSolution::Observable > &observables, bool showSatCounts, bool colorPivots, bool colorNotUsed, bool colorCycleSlips, bool colorPivotChanges, const char *id) const
Print an observation table to the GUI.
SolutionType solType
Type of th solution.
std::multiset< Observable > observableFiltered
Observables available from receivers, but filtered by GUI settings.
std::multiset< Observable > observableUsed
Observables used for the final solution.
std::multiset< Observable > pivots
List of pivot satellites.
KeyedVectorXd< RTK::Meas::MeasKeyTypes > measInnovation
𝐳 Measurement vector
std::vector< std::pair< CycleSlipDetector::Result, std::string > > cycleSlipDetectorResult
Cycle slip detector results and name of the receiver.
std::optional< KeyedKalmanFilter< double, RTK::States::StateKeyType, RTK::Meas::MeasKeyTypes >::NISResult > nisResultFinal
Normalized Innovation Squared (NIS) test result (last NIS iteration)
std::unordered_map< std::pair< Code, GnssObs::ObservationType >, PivotChange > changedPivotSatellites
List of pivot satellite changes.
std::vector< Outlier > outliers
List of found outliers.
std::optional< KeyedKalmanFilter< double, RTK::States::StateKeyType, RTK::Meas::MeasKeyTypes >::NISResult > nisResultInitial
Normalized Innovation Squared (NIS) test result (before removing anything)
size_t nSatellites
Amount of satellites used.
void guiTooltip(bool detailView, bool firstOpen, const char *displayName, const char *id, int *rootWindow) const override
Shows a GUI tooltip to look into details of the observation.
std::variant< PsrDD, CarrierDD, DopplerDD, States::AmbiguityDD > MeasKeyTypes
Alias for the measurement key type.
Definition Keys.hpp:98
void KeyedVectorView(const char *label, const KeyedVector< Scalar, RowKeyType, Rows > *matrix, float tableHeight=-1.0F, ImGuiTableFlags tableFlags=ImGuiTableFlags_Borders|ImGuiTableFlags_NoHostExtendX|ImGuiTableFlags_SizingFixedFit|ImGuiTableFlags_ScrollY)
Shows GUI elements to display the coefficients of a matrix.
@ GPST
GPS Time.
@ Freq_None
None.
Definition Frequency.hpp:27
static const internal::all_t all
Used to request all rows or columns in KeyedMatrices.
constexpr auto rad2deg(const T &rad)
Convert Radians to Degree.
Definition Units.hpp:39
Double differenced carrier-phase measurement phi_br^1s [m] (one for each satellite signal,...
Definition Keys.hpp:80
Double differenced range-rate (doppler) measurement d_br^1s [m/s] (one for each satellite signal,...
Definition Keys.hpp:89
Double differenced pseudorange measurement psr_br^1s [m] (one for each satellite signal,...
Definition Keys.hpp:71
SatSigId satSigId
Satellite Signal Id.
GnssObs::ObservationType obsType
Observation Type.
Outlier information.
GnssObs::ObservationType obsType
Observation Type.
SatSigId satSigId
Satellite Signal Id.
Identifies a satellite (satellite system and number)
Identifies a satellite signal (satellite frequency and number)
SatId toSatId() const
Returns a satellite identifier for the satellite signal.
Satellite System type.