36 if (ImGui::BeginTable(fmt::format(
"Satellites {}",
id).c_str(), 2,
37 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit
38 | ImGuiTableFlags_NoHostExtendX))
40 for (
const auto& [satSys, satsRecv] : satsReceived)
42 ImGui::TableNextColumn();
43 ImGui::TextUnformatted(fmt::format(
"{:<4}", satSys).c_str());
45 ImGui::TextUnformatted(fmt::format(
"(# {:2})", satsRecv.size()).c_str());
47 ImGui::TableNextColumn();
49 for (
const SatId& satId : satsRecv)
51 if (satId.satSys != satSys) {
continue; }
53 bool sameLine = printed != 0 && (printed % 7) != 0;
57 ImGui::SetCursorPosX(ImGui::GetCursorPosX() - ImGui::GetStyle().ItemSpacing.x);
58 ImGui::TextUnformatted(
", ");
60 ImGui::SetCursorPosX(ImGui::GetCursorPosX() - ImGui::GetStyle().ItemSpacing.x);
62 auto sat = std::ranges::find_if(
satData, [&](
const std::pair<SatId, RtkSolution::SatData>&
satData) {
74 if (isFiltered) { ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled)); }
75 else if (isUnused) { ImGui::PushStyleColor(ImGuiCol_Text, ImColor(115, 147, 179).Value); }
77 ImGui::TextUnformatted(fmt::format(
"{} ({:2.0f}°)", satId,
rad2deg(sat->second.satElevation)).c_str());
79 if (isFiltered || isUnused) { ImGui::PopStyleColor(); }
81 if (ImGui::IsItemHovered())
85 ImGui::BeginTooltip();
86 if (std::ranges::any_of(
filtered.frequencyFilter,
88 return satSigId.toSatId() == satId;
89 })) { ImGui::TextUnformatted(
"Signals excluded due to Frequency filter"); }
90 if (std::ranges::any_of(
filtered.codeFilter,
92 return satSigId.toSatId() == satId;
93 })) { ImGui::TextUnformatted(
"Signals excluded due to Code filter"); }
94 if (std::ranges::any_of(
filtered.excludedSatellites,
96 return satSigId.toSatId() == satId;
97 })) { ImGui::TextUnformatted(
"Satellite excluded due to satellite exclusion list"); }
98 if (std::ranges::any_of(
filtered.tempExcludedSignal,
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"); }
128 else if (isUnused) { ImGui::SetTooltip(
"Removed due to outlier check"); }
143 bool colorCycleSlips,
144 bool colorPivotChanges,
145 const char*
id)
const
147 std::array<bool, GnssObs::ObservationType_COUNT> hasObsType{};
148 std::map<SatelliteSystem, size_t> obsCount;
149 std::map<SatelliteSystem, std::set<SatId>> satellites;
152 hasObsType.at(obs.obsType) =
true;
153 auto satId = obs.satSigId.toSatId();
154 obsCount[satId.satSys]++;
155 satellites[satId.satSys].insert(satId);
157 auto obsTypeCount =
static_cast<int>(std::ranges::count(hasObsType,
true));
159 if (ImGui::BeginTable(fmt::format(
"Pivot sats {}",
id).c_str(), obsTypeCount + 1,
160 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit
161 | ImGuiTableFlags_NoHostExtendX))
163 ImGui::TableSetupColumn(
"");
165 if (hasObsType.at(obsType))
169 ImGui::TableSetupColumn(fmt::format(
"{} #{} ({} sats)", desc,
nObservations.at(obsType),
173 else { ImGui::TableSetupColumn(desc); }
180 ImGui::TableHeadersRow();
182 for (
const auto& [satSys, sats] : satellites)
184 ImGui::TableNextRow();
185 ImGui::TableNextColumn();
186 ImGui::TextUnformatted(fmt::format(
"{}", satSys).c_str());
190 ImGui::TextUnformatted(fmt::format(
"#{:3}", obsCount.at(satSys)).c_str());
191 if (ImGui::IsItemHovered()) { ImGui::SetTooltip(
"Number of signals"); }
193 ImGui::TextUnformatted(fmt::format(
"S{:3}", sats.size()).c_str());
194 if (ImGui::IsItemHovered()) { ImGui::SetTooltip(
"Number of satellites"); }
199 if (!hasObsType.at(o)) {
continue; }
200 ImGui::TableSetColumnIndex(
static_cast<int>(o + 1));
206 if (code.getFrequency().getSatSys() != satSys) {
continue; }
210 if (obs.satSigId.code != code || obs.obsType != o) {
continue; }
212 bool sameLine = printed != 0 && (printed % 2) != 0;
216 ImGui::SetCursorPosX(ImGui::GetCursorPosX() - ImGui::GetStyle().ItemSpacing.x);
217 if (lastFreq != code.
getFrequency()) { ImGui::PushStyleColor(ImGuiCol_Text, ImColor(243, 156, 18).Value); }
218 ImGui::TextUnformatted(
", ");
219 if (lastFreq != code.
getFrequency()) { ImGui::PopStyleColor(); }
221 ImGui::SetCursorPosX(ImGui::GetCursorPosX() - ImGui::GetStyle().ItemSpacing.x);
223 auto sat = std::ranges::find_if(
satData, [&](
const std::pair<SatId, RtkSolution::SatData>&
satData) {
224 return satData.first == obs.satSigId.toSatId();
245 [&obs](
const std::pair<CycleSlipDetector::Result, std::string>& cycleSlip) {
246 if (
const auto* s = std::get_if<CycleSlipDetector::CycleSlipLossOfLockIndicator>(&cycleSlip.first))
248 return s->signal == obs.satSigId;
250 if (
const auto* s = std::get_if<CycleSlipDetector::CycleSlipSingleFrequency>(&cycleSlip.first))
252 return s->signal == obs.satSigId;
254 if (
const auto* s = std::get_if<CycleSlipDetector::CycleSlipDualFrequency>(&cycleSlip.first))
256 return s->signals.front() == obs.satSigId || s->signals.back() == obs.satSigId;
264 if (colorNotUsed && isFiltered) { ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled)); }
265 else if (colorNotUsed && isUnused) { ImGui::PushStyleColor(ImGuiCol_Text, ImColor(115, 147, 179).Value); }
266 else if (colorCycleSlips && isCycleSlip) { ImGui::PushStyleColor(ImGuiCol_Text, ImColor(255, 191, 0).Value); }
267 else if (colorCycleSlips && isNewlyEstimated) { ImGui::PushStyleColor(ImGuiCol_Text, ImColor(245, 245, 220).Value); }
268 else if (colorPivotChanges && pivChanged) { ImGui::PushStyleColor(ImGuiCol_Text, ImColor(255, 191, 0).Value); }
269 else if (colorPivots && isPivot) { ImGui::PushStyleColor(ImGuiCol_Text, ImColor(80, 200, 120).Value); }
271 ImGui::TextUnformatted(fmt::format(
"{} ({:2.0f}°)", obs.satSigId,
rad2deg(sat->second.satElevation)).c_str());
273 if ((colorPivots && isPivot)
274 || (colorNotUsed && isFiltered)
275 || (colorNotUsed && isUnused)
276 || (colorCycleSlips && (isCycleSlip || isNewlyEstimated))
277 || (colorPivotChanges && pivChanged)) { ImGui::PopStyleColor(); }
279 if (ImGui::IsItemHovered())
281 if (isPivot && !colorPivotChanges)
283 ImGui::BeginTooltip();
284 ImGui::TextUnformatted(
"Pivot");
289 ImGui::BeginTooltip();
290 if (std::ranges::any_of(
filtered.frequencyFilter,
292 return satSigId == obs.satSigId;
293 })) { ImGui::TextUnformatted(
"Signal excluded due to Frequency filter"); }
294 else if (std::ranges::any_of(
filtered.codeFilter,
296 return satSigId == obs.satSigId;
297 })) { ImGui::TextUnformatted(
"Signal excluded due to Code filter"); }
298 else if (std::ranges::any_of(
filtered.excludedSatellites,
300 return satSigId == obs.satSigId;
301 })) { ImGui::TextUnformatted(
"Signal excluded due to satellite exclusion list"); }
302 else if (std::ranges::any_of(
filtered.tempExcludedSignal,
304 return satSigId == obs.satSigId;
305 })) { ImGui::TextUnformatted(
"Signal temporarily excluded"); }
306 else if (std::ranges::any_of(
filtered.notAllReceiversObserved,
308 return satSigId == obs.satSigId;
309 })) { ImGui::TextUnformatted(
"Signal not observed by all receivers"); }
310 else if (std::ranges::any_of(
filtered.singleObservation,
312 return satSigId == obs.satSigId;
313 })) { ImGui::TextUnformatted(
"No second signal for double difference."); }
314 else if (std::ranges::any_of(
filtered.noPseudorangeMeasurement,
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,
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;
326 sig !=
filtered.elevationMaskTriggered.end())
328 ImGui::TextUnformatted(fmt::format(
"Satellite triggered elevation mask (elevation {:2.0f}°)",
rad2deg(sig->second)).c_str());
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;
334 sig !=
filtered.snrMaskTriggered.end())
336 ImGui::TextUnformatted(fmt::format(
"Signal triggered SNR mask (SNR {:.0f} [dBHz])", sig->second).c_str());
342 if (
auto outlier = std::ranges::find_if(
outliers, [&obs](
const Outlier& outlier) {
343 return outlier.
satSigId == obs.satSigId && outlier.
obsType == obs.obsType;
347 ImGui::BeginTooltip();
348 ImGui::TextUnformatted(fmt::format(
"Outlier: {}", outlier->
type).c_str());
353 ImGui::BeginTooltip();
354 ImGui::TextUnformatted(
"Cannot calc double difference with single observation");
360 ImGui::BeginTooltip();
361 ImGui::TextUnformatted(fmt::format(
"{}: {}", cycleSlip->second, cycleSlip->first).c_str());
364 else if (isNewlyEstimated)
366 ImGui::BeginTooltip();
367 ImGui::TextUnformatted(
"Signal is newly estimated this epoch");
372 ImGui::BeginTooltip();
373 ImGui::TextUnformatted(fmt::format(
"{}",
changedPivotSatellites.at(std::make_pair(code, obs.obsType))).c_str());
393 ImGui::BeginTooltip();
394 ImGui::TextUnformatted(fmt::format(
"Meas. innovation: {:.2g}",
measInnovation(key)).c_str());
411 std::set<SatSigId> pivotSats;
414 pivotSats.insert(amb.pivotSatSigId);
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))
421 for (
const auto& pivotSatSigId : pivotSats)
423 ImGui::TableSetupColumn(fmt::format(
"{}", pivotSatSigId).c_str());
425 ImGui::TableHeadersRow();
427 bool addedEntry =
true;
428 for (
size_t row = 0; addedEntry; row++)
432 for (
const auto& pivotSatSigId : pivotSats)
437 if (amb.pivotSatSigId != pivotSatSigId) {
continue; }
438 if (i++ < row) {
continue; }
440 if (!addedEntry) { ImGui::TableNextRow(); }
441 ImGui::TableSetColumnIndex(p);
442 ImGui::TextUnformatted(fmt::format(
"{}", amb.satSigId).c_str());
444 ImGui::TextUnformatted(fmt::format(
"V {:7.1f}", amb.value.value).c_str());
445 if (ImGui::IsItemHovered()) { ImGui::SetTooltip(
"Value [cycles]"); }
447 ImGui::TextUnformatted(fmt::format(
"S {:7.1e}", amb.value.stdDev).c_str());
448 if (ImGui::IsItemHovered()) { ImGui::SetTooltip(
"Standard deviation [cycles]"); }
464 ImGui::BulletText(
"%s", fmt::format(
"Solution Type: {}Partial Fix {} / {}",
472 ImGui::BulletText(
"%s", fmt::format(
"Solution Type: {}",
solType).c_str());
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());
478 std::map<SatelliteSystem, std::unordered_set<SatId>> satsReceived;
481 satsReceived[obs.satSigId.toSatId().satSys].insert(obs.satSigId.toSatId());
483 size_t nSatellitesReceived = 0;
484 for (
const auto& satRecv : satsReceived) { nSatellitesReceived += satRecv.second.size(); }
486 ImGui::SetNextItemOpen(detailView, firstOpen ? ImGuiCond_Always : ImGuiCond_Once);
487 if (ImGui::TreeNode(fmt::format(
"Satellites: used {:3d} / {:3d} received",
nSatellites, nSatellitesReceived).c_str()))
493 ImGui::SetNextItemOpen(detailView, firstOpen ? ImGuiCond_Always : ImGuiCond_Once);
510 ImGui::SetNextItemOpen(detailView, firstOpen ? ImGuiCond_Always : ImGuiCond_Once);
511 if (ImGui::TreeNode(fmt::format(
"NIS Outlier check: {} observables removed",
nisRemovedCnt).c_str()))
514 ImGui::BulletText(
"%s", fmt::format(
"Final NIS {:.2g} {} {:.2g} r2",
522 ImGui::BulletText(
"NIS Outlier check not triggered");
526 ImGui::SetNextItemOpen(detailView, firstOpen ? ImGuiCond_Always : ImGuiCond_Once);
527 if (ImGui::TreeNode(fmt::format(
"Measurement innovation: Largest {:.2g}, Mean {:.2g}",
532 auto showLargestAndMean = [&]<
typename T>(
const char* desc,
const char* unit) {
534 return std::holds_alternative<T>(key);
537 std::vector<RTK::Meas::MeasKeyTypes> keys;
540 if (std::holds_alternative<T>(key)) { keys.push_back(key); }
542 ImGui::BulletText(
"%s", fmt::format(
"{} Largest {:.2g}, Mean {:.2g} [{}]",
554 if (ImGui::TreeNode(
"Vector"))
562 if (ImGui::TreeNode(fmt::format(
"Ambiguities: {}",
ambiguityDD_br.size()).c_str()))