| 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 | #include "RealTimeKinematic.hpp" | ||
| 10 | |||
| 11 | #include <array> | ||
| 12 | #include <algorithm> | ||
| 13 | #include <chrono> | ||
| 14 | #include <cmath> | ||
| 15 | #include <cstddef> | ||
| 16 | #include <cstdint> | ||
| 17 | #include <imgui.h> | ||
| 18 | #include <iterator> | ||
| 19 | #include <string> | ||
| 20 | #include <unordered_map> | ||
| 21 | #include <unordered_set> | ||
| 22 | #include <tuple> | ||
| 23 | #include <ranges> | ||
| 24 | #include <regex> | ||
| 25 | #include <utility> | ||
| 26 | #include <variant> | ||
| 27 | #include <vector> | ||
| 28 | #include <spdlog/common.h> | ||
| 29 | #include <spdlog/spdlog.h> | ||
| 30 | |||
| 31 | #include "Navigation/GNSS/Ambiguity/AmbiguityResolution.hpp" | ||
| 32 | #include "Navigation/GNSS/Ambiguity/CycleSlipDetector.hpp" | ||
| 33 | #include "Navigation/GNSS/Core/Code.hpp" | ||
| 34 | #include "Navigation/GNSS/Core/SatelliteIdentifier.hpp" | ||
| 35 | #include "Navigation/GNSS/Positioning/Observation.hpp" | ||
| 36 | #include "Navigation/GNSS/Positioning/RTK/Keys.hpp" | ||
| 37 | #include "Navigation/Math/KeyedLeastSquares.hpp" | ||
| 38 | #include "Navigation/Time/InsTime.hpp" | ||
| 39 | #include "Navigation/Time/TimeSystem.hpp" | ||
| 40 | #include "NodeData/GNSS/GnssObs.hpp" | ||
| 41 | #include "NodeData/GNSS/RtkSolution.hpp" | ||
| 42 | #include "internal/Node/Node.hpp" | ||
| 43 | #include "util/Json.hpp" | ||
| 44 | #include "util/Logger.hpp" | ||
| 45 | #include "util/Container/Vector.hpp" | ||
| 46 | #include "util/Container/UncertainValue.hpp" | ||
| 47 | |||
| 48 | #include "Navigation/Constants.hpp" | ||
| 49 | |||
| 50 | #include "internal/gui/NodeEditorApplication.hpp" | ||
| 51 | #include "internal/gui/widgets/HelpMarker.hpp" | ||
| 52 | #include "internal/gui/widgets/imgui_ex.hpp" | ||
| 53 | #include "internal/gui/widgets/InputWithUnit.hpp" | ||
| 54 | #include "internal/gui/widgets/EnumCombo.hpp" | ||
| 55 | |||
| 56 | #include <fmt/core.h> | ||
| 57 | #include <fmt/format.h> | ||
| 58 | #include "internal/FlowManager.hpp" | ||
| 59 | |||
| 60 | #include "NodeData/State/Pos.hpp" | ||
| 61 | |||
| 62 | #include "Navigation/GNSS/Ambiguity/internal/Search.hpp" | ||
| 63 | #include "Navigation/GNSS/Ambiguity/internal/Decorrelation.hpp" | ||
| 64 | #include "Navigation/GNSS/Core/Frequency.hpp" | ||
| 65 | #include "Navigation/GNSS/Functions.hpp" | ||
| 66 | #include "Navigation/Math/LeastSquares.hpp" | ||
| 67 | |||
| 68 | namespace States = NAV::RTK::States; | ||
| 69 | namespace Meas = NAV::RTK::Meas; | ||
| 70 | |||
| 71 | namespace NAV | ||
| 72 | { | ||
| 73 | |||
| 74 | 115 | RealTimeKinematic::RealTimeKinematic() | |
| 75 |
11/26✓ Branch 1 taken 115 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 115 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 115 times.
✗ Branch 9 not taken.
✓ Branch 12 taken 115 times.
✗ Branch 13 not taken.
✓ Branch 17 taken 115 times.
✗ Branch 18 not taken.
✓ Branch 21 taken 115 times.
✗ Branch 22 not taken.
✓ Branch 25 taken 115 times.
✗ Branch 26 not taken.
✓ Branch 29 taken 115 times.
✗ Branch 30 not taken.
✓ Branch 36 taken 115 times.
✗ Branch 37 not taken.
✓ Branch 39 taken 115 times.
✗ Branch 40 not taken.
✓ Branch 47 taken 115 times.
✗ Branch 48 not taken.
✗ Branch 57 not taken.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
✗ Branch 60 not taken.
|
345 | : Node(typeStatic()) |
| 76 | { | ||
| 77 | LOG_TRACE("{}: called", name); | ||
| 78 | |||
| 79 | 115 | _hasConfig = true; | |
| 80 | 115 | _guiConfigDefaultWindowSize = { 633, 670 }; | |
| 81 | |||
| 82 |
4/8✓ Branch 1 taken 115 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 115 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 115 times.
✓ Branch 9 taken 115 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
|
345 | CreateInputPin("Base Position", Pin::Type::Flow, { Pos::type() }, &RealTimeKinematic::recvBasePos, nullptr, 1); |
| 83 |
4/8✓ Branch 1 taken 115 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 115 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 115 times.
✓ Branch 9 taken 115 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
|
345 | CreateInputPin("GnssObs (Base)", Pin::Type::Flow, { GnssObs::type() }, &RealTimeKinematic::recvBaseGnssObs); |
| 84 |
4/8✓ Branch 1 taken 115 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 115 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 115 times.
✓ Branch 9 taken 115 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
|
345 | CreateInputPin("GnssObs (Rover)", Pin::Type::Flow, { GnssObs::type() }, &RealTimeKinematic::recvRoverGnssObs); |
| 85 |
1/2✓ Branch 1 taken 115 times.
✗ Branch 2 not taken.
|
115 | _dynamicInputPins.addPin(this); // GnssNavInfo |
| 86 | |||
| 87 |
5/10✓ Branch 2 taken 115 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 115 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 115 times.
✗ Branch 11 not taken.
✓ Branch 14 taken 115 times.
✓ Branch 15 taken 115 times.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
|
460 | CreateOutputPin(RtkSolution::type().c_str(), Pin::Type::Flow, { RtkSolution::type() }); |
| 88 | 575 | } | |
| 89 | |||
| 90 | 232 | RealTimeKinematic::~RealTimeKinematic() | |
| 91 | { | ||
| 92 | LOG_TRACE("{}: called", nameId()); | ||
| 93 | 232 | } | |
| 94 | |||
| 95 | 229 | std::string RealTimeKinematic::typeStatic() | |
| 96 | { | ||
| 97 |
1/2✓ Branch 1 taken 229 times.
✗ Branch 2 not taken.
|
458 | return "RealTimeKinematic - RTK"; |
| 98 | } | ||
| 99 | |||
| 100 | ✗ | std::string RealTimeKinematic::type() const | |
| 101 | { | ||
| 102 | ✗ | return typeStatic(); | |
| 103 | } | ||
| 104 | |||
| 105 | 114 | std::string RealTimeKinematic::category() | |
| 106 | { | ||
| 107 |
1/2✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
|
228 | return "Data Processor"; |
| 108 | } | ||
| 109 | |||
| 110 | ✗ | void RealTimeKinematic::guiConfig() | |
| 111 | { | ||
| 112 | ✗ | auto nSatColumnContent = [&](size_t pinIndex) -> bool { | |
| 113 | ✗ | if (auto gnssNavInfo = getInputValue<GnssNavInfo>(pinIndex)) | |
| 114 | { | ||
| 115 | ✗ | size_t usedSatNum = 0; | |
| 116 | ✗ | std::string usedSats; | |
| 117 | ✗ | std::string allSats; | |
| 118 | |||
| 119 | ✗ | std::string filler = ", "; | |
| 120 | ✗ | for (const auto& satellite : gnssNavInfo->v->satellites()) | |
| 121 | { | ||
| 122 | ✗ | if (_obsFilter.isSatelliteAllowed(satellite.first)) | |
| 123 | { | ||
| 124 | ✗ | usedSatNum++; | |
| 125 | ✗ | usedSats += (allSats.empty() ? "" : filler) + fmt::format("{}", satellite.first); | |
| 126 | } | ||
| 127 | ✗ | allSats += (allSats.empty() ? "" : filler) + fmt::format("{}", satellite.first); | |
| 128 | } | ||
| 129 | ✗ | ImGui::TextUnformatted(fmt::format("{} / {}", usedSatNum, gnssNavInfo->v->nSatellites()).c_str()); | |
| 130 | ✗ | if (ImGui::IsItemHovered()) | |
| 131 | { | ||
| 132 | ✗ | ImGui::SetTooltip("Used satellites: %s\n" | |
| 133 | "Avail satellites: %s", | ||
| 134 | usedSats.c_str(), allSats.c_str()); | ||
| 135 | } | ||
| 136 | ✗ | } | |
| 137 | ✗ | return false; | |
| 138 | ✗ | }; | |
| 139 | |||
| 140 | ✗ | if (_dynamicInputPins.ShowGuiWidgets(size_t(id), inputPins, this, { { .header = "# Sat", .content = nSatColumnContent } })) | |
| 141 | { | ||
| 142 | ✗ | flow::ApplyChanges(); | |
| 143 | } | ||
| 144 | |||
| 145 | ✗ | ImGui::SameLine(); | |
| 146 | ✗ | ImGui::BeginGroup(); | |
| 147 | ✗ | ImGui::PushFont(Application::MonoFont()); | |
| 148 | ✗ | auto totalSolutions = nFixSolutions + nFloatSolutions + nSingleSolutions; | |
| 149 | ✗ | ImGui::SetNextItemOpen(true, ImGuiCond_Always); | |
| 150 | ✗ | if (ImGui::TreeNode("Solution statistics:")) | |
| 151 | { | ||
| 152 | ✗ | ImGui::BulletText("Fix: %6.2f%% (%zu)", totalSolutions > 0 ? static_cast<double>(nFixSolutions) / static_cast<double>(totalSolutions) * 100.0 : 0.0, nFixSolutions); | |
| 153 | ✗ | ImGui::BulletText("Float: %6.2f%% (%zu)", totalSolutions > 0 ? static_cast<double>(nFloatSolutions) / static_cast<double>(totalSolutions) * 100.0 : 0.0, nFloatSolutions); | |
| 154 | ✗ | ImGui::BulletText("Single: %6.2f%% (%zu)", totalSolutions > 0 ? static_cast<double>(nSingleSolutions) / static_cast<double>(totalSolutions) * 100.0 : 0.0, nSingleSolutions); | |
| 155 | ✗ | ImGui::TreePop(); | |
| 156 | } | ||
| 157 | ✗ | if (ImGui::TreeNode(fmt::format("Cycle-slips: {}", _nCycleSlipsLLI + _nCycleSlipsSingle + _nCycleSlipsDual).c_str())) | |
| 158 | { | ||
| 159 | ✗ | ImGui::BulletText("LLI: %zu", _nCycleSlipsLLI); | |
| 160 | ✗ | ImGui::BulletText("Single Freq: %zu", _nCycleSlipsSingle); | |
| 161 | ✗ | ImGui::SameLine(); | |
| 162 | ✗ | gui::widgets::HelpMarker("Single Frequency detection is confirmed by\nthe dual frequency detector before accepted.\nIt then only counts towards the single frequency count though."); | |
| 163 | ✗ | ImGui::BulletText("Dual Freq: %zu", _nCycleSlipsDual); | |
| 164 | ✗ | ImGui::SameLine(); | |
| 165 | ✗ | gui::widgets::HelpMarker("Only cycle-slips which were not recognized by\nthe single frequency detector or had the LLI flag set."); | |
| 166 | ✗ | ImGui::TreePop(); | |
| 167 | } | ||
| 168 | ✗ | ImGui::BulletText("Pivot changes: %zu", _nPivotChange); | |
| 169 | ✗ | if (_kalmanFilter.isNISenabled()) { ImGui::BulletText("NIS triggered: %zu", _nMeasExcludedNIS); } | |
| 170 | ✗ | ImGui::PopFont(); | |
| 171 | ✗ | ImGui::EndGroup(); | |
| 172 | |||
| 173 | ✗ | const float unitWidth = 120.0F * gui::NodeEditorApplication::windowFontRatio(); | |
| 174 | ✗ | const float itemWidth = 310.0F * gui::NodeEditorApplication::windowFontRatio(); | |
| 175 | ✗ | const float cWidth = itemWidth - ImGui::GetStyle().IndentSpacing; | |
| 176 | |||
| 177 | ✗ | if (_obsFilter.ShowGuiWidgets<ReceiverType>(nameId().c_str(), itemWidth)) | |
| 178 | { | ||
| 179 | ✗ | flow::ApplyChanges(); | |
| 180 | } | ||
| 181 | |||
| 182 | ✗ | if (_obsEstimator.ShowGuiWidgets<ReceiverType>(nameId().c_str(), itemWidth)) | |
| 183 | { | ||
| 184 | ✗ | flow::ApplyChanges(); | |
| 185 | } | ||
| 186 | |||
| 187 | ✗ | ImGui::SetNextItemOpen(true, ImGuiCond_FirstUseEver); | |
| 188 | ✗ | if (ImGui::TreeNode(fmt::format("Kalman Filter##{}", nameId()).c_str())) | |
| 189 | { | ||
| 190 | ✗ | if (_kalmanFilter.showKalmanFilterGUI(nameId().c_str(), itemWidth)) | |
| 191 | { | ||
| 192 | ✗ | flow::ApplyChanges(); | |
| 193 | } | ||
| 194 | |||
| 195 | ✗ | if (_kalmanFilter.isNISenabled()) | |
| 196 | { | ||
| 197 | ✗ | auto tmp = static_cast<int>(_maxRemoveOutlier); | |
| 198 | ✗ | ImGui::SetNextItemWidth(cWidth); | |
| 199 | ✗ | if (ImGui::InputIntL(fmt::format("Max. remove per epoch##{}", nameId()).c_str(), &tmp, 0)) | |
| 200 | { | ||
| 201 | ✗ | _maxRemoveOutlier = static_cast<size_t>(tmp); | |
| 202 | ✗ | flow::ApplyChanges(); | |
| 203 | } | ||
| 204 | ✗ | ImGui::SameLine(); | |
| 205 | ✗ | gui::widgets::HelpMarker("Maximum amount of observations to remove per epoch if the NIS triggers"); | |
| 206 | |||
| 207 | ✗ | tmp = static_cast<int>(_outlierRemoveEpochs); | |
| 208 | ✗ | ImGui::SetNextItemWidth(cWidth); | |
| 209 | ✗ | if (ImGui::InputIntL(fmt::format("Epochs to exclude outlier##{}", nameId()).c_str(), &tmp, 1)) | |
| 210 | { | ||
| 211 | ✗ | _outlierRemoveEpochs = static_cast<size_t>(tmp); | |
| 212 | ✗ | flow::ApplyChanges(); | |
| 213 | } | ||
| 214 | ✗ | ImGui::SameLine(); | |
| 215 | ✗ | gui::widgets::HelpMarker("Amount of epochs to exclude an outlier for after being flagged"); | |
| 216 | |||
| 217 | ✗ | tmp = static_cast<int>(_outlierMinSat); | |
| 218 | ✗ | ImGui::SetNextItemWidth(cWidth); | |
| 219 | ✗ | if (ImGui::InputIntL(fmt::format("Min. satellites for check##{}", nameId()).c_str(), &tmp, 1)) | |
| 220 | { | ||
| 221 | ✗ | _outlierMinSat = static_cast<size_t>(tmp); | |
| 222 | ✗ | flow::ApplyChanges(); | |
| 223 | } | ||
| 224 | ✗ | ImGui::SameLine(); | |
| 225 | ✗ | gui::widgets::HelpMarker("Minumum amount of satellites for doing a NIS check"); | |
| 226 | |||
| 227 | ✗ | tmp = static_cast<int>(_outlierMinPsrObsKeep); | |
| 228 | ✗ | ImGui::SetNextItemWidth(cWidth); | |
| 229 | ✗ | if (ImGui::InputIntL(fmt::format("# of pseudorange obs to keep##{}", nameId()).c_str(), &tmp, 1)) | |
| 230 | { | ||
| 231 | ✗ | _outlierMinPsrObsKeep = static_cast<size_t>(tmp); | |
| 232 | ✗ | flow::ApplyChanges(); | |
| 233 | } | ||
| 234 | ✗ | ImGui::SameLine(); | |
| 235 | ✗ | gui::widgets::HelpMarker("Minumum amount of pseudorange observables to leave when doing a NIS check"); | |
| 236 | |||
| 237 | ✗ | ImGui::SetNextItemWidth(cWidth); | |
| 238 | ✗ | if (ImGui::InputDoubleL(fmt::format("Max Position Var for NIS in startup##{}", size_t(id)).c_str(), &_outlierMaxPosVarStartup, 0, std::numeric_limits<double>::max(), 0.0, 0.0, "%.4f m")) | |
| 239 | { | ||
| 240 | ✗ | flow::ApplyChanges(); | |
| 241 | } | ||
| 242 | ✗ | ImGui::SameLine(); | |
| 243 | ✗ | gui::widgets::HelpMarker("Maximum position variance\nin order to attempt outlier removal during the startup phase."); | |
| 244 | } | ||
| 245 | ✗ | ImGui::SetNextItemOpen(false, ImGuiCond_FirstUseEver); | |
| 246 | ✗ | if (ImGui::TreeNode(fmt::format("Matrices##Kalman Filter {}", nameId()).c_str())) | |
| 247 | { | ||
| 248 | ✗ | _kalmanFilter.showKalmanFilterMatrixViews(nameId().c_str()); | |
| 249 | ✗ | ImGui::TreePop(); | |
| 250 | } | ||
| 251 | ✗ | ImGui::TreePop(); | |
| 252 | } | ||
| 253 | |||
| 254 | ✗ | ImGui::SetNextItemOpen(true, ImGuiCond_FirstUseEver); | |
| 255 | ✗ | if (ImGui::TreeNode(fmt::format("System/Process noise (Standard Deviations)##{}", size_t(id)).c_str())) | |
| 256 | { | ||
| 257 | ✗ | if (gui::widgets::InputDouble2WithUnit(fmt::format("Acceleration due to user motion (Hor/Ver)##{}", size_t(id)).c_str(), cWidth, unitWidth, _gui_stdevAccel.data(), _gui_stdevAccelUnits, "m/√(s^3)\0\0", "%.2e", ImGuiInputTextFlags_CharsScientific)) | |
| 258 | { | ||
| 259 | ✗ | LOG_DEBUG("{}: stdevAccel changed to horizontal {} and vertical {}", nameId(), _gui_stdevAccel.at(0), _gui_stdevAccel.at(1)); | |
| 260 | ✗ | LOG_DEBUG("{}: stdevAccelNoiseUnits changed to {}", nameId(), fmt::underlying(_gui_stdevAccelUnits)); | |
| 261 | ✗ | flow::ApplyChanges(); | |
| 262 | } | ||
| 263 | ✗ | if (gui::widgets::InputDoubleWithUnit(fmt::format("Ambiguity noise (carrier-phase bias) (float)##{}", size_t(id)).c_str(), | |
| 264 | cWidth, unitWidth, &_gui_ambiguityFloatProcessNoiseStDev, | ||
| 265 | ✗ | _gui_stdevAmbiguityFloatUnits, "cycle\0\0", | |
| 266 | 0.0, 0.0, "%.2e", ImGuiInputTextFlags_CharsScientific)) | ||
| 267 | { | ||
| 268 | ✗ | LOG_DEBUG("{}: ambiguityFloatProcessNoiseStDev changed to {}", nameId(), _gui_ambiguityFloatProcessNoiseStDev); | |
| 269 | ✗ | LOG_DEBUG("{}: stdevAmbiguityFloatUnits changed to {}", nameId(), fmt::underlying(_gui_stdevAmbiguityFloatUnits)); | |
| 270 | |||
| 271 | ✗ | switch (_gui_stdevAmbiguityFloatUnits) | |
| 272 | { | ||
| 273 | ✗ | case StdevAmbiguityUnits::Cycle: | |
| 274 | ✗ | _ambiguityFloatProcessNoiseVariance = std::pow(_gui_ambiguityFloatProcessNoiseStDev, 2); | |
| 275 | ✗ | break; | |
| 276 | } | ||
| 277 | |||
| 278 | ✗ | flow::ApplyChanges(); | |
| 279 | } | ||
| 280 | ✗ | if (gui::widgets::InputDoubleWithUnit(fmt::format("Ambiguity noise (carrier-phase bias) (fix)##{}", size_t(id)).c_str(), | |
| 281 | cWidth, unitWidth, &_gui_ambiguityFixProcessNoiseStDev, | ||
| 282 | ✗ | _gui_stdevAmbiguityFixUnits, "cycle\0\0", | |
| 283 | 0.0, 0.0, "%.2e", ImGuiInputTextFlags_CharsScientific)) | ||
| 284 | { | ||
| 285 | ✗ | LOG_DEBUG("{}: ambiguityFixProcessNoiseStDev changed to {}", nameId(), _gui_ambiguityFixProcessNoiseStDev); | |
| 286 | ✗ | LOG_DEBUG("{}: stdevAmbiguityFixUnits changed to {}", nameId(), fmt::underlying(_gui_stdevAmbiguityFixUnits)); | |
| 287 | |||
| 288 | ✗ | switch (_gui_stdevAmbiguityFixUnits) | |
| 289 | { | ||
| 290 | ✗ | case StdevAmbiguityUnits::Cycle: | |
| 291 | ✗ | _ambiguityFixProcessNoiseVariance = std::pow(_gui_ambiguityFixProcessNoiseStDev, 2); | |
| 292 | ✗ | break; | |
| 293 | } | ||
| 294 | |||
| 295 | ✗ | flow::ApplyChanges(); | |
| 296 | } | ||
| 297 | |||
| 298 | ✗ | ImGui::TreePop(); | |
| 299 | } | ||
| 300 | |||
| 301 | ✗ | ImGui::SetNextItemOpen(true, ImGuiCond_FirstUseEver); | |
| 302 | ✗ | if (ImGui::TreeNode(fmt::format("Ambiguity resolution##{}", size_t(id)).c_str())) | |
| 303 | { | ||
| 304 | ✗ | if (!_obsFilter.isObsTypeUsed(GnssObs::Carrier)) { ImGui::BeginDisabled(); } | |
| 305 | |||
| 306 | ✗ | ImGui::SetNextItemWidth(cWidth); | |
| 307 | ✗ | if (gui::widgets::EnumCombo(fmt::format("Strategy##{}", size_t(id)).c_str(), _ambiguityResolutionStrategy)) | |
| 308 | { | ||
| 309 | ✗ | LOG_DEBUG("{}: Ambiguity resolution strategy changed to {}", nameId(), NAV::to_string(_ambiguityResolutionStrategy)); | |
| 310 | ✗ | flow::ApplyChanges(); | |
| 311 | } | ||
| 312 | |||
| 313 | ✗ | if (GuiAmbiguityResolution(fmt::format("Ambiguity resolution##{}", size_t(id)).c_str(), _ambiguityResolutionParameters, itemWidth)) | |
| 314 | { | ||
| 315 | ✗ | LOG_DEBUG("{}: Ambiguity resolution parameters changed to [{}][{}]", nameId(), | |
| 316 | NAV::to_string(_ambiguityResolutionParameters.decorrelationAlgorithm), | ||
| 317 | NAV::to_string(_ambiguityResolutionParameters.searchAlgorithm)); | ||
| 318 | ✗ | flow::ApplyChanges(); | |
| 319 | } | ||
| 320 | |||
| 321 | ✗ | ImGui::SetNextItemWidth(cWidth); | |
| 322 | ✗ | if (ImGui::InputDoubleL(fmt::format("Max Position Var for AR##{}", size_t(id)).c_str(), &_maxPosVar, 0, std::numeric_limits<double>::max(), 0.0, 0.0, "%.4f m")) | |
| 323 | { | ||
| 324 | ✗ | flow::ApplyChanges(); | |
| 325 | } | ||
| 326 | ✗ | ImGui::SameLine(); | |
| 327 | ✗ | gui::widgets::HelpMarker("Maximum position variance\nin order to attempt ambiguity fixing."); | |
| 328 | |||
| 329 | ✗ | auto tmp = static_cast<int>(_nMinSatForAmbFix); | |
| 330 | ✗ | ImGui::SetNextItemWidth(cWidth); | |
| 331 | ✗ | if (ImGui::InputIntL(fmt::format("Min Sat for Fix##{}", size_t(id)).c_str(), &tmp, 0)) | |
| 332 | { | ||
| 333 | ✗ | _nMinSatForAmbFix = static_cast<size_t>(tmp); | |
| 334 | ✗ | flow::ApplyChanges(); | |
| 335 | } | ||
| 336 | ✗ | ImGui::SameLine(); | |
| 337 | ✗ | gui::widgets::HelpMarker("Minimum amount of satellites with carrier observations\nin order to attempt ambiguity fixing."); | |
| 338 | ✗ | if (_ambiguityResolutionStrategy == AmbiguityResolutionStrategy::FixAndHold) | |
| 339 | { | ||
| 340 | ✗ | tmp = static_cast<int>(_nMinSatForAmbHold); | |
| 341 | ✗ | ImGui::SetNextItemWidth(cWidth); | |
| 342 | ✗ | if (ImGui::InputIntL(fmt::format("Min Sat for Hold##{}", size_t(id)).c_str(), &tmp, 0)) | |
| 343 | { | ||
| 344 | ✗ | _nMinSatForAmbHold = static_cast<size_t>(tmp); | |
| 345 | ✗ | flow::ApplyChanges(); | |
| 346 | } | ||
| 347 | ✗ | ImGui::SameLine(); | |
| 348 | ✗ | gui::widgets::HelpMarker("Minimum amount of satellites with carrier observations\nin order to attempt ambiguity holding."); | |
| 349 | } | ||
| 350 | ✗ | if (ImGui::Checkbox(fmt::format("Apply fixed ambiguities with KF update##{}", size_t(id)).c_str(), &_applyFixedAmbiguitiesWithUpdate)) | |
| 351 | { | ||
| 352 | ✗ | flow::ApplyChanges(); | |
| 353 | } | ||
| 354 | ✗ | ImGui::SameLine(); | |
| 355 | ✗ | gui::widgets::HelpMarker("Make a Kalman Filter update with the fixed ambiguities when checked.\n" | |
| 356 | "Otherwise apply via\n" | ||
| 357 | "- a = a_fix\n" | ||
| 358 | "- b = b_float - Q_ba * Q_aa^-1 (a_fix - a_float)"); | ||
| 359 | |||
| 360 | ✗ | if (_applyFixedAmbiguitiesWithUpdate | |
| 361 | ✗ | && gui::widgets::InputDoubleWithUnit(fmt::format("Apply fixed ambiguities measurement noise##{}", size_t(id)).c_str(), | |
| 362 | cWidth, unitWidth, &_gui_ambFixUpdateStdDev, | ||
| 363 | ✗ | _gui_ambFixUpdateStdDevUnits, "cycle\0\0", | |
| 364 | 0.0, 0.0, "%.2e", ImGuiInputTextFlags_CharsScientific)) | ||
| 365 | { | ||
| 366 | ✗ | LOG_DEBUG("{}: ambFixUpdateStdDev changed to {}", nameId(), _gui_ambFixUpdateStdDev); | |
| 367 | ✗ | LOG_DEBUG("{}: ambFixUpdateStdDevUnits changed to {}", nameId(), fmt::underlying(_gui_ambFixUpdateStdDevUnits)); | |
| 368 | |||
| 369 | ✗ | switch (_gui_ambFixUpdateStdDevUnits) | |
| 370 | { | ||
| 371 | ✗ | case StdevAmbiguityUnits::Cycle: | |
| 372 | ✗ | _ambFixUpdateVariance = std::pow(_gui_ambFixUpdateStdDev, 2); | |
| 373 | ✗ | break; | |
| 374 | } | ||
| 375 | |||
| 376 | ✗ | flow::ApplyChanges(); | |
| 377 | } | ||
| 378 | |||
| 379 | ✗ | ImGui::SetNextItemOpen(true, ImGuiCond_FirstUseEver); | |
| 380 | ✗ | if (ImGui::TreeNode(fmt::format("Cycle-slip detection##Tree{}", size_t(id)).c_str())) | |
| 381 | { | ||
| 382 | ✗ | ImGui::SetNextItemOpen(true, ImGuiCond_FirstUseEver); | |
| 383 | ✗ | if (ImGui::TreeNode(fmt::format("Base##Tree{}", size_t(id)).c_str())) | |
| 384 | { | ||
| 385 | ✗ | if (CycleSlipDetectorGui(fmt::format("Cycle-slip detector Base##{}", size_t(id)).c_str(), _cycleSlipDetector[Base], 145.0F)) | |
| 386 | { | ||
| 387 | ✗ | flow::ApplyChanges(); | |
| 388 | } | ||
| 389 | ✗ | ImGui::TreePop(); | |
| 390 | } | ||
| 391 | ✗ | ImGui::SetNextItemOpen(true, ImGuiCond_FirstUseEver); | |
| 392 | ✗ | if (ImGui::TreeNode(fmt::format("Rover##Tree{}", size_t(id)).c_str())) | |
| 393 | { | ||
| 394 | ✗ | if (CycleSlipDetectorGui(fmt::format("Cycle-slip detector Rover##{}", size_t(id)).c_str(), _cycleSlipDetector[Rover], 145.0F)) | |
| 395 | { | ||
| 396 | ✗ | flow::ApplyChanges(); | |
| 397 | } | ||
| 398 | ✗ | ImGui::TreePop(); | |
| 399 | } | ||
| 400 | |||
| 401 | ✗ | ImGui::TreePop(); | |
| 402 | } | ||
| 403 | |||
| 404 | ✗ | if (!_obsFilter.isObsTypeUsed(GnssObs::Carrier)) { ImGui::EndDisabled(); } | |
| 405 | ✗ | ImGui::TreePop(); | |
| 406 | } | ||
| 407 | ✗ | if (ImGui::TreeNode(fmt::format("Misc##{}", size_t(id)).c_str())) | |
| 408 | { | ||
| 409 | ✗ | if (ImGui::Checkbox(fmt::format("Output a SPP solution if no base observation available##{}", size_t(id)).c_str(), &_calcSPPIfNoBase)) | |
| 410 | { | ||
| 411 | ✗ | flow::ApplyChanges(); | |
| 412 | } | ||
| 413 | ✗ | ImGui::SetNextItemWidth(cWidth); | |
| 414 | ✗ | if (ImGui::InputDoubleL(fmt::format("Max Time between rover & base obs##{}", size_t(id)).c_str(), &_maxTimeBetweenBaseRoverForRTK, 1e-3)) | |
| 415 | { | ||
| 416 | ✗ | flow::ApplyChanges(); | |
| 417 | } | ||
| 418 | |||
| 419 | ✗ | ImGui::TreePop(); | |
| 420 | } | ||
| 421 | |||
| 422 | ✗ | ImGui::SetNextItemOpen(true, ImGuiCond_FirstUseEver); | |
| 423 | ✗ | if (ImGui::TreeNode(fmt::format("Events##{}", size_t(id)).c_str())) | |
| 424 | { | ||
| 425 | ✗ | if (ImGui::Checkbox(fmt::format("Output State Change Events##{}", size_t(id)).c_str(), &_outputStateEvents)) | |
| 426 | { | ||
| 427 | ✗ | flow::ApplyChanges(); | |
| 428 | } | ||
| 429 | ✗ | ImGui::SetNextItemWidth(cWidth); | |
| 430 | ✗ | if (ImGui::InputText(fmt::format("Events Filter Regex##{}", size_t(id)).c_str(), &_eventFilterRegex)) | |
| 431 | { | ||
| 432 | ✗ | flow::ApplyChanges(); | |
| 433 | } | ||
| 434 | ✗ | if (!_events.empty()) | |
| 435 | { | ||
| 436 | ✗ | std::optional<std::regex> filter; | |
| 437 | try | ||
| 438 | { | ||
| 439 | ✗ | filter = std::regex(_eventFilterRegex, std::regex_constants::ECMAScript | std::regex_constants::icase); | |
| 440 | } | ||
| 441 | ✗ | catch (...) // NOLINT(bugprone-empty-catch) | |
| 442 | ✗ | {} | |
| 443 | |||
| 444 | ✗ | if (ImGui::BeginChild(fmt::format("Events list {}", size_t(id)).c_str(), ImVec2(0.0F, 600.0F), true)) | |
| 445 | { | ||
| 446 | ✗ | ImGui::PushFont(Application::MonoFont()); | |
| 447 | ✗ | for (size_t i = 0; i < _events.size(); i++) | |
| 448 | { | ||
| 449 | ✗ | if (!std::any_of(_events.at(i).second.begin(), _events.at(i).second.end(), [&](const std::string& text) { | |
| 450 | ✗ | return _eventFilterRegex.empty() || (filter && std::regex_search(text, *filter)); | |
| 451 | })) | ||
| 452 | { | ||
| 453 | ✗ | continue; | |
| 454 | } | ||
| 455 | |||
| 456 | ✗ | if (i != 0) { ImGui::Separator(); } | |
| 457 | ✗ | ImGui::SetNextItemOpen(true, ImGuiCond_Always); | |
| 458 | ✗ | if (ImGui::TreeNode(fmt::format("{} GPST", _events.at(i).first.toYMDHMS(GPST)).c_str())) | |
| 459 | { | ||
| 460 | ✗ | for (const auto& text : _events.at(i).second) | |
| 461 | { | ||
| 462 | ✗ | if (_eventFilterRegex.empty() || (filter && std::regex_search(text, *filter))) | |
| 463 | { | ||
| 464 | ✗ | ImGui::BulletText("%s", text.c_str()); | |
| 465 | } | ||
| 466 | } | ||
| 467 | ✗ | ImGui::TreePop(); | |
| 468 | } | ||
| 469 | } | ||
| 470 | ✗ | ImGui::PopFont(); | |
| 471 | } | ||
| 472 | ✗ | ImGui::EndChild(); | |
| 473 | ✗ | } | |
| 474 | ✗ | ImGui::TreePop(); | |
| 475 | } | ||
| 476 | ✗ | } | |
| 477 | |||
| 478 | ✗ | [[nodiscard]] json RealTimeKinematic::save() const | |
| 479 | { | ||
| 480 | LOG_TRACE("{}: called", nameId()); | ||
| 481 | |||
| 482 | ✗ | json j; | |
| 483 | |||
| 484 | ✗ | j["dynamicInputPins"] = _dynamicInputPins; | |
| 485 | |||
| 486 | ✗ | j["obsFilter"] = _obsFilter; | |
| 487 | ✗ | j["obsEstimator"] = _obsEstimator; | |
| 488 | ✗ | j["kalmanFilter"] = _kalmanFilter; | |
| 489 | ✗ | j["maxRemoveOutlier"] = _maxRemoveOutlier; | |
| 490 | ✗ | j["outlierRemoveEpochs"] = _outlierRemoveEpochs; | |
| 491 | ✗ | j["outlierMinSat"] = _outlierMinSat; | |
| 492 | ✗ | j["outlierMinPsrObsKeep"] = _outlierMinPsrObsKeep; | |
| 493 | ✗ | j["outlierMaxPosVarStartup"] = _outlierMaxPosVarStartup; | |
| 494 | ✗ | j["calcSPPIfNoBase"] = _calcSPPIfNoBase; | |
| 495 | ✗ | j["maxTimeBetweenBaseRoverForRTK"] = _maxTimeBetweenBaseRoverForRTK; | |
| 496 | |||
| 497 | ✗ | j["ambiguityResolutionParameters"] = _ambiguityResolutionParameters; | |
| 498 | ✗ | j["ambiguityResolutionStrategy"] = _ambiguityResolutionStrategy; | |
| 499 | ✗ | j["nMinSatForAmbFix"] = _nMinSatForAmbFix; | |
| 500 | ✗ | j["nMinSatForAmbHold"] = _nMinSatForAmbHold; | |
| 501 | ✗ | j["maxPosVar"] = _maxPosVar; | |
| 502 | ✗ | j["applyFixedAmbiguitiesWithUpdate"] = _applyFixedAmbiguitiesWithUpdate; | |
| 503 | ✗ | j["ambFixUpdateStdDev"] = _gui_ambFixUpdateStdDev; | |
| 504 | ✗ | j["ambFixUpdateStdDevUnits"] = _gui_ambFixUpdateStdDevUnits; | |
| 505 | |||
| 506 | ✗ | j["cycleSlipDetector"] = _cycleSlipDetector; | |
| 507 | ✗ | j["eventFilterRegex"] = _eventFilterRegex; | |
| 508 | ✗ | j["outputStateEvents"] = _outputStateEvents; | |
| 509 | |||
| 510 | ✗ | j["stdevAccelUnits"] = _gui_stdevAccelUnits; | |
| 511 | ✗ | j["stdevAccel"] = _gui_stdevAccel; | |
| 512 | ✗ | j["stdevAmbiguityFloatUnits"] = _gui_stdevAmbiguityFloatUnits; | |
| 513 | ✗ | j["ambiguityFloatProcessNoiseStDev"] = _gui_ambiguityFloatProcessNoiseStDev; | |
| 514 | ✗ | j["stdevAmbiguityFixUnits"] = _gui_stdevAmbiguityFixUnits; | |
| 515 | ✗ | j["ambiguityFixProcessNoiseStDev"] = _gui_ambiguityFixProcessNoiseStDev; | |
| 516 | |||
| 517 | ✗ | return j; | |
| 518 | ✗ | } | |
| 519 | |||
| 520 | 1 | void RealTimeKinematic::restore(json const& j) | |
| 521 | { | ||
| 522 | LOG_TRACE("{}: called", nameId()); | ||
| 523 | |||
| 524 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("dynamicInputPins")) { NAV::gui::widgets::from_json(j.at("dynamicInputPins"), _dynamicInputPins, this); } |
| 525 | |||
| 526 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("obsFilter")) { j.at("obsFilter").get_to(_obsFilter); } |
| 527 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("obsEstimator")) { j.at("obsEstimator").get_to(_obsEstimator); } |
| 528 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("kalmanFilter")) { j.at("kalmanFilter").get_to(_kalmanFilter); } |
| 529 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("maxRemoveOutlier")) { j.at("maxRemoveOutlier").get_to(_maxRemoveOutlier); } |
| 530 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("outlierRemoveEpochs")) { j.at("outlierRemoveEpochs").get_to(_outlierRemoveEpochs); } |
| 531 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("outlierMinSat")) { j.at("outlierMinSat").get_to(_outlierMinSat); } |
| 532 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("outlierMinPsrObsKeep")) { j.at("outlierMinPsrObsKeep").get_to(_outlierMinPsrObsKeep); } |
| 533 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("outlierMaxPosVarStartup")) { j.at("outlierMaxPosVarStartup").get_to(_outlierMaxPosVarStartup); } |
| 534 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("calcSPPIfNoBase")) { j.at("calcSPPIfNoBase").get_to(_calcSPPIfNoBase); } |
| 535 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("maxTimeBetweenBaseRoverForRTK")) { j.at("maxTimeBetweenBaseRoverForRTK").get_to(_maxTimeBetweenBaseRoverForRTK); } |
| 536 | |||
| 537 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("ambiguityResolutionParameters")) { j.at("ambiguityResolutionParameters").get_to(_ambiguityResolutionParameters); } |
| 538 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("ambiguityResolutionStrategy")) { j.at("ambiguityResolutionStrategy").get_to(_ambiguityResolutionStrategy); } |
| 539 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("nMinSatForAmbFix")) { j.at("nMinSatForAmbFix").get_to(_nMinSatForAmbFix); } |
| 540 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("nMinSatForAmbHold")) { j.at("nMinSatForAmbHold").get_to(_nMinSatForAmbHold); } |
| 541 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("maxPosVar")) { j.at("maxPosVar").get_to(_maxPosVar); } |
| 542 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("applyFixedAmbiguitiesWithUpdate")) { j.at("applyFixedAmbiguitiesWithUpdate").get_to(_applyFixedAmbiguitiesWithUpdate); } |
| 543 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("ambFixUpdateStdDevUnits")) { j.at("ambFixUpdateStdDevUnits").get_to(_gui_ambFixUpdateStdDevUnits); } |
| 544 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("ambFixUpdateStdDev")) |
| 545 | { | ||
| 546 | 1 | j.at("ambFixUpdateStdDev").get_to(_gui_ambFixUpdateStdDev); | |
| 547 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | switch (_gui_ambFixUpdateStdDevUnits) |
| 548 | { | ||
| 549 | 1 | case StdevAmbiguityUnits::Cycle: | |
| 550 | 1 | _ambFixUpdateVariance = std::pow(_gui_ambFixUpdateStdDev, 2); | |
| 551 | 1 | break; | |
| 552 | } | ||
| 553 | } | ||
| 554 | |||
| 555 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("cycleSlipDetector")) { j.at("cycleSlipDetector").get_to(_cycleSlipDetector); } |
| 556 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("eventFilterRegex")) { j.at("eventFilterRegex").get_to(_eventFilterRegex); } |
| 557 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("outputStateEvents")) { j.at("outputStateEvents").get_to(_outputStateEvents); } |
| 558 | |||
| 559 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("stdevAccelUnits")) { _gui_stdevAccelUnits = j.at("stdevAccelUnits"); } |
| 560 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("stdevAccel")) { _gui_stdevAccel = j.at("stdevAccel"); } |
| 561 | |||
| 562 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("stdevAmbiguityFloatUnits")) { j.at("stdevAmbiguityFloatUnits").get_to(_gui_stdevAmbiguityFloatUnits); } |
| 563 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("ambiguityFloatProcessNoiseStDev")) |
| 564 | { | ||
| 565 | 1 | j.at("ambiguityFloatProcessNoiseStDev").get_to(_gui_ambiguityFloatProcessNoiseStDev); | |
| 566 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | switch (_gui_stdevAmbiguityFloatUnits) |
| 567 | { | ||
| 568 | 1 | case StdevAmbiguityUnits::Cycle: | |
| 569 | 1 | _ambiguityFloatProcessNoiseVariance = std::pow(_gui_ambiguityFloatProcessNoiseStDev, 2); | |
| 570 | 1 | break; | |
| 571 | } | ||
| 572 | } | ||
| 573 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("stdevAmbiguityFixUnits")) { j.at("stdevAmbiguityFixUnits").get_to(_gui_stdevAmbiguityFixUnits); } |
| 574 | |||
| 575 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (j.contains("ambiguityFixProcessNoiseStDev")) |
| 576 | { | ||
| 577 | 1 | j.at("ambiguityFixProcessNoiseStDev").get_to(_gui_ambiguityFixProcessNoiseStDev); | |
| 578 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | switch (_gui_stdevAmbiguityFixUnits) |
| 579 | { | ||
| 580 | 1 | case StdevAmbiguityUnits::Cycle: | |
| 581 | 1 | _ambiguityFixProcessNoiseVariance = std::pow(_gui_ambiguityFixProcessNoiseStDev, 2); | |
| 582 | 1 | break; | |
| 583 | } | ||
| 584 | } | ||
| 585 | 1 | } | |
| 586 | |||
| 587 | 3 | bool RealTimeKinematic::initialize() | |
| 588 | { | ||
| 589 | LOG_TRACE("{}: called", nameId()); | ||
| 590 | |||
| 591 |
2/4✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
|
6 | if (std::all_of(inputPins.begin() + INPUT_PORT_INDEX_GNSS_NAV_INFO, inputPins.end(), [](const InputPin& inputPin) { return !inputPin.isPinLinked(); })) |
| 592 | { | ||
| 593 | ✗ | LOG_ERROR("{}: You need to connect a GNSS NavigationInfo provider", nameId()); | |
| 594 | ✗ | return false; | |
| 595 | } | ||
| 596 | |||
| 597 | 3 | _obsFilter.reset(); | |
| 598 | 3 | _events.clear(); | |
| 599 | 3 | _pivotSatellites.clear(); | |
| 600 | 3 | _lastUpdate.reset(); | |
| 601 | 3 | _dataInterval = 1; | |
| 602 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | _receiver = { { Receiver(static_cast<ReceiverType>(0), _obsFilter.getSystemFilter().toVector()), |
| 603 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
6 | Receiver(static_cast<ReceiverType>(1), _obsFilter.getSystemFilter().toVector()) } }; |
| 604 | 3 | _lastSolutionStatus = RtkSolution::SolutionType::RTK_Float; | |
| 605 | 3 | _ambiguitiesHold.clear(); | |
| 606 | 3 | nFixSolutions = 0; | |
| 607 | 3 | nFloatSolutions = 0; | |
| 608 | 3 | nSingleSolutions = 0; | |
| 609 | 3 | _nPivotChange = 0; | |
| 610 | 3 | _nCycleSlipsLLI = 0; | |
| 611 | 3 | _nCycleSlipsSingle = 0; | |
| 612 | 3 | _nCycleSlipsDual = 0; | |
| 613 | 3 | _nMeasExcludedNIS = 0; | |
| 614 | |||
| 615 | 3 | _outlierMaxPosVarStartupTriggered = false; | |
| 616 | 3 | _maxPosVarTriggered = false; | |
| 617 | 3 | _nMinSatForAmbFixTriggered = false; | |
| 618 | 3 | _nMinSatForAmbHoldTriggered = false; | |
| 619 | |||
| 620 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | _sppAlgorithm.reset(); |
| 621 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | _sppAlgorithm._obsFilter = _obsFilter; |
| 622 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | _sppAlgorithm._obsFilter.useObsType(GnssObs::Pseudorange); |
| 623 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | _sppAlgorithm._obsFilter.useObsType(GnssObs::Doppler); |
| 624 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | _sppAlgorithm._obsEstimator = _obsEstimator; |
| 625 | 3 | _sppAlgorithm._estimateInterFreqBiases = false; | |
| 626 | |||
| 627 | 3 | json j; | |
| 628 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | to_json(j, _kalmanFilter); |
| 629 |
1/2✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
|
6 | _kalmanFilter = KeyedKalmanFilterD<States::StateKeyType, |
| 630 | 3 | Meas::MeasKeyTypes>{ States::PosVel, {} }; | |
| 631 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | from_json(j, _kalmanFilter); |
| 632 | |||
| 633 |
3/6✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 3 times.
✗ Branch 10 not taken.
|
3 | _kalmanFilter.F.block<3>(States::Pos, States::Vel) = Eigen::Matrix3d::Identity(); |
| 634 | |||
| 635 |
3/4✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 3 times.
|
9 | for (auto& detector : _cycleSlipDetector) { detector.reset(); } |
| 636 | |||
| 637 | // 𝜎²_a Variance of the acceleration due to user motion in horizontal and vertical component in [m^2 / s^3] | ||
| 638 | 3 | std::array<double, 2> varAccel{}; | |
| 639 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | switch (_gui_stdevAccelUnits) |
| 640 | { | ||
| 641 | 3 | case StdevAccelUnits::m_sqrts3: // [m / √(s^3)] | |
| 642 |
2/4✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
|
3 | varAccel = { std::pow(_gui_stdevAccel.at(0), 2), std::pow(_gui_stdevAccel.at(1), 2) }; |
| 643 | 3 | break; | |
| 644 | } | ||
| 645 | LOG_DATA(" sigma2_accel = h: {}, v: {} [m^2 / s^3]", nameId(), varAccel.at(0), varAccel.at(1)); | ||
| 646 |
8/16✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 3 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 3 times.
✗ Branch 13 not taken.
✓ Branch 15 taken 3 times.
✗ Branch 16 not taken.
✓ Branch 18 taken 3 times.
✗ Branch 19 not taken.
✓ Branch 21 taken 3 times.
✗ Branch 22 not taken.
✓ Branch 24 taken 3 times.
✗ Branch 25 not taken.
|
3 | _kalmanFilter.W.block<3>(States::Vel, States::Vel).diagonal() << varAccel.at(0), varAccel.at(0), varAccel.at(1); |
| 647 | |||
| 648 |
2/4✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
|
6 | LOG_DEBUG("RealTimeKinematic initialized"); |
| 649 | |||
| 650 | 3 | return true; | |
| 651 | 12 | } | |
| 652 | |||
| 653 | 1 | void RealTimeKinematic::deinitialize() | |
| 654 | { | ||
| 655 | LOG_TRACE("{}: called", nameId()); | ||
| 656 | 1 | } | |
| 657 | |||
| 658 | 116 | void RealTimeKinematic::pinAddCallback(Node* node) | |
| 659 | { | ||
| 660 |
5/10✓ Branch 2 taken 116 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 116 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 116 times.
✗ Branch 11 not taken.
✓ Branch 14 taken 116 times.
✓ Branch 15 taken 116 times.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
|
464 | node->CreateInputPin(NAV::GnssNavInfo::type().c_str(), Pin::Type::Object, { NAV::GnssNavInfo::type() }); |
| 661 | 232 | } | |
| 662 | |||
| 663 | ✗ | void RealTimeKinematic::pinDeleteCallback(Node* node, size_t pinIdx) | |
| 664 | { | ||
| 665 | ✗ | node->DeleteInputPin(pinIdx); | |
| 666 | ✗ | } | |
| 667 | |||
| 668 | 69 | void RealTimeKinematic::addEventToGui(const std::shared_ptr<RtkSolution>& rtkSol, const std::string& text) | |
| 669 | { | ||
| 670 |
1/2✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
|
152 | auto iter = std::ranges::find_if(_events, [&](const auto& event) { return event.first == rtkSol->insTime; }); |
| 671 |
2/2✓ Branch 2 taken 4 times.
✓ Branch 3 taken 65 times.
|
69 | if (iter == _events.end()) |
| 672 | { | ||
| 673 |
4/8✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 4 times.
✓ Branch 9 taken 4 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
|
12 | _events.emplace_back(rtkSol->insTime, std::vector{ text }); |
| 674 | } | ||
| 675 | else | ||
| 676 | { | ||
| 677 |
1/2✓ Branch 2 taken 65 times.
✗ Branch 3 not taken.
|
65 | iter->second.emplace_back(text); |
| 678 | } | ||
| 679 |
1/2✓ Branch 2 taken 69 times.
✗ Branch 3 not taken.
|
69 | rtkSol->addEvent(text); |
| 680 | 73 | } | |
| 681 | |||
| 682 | 601 | void RealTimeKinematic::printObservations([[maybe_unused]] const Observations& observations) | |
| 683 | { | ||
| 684 | #if LOG_LEVEL <= LOG_LEVEL_DATA | ||
| 685 | unordered_map<SatId, std::pair<Frequency, Code>> satData; | ||
| 686 | unordered_map<SatSigId, std::set<GnssObs::ObservationType>> sigData; | ||
| 687 | for (const auto& obs : observations.signals) | ||
| 688 | { | ||
| 689 | satData[obs.first.toSatId()].first |= obs.first.freq(); | ||
| 690 | satData[obs.first.toSatId()].second |= obs.first.code; | ||
| 691 | for (size_t obsType = 0; obsType < GnssObs::ObservationType_COUNT; obsType++) | ||
| 692 | { | ||
| 693 | if (std::ranges::all_of(obs.second.recvObs, [&obsType](const auto& recvObs) { | ||
| 694 | return recvObs.second->obs.contains(static_cast<GnssObs::ObservationType>(obsType)); | ||
| 695 | })) | ||
| 696 | { | ||
| 697 | sigData[obs.first].insert(static_cast<GnssObs::ObservationType>(obsType)); | ||
| 698 | } | ||
| 699 | } | ||
| 700 | } | ||
| 701 | for ([[maybe_unused]] const auto& [satId, freqCode] : satData) | ||
| 702 | { | ||
| 703 | LOG_DATA("{}: [{}] on frequencies [{}] with codes [{}]", nameId(), satId, freqCode.first, freqCode.second); | ||
| 704 | for (const auto& [satSigId, obs] : sigData) | ||
| 705 | { | ||
| 706 | if (satSigId.toSatId() != satId) { continue; } | ||
| 707 | std::string str; | ||
| 708 | for (const auto& o : obs) | ||
| 709 | { | ||
| 710 | if (!str.empty()) { str += ", "; } | ||
| 711 | str += fmt::format("{}", o); | ||
| 712 | } | ||
| 713 | LOG_DATA("{}: [{}] has obs: {}", nameId(), satSigId.code, str); | ||
| 714 | } | ||
| 715 | } | ||
| 716 | #endif | ||
| 717 | 601 | } | |
| 718 | |||
| 719 | 1 | void RealTimeKinematic::recvBasePos(InputPin::NodeDataQueue& queue, size_t /* pinIdx */) | |
| 720 | { | ||
| 721 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | auto gnssObs = std::static_pointer_cast<const Pos>(queue.extract_front()); |
| 722 | LOG_DATA("{}: Received Base Position for [{}]", nameId(), gnssObs->insTime.toYMDHMS(GPST)); | ||
| 723 | |||
| 724 |
2/4✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
|
1 | if (_receiver[Base].e_posMarker.isZero()) |
| 725 | { | ||
| 726 |
1/2✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
1 | _receiver[Base].e_posMarker = gnssObs->e_position(); |
| 727 |
1/2✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
1 | _receiver[Base].lla_posMarker = gnssObs->lla_position(); |
| 728 | } | ||
| 729 | else | ||
| 730 | { | ||
| 731 | ✗ | _receiver[Base].e_posMarker = (_receiver[Base].e_posMarker + gnssObs->e_position()) / 2; | |
| 732 | ✗ | _receiver[Base].lla_posMarker = trafo::ecef2lla_WGS84(_receiver[Base].e_posMarker); | |
| 733 | } | ||
| 734 | |||
| 735 |
2/14✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✓ Branch 26 taken 1 times.
|
1 | if (_receiver[Base].gnssObs && _receiver[Rover].gnssObs && std::chrono::abs(_receiver[Base].gnssObs->insTime - _receiver[Rover].gnssObs->insTime) < std::chrono::milliseconds(10)) |
| 736 | { | ||
| 737 | ✗ | calcRealTimeKinematicSolution(); | |
| 738 | } | ||
| 739 | 1 | } | |
| 740 | |||
| 741 | 601 | void RealTimeKinematic::recvBaseGnssObs(InputPin::NodeDataQueue& queue, size_t /* pinIdx */) | |
| 742 | { | ||
| 743 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | _receiver[Base].gnssObs = std::static_pointer_cast<const GnssObs>(queue.extract_front()); |
| 744 | 601 | _baseObsReceivedThisEpoch = true; | |
| 745 | LOG_DATA("{}: Received Base GNSS Obs for [{}]", nameId(), _receiver[Base].gnssObs->insTime.toYMDHMS(GPST)); | ||
| 746 | |||
| 747 |
2/10✗ Branch 2 not taken.
✓ Branch 3 taken 601 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 601 times.
|
601 | if (_receiver[Rover].gnssObs && (_receiver[Base].gnssObs->insTime - _receiver[Rover].gnssObs->insTime) > std::chrono::milliseconds(500)) |
| 748 | { | ||
| 749 | LOG_TRACE("{}: Data gap between Base [{}] and Rover [{}] is {:.1f}s. Falling back to SPP for gaps larger than 0.5s", nameId(), | ||
| 750 | _receiver[Base].gnssObs->insTime.toYMDHMS(GPST), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), | ||
| 751 | static_cast<double>((_receiver[Base].gnssObs->insTime - _receiver[Rover].gnssObs->insTime).count())); | ||
| 752 | ✗ | if (auto rtkSol = calcFallbackSppSolution()) | |
| 753 | { | ||
| 754 | ✗ | nSingleSolutions++; | |
| 755 | ✗ | invokeCallbacks(OUTPUT_PORT_INDEX_RTKSOL, rtkSol); | |
| 756 | ✗ | } | |
| 757 | ✗ | _receiver[Rover].gnssObs = nullptr; | |
| 758 | } | ||
| 759 |
3/6✓ Branch 3 taken 601 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 601 times.
✗ Branch 8 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 601 times.
|
1202 | else if (!_receiver[Base].e_posMarker.isZero() && _receiver[Base].gnssObs && _receiver[Rover].gnssObs |
| 760 |
2/12✓ Branch 0 taken 601 times.
✗ Branch 1 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✓ Branch 20 taken 601 times.
|
1202 | && std::chrono::abs(_receiver[Base].gnssObs->insTime - _receiver[Rover].gnssObs->insTime) < std::chrono::milliseconds(100)) |
| 761 | { | ||
| 762 | ✗ | calcRealTimeKinematicSolution(); | |
| 763 | } | ||
| 764 | 601 | } | |
| 765 | |||
| 766 | 601 | void RealTimeKinematic::recvRoverGnssObs(InputPin::NodeDataQueue& queue, size_t /* pinIdx */) | |
| 767 | { | ||
| 768 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | _receiver[Rover].gnssObs = std::static_pointer_cast<const GnssObs>(queue.extract_front()); |
| 769 | LOG_DATA("{}: Received Rover GNSS Obs for [{}]", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST)); | ||
| 770 | |||
| 771 |
5/10✓ Branch 2 taken 601 times.
✗ Branch 3 not taken.
✓ Branch 10 taken 601 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 601 times.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✓ Branch 17 taken 601 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 601 times.
|
601 | if (_receiver[Base].gnssObs && (_receiver[Rover].gnssObs->insTime - _receiver[Base].gnssObs->insTime) > std::chrono::milliseconds(static_cast<int>(_maxTimeBetweenBaseRoverForRTK * 1e3))) |
| 772 | { | ||
| 773 | ✗ | if (!_calcSPPIfNoBase) { return; } | |
| 774 | LOG_TRACE("{}: Calculating SPP Fallback, as Rover obs [{}] is {:.3f} seconds away from last base obs [{}]. Maximum time to consider the base observation is {} seconds.", | ||
| 775 | nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), | ||
| 776 | static_cast<double>((_receiver[Rover].gnssObs->insTime - _receiver[Base].gnssObs->insTime).count()), | ||
| 777 | _receiver[Base].gnssObs->insTime.toYMDHMS(GPST), | ||
| 778 | _maxTimeBetweenBaseRoverForRTK); | ||
| 779 | ✗ | if (auto rtkSol = calcFallbackSppSolution()) | |
| 780 | { | ||
| 781 | ✗ | nSingleSolutions++; | |
| 782 | ✗ | invokeCallbacks(OUTPUT_PORT_INDEX_RTKSOL, rtkSol); | |
| 783 | ✗ | } | |
| 784 | ✗ | _receiver[Rover].gnssObs = nullptr; | |
| 785 | } | ||
| 786 |
3/6✓ Branch 3 taken 601 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 601 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 601 times.
✗ Branch 12 not taken.
|
1202 | else if (!_receiver[Base].e_posMarker.isZero() && _receiver[Base].gnssObs && _receiver[Rover].gnssObs |
| 787 |
6/12✓ Branch 0 taken 601 times.
✗ Branch 1 not taken.
✓ Branch 8 taken 601 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 601 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 601 times.
✗ Branch 15 not taken.
✓ Branch 17 taken 601 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 601 times.
✗ Branch 20 not taken.
|
1202 | && std::chrono::abs(_receiver[Base].gnssObs->insTime - _receiver[Rover].gnssObs->insTime) < std::chrono::milliseconds(100)) |
| 788 | { | ||
| 789 | 601 | calcRealTimeKinematicSolution(); | |
| 790 | } | ||
| 791 | } | ||
| 792 | |||
| 793 | 1 | void RealTimeKinematic::assignSolutionToFilter(const std::shared_ptr<NAV::SppSolution>& sppSol) | |
| 794 | { | ||
| 795 | 1 | _lastUpdate = sppSol->insTime; | |
| 796 | 1 | _receiver[Rover].e_posMarker = sppSol->e_position(); | |
| 797 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | _receiver[Rover].lla_posMarker = trafo::ecef2lla_WGS84(_receiver[Rover].e_posMarker); |
| 798 |
1/2✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
|
1 | if (!sppSol->e_velocity().hasNaN()) |
| 799 | { | ||
| 800 | 1 | _receiver[Rover].e_vel = sppSol->e_velocity(); | |
| 801 | } | ||
| 802 | |||
| 803 |
2/4✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
|
1 | _kalmanFilter.x.segment<3>(States::Pos) = _receiver[Rover].e_posMarker; |
| 804 |
2/4✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
|
1 | _kalmanFilter.x.segment<3>(States::Vel) = _receiver[Rover].e_vel; |
| 805 | |||
| 806 |
1/2✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
|
1 | if (sppSol->e_CovarianceMatrix().has_value()) |
| 807 | { | ||
| 808 |
2/4✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
|
1 | if (sppSol->e_CovarianceMatrix()->get().hasRows(Keys::PosVel<Keys::MotionModelKey>)) |
| 809 | { | ||
| 810 |
3/6✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
|
1 | _kalmanFilter.P(States::PosVel, States::PosVel) = (*sppSol->e_CovarianceMatrix())(Keys::PosVel<Keys::MotionModelKey>, Keys::PosVel<Keys::MotionModelKey>); |
| 811 | } | ||
| 812 | else | ||
| 813 | { | ||
| 814 | ✗ | _kalmanFilter.P(States::Pos, States::Pos) = (*sppSol->e_CovarianceMatrix())(Keys::Pos<Keys::MotionModelKey>, Keys::Pos<Keys::MotionModelKey>); | |
| 815 | ✗ | _kalmanFilter.P(States::Vel, States::Vel) = Eigen::DiagonalMatrix<double, 3>(1, 1, 1); | |
| 816 | } | ||
| 817 | } | ||
| 818 | 1 | } | |
| 819 | |||
| 820 | 601 | void RealTimeKinematic::calcRealTimeKinematicSolution() | |
| 821 | { | ||
| 822 |
3/4✓ Branch 1 taken 1 times.
✓ Branch 2 taken 600 times.
✓ Branch 6 taken 600 times.
✗ Branch 7 not taken.
|
601 | auto dt = _lastUpdate.empty() ? 0.0 : static_cast<double>((_receiver[Rover].gnssObs->insTime - _lastUpdate).count()); |
| 823 | LOG_DATA("{}: Calculate RTK Solution for rover [{}], base [{}] (dt = {:.2f})", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), _receiver[Base].gnssObs->insTime.toYMDHMS(GPST), dt); | ||
| 824 | |||
| 825 | // Collection of all connected navigation data providers | ||
| 826 | 601 | std::vector<InputPin::IncomingLink::ValueWrapper<GnssNavInfo>> gnssNavInfoWrappers; | |
| 827 | 601 | std::vector<const GnssNavInfo*> gnssNavInfos; | |
| 828 |
3/4✓ Branch 1 taken 1803 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1202 times.
✓ Branch 4 taken 601 times.
|
1803 | for (size_t i = 0; i < _dynamicInputPins.getNumberOfDynamicPins(); i++) |
| 829 | { | ||
| 830 |
2/4✓ Branch 1 taken 1202 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1202 times.
✗ Branch 5 not taken.
|
1202 | if (auto gnssNavInfo = getInputValue<GnssNavInfo>(INPUT_PORT_INDEX_GNSS_NAV_INFO + i)) |
| 831 | { | ||
| 832 |
1/2✓ Branch 2 taken 1202 times.
✗ Branch 3 not taken.
|
1202 | gnssNavInfoWrappers.push_back(*gnssNavInfo); |
| 833 |
1/2✓ Branch 2 taken 1202 times.
✗ Branch 3 not taken.
|
1202 | gnssNavInfos.push_back(gnssNavInfo->v); |
| 834 | 1202 | } | |
| 835 | } | ||
| 836 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | if (!gnssNavInfos.empty()) |
| 837 | { | ||
| 838 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | auto rtkSol = std::make_shared<RtkSolution>(); |
| 839 | 601 | rtkSol->insTime = _receiver[Rover].gnssObs->insTime; | |
| 840 | 601 | rtkSol->baseTime = _receiver[Base].gnssObs->insTime; | |
| 841 | |||
| 842 |
4/4✓ Branch 0 taken 600 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 60 times.
✓ Branch 3 taken 540 times.
|
601 | bool startupPhase = dt == 0.0 ? true : static_cast<double>(nFixSolutions + nFloatSolutions + nSingleSolutions) * dt < 60.0; |
| 843 | |||
| 844 |
6/8✓ Branch 0 taken 540 times.
✓ Branch 1 taken 61 times.
✓ Branch 5 taken 540 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 540 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 540 times.
✓ Branch 10 taken 61 times.
|
601 | if (!startupPhase && !_receiver[Rover].e_posMarker.isZero()) |
| 845 | { | ||
| 846 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 540 times.
|
540 | if (dt > 10.0) |
| 847 | { | ||
| 848 | ✗ | _ambiguitiesHold.clear(); | |
| 849 | ✗ | _pivotSatellites.clear(); | |
| 850 | ✗ | std::vector<States::StateKeyType> ambKeys; | |
| 851 | ✗ | for (size_t i = States::KFStates_COUNT; i < _kalmanFilter.x.rowKeys().size(); i++) // 0-2 Pos, 3-5 Vel | |
| 852 | { | ||
| 853 | ✗ | if (const auto* ambDD = std::get_if<States::AmbiguityDD>(&_kalmanFilter.x.rowKeys().at(i))) | |
| 854 | { | ||
| 855 | ✗ | ambKeys.emplace_back(*ambDD); | |
| 856 | } | ||
| 857 | } | ||
| 858 | ✗ | _kalmanFilter.removeStates(ambKeys); | |
| 859 | ✗ | _receiver[Rover].e_posMarker.setZero(); | |
| 860 | ✗ | _receiver[Rover].e_vel.setZero(); | |
| 861 | |||
| 862 | ✗ | std::string text = fmt::format("Outage of {:.1f} [s] (interval {:.1f} [s]). Reinitializing with SPP solution.", dt, _dataInterval); | |
| 863 | ✗ | LOG_WARN("{}: [{}] {}", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), text); | |
| 864 | ✗ | addEventToGui(rtkSol, text); | |
| 865 | ✗ | } | |
| 866 |
3/6✓ Branch 1 taken 540 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 540 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 540 times.
|
540 | else if (_kalmanFilter.x.rows() > States::KFStates_COUNT && dt > 3.0 * _dataInterval) |
| 867 | { | ||
| 868 | ✗ | _ambiguitiesHold.clear(); | |
| 869 | ✗ | std::vector<States::StateKeyType> ambKeys; | |
| 870 | ✗ | for (size_t i = States::KFStates_COUNT; i < _kalmanFilter.x.rowKeys().size(); i++) // 0-2 Pos, 3-5 Vel | |
| 871 | { | ||
| 872 | ✗ | if (const auto* ambDD = std::get_if<States::AmbiguityDD>(&_kalmanFilter.x.rowKeys().at(i))) | |
| 873 | { | ||
| 874 | ✗ | ambKeys.emplace_back(*ambDD); | |
| 875 | } | ||
| 876 | } | ||
| 877 | ✗ | _kalmanFilter.removeStates(ambKeys); | |
| 878 | |||
| 879 | ✗ | std::string text = fmt::format("Outage of {:.1f} [s] (interval {:.1f} [s]). Resetting ambiguities.", dt, _dataInterval); | |
| 880 | ✗ | LOG_WARN("{}: [{}] {}", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), text); | |
| 881 | ✗ | addEventToGui(rtkSol, text); | |
| 882 | ✗ | } | |
| 883 | } | ||
| 884 | |||
| 885 | // Calculate a Single point solution if the Rover Position is not known yet | ||
| 886 |
3/4✓ Branch 3 taken 601 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 600 times.
|
601 | if (_receiver[Rover].e_posMarker.isZero()) |
| 887 | { | ||
| 888 |
3/6✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
|
1 | if (auto sol = _sppAlgorithm.calcSppSolution(_receiver[Rover].gnssObs, gnssNavInfos, nameId())) |
| 889 | { | ||
| 890 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | assignSolutionToFilter(sol); |
| 891 | |||
| 892 |
9/18✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
✓ Branch 17 taken 1 times.
✗ Branch 18 not taken.
✓ Branch 21 taken 1 times.
✗ Branch 22 not taken.
✓ Branch 26 taken 1 times.
✗ Branch 27 not taken.
✓ Branch 30 taken 1 times.
✗ Branch 31 not taken.
✓ Branch 34 taken 1 times.
✗ Branch 35 not taken.
|
1 | LOG_DEBUG("{}: Initial base position: {}°, {}°, {}m (ECEF {} {} {} [m])", nameId(), |
| 893 | rad2deg(_receiver[Base].lla_posMarker(0)), rad2deg(_receiver[Base].lla_posMarker(1)), _receiver[Base].lla_posMarker(2), | ||
| 894 | _receiver[Base].e_posMarker.x(), _receiver[Base].e_posMarker.y(), _receiver[Base].e_posMarker.z()); | ||
| 895 |
9/18✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
✓ Branch 17 taken 1 times.
✗ Branch 18 not taken.
✓ Branch 21 taken 1 times.
✗ Branch 22 not taken.
✓ Branch 26 taken 1 times.
✗ Branch 27 not taken.
✓ Branch 30 taken 1 times.
✗ Branch 31 not taken.
✓ Branch 34 taken 1 times.
✗ Branch 35 not taken.
|
1 | LOG_DEBUG("{}: Initial rover position: {}°, {}°, {}m (ECEF {} {} {} [m])", nameId(), |
| 896 | rad2deg(_receiver[Rover].lla_posMarker(0)), rad2deg(_receiver[Rover].lla_posMarker(1)), _receiver[Rover].lla_posMarker(2), | ||
| 897 | _receiver[Rover].e_posMarker.x(), _receiver[Rover].e_posMarker.y(), _receiver[Rover].e_posMarker.z()); | ||
| 898 |
4/8✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
|
1 | LOG_DEBUG("{}: Initial rover velocity: {} [m/s] (ECEF)", nameId(), _receiver[Rover].e_vel.transpose()); |
| 899 | } | ||
| 900 | else | ||
| 901 | { | ||
| 902 | LOG_DATA("{}: Could not calculate initial position solution.", nameId()); | ||
| 903 | ✗ | return; | |
| 904 | 1 | } | |
| 905 | } | ||
| 906 | |||
| 907 |
2/2✓ Branch 0 taken 600 times.
✓ Branch 1 taken 1 times.
|
601 | if (_outlierMaxPosVarStartupTriggered) { startupPhase = false; } |
| 908 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | else if (startupPhase) |
| 909 | { | ||
| 910 |
3/6✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
|
1 | if (double posVar = _kalmanFilter.P(States::Pos, States::Pos).diagonal().sum() / 3.0; |
| 911 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | posVar < _outlierMaxPosVarStartup) |
| 912 | { | ||
| 913 | 1 | _outlierMaxPosVarStartupTriggered = true; | |
| 914 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
2 | addEventToGui(rtkSol, fmt::format("Doing NIS check from now on because\nposition variance is below {:.4f}m^2", _outlierMaxPosVarStartup)); |
| 915 | } | ||
| 916 | } | ||
| 917 | |||
| 918 | // Collection of all connected Ionospheric Corrections | ||
| 919 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | auto ionosphericCorrections = std::make_shared<const IonosphericCorrections>(gnssNavInfos); |
| 920 | |||
| 921 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | kalmanFilterPrediction(); |
| 922 | |||
| 923 | { | ||
| 924 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | auto logLevel = spdlog::get_level(); |
| 925 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | spdlog::set_level(spdlog::level::debug); |
| 926 | // Calculate all possible observations here, to put into solution for analysis | ||
| 927 |
2/4✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 601 times.
✗ Branch 6 not taken.
|
1202 | ObservationFilter obsFilter{ ReceiverType::ReceiverType_COUNT }; |
| 928 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | obsFilter.disableFilter(); |
| 929 | |||
| 930 | 601 | Observations observations; | |
| 931 | 601 | obsFilter.selectObservationsForCalculation(Rover, | |
| 932 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | _receiver[Rover].e_posMarker, |
| 933 | 601 | _receiver[Rover].lla_posMarker, | |
| 934 | 601 | _receiver[Rover].gnssObs, | |
| 935 | gnssNavInfos, | ||
| 936 |
2/4✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 601 times.
✗ Branch 5 not taken.
|
1202 | observations, nullptr, nameId() + " no filter"); |
| 937 | 601 | obsFilter.selectObservationsForCalculation(Base, | |
| 938 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | _receiver[Base].e_posMarker, |
| 939 | 601 | _receiver[Base].lla_posMarker, | |
| 940 | 601 | _receiver[Base].gnssObs, | |
| 941 | gnssNavInfos, | ||
| 942 |
2/4✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 601 times.
✗ Branch 5 not taken.
|
1202 | observations, nullptr, nameId() + " no filter"); |
| 943 |
1/2✓ Branch 2 taken 601 times.
✗ Branch 3 not taken.
|
601 | removeSingleObservations(observations, nullptr, nullptr); |
| 944 | |||
| 945 |
2/2✓ Branch 7 taken 40868 times.
✓ Branch 8 taken 601 times.
|
41469 | for (const auto& [satSigId, sigObs] : observations.signals) |
| 946 | { | ||
| 947 |
1/2✓ Branch 1 taken 40868 times.
✗ Branch 2 not taken.
|
40868 | auto satId = satSigId.toSatId(); |
| 948 |
1/2✓ Branch 2 taken 40868 times.
✗ Branch 3 not taken.
|
40868 | if (std::ranges::find_if(rtkSol->satData, [&satId](const auto& sat) { |
| 949 | 388246 | return satId == sat.first; | |
| 950 | }) | ||
| 951 |
2/2✓ Branch 3 taken 28848 times.
✓ Branch 4 taken 12020 times.
|
81736 | != rtkSol->satData.end()) |
| 952 | { | ||
| 953 | 28848 | continue; | |
| 954 | } | ||
| 955 |
1/2✓ Branch 2 taken 12020 times.
✗ Branch 3 not taken.
|
24040 | rtkSol->satData.emplace_back(satSigId.toSatId(), |
| 956 |
2/4✓ Branch 1 taken 12020 times.
✗ Branch 2 not taken.
✓ Branch 7 taken 12020 times.
✗ Branch 8 not taken.
|
12020 | RtkSolution::SatData{ .satElevation = sigObs.recvObs.at(Rover)->satElevation(_receiver[Rover].e_posMarker, _receiver[Rover].lla_posMarker), |
| 957 |
3/6✓ Branch 1 taken 12020 times.
✗ Branch 2 not taken.
✓ Branch 7 taken 12020 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 12020 times.
✗ Branch 11 not taken.
|
24040 | .satAzimuth = sigObs.recvObs.at(Rover)->satElevation(_receiver[Rover].e_posMarker, _receiver[Rover].lla_posMarker) }); |
| 958 | } | ||
| 959 |
2/2✓ Branch 7 taken 40868 times.
✓ Branch 8 taken 601 times.
|
41469 | for (const auto& [satSigId, sigObs] : observations.signals) |
| 960 | { | ||
| 961 |
3/4✓ Branch 1 taken 40868 times.
✗ Branch 2 not taken.
✓ Branch 9 taken 122604 times.
✓ Branch 10 taken 40868 times.
|
163472 | for (const auto& obs : sigObs.recvObs.at(Rover)->obs) |
| 962 | { | ||
| 963 |
1/2✓ Branch 2 taken 122604 times.
✗ Branch 3 not taken.
|
122604 | rtkSol->observableReceived.emplace(satSigId, obs.first); |
| 964 | } | ||
| 965 | } | ||
| 966 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | spdlog::set_level(logLevel); |
| 967 | 601 | } | |
| 968 | |||
| 969 | 601 | Observations observations; | |
| 970 | 601 | ObservationFilter::Filtered filtered; | |
| 971 | 1202 | _obsFilter.selectObservationsForCalculation(Rover, | |
| 972 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | _receiver[Rover].e_posMarker, |
| 973 | 601 | _receiver[Rover].lla_posMarker, | |
| 974 | 601 | _receiver[Rover].gnssObs, | |
| 975 | gnssNavInfos, | ||
| 976 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
1202 | observations, &filtered, nameId()); |
| 977 | 1202 | _obsFilter.selectObservationsForCalculation(Base, | |
| 978 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | _receiver[Base].e_posMarker, |
| 979 | 601 | _receiver[Base].lla_posMarker, | |
| 980 | 601 | _receiver[Base].gnssObs, | |
| 981 | gnssNavInfos, | ||
| 982 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
1202 | observations, &filtered, nameId()); |
| 983 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | removeSingleObservations(observations, &filtered, rtkSol); |
| 984 |
1/2✓ Branch 2 taken 601 times.
✗ Branch 3 not taken.
|
601 | rtkSol->filtered = filtered; |
| 985 | |||
| 986 |
2/2✓ Branch 7 taken 27786 times.
✓ Branch 8 taken 601 times.
|
28387 | for (const auto& [satSigId, sigObs] : observations.signals) |
| 987 | { | ||
| 988 |
3/4✓ Branch 1 taken 27786 times.
✗ Branch 2 not taken.
✓ Branch 9 taken 83358 times.
✓ Branch 10 taken 27786 times.
|
111144 | for (const auto& obs : sigObs.recvObs.at(Rover)->obs) |
| 989 | { | ||
| 990 |
1/2✓ Branch 2 taken 83358 times.
✗ Branch 3 not taken.
|
83358 | rtkSol->observableFiltered.emplace(satSigId, obs.first); |
| 991 | } | ||
| 992 | } | ||
| 993 | 601 | rtkSol->solType = RtkSolution::SolutionType::Predicted; | |
| 994 | |||
| 995 |
3/6✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 601 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 601 times.
✗ Branch 7 not taken.
|
601 | if (!observations.signals.empty() && observations.satellites.size() > 1) |
| 996 | { | ||
| 997 |
2/4✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 601 times.
✗ Branch 6 not taken.
|
601 | _obsEstimator.calcObservationEstimates(observations, _receiver[Rover], ionosphericCorrections, nameId(), ObservationEstimator::DoubleDifference); |
| 998 |
2/4✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 601 times.
✗ Branch 6 not taken.
|
601 | _obsEstimator.calcObservationEstimates(observations, _receiver[Base], ionosphericCorrections, nameId(), ObservationEstimator::DoubleDifference); |
| 999 | |||
| 1000 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | checkForCycleSlip(observations, rtkSol); |
| 1001 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | updatePivotSatellites(observations, rtkSol); |
| 1002 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | addOrRemoveKalmanFilterAmbiguities(observations, rtkSol); |
| 1003 | |||
| 1004 | 601 | printObservations(observations); | |
| 1005 | |||
| 1006 |
3/4✓ Branch 0 taken 600 times.
✓ Branch 1 taken 1 times.
✓ Branch 3 taken 600 times.
✗ Branch 4 not taken.
|
601 | bool nisEnabled = !startupPhase && _kalmanFilter.isNISenabled(); |
| 1007 |
3/4✓ Branch 0 taken 600 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 600 times.
✗ Branch 3 not taken.
|
601 | size_t maxIter = nisEnabled ? (_maxRemoveOutlier == 0 ? 1000 : _maxRemoveOutlier) : 1; |
| 1008 | |||
| 1009 | 601 | bool pivotChanged = true; | |
| 1010 | 601 | Differences singleDifferences; | |
| 1011 | 601 | Differences doubleDifferences; | |
| 1012 | 601 | unordered_map<GnssObs::ObservationType, KeyedMatrixXd<RTK::Meas::SingleObs<RealTimeKinematic::ReceiverType>>> Rtilde; | |
| 1013 | |||
| 1014 |
2/2✓ Branch 0 taken 601 times.
✓ Branch 1 taken 1 times.
|
602 | for (size_t i = 0; i < maxIter; i++) |
| 1015 | { | ||
| 1016 | LOG_DATA("{}: Iteration: {}/{}", nameId(), i, maxIter); | ||
| 1017 | |||
| 1018 |
1/2✓ Branch 0 taken 601 times.
✗ Branch 1 not taken.
|
601 | if (pivotChanged) |
| 1019 | { | ||
| 1020 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | singleDifferences = calcSingleDifferences(observations); |
| 1021 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | doubleDifferences = calcDoubleDifferences(observations, singleDifferences); |
| 1022 | |||
| 1023 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | Rtilde = calcSingleObsMeasurementNoiseMatrices(observations); |
| 1024 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | calcKalmanUpdateMatrices(observations, doubleDifferences, Rtilde); |
| 1025 | 601 | pivotChanged = false; | |
| 1026 | } | ||
| 1027 | |||
| 1028 |
3/4✓ Branch 0 taken 600 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 600 times.
✗ Branch 3 not taken.
|
601 | if (nisEnabled && !startupPhase) |
| 1029 | { | ||
| 1030 | LOG_DATA("{}: Doing NIS check", nameId()); | ||
| 1031 |
2/4✓ Branch 1 taken 600 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 600 times.
✗ Branch 5 not taken.
|
600 | auto nisResult = _kalmanFilter.checkForOutliersNIS(nameId()); |
| 1032 |
1/2✓ Branch 2 taken 600 times.
✗ Branch 3 not taken.
|
600 | if (!rtkSol->nisResultInitial) { rtkSol->nisResultInitial = nisResult; } |
| 1033 | 600 | rtkSol->nisResultFinal = nisResult; | |
| 1034 |
2/6✗ Branch 0 not taken.
✓ Branch 1 taken 600 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 600 times.
|
600 | if (nisResult.triggered && observations.satellites.size() <= _outlierMinSat) |
| 1035 | { | ||
| 1036 | ✗ | nisResult.triggered = false; | |
| 1037 | ✗ | addEventToGui(rtkSol, "Stopped doing NIS check, as minimum amount of satellites reached."); | |
| 1038 | } | ||
| 1039 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 600 times.
|
600 | if (nisResult.triggered) |
| 1040 | { | ||
| 1041 | LOG_DATA("{}: NIS triggered", nameId()); | ||
| 1042 | |||
| 1043 | ✗ | if (auto outliers = removeOutlier(observations, rtkSol); | |
| 1044 | ✗ | !outliers.empty()) | |
| 1045 | { | ||
| 1046 | ✗ | removeSingleObservations(observations, &rtkSol->filtered, rtkSol); | |
| 1047 | ✗ | for (const auto& outlier : outliers) | |
| 1048 | { | ||
| 1049 | ✗ | pivotChanged |= outlier.pivot.has_value(); | |
| 1050 | } | ||
| 1051 | } | ||
| 1052 | else | ||
| 1053 | { | ||
| 1054 | LOG_DATA("{}: NIS check finished as nothing removed", nameId()); | ||
| 1055 | ✗ | break; | |
| 1056 | ✗ | } | |
| 1057 | } | ||
| 1058 | else | ||
| 1059 | { | ||
| 1060 | LOG_DATA("{}: NIS check successful", nameId()); | ||
| 1061 | 600 | break; | |
| 1062 | } | ||
| 1063 | } | ||
| 1064 | } | ||
| 1065 |
2/2✓ Branch 0 taken 600 times.
✓ Branch 1 taken 1 times.
|
601 | if (nisEnabled) |
| 1066 | { | ||
| 1067 | LOG_DATA("{}: Final NIS = {} < {} = r2. {} removed", nameId(), rtkSol->nisResultFinal->NIS, rtkSol->nisResultFinal->r2, rtkSol->nisRemovedCnt); | ||
| 1068 |
2/4✓ Branch 1 taken 600 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 600 times.
✗ Branch 5 not taken.
|
600 | observations.recalcObservableCounts(nameId()); |
| 1069 | } | ||
| 1070 | |||
| 1071 |
2/4✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 601 times.
✗ Branch 4 not taken.
|
601 | if (kalmanFilterUpdate(rtkSol).valid) |
| 1072 | { | ||
| 1073 |
3/6✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 601 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 601 times.
✗ Branch 7 not taken.
|
601 | if (resolveAmbiguities(observations.nObservablesUniqueSatellite.at(GnssObs::Carrier), rtkSol)) |
| 1074 | { | ||
| 1075 | 601 | rtkSol->solType = RtkSolution::SolutionType::RTK_Fixed; | |
| 1076 | 601 | nFixSolutions++; | |
| 1077 | } | ||
| 1078 | else | ||
| 1079 | { | ||
| 1080 | ✗ | rtkSol->solType = RtkSolution::SolutionType::RTK_Float; | |
| 1081 | ✗ | nFloatSolutions++; | |
| 1082 | } | ||
| 1083 | } | ||
| 1084 | |||
| 1085 |
1/2✓ Branch 2 taken 601 times.
✗ Branch 3 not taken.
|
601 | rtkSol->measInnovation = _kalmanFilter.z; |
| 1086 | 601 | } | |
| 1087 | else | ||
| 1088 | { | ||
| 1089 | LOG_TRACE("{}: No observations found. Check that your base and rover file match and reconfigure the filter.", nameId()); | ||
| 1090 | } | ||
| 1091 | |||
| 1092 | 601 | _lastSolutionStatus = rtkSol->solType; | |
| 1093 | |||
| 1094 | // Write out results | ||
| 1095 |
2/2✓ Branch 0 taken 1803 times.
✓ Branch 1 taken 601 times.
|
2404 | for (size_t obsType = 0; obsType < GnssObs::ObservationType_COUNT; obsType++) |
| 1096 | { | ||
| 1097 |
2/4✓ Branch 2 taken 1803 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1803 times.
✗ Branch 6 not taken.
|
1803 | rtkSol->nObservations.emplace(static_cast<GnssObs::ObservationType>(obsType), observations.nObservables.at(obsType)); |
| 1098 |
2/4✓ Branch 2 taken 1803 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1803 times.
✗ Branch 6 not taken.
|
1803 | rtkSol->nObservationsUniqueSatellite.emplace(static_cast<GnssObs::ObservationType>(obsType), observations.nObservablesUniqueSatellite.at(obsType)); |
| 1099 | } | ||
| 1100 | 601 | rtkSol->nSatellites = observations.satellites.size(); | |
| 1101 |
2/4✓ Branch 3 taken 601 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 601 times.
✗ Branch 7 not taken.
|
601 | rtkSol->distanceBaseRover = (_receiver[Base].e_posMarker - _receiver[Rover].e_posMarker).norm(); |
| 1102 | |||
| 1103 |
1/2✓ Branch 4 taken 601 times.
✗ Branch 5 not taken.
|
1202 | rtkSol->setPosVelAndCov_e(_receiver[Rover].e_posMarker, _receiver[Rover].e_vel, |
| 1104 |
1/2✓ Branch 3 taken 601 times.
✗ Branch 4 not taken.
|
601 | _kalmanFilter.P.block<6>(States::PosVel, States::PosVel)); |
| 1105 |
2/6✗ Branch 1 not taken.
✓ Branch 2 taken 601 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 601 times.
✗ Branch 7 not taken.
|
601 | if (rtkSol->solType == RtkSolution::SolutionType::RTK_Fixed || rtkSol->solType == RtkSolution::SolutionType::RTK_Float) |
| 1106 | { | ||
| 1107 |
1/2✓ Branch 4 taken 601 times.
✗ Branch 5 not taken.
|
601 | rtkSol->ambiguityDD_br.reserve(_kalmanFilter.x.rowKeys().size() - States::KFStates_COUNT); |
| 1108 |
2/2✓ Branch 2 taken 23579 times.
✓ Branch 3 taken 601 times.
|
24180 | for (size_t i = States::KFStates_COUNT; i < _kalmanFilter.x.rowKeys().size(); i++) // 0-2 Pos, 3-5 Vel |
| 1109 | { | ||
| 1110 |
2/4✓ Branch 2 taken 23579 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 23579 times.
✗ Branch 6 not taken.
|
23579 | if (const auto* ambDD = std::get_if<States::AmbiguityDD>(&_kalmanFilter.x.rowKeys().at(i))) |
| 1111 | { | ||
| 1112 | 23579 | const auto& key = States::AmbiguityDD(ambDD->satSigId); | |
| 1113 | LOG_DATA("{}: Key: {}", nameId(), key); | ||
| 1114 | auto ambDDSol = RtkSolution::AmbiguityDD{ | ||
| 1115 |
1/2✓ Branch 2 taken 23579 times.
✗ Branch 3 not taken.
|
23579 | .pivotSatSigId = _pivotSatellites.at({ ambDD->satSigId.code, GnssObs::Carrier }).satSigId, |
| 1116 | .satSigId = ambDD->satSigId, | ||
| 1117 |
1/2✓ Branch 1 taken 23579 times.
✗ Branch 2 not taken.
|
23579 | .value = UncertainValue<double>{ .value = _kalmanFilter.x(key), |
| 1118 |
1/2✓ Branch 2 taken 23579 times.
✗ Branch 3 not taken.
|
23579 | .stdDev = std::sqrt(_kalmanFilter.P(key, key)) } |
| 1119 | 47158 | }; | |
| 1120 |
1/2✓ Branch 2 taken 23579 times.
✗ Branch 3 not taken.
|
23579 | rtkSol->ambiguityDD_br.push_back(ambDDSol); |
| 1121 | } | ||
| 1122 | } | ||
| 1123 |
2/2✓ Branch 5 taken 12621 times.
✓ Branch 6 taken 601 times.
|
13222 | for (const auto& pivot : _pivotSatellites) |
| 1124 | { | ||
| 1125 |
1/2✓ Branch 2 taken 12621 times.
✗ Branch 3 not taken.
|
12621 | rtkSol->pivots.emplace(pivot.second.satSigId, pivot.first.second); |
| 1126 | } | ||
| 1127 |
2/2✓ Branch 7 taken 27786 times.
✓ Branch 8 taken 601 times.
|
28387 | for (const auto& [satSigId, sigObs] : observations.signals) |
| 1128 | { | ||
| 1129 |
3/4✓ Branch 1 taken 27786 times.
✗ Branch 2 not taken.
✓ Branch 9 taken 83358 times.
✓ Branch 10 taken 27786 times.
|
111144 | for (const auto& obs : sigObs.recvObs.at(Rover)->obs) |
| 1130 | { | ||
| 1131 |
1/2✓ Branch 2 taken 83358 times.
✗ Branch 3 not taken.
|
83358 | rtkSol->observableUsed.emplace(satSigId, obs.first); |
| 1132 | } | ||
| 1133 | } | ||
| 1134 | } | ||
| 1135 | |||
| 1136 |
2/2✓ Branch 0 taken 600 times.
✓ Branch 1 taken 1 times.
|
601 | if (dt > 1e-3) { _dataInterval = dt; } |
| 1137 | 601 | _lastUpdate = rtkSol->insTime; | |
| 1138 |
1/2✓ Branch 2 taken 601 times.
✗ Branch 3 not taken.
|
601 | invokeCallbacks(OUTPUT_PORT_INDEX_RTKSOL, rtkSol); |
| 1139 | 601 | } | |
| 1140 | |||
| 1141 | 601 | _receiver[Rover].gnssObs = nullptr; | |
| 1142 | 601 | _baseObsReceivedThisEpoch = false; | |
| 1143 | 601 | } | |
| 1144 | |||
| 1145 | ✗ | std::shared_ptr<RtkSolution> RealTimeKinematic::calcFallbackSppSolution() | |
| 1146 | { | ||
| 1147 | LOG_DATA("{}: Calculate SPP Solution for [{}] (fallback)", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST)); | ||
| 1148 | |||
| 1149 | // Collection of all connected navigation data providers | ||
| 1150 | ✗ | std::vector<InputPin::IncomingLink::ValueWrapper<GnssNavInfo>> gnssNavInfoWrappers; | |
| 1151 | ✗ | std::vector<const GnssNavInfo*> gnssNavInfos; | |
| 1152 | ✗ | for (size_t i = 0; i < _dynamicInputPins.getNumberOfDynamicPins(); i++) | |
| 1153 | { | ||
| 1154 | ✗ | if (auto gnssNavInfo = getInputValue<GnssNavInfo>(INPUT_PORT_INDEX_GNSS_NAV_INFO + i)) | |
| 1155 | { | ||
| 1156 | ✗ | gnssNavInfoWrappers.push_back(*gnssNavInfo); | |
| 1157 | ✗ | gnssNavInfos.push_back(gnssNavInfo->v); | |
| 1158 | ✗ | } | |
| 1159 | } | ||
| 1160 | |||
| 1161 | ✗ | if (auto sppSol = _sppAlgorithm.calcSppSolution(_receiver[Rover].gnssObs, gnssNavInfos, nameId())) | |
| 1162 | { | ||
| 1163 | ✗ | auto rtkSol = std::make_shared<RtkSolution>(); | |
| 1164 | ✗ | rtkSol->insTime = sppSol->insTime; | |
| 1165 | ✗ | rtkSol->solType = RtkSolution::SolutionType::SPP; | |
| 1166 | ✗ | rtkSol->nSatellites = sppSol->nSatellites; | |
| 1167 | ✗ | if (!_receiver[Base].e_posMarker.isZero()) | |
| 1168 | { | ||
| 1169 | ✗ | rtkSol->distanceBaseRover = (_receiver[Base].e_posMarker - _receiver[Rover].e_posMarker).norm(); | |
| 1170 | } | ||
| 1171 | ✗ | rtkSol->nObservations[GnssObs::Pseudorange] = sppSol->nMeasPsr; | |
| 1172 | ✗ | rtkSol->nObservations[GnssObs::Doppler] = sppSol->nMeasDopp; | |
| 1173 | |||
| 1174 | ✗ | if (sppSol->e_CovarianceMatrix().has_value()) | |
| 1175 | { | ||
| 1176 | ✗ | if (sppSol->e_CovarianceMatrix()->get().hasAnyRows(Keys::Vel<Keys::MotionModelKey>)) | |
| 1177 | { | ||
| 1178 | ✗ | rtkSol->setPosVelAndCov_e(sppSol->e_position(), sppSol->e_velocity(), | |
| 1179 | ✗ | (*sppSol->e_CovarianceMatrix())(Keys::PosVel<Keys::MotionModelKey>, Keys::PosVel<Keys::MotionModelKey>)); | |
| 1180 | } | ||
| 1181 | else | ||
| 1182 | { | ||
| 1183 | ✗ | rtkSol->setPositionAndCov_e(sppSol->e_position(), | |
| 1184 | ✗ | (*sppSol->e_CovarianceMatrix())(Keys::Pos<Keys::MotionModelKey>, Keys::Pos<Keys::MotionModelKey>)); | |
| 1185 | } | ||
| 1186 | } | ||
| 1187 | else | ||
| 1188 | { | ||
| 1189 | ✗ | rtkSol->setPosition_e(sppSol->e_position()); | |
| 1190 | ✗ | rtkSol->setVelocity_e(sppSol->e_velocity()); | |
| 1191 | } | ||
| 1192 | |||
| 1193 | ✗ | assignSolutionToFilter(sppSol); | |
| 1194 | |||
| 1195 | ✗ | return rtkSol; | |
| 1196 | ✗ | } | |
| 1197 | |||
| 1198 | ✗ | return nullptr; | |
| 1199 | ✗ | } | |
| 1200 | |||
| 1201 | 601 | void RealTimeKinematic::kalmanFilterPrediction() | |
| 1202 | { | ||
| 1203 | // Update the State transition matrix (𝚽) and the Process noise covariance matrix (𝐐) | ||
| 1204 | |||
| 1205 |
1/2✓ Branch 3 taken 601 times.
✗ Branch 4 not taken.
|
601 | auto dt = static_cast<double>((_receiver[Rover].gnssObs->insTime - _lastUpdate).count()); |
| 1206 | LOG_DATA("{}: dt = {}s", nameId(), dt); | ||
| 1207 | |||
| 1208 |
6/12✓ Branch 2 taken 601 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 601 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 601 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 601 times.
✗ Branch 13 not taken.
✓ Branch 17 taken 601 times.
✗ Branch 18 not taken.
✓ Branch 20 taken 601 times.
✗ Branch 21 not taken.
|
601 | _kalmanFilter.G.block<3>(States::Vel, States::Vel) = trafo::e_Quat_n(_receiver[Rover].lla_posMarker(0), _receiver[Rover].lla_posMarker(1)).toRotationMatrix(); |
| 1209 | |||
| 1210 | LOG_DATA("{}: F =\n{}", nameId(), _kalmanFilter.F); | ||
| 1211 | LOG_DATA("{}: G =\n{}", nameId(), _kalmanFilter.G); | ||
| 1212 | LOG_DATA("{}: W =\n{}", nameId(), _kalmanFilter.W); | ||
| 1213 | LOG_DATA("{}: GWG^T =\n{}", nameId(), | ||
| 1214 | KeyedMatrixXd<States::StateKeyType>(_kalmanFilter.G(all, all) | ||
| 1215 | * _kalmanFilter.W(all, all) | ||
| 1216 | * _kalmanFilter.G(all, all).transpose(), | ||
| 1217 | _kalmanFilter.G.rowKeys())); | ||
| 1218 | |||
| 1219 |
1/2✓ Branch 3 taken 601 times.
✗ Branch 4 not taken.
|
1202 | auto [Phi, Q] = calcPhiAndQWithVanLoanMethod(_kalmanFilter.F.block<6>(States::PosVel, States::PosVel), |
| 1220 |
1/2✓ Branch 3 taken 601 times.
✗ Branch 4 not taken.
|
601 | _kalmanFilter.G.block<6>(States::PosVel, States::PosVel), |
| 1221 |
1/2✓ Branch 3 taken 601 times.
✗ Branch 4 not taken.
|
1202 | _kalmanFilter.W.block<6>(States::PosVel, States::PosVel), |
| 1222 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | dt); |
| 1223 |
2/4✓ Branch 3 taken 601 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 601 times.
✗ Branch 7 not taken.
|
601 | _kalmanFilter.Phi.block<6>(States::PosVel, States::PosVel) = Phi; |
| 1224 |
2/4✓ Branch 3 taken 601 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 601 times.
✗ Branch 7 not taken.
|
601 | _kalmanFilter.Q.block<6>(States::PosVel, States::PosVel) = Q; |
| 1225 | |||
| 1226 |
2/2✓ Branch 0 taken 600 times.
✓ Branch 1 taken 1 times.
|
601 | auto ambNoiseVar = _lastSolutionStatus == RtkSolution::SolutionType::RTK_Fixed ? _ambiguityFixProcessNoiseVariance : _ambiguityFloatProcessNoiseVariance; |
| 1227 |
2/2✓ Branch 2 taken 23538 times.
✓ Branch 3 taken 601 times.
|
24139 | for (size_t i = States::KFStates_COUNT; i < _kalmanFilter.x.rowKeys().size(); i++) // 0-2 Pos, 3-5 Vel |
| 1228 | { | ||
| 1229 |
2/4✓ Branch 2 taken 23538 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 23538 times.
✗ Branch 6 not taken.
|
23538 | if (const auto* ambDD = std::get_if<States::AmbiguityDD>(&_kalmanFilter.x.rowKeys().at(i))) |
| 1230 | { | ||
| 1231 | 23538 | const auto& key = States::AmbiguityDD(ambDD->satSigId); | |
| 1232 | |||
| 1233 |
1/2✓ Branch 3 taken 23538 times.
✗ Branch 4 not taken.
|
23538 | _kalmanFilter.W(key, key) = ambNoiseVar; |
| 1234 |
1/2✓ Branch 3 taken 23538 times.
✗ Branch 4 not taken.
|
23538 | _kalmanFilter.Q(key, key) = ambNoiseVar * dt; |
| 1235 | } | ||
| 1236 | } | ||
| 1237 | |||
| 1238 | LOG_DATA("{}: Phi =\n{}", nameId(), _kalmanFilter.Phi); | ||
| 1239 | LOG_DATA("{}: Q =\n{}", nameId(), _kalmanFilter.Q); | ||
| 1240 | |||
| 1241 | LOG_DATA("{}: P (a posteriori, t-1 = {}) =\n{}", nameId(), _lastUpdate, _kalmanFilter.P); | ||
| 1242 | LOG_DATA("{}: x (a posteriori, t-1 = {}) =\n{}", nameId(), _lastUpdate, _kalmanFilter.x.transposed()); | ||
| 1243 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | _kalmanFilter.predict(); |
| 1244 | LOG_DATA("{}: Doing KF prediction", nameId()); | ||
| 1245 | LOG_DATA("{}: x (a priori , t = {}) =\n{}", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), _kalmanFilter.x.transposed()); | ||
| 1246 | LOG_DATA("{}: P (a priori , t = {}) =\n{}", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), _kalmanFilter.P); | ||
| 1247 |
2/4✓ Branch 2 taken 601 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 601 times.
✗ Branch 7 not taken.
|
601 | _receiver[Rover].e_posMarker = _kalmanFilter.x.segment<3>(States::Pos); |
| 1248 |
2/4✓ Branch 2 taken 601 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 601 times.
✗ Branch 7 not taken.
|
601 | _receiver[Rover].e_vel = _kalmanFilter.x.segment<3>(States::Vel); |
| 1249 |
1/2✓ Branch 2 taken 601 times.
✗ Branch 3 not taken.
|
601 | _receiver[Rover].lla_posMarker = trafo::ecef2lla_WGS84(_receiver[Rover].e_posMarker); |
| 1250 | 601 | } | |
| 1251 | |||
| 1252 | 601 | void RealTimeKinematic::checkForCycleSlip(Observations& observations, const std::shared_ptr<RtkSolution>& rtkSol) | |
| 1253 | { | ||
| 1254 |
2/4✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 601 times.
|
601 | if (std::ranges::none_of(_cycleSlipDetector, [](const CycleSlipDetector& detector) { |
| 1255 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | return detector.isEnabled(CycleSlipDetector::Detector::LLI) |
| 1256 | ✗ | || detector.isEnabled(CycleSlipDetector::Detector::SingleFrequency) | |
| 1257 |
1/6✗ Branch 0 not taken.
✓ Branch 1 taken 601 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
601 | || detector.isEnabled(CycleSlipDetector::Detector::DualFrequency); |
| 1258 | })) | ||
| 1259 | { | ||
| 1260 | ✗ | return; | |
| 1261 | } | ||
| 1262 | LOG_DATA("{}: Checking for cycle-slips", nameId()); | ||
| 1263 | |||
| 1264 | 601 | std::array<std::vector<CycleSlipDetector::SatelliteObservation>, ReceiverType_COUNT> cycleSlipObservations; | |
| 1265 | |||
| 1266 |
2/2✓ Branch 5 taken 8060 times.
✓ Branch 6 taken 601 times.
|
8661 | for (const auto& satId : observations.satellites) |
| 1267 | { | ||
| 1268 | 8060 | bool added = false; | |
| 1269 |
1/2✓ Branch 7 taken 151712 times.
✗ Branch 8 not taken.
|
151712 | for (const auto& [satSigId, signalObs] : observations.signals) // Signals |
| 1270 | { | ||
| 1271 |
3/4✓ Branch 1 taken 151712 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 143652 times.
✓ Branch 5 taken 8060 times.
|
151712 | if (satSigId.toSatId() != satId) { continue; } |
| 1272 | |||
| 1273 |
2/2✓ Branch 0 taken 16120 times.
✓ Branch 1 taken 8060 times.
|
24180 | for (size_t r = 0; r < ReceiverType_COUNT; r++) // ReceiverType |
| 1274 | { | ||
| 1275 | 16120 | auto recvType = static_cast<ReceiverType>(r); | |
| 1276 | |||
| 1277 |
3/4✓ Branch 0 taken 8060 times.
✓ Branch 1 taken 8060 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8060 times.
|
16120 | if (recvType == Base && !_baseObsReceivedThisEpoch) { continue; } // Process base observation only in epoch where received |
| 1278 | |||
| 1279 |
2/4✓ Branch 1 taken 16120 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16120 times.
✗ Branch 5 not taken.
|
16120 | if (auto detector = _cycleSlipDetector.at(recvType); |
| 1280 |
1/2✓ Branch 1 taken 16120 times.
✗ Branch 2 not taken.
|
16120 | !detector.isEnabled(CycleSlipDetector::Detector::LLI) |
| 1281 | ✗ | && !detector.isEnabled(CycleSlipDetector::Detector::SingleFrequency) | |
| 1282 |
2/8✗ Branch 0 not taken.
✓ Branch 1 taken 16120 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 16120 times.
|
16120 | && !detector.isEnabled(CycleSlipDetector::Detector::DualFrequency)) |
| 1283 | { | ||
| 1284 | ✗ | continue; | |
| 1285 | 16120 | } | |
| 1286 | |||
| 1287 |
3/4✓ Branch 1 taken 16120 times.
✗ Branch 2 not taken.
✓ Branch 9 taken 1096160 times.
✓ Branch 10 taken 16120 times.
|
1112280 | for (const auto& receiverObs : _receiver.at(recvType).gnssObs->data) // ObservationType |
| 1288 | { | ||
| 1289 |
6/8✓ Branch 1 taken 1096160 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 55572 times.
✓ Branch 5 taken 1040588 times.
✓ Branch 7 taken 55572 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 55572 times.
✓ Branch 10 taken 1040588 times.
|
1096160 | if (receiverObs.satSigId.toSatId() == satId && receiverObs.carrierPhase) |
| 1290 | { | ||
| 1291 |
2/4✓ Branch 1 taken 55572 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 55572 times.
✗ Branch 6 not taken.
|
55572 | [[maybe_unused]] auto lambda = InsConst::C / receiverObs.satSigId.freq().getFrequency(signalObs.freqNum()); |
| 1292 | LOG_DATA("[{}] [{}] Added to cycle-slip detection: {:.1f} [m]", receiverObs.satSigId, recvType, receiverObs.carrierPhase->value * lambda); | ||
| 1293 | |||
| 1294 | 55572 | GnssObs::ObservationData::CarrierPhase carrier{ .value = receiverObs.carrierPhase->value, .LLI = receiverObs.carrierPhase->LLI }; | |
| 1295 | 55572 | auto signal = CycleSlipDetector::SatelliteObservation::Signal{ .code = receiverObs.satSigId.code, .measurement = carrier }; | |
| 1296 | |||
| 1297 |
2/4✓ Branch 1 taken 55572 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 55572 times.
✗ Branch 5 not taken.
|
55572 | auto sat = std::ranges::find_if(cycleSlipObservations.at(recvType), |
| 1298 | 407950 | [&satId](const auto& satObs) { return satObs.satId == satId; }); | |
| 1299 | |||
| 1300 |
3/4✓ Branch 1 taken 55572 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 16120 times.
✓ Branch 6 taken 39452 times.
|
55572 | if (sat == cycleSlipObservations.at(recvType).end()) |
| 1301 | { | ||
| 1302 | CycleSlipDetector::SatelliteObservation satObs = { .satId = satId, | ||
| 1303 | .signals = { signal }, | ||
| 1304 |
1/2✓ Branch 1 taken 16120 times.
✗ Branch 2 not taken.
|
32240 | .freqNum = signalObs.freqNum() }; |
| 1305 |
2/4✓ Branch 1 taken 16120 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16120 times.
✗ Branch 5 not taken.
|
16120 | cycleSlipObservations.at(recvType).push_back(satObs); |
| 1306 | 16120 | } | |
| 1307 | else | ||
| 1308 | { | ||
| 1309 |
1/2✓ Branch 2 taken 39452 times.
✗ Branch 3 not taken.
|
39452 | sat->signals.push_back(signal); |
| 1310 | } | ||
| 1311 | 55572 | added = true; | |
| 1312 | } | ||
| 1313 | } | ||
| 1314 | } | ||
| 1315 |
1/2✓ Branch 0 taken 8060 times.
✗ Branch 1 not taken.
|
8060 | if (added) { break; } |
| 1316 | } | ||
| 1317 | } | ||
| 1318 | |||
| 1319 | 601 | std::vector<NAV::Code> pivotsToChange; | |
| 1320 | 601 | std::vector<States::AmbiguityDD> removedStates; | |
| 1321 | ✗ | auto removeSatSig = [&](const SatSigId& satSigId, [[maybe_unused]] const std::string& reason) -> bool { | |
| 1322 | ✗ | auto key = States::AmbiguityDD(satSigId); | |
| 1323 | ✗ | if (_kalmanFilter.x.hasRow(key)) | |
| 1324 | { | ||
| 1325 | LOG_TRACE("{}: [{}] Cycle-slip detected in [{}], due to {}. Removing state: {}", nameId(), | ||
| 1326 | _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), satSigId, reason, key); | ||
| 1327 | ✗ | _ambiguitiesHold.erase(satSigId); | |
| 1328 | ✗ | _kalmanFilter.removeState(key); | |
| 1329 | ✗ | if (_outputStateEvents) { removedStates.push_back(key); } | |
| 1330 | ✗ | for (auto& detector : _cycleSlipDetector) { detector.resetSignal(key.satSigId); } | |
| 1331 | } | ||
| 1332 | ✗ | else if (_pivotSatellites.contains({ satSigId.code, GnssObs::Carrier }) && _pivotSatellites.at({ satSigId.code, GnssObs::Carrier }).satSigId == satSigId) | |
| 1333 | { | ||
| 1334 | LOG_TRACE("{}: [{}] Cycle-slip detected in pivot satellite [{}], due to {}", nameId(), | ||
| 1335 | _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), satSigId, reason); | ||
| 1336 | ✗ | pivotsToChange.push_back(satSigId.code); | |
| 1337 | } | ||
| 1338 | else | ||
| 1339 | { | ||
| 1340 | LOG_DATA("{}: [{}] Cycle-slip detected in [{}], due to {}. Not yet estimated (no action taken).", nameId(), | ||
| 1341 | _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), satSigId, reason); | ||
| 1342 | ✗ | return false; | |
| 1343 | } | ||
| 1344 | ✗ | return true; | |
| 1345 | 601 | }; | |
| 1346 | |||
| 1347 |
2/2✓ Branch 0 taken 1202 times.
✓ Branch 1 taken 601 times.
|
3606 | for (size_t i = 0; i < cycleSlipObservations.size(); ++i) |
| 1348 | { | ||
| 1349 | 1202 | std::string receiverName = fmt::format("{}", static_cast<ReceiverType>(i)); | |
| 1350 | LOG_DATA("{}: [{}][{}] Checking for cycle-slips", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), receiverName); | ||
| 1351 | |||
| 1352 |
5/10✓ Branch 1 taken 1202 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1202 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1202 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1202 times.
✗ Branch 11 not taken.
✓ Branch 14 taken 1202 times.
✗ Branch 15 not taken.
|
1202 | auto cycleSlips = _cycleSlipDetector.at(i).checkForCycleSlip(_receiver.at(i).gnssObs->insTime, cycleSlipObservations.at(i), nameId()); |
| 1353 |
1/2✗ Branch 5 not taken.
✓ Branch 6 taken 1202 times.
|
1202 | for (const auto& cycleSlip : cycleSlips) |
| 1354 | { | ||
| 1355 | ✗ | if (const auto* s = std::get_if<CycleSlipDetector::CycleSlipLossOfLockIndicator>(&cycleSlip)) | |
| 1356 | { | ||
| 1357 | ✗ | if (s->signal.code & _obsFilter.getCodeFilter() | |
| 1358 | ✗ | && removeSatSig(s->signal, "LLI set in " + receiverName)) | |
| 1359 | { | ||
| 1360 | ✗ | rtkSol->cycleSlipDetectorResult.emplace_back(cycleSlip, receiverName); | |
| 1361 | } | ||
| 1362 | } | ||
| 1363 | ✗ | else if (const auto* s = std::get_if<CycleSlipDetector::CycleSlipSingleFrequency>(&cycleSlip)) | |
| 1364 | { | ||
| 1365 | ✗ | if (s->signal.code & _obsFilter.getCodeFilter() | |
| 1366 | ✗ | && removeSatSig(s->signal, "single frequency check in " + receiverName)) | |
| 1367 | { | ||
| 1368 | ✗ | rtkSol->cycleSlipDetectorResult.emplace_back(cycleSlip, receiverName); | |
| 1369 | } | ||
| 1370 | } | ||
| 1371 | ✗ | else if (const auto* s = std::get_if<CycleSlipDetector::CycleSlipDualFrequency>(&cycleSlip)) | |
| 1372 | { | ||
| 1373 | ✗ | bool first = true; | |
| 1374 | ✗ | for (const auto& signal : s->signals) | |
| 1375 | { | ||
| 1376 | ✗ | if (signal.code & _obsFilter.getCodeFilter() | |
| 1377 | ✗ | && removeSatSig(signal, "dual frequency check in " + receiverName) | |
| 1378 | ✗ | && first) | |
| 1379 | { | ||
| 1380 | ✗ | rtkSol->cycleSlipDetectorResult.emplace_back(cycleSlip, receiverName); | |
| 1381 | ✗ | first = false; | |
| 1382 | } | ||
| 1383 | } | ||
| 1384 | } | ||
| 1385 | } | ||
| 1386 | 1202 | } | |
| 1387 | |||
| 1388 |
1/2✗ Branch 4 not taken.
✓ Branch 5 taken 601 times.
|
601 | for (const auto& code : pivotsToChange) |
| 1389 | { | ||
| 1390 | LOG_TRACE("{}: [{}] Changing pivot because of cycle-slip", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), code); | ||
| 1391 | ✗ | updatePivotSatellite(code, GnssObs::Carrier, observations, rtkSol, RtkSolution::PivotChange::Reason::PivotCycleSlip); | |
| 1392 | } | ||
| 1393 | |||
| 1394 |
1/2✗ Branch 6 not taken.
✓ Branch 7 taken 601 times.
|
601 | for (const auto& cycleSlip : rtkSol->cycleSlipDetectorResult) |
| 1395 | { | ||
| 1396 | ✗ | switch (cycleSlip.first.index()) | |
| 1397 | { | ||
| 1398 | ✗ | case 0: // CycleSlipLossOfLockIndicator | |
| 1399 | ✗ | _nCycleSlipsLLI++; | |
| 1400 | ✗ | break; | |
| 1401 | ✗ | case 1: // CycleSlipDualFrequency | |
| 1402 | ✗ | _nCycleSlipsDual++; | |
| 1403 | ✗ | break; | |
| 1404 | ✗ | case 2: // CycleSlipSingleFrequency | |
| 1405 | ✗ | _nCycleSlipsSingle++; | |
| 1406 | ✗ | break; | |
| 1407 | ✗ | default: | |
| 1408 | ✗ | break; | |
| 1409 | } | ||
| 1410 | ✗ | auto text = fmt::format("{}: {}", cycleSlip.second, cycleSlip.first); | |
| 1411 | LOG_DATA("{}: {}", nameId(), text); | ||
| 1412 | ✗ | addEventToGui(rtkSol, text); | |
| 1413 | ✗ | } | |
| 1414 |
1/2✗ Branch 5 not taken.
✓ Branch 6 taken 601 times.
|
601 | for (const auto& state : removedStates) |
| 1415 | { | ||
| 1416 | ✗ | auto text = fmt::format("State [{}] removed, because cycle-slip detected.", state); | |
| 1417 | LOG_DATA("{}: {}", nameId(), text); | ||
| 1418 | ✗ | addEventToGui(rtkSol, text); | |
| 1419 | ✗ | } | |
| 1420 | 601 | } | |
| 1421 | |||
| 1422 | 1203 | void RealTimeKinematic::removeSingleObservations(Observations& observations, ObservationFilter::Filtered* filtered, const std::shared_ptr<RtkSolution>& rtkSol) | |
| 1423 | { | ||
| 1424 | LOG_DATA("{}: Checking for observations to remove when only one observable per code for double difference", nameId()); | ||
| 1425 | |||
| 1426 | 1203 | std::unordered_map<std::pair<Code, GnssObs::ObservationType>, uint16_t> signalCount; | |
| 1427 |
2/2✓ Branch 7 taken 68699 times.
✓ Branch 8 taken 1203 times.
|
69902 | for (const auto& [satSigId, sigObs] : observations.signals) |
| 1428 | { | ||
| 1429 |
1/2✓ Branch 1 taken 68699 times.
✗ Branch 2 not taken.
|
68699 | const auto& recvObs = sigObs.recvObs.at(Base); |
| 1430 |
2/2✓ Branch 5 taken 206097 times.
✓ Branch 6 taken 68699 times.
|
274796 | for (const auto& obs : recvObs->obs) |
| 1431 | { | ||
| 1432 |
1/2✓ Branch 2 taken 206097 times.
✗ Branch 3 not taken.
|
206097 | signalCount[std::make_pair(satSigId.code, obs.first)]++; |
| 1433 | } | ||
| 1434 | } | ||
| 1435 | |||
| 1436 | 1203 | std::vector<SatSigId> sigToRemove; | |
| 1437 |
2/2✓ Branch 7 taken 68699 times.
✓ Branch 8 taken 1203 times.
|
69902 | for (auto& [satSigId, sigObs] : observations.signals) |
| 1438 | { | ||
| 1439 |
2/2✓ Branch 5 taken 137398 times.
✓ Branch 6 taken 68699 times.
|
206097 | for (auto& recvObs : sigObs.recvObs) |
| 1440 | { | ||
| 1441 | 137398 | std::vector<GnssObs::ObservationType> obsToRemove; | |
| 1442 |
2/2✓ Branch 6 taken 412194 times.
✓ Branch 7 taken 137398 times.
|
549592 | for (const auto& obs : recvObs.second->obs) |
| 1443 | { | ||
| 1444 |
2/4✓ Branch 2 taken 412194 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 412194 times.
|
412194 | if (signalCount[std::make_pair(satSigId.code, obs.first)] < 2) |
| 1445 | { | ||
| 1446 | ✗ | obsToRemove.push_back(obs.first); | |
| 1447 | } | ||
| 1448 | } | ||
| 1449 |
1/2✗ Branch 5 not taken.
✓ Branch 6 taken 137398 times.
|
137398 | for (const GnssObs::ObservationType& obs : obsToRemove) |
| 1450 | { | ||
| 1451 | LOG_DATA("{}: Removing [{}] {}", nameId(), satSigId, obs); | ||
| 1452 | ✗ | recvObs.second->obs.erase(obs); | |
| 1453 | ✗ | if (filtered) { filtered->singleObservation.push_back(satSigId); } | |
| 1454 | } | ||
| 1455 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 137398 times.
|
137398 | if (recvObs.second->obs.empty()) |
| 1456 | { | ||
| 1457 | ✗ | sigToRemove.push_back(satSigId); | |
| 1458 | ✗ | break; | |
| 1459 | } | ||
| 1460 | 137398 | } | |
| 1461 | } | ||
| 1462 | |||
| 1463 |
1/2✗ Branch 4 not taken.
✓ Branch 5 taken 1203 times.
|
1203 | for (const auto& satSigId : sigToRemove) |
| 1464 | { | ||
| 1465 | LOG_DATA("{}: Removing [{}], because no more observations left for this signal", nameId(), satSigId); | ||
| 1466 | |||
| 1467 | ✗ | if (filtered) // Only update pivot if called with filtered (means we modifying the actual observation data) | |
| 1468 | { | ||
| 1469 | ✗ | for (const auto& obsType : _obsFilter.getUsedObservationTypes()) | |
| 1470 | { | ||
| 1471 | ✗ | auto key = std::make_pair(satSigId.code, obsType); | |
| 1472 | ✗ | if (_pivotSatellites.contains(key) && signalCount[key] < 2) | |
| 1473 | { | ||
| 1474 | LOG_DATA("{}: Pivot [{}][{}] does not have enough signals anymore", nameId(), satSigId.code, obsType); | ||
| 1475 | ✗ | updatePivotSatellite(satSigId.code, obsType, observations, rtkSol, RtkSolution::PivotChange::Reason::PivotNotObservedInEpoch); | |
| 1476 | } | ||
| 1477 | } | ||
| 1478 | } | ||
| 1479 | |||
| 1480 | ✗ | observations.removeSignal(satSigId, nameId()); | |
| 1481 | } | ||
| 1482 | 1203 | } | |
| 1483 | |||
| 1484 | 12621 | std::optional<RtkSolution::PivotChange> RealTimeKinematic::updatePivotSatellite(Code code, GnssObs::ObservationType obsType, | |
| 1485 | Observations& observations, const std::shared_ptr<RtkSolution>& rtkSol, | ||
| 1486 | const RtkSolution::PivotChange::Reason& reason) | ||
| 1487 | { | ||
| 1488 | using Reason = RtkSolution::PivotChange::Reason; | ||
| 1489 | 12621 | std::optional<RtkSolution::PivotChange> pivotChange; | |
| 1490 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12621 times.
|
12621 | if (reason != Reason::None) |
| 1491 | { | ||
| 1492 | ✗ | pivotChange = RtkSolution::PivotChange{}; | |
| 1493 | ✗ | pivotChange->reason = reason; | |
| 1494 | ✗ | pivotChange->obsType = obsType; | |
| 1495 | |||
| 1496 | ✗ | if (_pivotSatellites.contains({ code, obsType })) | |
| 1497 | { | ||
| 1498 | ✗ | const auto& pivot = _pivotSatellites.at({ code, obsType }); | |
| 1499 | ✗ | if (observations.signals.contains(pivot.satSigId)) | |
| 1500 | { | ||
| 1501 | ✗ | auto oldPivotObs = observations.signals.at(pivot.satSigId).recvObs.begin(); | |
| 1502 | ✗ | pivotChange->oldPivotSat = pivot.satSigId; | |
| 1503 | ✗ | pivotChange->oldPivotElevation = oldPivotObs->second->satElevation(_receiver[Rover].e_posMarker, _receiver[Rover].lla_posMarker); | |
| 1504 | } | ||
| 1505 | ✗ | _pivotSatellites.erase({ code, obsType }); | |
| 1506 | } | ||
| 1507 | } | ||
| 1508 | |||
| 1509 | 316566 | auto sumRecvElevation = [&](double el, const auto& recvData) { | |
| 1510 | 316566 | return el + recvData.second->satElevation(_receiver[Rover].e_posMarker, _receiver[Rover].lla_posMarker); | |
| 1511 | 12621 | }; | |
| 1512 | |||
| 1513 |
2/2✓ Branch 7 taken 583506 times.
✓ Branch 8 taken 12621 times.
|
596127 | for (const auto& [satSigId, observation] : observations.signals) |
| 1514 | { | ||
| 1515 |
3/4✓ Branch 1 taken 583506 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 500148 times.
✓ Branch 4 taken 83358 times.
|
583506 | if (satSigId.code != code) { continue; } |
| 1516 | |||
| 1517 |
1/2✓ Branch 1 taken 83358 times.
✗ Branch 2 not taken.
|
83358 | bool anySignalOnThisCodeWasEstimatedYet = std::ranges::any_of(observations.signals, |
| 1518 |
1/2✓ Branch 1 taken 903048 times.
✗ Branch 2 not taken.
|
903048 | [&](const auto& obs) { return obs.first.code == code |
| 1519 |
5/6✓ Branch 0 taken 84096 times.
✓ Branch 1 taken 818952 times.
✓ Branch 5 taken 84096 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 83223 times.
✓ Branch 8 taken 873 times.
|
903048 | && _kalmanFilter.x.hasRow(States::AmbiguityDD(obs.first)); }); |
| 1520 |
1/2✓ Branch 1 taken 83358 times.
✗ Branch 2 not taken.
|
83358 | bool anySignalOnThisCodeWithFix = std::ranges::any_of(_ambiguitiesHold, |
| 1521 | 537807 | [&code](const auto& amb) { return amb.first.code == code; }); | |
| 1522 | |||
| 1523 | LOG_DATA("{}: [{}] Checking [{}] for new pivot and obsType [{}]", | ||
| 1524 | nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), satSigId, obsType); | ||
| 1525 | |||
| 1526 | 83358 | size_t nFound = 0; | |
| 1527 |
2/2✓ Branch 5 taken 166716 times.
✓ Branch 6 taken 83358 times.
|
250074 | for (const auto& recvObs : observation.recvObs) |
| 1528 | { | ||
| 1529 |
2/4✓ Branch 2 taken 166716 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 166716 times.
✗ Branch 5 not taken.
|
166716 | if (recvObs.second->obs.contains(obsType)) { nFound++; } |
| 1530 | } | ||
| 1531 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 83358 times.
|
83358 | if (nFound != observation.recvObs.size()) { continue; } |
| 1532 | |||
| 1533 | #if LOG_LEVEL <= LOG_LEVEL_DATA | ||
| 1534 | if (obsType == GnssObs::Carrier) | ||
| 1535 | { | ||
| 1536 | std::string ambHold; | ||
| 1537 | ambHold.reserve(_ambiguitiesHold.size() * 17); | ||
| 1538 | for (const auto& amb : _ambiguitiesHold) | ||
| 1539 | { | ||
| 1540 | ambHold += fmt::format("[{} = {}]", amb.first, amb.second); | ||
| 1541 | } | ||
| 1542 | LOG_DATA("{}: [{}] anySignalOnThisCodeWasEstimatedYet = {}, anySignalOnThisCodeWithFix = {}, ambHold = {}", | ||
| 1543 | nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), anySignalOnThisCodeWasEstimatedYet, anySignalOnThisCodeWithFix, ambHold); | ||
| 1544 | } | ||
| 1545 | #endif | ||
| 1546 | |||
| 1547 | // Don't allow same satellite to be pivot again | ||
| 1548 |
2/4✓ Branch 2 taken 83358 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 83358 times.
|
83358 | if (pivotChange->oldPivotSat == satSigId) { continue; } |
| 1549 | |||
| 1550 | 83358 | if (_kalmanFilter.x.rows() != States::KFStates_COUNT // Ignore first epoch, as we need to select a pivot | |
| 1551 |
2/2✓ Branch 0 taken 27741 times.
✓ Branch 1 taken 55482 times.
|
83223 | && obsType == GnssObs::Carrier // Ambiguity do not matter for pseudorange or doppler |
| 1552 |
1/6✗ Branch 0 not taken.
✓ Branch 1 taken 27741 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
27741 | && (anySignalOnThisCodeWasEstimatedYet || _pivotSatellites.contains({ code, GnssObs::Carrier })) // We have at least one satellite with an estimated ambiguity on this code |
| 1553 |
3/4✓ Branch 3 taken 27741 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 4206 times.
✓ Branch 6 taken 23535 times.
|
27741 | && !_kalmanFilter.x.hasRow(States::AmbiguityDD(satSigId)) // Only consider signals which were estimated once (float solution) |
| 1554 |
1/2✓ Branch 0 taken 4206 times.
✗ Branch 1 not taken.
|
4206 | && _ambiguityResolutionParameters.searchAlgorithm != AmbiguityResolutionParameters::SearchAlgorithm::None // If we estimating ambiguities |
| 1555 |
1/6✗ Branch 0 not taken.
✓ Branch 1 taken 4206 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
4206 | && (anySignalOnThisCodeWithFix || _pivotSatellites.contains({ code, GnssObs::Carrier })) // We have at least one satellite with a fix on this code |
| 1556 |
6/8✓ Branch 0 taken 83223 times.
✓ Branch 1 taken 135 times.
✓ Branch 3 taken 4206 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 4206 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 4206 times.
✓ Branch 8 taken 79152 times.
|
166581 | && !_ambiguitiesHold.contains(satSigId)) // Only consider signals, which have a fix |
| 1557 | { | ||
| 1558 | 4206 | continue; | |
| 1559 | } | ||
| 1560 | |||
| 1561 |
1/2✓ Branch 3 taken 79152 times.
✗ Branch 4 not taken.
|
79152 | double satElevation = std::accumulate(observation.recvObs.begin(), observation.recvObs.end(), 0.0, sumRecvElevation) // NOLINT(boost-use-ranges,modernize-use-ranges) // There is no ranges::accumulate |
| 1562 | 79152 | / static_cast<double>(observation.recvObs.size()); | |
| 1563 | |||
| 1564 |
3/4✓ Branch 2 taken 79152 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 21 times.
✓ Branch 5 taken 79131 times.
|
79152 | if (!_pivotSatellites.contains({ code, obsType })) // No pivot for this code yet |
| 1565 | { | ||
| 1566 |
1/2✓ Branch 3 taken 21 times.
✗ Branch 4 not taken.
|
21 | _pivotSatellites.insert(std::make_pair(std::make_pair(code, obsType), satSigId)); |
| 1567 |
1/2✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
|
21 | if (!pivotChange) |
| 1568 | { | ||
| 1569 | LOG_DATA("{}: [{}] Setting [{}] as new pivot for obsType [{}] (elevation {:.4}°, no previous pivot on this code)", nameId(), | ||
| 1570 | _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), satSigId, obsType, rad2deg(satElevation)); | ||
| 1571 |
3/4✓ Branch 0 taken 21 times.
✓ Branch 1 taken 21 times.
✓ Branch 3 taken 21 times.
✗ Branch 4 not taken.
|
63 | pivotChange = RtkSolution::PivotChange{ .reason = Reason::NewCode, |
| 1572 | .obsType = obsType, | ||
| 1573 | .oldPivotSat = {}, | ||
| 1574 | .oldPivotElevation = 0.0, | ||
| 1575 | .newPivotSat = satSigId, | ||
| 1576 | 21 | .newPivotElevation = satElevation }; | |
| 1577 | } | ||
| 1578 | else | ||
| 1579 | { | ||
| 1580 | LOG_DATA("{}: [{}] Setting [{}] as new pivot for obsType [{}] (elevation {:.4}°)", nameId(), | ||
| 1581 | _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), satSigId, obsType, rad2deg(satElevation)); | ||
| 1582 | ✗ | pivotChange->newPivotSat = satSigId; | |
| 1583 | ✗ | pivotChange->newPivotElevation = satElevation; | |
| 1584 | } | ||
| 1585 | } | ||
| 1586 | else // Check if better pivot available | ||
| 1587 | { | ||
| 1588 |
1/2✓ Branch 2 taken 79131 times.
✗ Branch 3 not taken.
|
79131 | const auto& pivotSat = _pivotSatellites.at({ code, obsType }); |
| 1589 | |||
| 1590 |
2/4✓ Branch 1 taken 79131 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 79131 times.
|
79131 | if (!observations.signals.contains(pivotSat.satSigId)) // Old pivot satellite was not observed this epoch, so we have to choose another one |
| 1591 | { | ||
| 1592 | LOG_DATA("{}: [{}] Setting [{}] as new pivot for obsType [{}] (elevation {:.4}°, old pivot [{}] not observed this epoch)", nameId(), | ||
| 1593 | _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), satSigId, obsType, rad2deg(satElevation), _pivotSatellites.at({ code, obsType }).satSigId); | ||
| 1594 | ✗ | pivotChange = RtkSolution::PivotChange{ .reason = Reason::PivotNotObservedInEpoch, | |
| 1595 | .obsType = obsType, | ||
| 1596 | ✗ | .oldPivotSat = _pivotSatellites.at({ code, obsType }).satSigId, | |
| 1597 | .oldPivotElevation = 0.0, | ||
| 1598 | .newPivotSat = satSigId, | ||
| 1599 | ✗ | .newPivotElevation = satElevation }; | |
| 1600 | ✗ | _pivotSatellites.at({ code, obsType }) = PivotSatellite(satSigId); | |
| 1601 | ✗ | continue; | |
| 1602 | } | ||
| 1603 | |||
| 1604 |
1/2✓ Branch 1 taken 79131 times.
✗ Branch 2 not taken.
|
79131 | const auto& pivotObs = observations.signals.at(pivotSat.satSigId); |
| 1605 |
1/2✓ Branch 3 taken 79131 times.
✗ Branch 4 not taken.
|
79131 | double pivElevation = std::accumulate(pivotObs.recvObs.begin(), pivotObs.recvObs.end(), 0.0, sumRecvElevation) // NOLINT(boost-use-ranges,modernize-use-ranges) // There is no ranges::accumulate |
| 1606 | 79131 | / static_cast<double>(pivotObs.recvObs.size()); | |
| 1607 | |||
| 1608 |
2/2✓ Branch 0 taken 36 times.
✓ Branch 1 taken 79095 times.
|
79131 | if (satElevation > pivElevation) |
| 1609 | { | ||
| 1610 | LOG_DATA("{}: [{}] Setting [{}] as new pivot for obsType [{}] (elevation {:.4}°, larger than previous pivot [{}] elevation {:.4}°)", nameId(), | ||
| 1611 | _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), satSigId, obsType, rad2deg(satElevation), _pivotSatellites.at({ code, obsType }).satSigId, rad2deg(pivElevation)); | ||
| 1612 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
|
36 | if (!pivotChange) |
| 1613 | { | ||
| 1614 | ✗ | pivotChange = RtkSolution::PivotChange{ .reason = Reason::HigherElevationFound, | |
| 1615 | .obsType = obsType, | ||
| 1616 | ✗ | .oldPivotSat = _pivotSatellites.at({ code, obsType }).satSigId, | |
| 1617 | .oldPivotElevation = pivElevation, | ||
| 1618 | .newPivotSat = satSigId, | ||
| 1619 | ✗ | .newPivotElevation = satElevation }; | |
| 1620 | } | ||
| 1621 | else | ||
| 1622 | { | ||
| 1623 | 36 | pivotChange->newPivotSat = satSigId; | |
| 1624 | 36 | pivotChange->newPivotElevation = satElevation; | |
| 1625 | } | ||
| 1626 |
1/2✓ Branch 3 taken 36 times.
✗ Branch 4 not taken.
|
36 | _pivotSatellites.at({ code, obsType }) = PivotSatellite(satSigId); |
| 1627 | } | ||
| 1628 | } | ||
| 1629 | } | ||
| 1630 | |||
| 1631 | // React on changed pivot satellite | ||
| 1632 |
2/2✓ Branch 1 taken 21 times.
✓ Branch 2 taken 12600 times.
|
12621 | if (pivotChange) |
| 1633 | { | ||
| 1634 |
5/10✓ Branch 2 taken 21 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 21 times.
✗ Branch 5 not taken.
✓ Branch 9 taken 21 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 21 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 21 times.
✗ Branch 15 not taken.
|
21 | INS_ASSERT_USER_ERROR(!_pivotSatellites.contains({ code, obsType }) |
| 1635 | || _pivotSatellites.at({ code, obsType }).satSigId == pivotChange->newPivotSat, | ||
| 1636 | "The new pivot satellite and current one must match."); | ||
| 1637 |
2/12✓ Branch 3 taken 21 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 21 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
|
21 | INS_ASSERT_USER_ERROR(pivotChange->oldPivotSat != pivotChange->newPivotSat |
| 1638 | || pivotChange->newPivotSat == SatSigId{}, | ||
| 1639 | "Old and new pivot satellite have to be different."); | ||
| 1640 | |||
| 1641 | LOG_TRACE("{}: [{}] {}", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), *pivotChange, obsType); | ||
| 1642 | |||
| 1643 |
1/2✓ Branch 4 taken 21 times.
✗ Branch 5 not taken.
|
21 | rtkSol->changedPivotSatellites.emplace(std::make_pair(code, obsType), *pivotChange); |
| 1644 | 21 | _nPivotChange++; | |
| 1645 |
1/2✓ Branch 3 taken 21 times.
✗ Branch 4 not taken.
|
42 | addEventToGui(rtkSol, fmt::format("{}", *pivotChange)); |
| 1646 | |||
| 1647 |
5/8✓ Branch 0 taken 21 times.
✓ Branch 1 taken 21 times.
✓ Branch 3 taken 21 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 21 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 21 times.
|
42 | if (pivotChange->newPivotSat == SatSigId{}) // No new pivot selected |
| 1648 | { | ||
| 1649 | ✗ | observations.removeMeasurementsFor(pivotChange->oldPivotSat.code, obsType, nameId()); | |
| 1650 | ✗ | if (obsType == GnssObs::Carrier) | |
| 1651 | { | ||
| 1652 | ✗ | for (size_t i = States::KFStates_COUNT; i < _kalmanFilter.x.rowKeys().size(); i++) // 0-2 Pos, 3-5 Vel | |
| 1653 | { | ||
| 1654 | ✗ | const auto& key = _kalmanFilter.x.rowKeys().at(i); | |
| 1655 | ✗ | if (const auto* ambDD = std::get_if<States::AmbiguityDD>(&key)) | |
| 1656 | { | ||
| 1657 | ✗ | if (ambDD->satSigId.code != pivotChange->oldPivotSat.code) { continue; } | |
| 1658 | |||
| 1659 | LOG_DATA("{}: [{}] Removing Amibguity as no pivot anymore {}", nameId(), ambDD->satSigId, key); | ||
| 1660 | ✗ | if (_outputStateEvents) { addEventToGui(rtkSol, fmt::format("State [{}] removed (not observed anymore)", key)); } | |
| 1661 | ✗ | _ambiguitiesHold.erase(ambDD->satSigId); | |
| 1662 | ✗ | _kalmanFilter.removeState(key); // After this 'key' is invalidated | |
| 1663 | ✗ | i--; | |
| 1664 | } | ||
| 1665 | } | ||
| 1666 | } | ||
| 1667 | } | ||
| 1668 |
2/6✗ Branch 1 not taken.
✓ Branch 2 taken 21 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 21 times.
|
21 | else if (pivotChange->reason != Reason::NewCode && obsType == GnssObs::Carrier) |
| 1669 | { | ||
| 1670 | ✗ | updateKalmanFilterAmbiguitiesForPivotChange(pivotChange->newPivotSat, pivotChange->oldPivotSat, | |
| 1671 | ✗ | pivotChange->reason != Reason::PivotNotObservedInEpoch | |
| 1672 | ✗ | && pivotChange->reason != Reason::PivotCycleSlip | |
| 1673 | ✗ | && pivotChange->reason != Reason::PivotOutlier, | |
| 1674 | rtkSol); | ||
| 1675 | } | ||
| 1676 | } | ||
| 1677 | 25242 | return pivotChange; | |
| 1678 | } | ||
| 1679 | |||
| 1680 | 602 | void RealTimeKinematic::printPivotSatellites() | |
| 1681 | { | ||
| 1682 | LOG_DATA("{}: Current pivot satellites:", nameId()); | ||
| 1683 |
2/2✓ Branch 7 taken 12621 times.
✓ Branch 8 taken 602 times.
|
13223 | for ([[maybe_unused]] const auto& [key, pivot] : _pivotSatellites) |
| 1684 | { | ||
| 1685 | LOG_DATA("{}: [{} - {}]: {}", nameId(), key.first, key.second, pivot.satSigId); | ||
| 1686 |
2/2✓ Branch 0 taken 8414 times.
✓ Branch 1 taken 4207 times.
|
12621 | if (key.second != GnssObs::Carrier) { continue; } |
| 1687 | 4207 | if (auto state = States::AmbiguityDD{ pivot.satSigId }; | |
| 1688 |
2/4✓ Branch 2 taken 4207 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 4207 times.
|
4207 | _kalmanFilter.x.hasRow(state)) |
| 1689 | { | ||
| 1690 | ✗ | LOG_ERROR("{}: [{}] The Kalman Filter has the state [{}] but this is a pivot. Pivots should not be in the state.", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), state); | |
| 1691 | } | ||
| 1692 | } | ||
| 1693 | 602 | } | |
| 1694 | |||
| 1695 | 601 | void RealTimeKinematic::updatePivotSatellites(Observations& observations, const std::shared_ptr<RtkSolution>& rtkSol) | |
| 1696 | { | ||
| 1697 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | printPivotSatellites(); |
| 1698 | |||
| 1699 | 601 | std::set<Code> codes; | |
| 1700 |
2/2✓ Branch 7 taken 27786 times.
✓ Branch 8 taken 601 times.
|
28387 | for (const auto& [satSigId, observation] : observations.signals) |
| 1701 | { | ||
| 1702 |
1/2✓ Branch 1 taken 27786 times.
✗ Branch 2 not taken.
|
27786 | codes.insert(satSigId.code); |
| 1703 | } | ||
| 1704 | 601 | bool anyPivotChanged = false; | |
| 1705 |
2/2✓ Branch 5 taken 4207 times.
✓ Branch 6 taken 601 times.
|
4808 | for (const auto& code : codes) |
| 1706 | { | ||
| 1707 |
2/2✓ Branch 5 taken 12621 times.
✓ Branch 6 taken 4207 times.
|
16828 | for (const auto& obsType : _obsFilter.getUsedObservationTypes()) |
| 1708 | { | ||
| 1709 | 12621 | auto reason = RtkSolution::PivotChange::Reason::None; | |
| 1710 | |||
| 1711 |
3/4✓ Branch 2 taken 12621 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 12600 times.
✓ Branch 5 taken 21 times.
|
12621 | if (_pivotSatellites.contains({ code, obsType })) |
| 1712 | { | ||
| 1713 |
1/2✓ Branch 2 taken 12600 times.
✗ Branch 3 not taken.
|
12600 | const auto& pivotSat = _pivotSatellites.at({ code, obsType }); |
| 1714 | 12600 | const auto& pivotSatSigId = pivotSat.satSigId; | |
| 1715 |
1/2✓ Branch 1 taken 12600 times.
✗ Branch 2 not taken.
|
12600 | auto obs = std::ranges::find_if(observations.signals, [&](const auto& obs) { |
| 1716 | 382533 | size_t nFound = 0; | |
| 1717 |
2/2✓ Branch 5 taken 765066 times.
✓ Branch 6 taken 382533 times.
|
1147599 | for (const auto& recvObs : obs.second.recvObs) |
| 1718 | { | ||
| 1719 |
2/4✓ Branch 2 taken 765066 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 765066 times.
✗ Branch 5 not taken.
|
765066 | if (recvObs.second->obs.contains(obsType)) { nFound++; } |
| 1720 | } | ||
| 1721 |
3/4✓ Branch 1 taken 12600 times.
✓ Branch 2 taken 369933 times.
✓ Branch 4 taken 12600 times.
✗ Branch 5 not taken.
|
382533 | return obs.first == pivotSatSigId && nFound == obs.second.recvObs.size(); |
| 1722 | }); | ||
| 1723 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 12600 times.
|
12600 | if (obs == observations.signals.end()) |
| 1724 | { | ||
| 1725 | LOG_DATA("{}: [{}] Removing pivot satellite [{}] on observable [{}] because not observed this epoch.", nameId(), | ||
| 1726 | _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), pivotSat.satSigId, obsType); | ||
| 1727 | ✗ | reason = RtkSolution::PivotChange::Reason::PivotNotObservedInEpoch; | |
| 1728 | } | ||
| 1729 | } | ||
| 1730 | |||
| 1731 |
1/2✓ Branch 1 taken 12621 times.
✗ Branch 2 not taken.
|
12621 | anyPivotChanged |= updatePivotSatellite(code, obsType, observations, rtkSol, reason).has_value(); |
| 1732 | } | ||
| 1733 | } | ||
| 1734 | |||
| 1735 | LOG_DATA("{}: {}", nameId(), anyPivotChanged ? "Pivot satellite changed" : "No pivot satellite change"); | ||
| 1736 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 600 times.
|
601 | if (anyPivotChanged) |
| 1737 | { | ||
| 1738 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | removeSingleObservations(observations, &rtkSol->filtered, rtkSol); |
| 1739 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | printPivotSatellites(); |
| 1740 | } | ||
| 1741 | 601 | } | |
| 1742 | |||
| 1743 | ✗ | void RealTimeKinematic::updateKalmanFilterAmbiguitiesForPivotChange(const SatSigId& newPivotSatSigId, const SatSigId& oldPivotSatSigId, | |
| 1744 | bool oldPivotObservedInEpoch, const std::shared_ptr<RtkSolution>& rtkSol) | ||
| 1745 | { | ||
| 1746 | ✗ | auto newPivotKey = States::AmbiguityDD{ newPivotSatSigId }; | |
| 1747 | ✗ | if (_kalmanFilter.x.hasRow(newPivotKey)) | |
| 1748 | { | ||
| 1749 | ✗ | std::vector<States::StateKeyType> ambiguitiesToChange; | |
| 1750 | ✗ | for (size_t i = States::KFStates_COUNT; i < _kalmanFilter.x.rowKeys().size(); i++) | |
| 1751 | { | ||
| 1752 | ✗ | const auto* ambDD = std::get_if<States::AmbiguityDD>(&_kalmanFilter.x.rowKeys().at(i)); | |
| 1753 | ✗ | if (ambDD && newPivotSatSigId.code == ambDD->satSigId.code | |
| 1754 | ✗ | && (oldPivotObservedInEpoch || newPivotSatSigId != ambDD->satSigId)) | |
| 1755 | { | ||
| 1756 | ✗ | ambiguitiesToChange.emplace_back(*ambDD); | |
| 1757 | } | ||
| 1758 | } | ||
| 1759 | LOG_TRACE("{}: [{}] New pivot [{}] adapts ambiguities [{}]", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), | ||
| 1760 | newPivotSatSigId, fmt::join(ambiguitiesToChange, ", ")); | ||
| 1761 | |||
| 1762 | ✗ | auto nStates = static_cast<int>(_kalmanFilter.x.rowKeys().size()); | |
| 1763 | |||
| 1764 | ✗ | KeyedMatrixXd<States::StateKeyType> D(Eigen::MatrixXd::Identity(nStates, nStates), | |
| 1765 | ✗ | _kalmanFilter.x.rowKeys(), _kalmanFilter.x.rowKeys()); | |
| 1766 | ✗ | D(ambiguitiesToChange, newPivotKey).setConstant(-1.0); | |
| 1767 | ✗ | if (!oldPivotObservedInEpoch) { D(newPivotKey, newPivotKey) = 0.0; } | |
| 1768 | LOG_DATA("{}: D = \n{}", nameId(), D); | ||
| 1769 | |||
| 1770 | LOG_DATA("{}: x_old = \n{}", nameId(), _kalmanFilter.x.transposed()); | ||
| 1771 | ✗ | _kalmanFilter.x(all) = D(all, all) * _kalmanFilter.x(all); | |
| 1772 | LOG_DATA("{}: D * x_old = \n{}", nameId(), _kalmanFilter.x.transposed()); | ||
| 1773 | |||
| 1774 | LOG_DATA("{}: P_old = \n{}", nameId(), _kalmanFilter.P); | ||
| 1775 | // Error propagation | ||
| 1776 | ✗ | _kalmanFilter.P(all, all) = D(all, all) * _kalmanFilter.P(all, all) * D(all, all).transpose(); | |
| 1777 | |||
| 1778 | #if LOG_LEVEL <= LOG_LEVEL_DATA | ||
| 1779 | { | ||
| 1780 | std::string ambHold; | ||
| 1781 | ambHold.reserve(_ambiguitiesHold.size() * 17); | ||
| 1782 | for (const auto& amb : ambiguitiesToChange) | ||
| 1783 | { | ||
| 1784 | if (const auto* ambDD = std::get_if<States::AmbiguityDD>(&amb)) | ||
| 1785 | { | ||
| 1786 | if (_ambiguitiesHold.contains(ambDD->satSigId)) | ||
| 1787 | { | ||
| 1788 | ambHold += fmt::format("[{} = {}]", ambDD->satSigId, _ambiguitiesHold.at(ambDD->satSigId)); | ||
| 1789 | } | ||
| 1790 | } | ||
| 1791 | } | ||
| 1792 | LOG_DATA("{}: ambHold (pre ) = {}", nameId(), ambHold); | ||
| 1793 | } | ||
| 1794 | #endif | ||
| 1795 | |||
| 1796 | ✗ | for (const auto& amb : ambiguitiesToChange) | |
| 1797 | { | ||
| 1798 | ✗ | if (const auto* ambDD = std::get_if<States::AmbiguityDD>(&amb)) | |
| 1799 | { | ||
| 1800 | ✗ | if (_ambiguitiesHold.contains(ambDD->satSigId)) | |
| 1801 | { | ||
| 1802 | ✗ | _ambiguitiesHold.at(ambDD->satSigId) = _kalmanFilter.x(amb); | |
| 1803 | } | ||
| 1804 | } | ||
| 1805 | } | ||
| 1806 | |||
| 1807 | ✗ | if (oldPivotObservedInEpoch) | |
| 1808 | { | ||
| 1809 | ✗ | auto oldPivotKey = States::AmbiguityDD{ oldPivotSatSigId }; | |
| 1810 | ✗ | if (_ambiguityResolutionParameters.searchAlgorithm != AmbiguityResolutionParameters::SearchAlgorithm::None | |
| 1811 | ✗ | && _ambiguitiesHold.contains(newPivotSatSigId)) | |
| 1812 | { | ||
| 1813 | ✗ | _ambiguitiesHold[oldPivotSatSigId] = _ambiguitiesHold.at(newPivotSatSigId); | |
| 1814 | ✗ | _ambiguitiesHold.erase(newPivotSatSigId); | |
| 1815 | } | ||
| 1816 | ✗ | _kalmanFilter.replaceState(newPivotKey, oldPivotKey); | |
| 1817 | ✗ | if (_outputStateEvents) | |
| 1818 | { | ||
| 1819 | ✗ | addEventToGui(rtkSol, fmt::format("State [{}] replaced with [{}] (because [{}] is new pivot satellite)", newPivotKey, oldPivotKey, newPivotSatSigId)); | |
| 1820 | } | ||
| 1821 | LOG_TRACE("{}: [{}] State [{}] replaced with [{}] (because [{}] is new pivot satellite)", nameId(), | ||
| 1822 | _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), newPivotKey, oldPivotKey, newPivotSatSigId); | ||
| 1823 | } | ||
| 1824 | else | ||
| 1825 | { | ||
| 1826 | ✗ | _ambiguitiesHold.erase(newPivotSatSigId); | |
| 1827 | ✗ | _kalmanFilter.removeState(newPivotKey); | |
| 1828 | ✗ | for (auto& detector : _cycleSlipDetector) | |
| 1829 | { | ||
| 1830 | ✗ | detector.resetSignal(newPivotSatSigId); | |
| 1831 | ✗ | detector.resetSignal(oldPivotSatSigId); | |
| 1832 | } | ||
| 1833 | ✗ | if (_outputStateEvents) | |
| 1834 | { | ||
| 1835 | ✗ | addEventToGui(rtkSol, fmt::format("State [{}] removed (because new pivot satellite)", newPivotKey)); | |
| 1836 | } | ||
| 1837 | LOG_TRACE("{}: [{}] State [{}] removed (because new pivot satellite)", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), newPivotKey); | ||
| 1838 | } | ||
| 1839 | LOG_DATA("{}: x_new = \n{}", nameId(), _kalmanFilter.x.transposed()); | ||
| 1840 | LOG_DATA("{}: P_new = \n{}", nameId(), _kalmanFilter.P); | ||
| 1841 | |||
| 1842 | #if LOG_LEVEL <= LOG_LEVEL_DATA | ||
| 1843 | { | ||
| 1844 | std::erase_if(ambiguitiesToChange, [&newPivotSatSigId](const auto& amb) { | ||
| 1845 | if (const auto* ambDD = std::get_if<States::AmbiguityDD>(&amb)) | ||
| 1846 | { | ||
| 1847 | return ambDD->satSigId == newPivotSatSigId; | ||
| 1848 | } | ||
| 1849 | return false; | ||
| 1850 | }); | ||
| 1851 | if (oldPivotObservedInEpoch) | ||
| 1852 | { | ||
| 1853 | ambiguitiesToChange.emplace_back(States::AmbiguityDD{ oldPivotSatSigId }); | ||
| 1854 | } | ||
| 1855 | |||
| 1856 | std::string ambHold; | ||
| 1857 | ambHold.reserve(_ambiguitiesHold.size() * 17); | ||
| 1858 | for (const auto& amb : ambiguitiesToChange) | ||
| 1859 | { | ||
| 1860 | if (const auto* ambDD = std::get_if<States::AmbiguityDD>(&amb)) | ||
| 1861 | { | ||
| 1862 | if (_ambiguitiesHold.contains(ambDD->satSigId)) | ||
| 1863 | { | ||
| 1864 | ambHold += fmt::format("[{} = {}]", ambDD->satSigId, _ambiguitiesHold.at(ambDD->satSigId)); | ||
| 1865 | } | ||
| 1866 | } | ||
| 1867 | } | ||
| 1868 | LOG_DATA("{}: ambHold (post) = {}", nameId(), ambHold); | ||
| 1869 | } | ||
| 1870 | #endif | ||
| 1871 | ✗ | } | |
| 1872 | ✗ | } | |
| 1873 | |||
| 1874 | 601 | void RealTimeKinematic::addOrRemoveKalmanFilterAmbiguities(const Observations& observations, const std::shared_ptr<RtkSolution>& rtkSol) | |
| 1875 | { | ||
| 1876 | 601 | bool anyStateChanged = false; | |
| 1877 | |||
| 1878 | 47 | auto addEvent = [&](const std::string& text) { | |
| 1879 |
1/2✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
|
47 | if (_outputStateEvents) { addEventToGui(rtkSol, text); } |
| 1880 | 648 | }; | |
| 1881 | |||
| 1882 | 601 | std::vector<States::StateKeyType> newAddedAmbiguities; | |
| 1883 | |||
| 1884 |
2/2✓ Branch 7 taken 27786 times.
✓ Branch 8 taken 601 times.
|
28387 | for (const auto& [satSigId, observation] : observations.signals) |
| 1885 | { | ||
| 1886 |
2/4✓ Branch 1 taken 27786 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 27786 times.
✗ Branch 6 not taken.
|
27786 | if (!observation.recvObs.at(Rover)->obs.contains(GnssObs::Carrier) |
| 1887 |
5/10✓ Branch 0 taken 27786 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 27786 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 27786 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 27786 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 27786 times.
|
27786 | || !observation.recvObs.at(Base)->obs.contains(GnssObs::Carrier)) { continue; } |
| 1888 | |||
| 1889 | 27786 | auto key = States::AmbiguityDD{ satSigId }; | |
| 1890 |
1/2✓ Branch 2 taken 27786 times.
✗ Branch 3 not taken.
|
27786 | if (!_kalmanFilter.x.hasRow(key) // Ambiguity not in state yet |
| 1891 |
2/4✓ Branch 2 taken 4251 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4251 times.
✗ Branch 5 not taken.
|
4251 | && _pivotSatellites.contains({ satSigId.code, GnssObs::Carrier }) // There should be a pivot satellite for this observation |
| 1892 |
8/10✓ Branch 0 taken 4251 times.
✓ Branch 1 taken 23535 times.
✓ Branch 4 taken 4251 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4251 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 44 times.
✓ Branch 10 taken 4207 times.
✓ Branch 11 taken 44 times.
✓ Branch 12 taken 27742 times.
|
32037 | && _pivotSatellites.at({ satSigId.code, GnssObs::Carrier }).satSigId != satSigId) // Don't add a ambiguity for pivot satellite |
| 1893 | { | ||
| 1894 | 44 | anyStateChanged = true; | |
| 1895 | LOG_TRACE("{}: [{}] Adding state: {}", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), key); | ||
| 1896 |
1/2✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
|
44 | _kalmanFilter.addState(key); |
| 1897 |
1/2✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
|
44 | newAddedAmbiguities.emplace_back(key); |
| 1898 |
1/2✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
|
44 | if (std::ranges::find(rtkSol->newEstimatedAmbiguity, satSigId) |
| 1899 |
1/2✓ Branch 3 taken 44 times.
✗ Branch 4 not taken.
|
88 | == rtkSol->newEstimatedAmbiguity.end()) |
| 1900 | { | ||
| 1901 |
1/2✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
|
44 | rtkSol->newEstimatedAmbiguity.push_back(satSigId); |
| 1902 | } | ||
| 1903 | |||
| 1904 | // F: Entries are all 0 | ||
| 1905 | // Ambiguities are modeled as RW with very small noise to keep numerical stability | ||
| 1906 |
1/2✓ Branch 3 taken 44 times.
✗ Branch 4 not taken.
|
44 | _kalmanFilter.G(key, key) = 1; |
| 1907 |
1/2✓ Branch 3 taken 44 times.
✗ Branch 4 not taken.
|
44 | _kalmanFilter.Phi(key, key) = 1; // This is set, because we only calculate the matrix for PosVel |
| 1908 | |||
| 1909 |
3/4✓ Branch 0 taken 6 times.
✓ Branch 1 taken 38 times.
✓ Branch 5 taken 44 times.
✗ Branch 6 not taken.
|
44 | _kalmanFilter.W(key, key) = _lastSolutionStatus == RtkSolution::SolutionType::RTK_Fixed ? _ambiguityFixProcessNoiseVariance : _ambiguityFloatProcessNoiseVariance; |
| 1910 | |||
| 1911 |
1/2✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
|
44 | const auto& pivotSat = _pivotSatellites.at({ satSigId.code, GnssObs::Carrier }); |
| 1912 | LOG_DATA("{}: {} - pivot {}", nameId(), satSigId, pivotSat.satSigId); | ||
| 1913 |
2/4✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 44 times.
|
44 | if (!observations.signals.contains(pivotSat.satSigId)) |
| 1914 | { | ||
| 1915 | ✗ | LOG_CRITICAL("{}: [{}] The pivot satellite [{}] does not have a carrier measurement. It should have been switched before.", | |
| 1916 | nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), pivotSat.satSigId); | ||
| 1917 | } | ||
| 1918 |
1/2✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
|
44 | const auto& pivotObs = observations.signals.at(pivotSat.satSigId); |
| 1919 | |||
| 1920 | // Initialize with difference of 1/lambda (carrier-phase_DD - pseudorange_DD) measurement. | ||
| 1921 |
2/4✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 44 times.
✗ Branch 6 not taken.
|
44 | double lambda_j = InsConst::C / satSigId.freq().getFrequency(observation.freqNum()); |
| 1922 | |||
| 1923 | // Carrier is always there, otherwise we would not initialize the ambiguity | ||
| 1924 |
2/4✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 44 times.
✗ Branch 6 not taken.
|
44 | const auto& phi_r_1 = pivotObs.recvObs.at(Rover)->obs.at(GnssObs::Carrier); |
| 1925 |
2/4✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 44 times.
✗ Branch 6 not taken.
|
44 | const auto& phi_b_1 = pivotObs.recvObs.at(Base)->obs.at(GnssObs::Carrier); |
| 1926 |
2/4✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 44 times.
✗ Branch 6 not taken.
|
44 | const auto& phi_r_s = observation.recvObs.at(Rover)->obs.at(GnssObs::Carrier); |
| 1927 |
2/4✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 44 times.
✗ Branch 6 not taken.
|
44 | const auto& phi_b_s = observation.recvObs.at(Base)->obs.at(GnssObs::Carrier); |
| 1928 | |||
| 1929 | 44 | double phi_br_s_meas = phi_r_s.measurement - phi_b_s.measurement; | |
| 1930 | 44 | double phi_br_1_meas = phi_r_1.measurement - phi_b_1.measurement; | |
| 1931 | 44 | double phi_br_1s_meas = phi_br_s_meas - phi_br_1_meas; | |
| 1932 | |||
| 1933 | 44 | double varCarrierPseudorange = std::numeric_limits<double>::infinity(); | |
| 1934 | 44 | bool forceCarrierPseudorange = false; | |
| 1935 | |||
| 1936 | // Pseudorange could be missing | ||
| 1937 |
2/4✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 44 times.
✗ Branch 6 not taken.
|
44 | if (observation.recvObs.at(Rover)->obs.contains(GnssObs::Pseudorange) |
| 1938 |
3/6✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 44 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 44 times.
✗ Branch 8 not taken.
|
44 | && observation.recvObs.at(Base)->obs.contains(GnssObs::Pseudorange) |
| 1939 |
4/8✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
✓ Branch 4 taken 44 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 44 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 44 times.
✗ Branch 9 not taken.
|
88 | && _pivotSatellites.contains({ satSigId.code, GnssObs::Pseudorange })) |
| 1940 | { | ||
| 1941 |
1/2✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
|
44 | const auto& pivotSatPsr = _pivotSatellites.at({ satSigId.code, GnssObs::Pseudorange }); |
| 1942 |
2/4✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 44 times.
|
44 | if (pivotSatPsr.satSigId == satSigId) // We cannot initialize the ambiguity with different pivot satellites |
| 1943 | { | ||
| 1944 | ✗ | const auto& pivotObsPsr = observations.signals.at(pivotSatPsr.satSigId); | |
| 1945 | ✗ | const auto& p_r_1 = pivotObsPsr.recvObs.at(Rover)->obs.at(GnssObs::Pseudorange); | |
| 1946 | ✗ | const auto& p_b_1 = pivotObsPsr.recvObs.at(Base)->obs.at(GnssObs::Pseudorange); | |
| 1947 | ✗ | const auto& p_r_s = observation.recvObs.at(Rover)->obs.at(GnssObs::Pseudorange); | |
| 1948 | ✗ | const auto& p_b_s = observation.recvObs.at(Base)->obs.at(GnssObs::Pseudorange); | |
| 1949 | |||
| 1950 | ✗ | varCarrierPseudorange = phi_r_s.measVar + p_r_s.measVar + phi_b_s.measVar + p_b_s.measVar | |
| 1951 | ✗ | + phi_r_1.measVar + p_r_1.measVar + phi_b_1.measVar + p_b_1.measVar; | |
| 1952 | |||
| 1953 | ✗ | double posUncertanty = _kalmanFilter.P.block<3>(States::Pos, States::Pos).diagonal().cwiseSqrt().norm(); | |
| 1954 | ✗ | forceCarrierPseudorange = posUncertanty > 0.06; // Always use the Carrier-Pseudorange difference when position is not good (stdDev 5cm) | |
| 1955 | } | ||
| 1956 | } | ||
| 1957 | |||
| 1958 |
2/4✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 44 times.
✗ Branch 7 not taken.
|
44 | Eigen::Vector3d e_pLOS_1s = pivotObs.recvObs.at(Rover)->e_pLOS(_receiver[Rover].e_posMarker) |
| 1959 |
4/8✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 44 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 44 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 44 times.
✗ Branch 13 not taken.
|
88 | - observation.recvObs.at(Rover)->e_pLOS(_receiver[Rover].e_posMarker); |
| 1960 | 44 | const double varCarrierGeometry = phi_r_s.measVar + phi_b_s.measVar + phi_r_1.measVar + phi_b_1.measVar | |
| 1961 |
5/10✓ Branch 3 taken 44 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 44 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 44 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 44 times.
✗ Branch 13 not taken.
✓ Branch 15 taken 44 times.
✗ Branch 16 not taken.
|
44 | + e_pLOS_1s.transpose() * _kalmanFilter.P.block<3>(States::Pos, States::Pos) * e_pLOS_1s; |
| 1962 | LOG_DATA("{}: varCarrierGeometry = {:.2g} [m], varCarrierPseudorange = {:.2g} [m]", nameId(), varCarrierGeometry, varCarrierPseudorange); | ||
| 1963 | |||
| 1964 |
2/4✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 44 times.
|
44 | if (varCarrierPseudorange < varCarrierGeometry || forceCarrierPseudorange) |
| 1965 | { | ||
| 1966 | ✗ | const auto& p_r_1 = pivotObs.recvObs.at(Rover)->obs.at(GnssObs::Pseudorange); | |
| 1967 | ✗ | const auto& p_b_1 = pivotObs.recvObs.at(Base)->obs.at(GnssObs::Pseudorange); | |
| 1968 | ✗ | const auto& p_r_s = observation.recvObs.at(Rover)->obs.at(GnssObs::Pseudorange); | |
| 1969 | ✗ | const auto& p_b_s = observation.recvObs.at(Base)->obs.at(GnssObs::Pseudorange); | |
| 1970 | |||
| 1971 | ✗ | double p_br_s_meas = p_r_s.measurement - p_b_s.measurement; | |
| 1972 | ✗ | double p_br_1_meas = p_r_1.measurement - p_b_1.measurement; | |
| 1973 | ✗ | double p_br_1s_meas = p_br_s_meas - p_br_1_meas; | |
| 1974 | |||
| 1975 | ✗ | _kalmanFilter.x(key) = (phi_br_1s_meas - p_br_1s_meas) / lambda_j; | |
| 1976 | |||
| 1977 | ✗ | _kalmanFilter.P(key, key) = varCarrierPseudorange * 3.0 / std::pow(lambda_j, 2); // Safety factor of 3 and division by lambda^2, because state in [cycles] | |
| 1978 | auto msg = fmt::format("ddCP({:.1f}m) - ddPR({:.1f}m) = {:.1f} [cycles], std = {:.2f} [cycles]", | ||
| 1979 | ✗ | phi_br_1s_meas, p_br_1s_meas, _kalmanFilter.x(key), std::sqrt(_kalmanFilter.P(key, key))); | |
| 1980 | LOG_DATA("{}: [{}] Init Amibguity: {}", nameId(), satSigId, msg); | ||
| 1981 | |||
| 1982 | ✗ | addEvent(fmt::format("State [{}] added ({})", key, msg)); | |
| 1983 | ✗ | } | |
| 1984 | else // Carrier-Geometry Difference | ||
| 1985 | { | ||
| 1986 | 44 | double phi_br_s_est = phi_r_s.estimate - phi_b_s.estimate; | |
| 1987 | 44 | double phi_br_1_est = phi_r_1.estimate - phi_b_1.estimate; | |
| 1988 | 44 | double phi_br_1s_est = phi_br_s_est - phi_br_1_est; | |
| 1989 | |||
| 1990 |
1/2✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
|
44 | _kalmanFilter.x(key) = (phi_br_1s_meas - phi_br_1s_est) / lambda_j; |
| 1991 | |||
| 1992 |
1/2✓ Branch 4 taken 44 times.
✗ Branch 5 not taken.
|
44 | _kalmanFilter.P(key, key) = varCarrierGeometry * 3.0 / std::pow(lambda_j, 2); // Safety factor of 3 and division by lambda^2, because state in [cycles] |
| 1993 | auto msg = fmt::format("ddCP({:.1f}m) - ddEst({:.1f}m) = {:.1f} [cycles], std = {:.2f} [cycles]", | ||
| 1994 |
2/4✓ Branch 3 taken 44 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 44 times.
✗ Branch 8 not taken.
|
44 | phi_br_1s_meas, phi_br_1s_est, _kalmanFilter.x(key), std::sqrt(_kalmanFilter.P(key, key))); |
| 1995 | LOG_DATA("{}: [{}] Init Amibguity: {}", nameId(), satSigId, msg); | ||
| 1996 | |||
| 1997 |
1/2✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
|
88 | addEvent(fmt::format("State [{}] added ({})", key, msg)); |
| 1998 | 44 | } | |
| 1999 | } | ||
| 2000 | } | ||
| 2001 | |||
| 2002 |
2/2✓ Branch 2 taken 23582 times.
✓ Branch 3 taken 601 times.
|
24183 | for (size_t i = States::KFStates_COUNT; i < _kalmanFilter.x.rowKeys().size(); i++) // 0-2 Pos, 3-5 Vel |
| 2003 | { | ||
| 2004 |
1/2✓ Branch 2 taken 23582 times.
✗ Branch 3 not taken.
|
23582 | const auto& key = _kalmanFilter.x.rowKeys().at(i); |
| 2005 |
1/2✓ Branch 1 taken 23582 times.
✗ Branch 2 not taken.
|
23582 | if (const auto* ambDD = std::get_if<States::AmbiguityDD>(&key)) |
| 2006 | { | ||
| 2007 |
3/4✓ Branch 1 taken 23582 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 23579 times.
✓ Branch 4 taken 3 times.
|
23582 | if (observations.countObservations(ambDD->satSigId, GnssObs::Carrier) == ReceiverType_COUNT) { continue; } |
| 2008 | |||
| 2009 | LOG_DATA("{}: [{}] Removing Ambiguity as not observed anymore {}", nameId(), ambDD->satSigId, key); | ||
| 2010 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | _ambiguitiesHold.erase(ambDD->satSigId); |
| 2011 |
1/2✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
6 | addEvent(fmt::format("State [{}] removed (not observed anymore)", key)); |
| 2012 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | _kalmanFilter.removeState(key); // After this 'key' is invalidated |
| 2013 | 3 | i--; | |
| 2014 | } | ||
| 2015 | } | ||
| 2016 | |||
| 2017 | if (anyStateChanged) | ||
| 2018 | { | ||
| 2019 | LOG_DATA("{}: x =\n{}", nameId(), _kalmanFilter.x.transposed()); | ||
| 2020 | LOG_DATA("{}: P =\n{}", nameId(), _kalmanFilter.P); | ||
| 2021 | LOG_DATA("{}: G =\n{}", nameId(), _kalmanFilter.G); | ||
| 2022 | LOG_DATA("{}: W =\n{}", nameId(), _kalmanFilter.W); | ||
| 2023 | } | ||
| 2024 | 601 | } | |
| 2025 | |||
| 2026 | 601 | RealTimeKinematic::Differences RealTimeKinematic::calcSingleDifferences(const Observations& observations) const // NOLINT(readability-convert-member-functions-to-static) | |
| 2027 | { | ||
| 2028 | 601 | Differences singleDifferences; | |
| 2029 |
1/2✓ Branch 2 taken 601 times.
✗ Branch 3 not taken.
|
601 | singleDifferences.reserve(observations.signals.size()); |
| 2030 | |||
| 2031 | LOG_DATA("{}: Calculating single differences (rover - base):", nameId()); | ||
| 2032 |
2/2✓ Branch 7 taken 27786 times.
✓ Branch 8 taken 601 times.
|
28387 | for (const auto& [satSigId, observation] : observations.signals) |
| 2033 | { | ||
| 2034 |
1/2✓ Branch 1 taken 27786 times.
✗ Branch 2 not taken.
|
27786 | const auto& baseObservations = observation.recvObs.at(Base)->obs; |
| 2035 |
1/2✓ Branch 1 taken 27786 times.
✗ Branch 2 not taken.
|
27786 | const auto& roverObservations = observation.recvObs.at(Rover)->obs; |
| 2036 | |||
| 2037 |
2/2✓ Branch 6 taken 83358 times.
✓ Branch 7 taken 27786 times.
|
111144 | for (const auto& [obsType, baseObs] : baseObservations) |
| 2038 | { | ||
| 2039 |
1/2✓ Branch 1 taken 83358 times.
✗ Branch 2 not taken.
|
83358 | const auto& roverObs = roverObservations.at(obsType); |
| 2040 | |||
| 2041 |
2/4✓ Branch 1 taken 83358 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 83358 times.
✗ Branch 5 not taken.
|
83358 | singleDifferences[satSigId][obsType].estimate = roverObs.estimate - baseObs.estimate; |
| 2042 |
2/4✓ Branch 1 taken 83358 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 83358 times.
✗ Branch 5 not taken.
|
83358 | singleDifferences[satSigId].at(obsType).measurement = roverObs.measurement - baseObs.measurement; |
| 2043 | |||
| 2044 | LOG_DATA("{}: [{}][{:11}][Meas] r {:.3f} - b {:.3f} = {:.3f}", nameId(), satSigId, obsType, | ||
| 2045 | roverObs.measurement, baseObs.measurement, singleDifferences[satSigId][obsType].measurement); | ||
| 2046 | LOG_DATA("{}: [{}][{:11}][Est ] r {:.3f} - b {:.3f} = {:.3f} (diff to meas {:.3e})", nameId(), satSigId, obsType, | ||
| 2047 | roverObs.estimate, baseObs.estimate, singleDifferences[satSigId][obsType].estimate, | ||
| 2048 | singleDifferences[satSigId][obsType].measurement - singleDifferences[satSigId][obsType].estimate); | ||
| 2049 | } | ||
| 2050 | } | ||
| 2051 | |||
| 2052 | 601 | return singleDifferences; | |
| 2053 | ✗ | } | |
| 2054 | |||
| 2055 | 601 | RealTimeKinematic::Differences RealTimeKinematic::calcDoubleDifferences(const Observations& observations, const Differences& singleDifferences) const | |
| 2056 | { | ||
| 2057 | 601 | Differences doubleDifferences; | |
| 2058 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 601 times.
|
601 | if (singleDifferences.empty()) { return doubleDifferences; } |
| 2059 |
1/2✓ Branch 2 taken 601 times.
✗ Branch 3 not taken.
|
601 | doubleDifferences.reserve(singleDifferences.size() - 1); |
| 2060 | |||
| 2061 | LOG_DATA("{}: [{}] Calculating double differences (sat - pivot):", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST)); | ||
| 2062 |
2/2✓ Branch 7 taken 27786 times.
✓ Branch 8 taken 601 times.
|
28387 | for (const auto& [satSigId_s, singleDiff_s] : singleDifferences) |
| 2063 | { | ||
| 2064 |
2/2✓ Branch 7 taken 83358 times.
✓ Branch 8 taken 27786 times.
|
111144 | for (const auto& [obsType, sDiff_s] : singleDiff_s) |
| 2065 | { | ||
| 2066 |
2/4✓ Branch 2 taken 83358 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 83358 times.
|
83358 | if (!_pivotSatellites.contains({ satSigId_s.code, obsType })) |
| 2067 | { | ||
| 2068 | LOG_DATA("{}: [{}] Not calculating double difference for [{} {}] because no pivot satellite.", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), satSigId_s.code, obsType); | ||
| 2069 | ✗ | continue; | |
| 2070 | } | ||
| 2071 |
1/2✓ Branch 2 taken 83358 times.
✗ Branch 3 not taken.
|
83358 | const auto& satSigId_1 = _pivotSatellites.at({ satSigId_s.code, obsType }).satSigId; |
| 2072 |
3/4✓ Branch 1 taken 83358 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 12621 times.
✓ Branch 4 taken 70737 times.
|
83358 | if (satSigId_s == satSigId_1) { continue; } // No double difference with itself |
| 2073 | |||
| 2074 |
2/4✓ Branch 1 taken 70737 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 70737 times.
|
70737 | if (!singleDifferences.contains(satSigId_1)) |
| 2075 | { | ||
| 2076 | ✗ | LOG_WARN("{}: [{}] Pivot [{}] has no single difference. This is a bug.", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), satSigId_1); | |
| 2077 | ✗ | continue; | |
| 2078 | } | ||
| 2079 |
3/6✓ Branch 1 taken 70737 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 70737 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 70737 times.
|
70737 | if (!singleDifferences.at(satSigId_1).contains(obsType)) |
| 2080 | { | ||
| 2081 | ✗ | LOG_WARN("{}: [{}] Pivot [{}] has no observation type [{}]. This is a bug.", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), satSigId_1, obsType); | |
| 2082 | ✗ | continue; | |
| 2083 | } | ||
| 2084 | |||
| 2085 |
1/2✓ Branch 1 taken 70737 times.
✗ Branch 2 not taken.
|
70737 | const auto& singleDiff_1 = singleDifferences.at(satSigId_1); |
| 2086 | |||
| 2087 |
1/2✓ Branch 1 taken 70737 times.
✗ Branch 2 not taken.
|
70737 | const auto& sDiff_1 = singleDiff_1.at(obsType); |
| 2088 | |||
| 2089 |
2/4✓ Branch 1 taken 70737 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 70737 times.
✗ Branch 5 not taken.
|
70737 | doubleDifferences[satSigId_s][obsType].estimate = sDiff_s.estimate - sDiff_1.estimate; |
| 2090 |
2/4✓ Branch 1 taken 70737 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 70737 times.
✗ Branch 5 not taken.
|
70737 | doubleDifferences[satSigId_s].at(obsType).measurement = sDiff_s.measurement - sDiff_1.measurement; |
| 2091 | LOG_DATA("{}: [{} - {}][{:11}][Meas] sat {:.3f} - piv {:.3f} = {:.3f}", nameId(), satSigId_s, satSigId_1, obsType, | ||
| 2092 | sDiff_s.measurement, sDiff_1.measurement, doubleDifferences[satSigId_s][obsType].measurement); | ||
| 2093 | |||
| 2094 |
2/2✓ Branch 0 taken 23579 times.
✓ Branch 1 taken 47158 times.
|
70737 | if (obsType == GnssObs::Carrier) // Pivot satellite ambiguity is 0 and not in state |
| 2095 | { | ||
| 2096 |
3/6✓ Branch 1 taken 23579 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 23579 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 23579 times.
✗ Branch 9 not taken.
|
23579 | double lambda_j_1 = InsConst::C / satSigId_1.freq().getFrequency(observations.signals.at(satSigId_1).freqNum()); |
| 2097 |
1/2✓ Branch 3 taken 23579 times.
✗ Branch 4 not taken.
|
23579 | double N_br_1s = _kalmanFilter.x(States::AmbiguityDD{ satSigId_s }); |
| 2098 |
2/4✓ Branch 1 taken 23579 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 23579 times.
✗ Branch 5 not taken.
|
23579 | doubleDifferences[satSigId_s].at(obsType).estimate += lambda_j_1 * N_br_1s; |
| 2099 | } | ||
| 2100 | LOG_DATA("{}: [{} - {}][{:11}][Est ] sat {:.3f} - piv {:.3f} = {:.3f} (diff to meas {:.3e})", nameId(), satSigId_s, satSigId_1, obsType, | ||
| 2101 | sDiff_s.estimate, sDiff_1.estimate, doubleDifferences[satSigId_s][obsType].estimate, | ||
| 2102 | doubleDifferences[satSigId_s][obsType].measurement - doubleDifferences[satSigId_s][obsType].estimate); | ||
| 2103 | } | ||
| 2104 | } | ||
| 2105 | |||
| 2106 | 601 | return doubleDifferences; | |
| 2107 | ✗ | } | |
| 2108 | |||
| 2109 | unordered_map<GnssObs::ObservationType, KeyedMatrixXd<RTK::Meas::SingleObs<RealTimeKinematic::ReceiverType>>> | ||
| 2110 | 601 | RealTimeKinematic::calcSingleObsMeasurementNoiseMatrices(const Observations& observations) const // NOLINT(readability-convert-member-functions-to-static) | |
| 2111 | { | ||
| 2112 | 601 | unordered_map<GnssObs::ObservationType, std::vector<RTK::Meas::SingleObs<ReceiverType>>> measKeys; | |
| 2113 |
2/2✓ Branch 7 taken 27786 times.
✓ Branch 8 taken 601 times.
|
28387 | for (const auto& [satSigId, observation] : observations.signals) |
| 2114 | { | ||
| 2115 |
3/4✓ Branch 1 taken 27786 times.
✗ Branch 2 not taken.
✓ Branch 10 taken 83358 times.
✓ Branch 11 taken 27786 times.
|
111144 | for (const auto& [obsType, baseObs] : observation.recvObs.at(Base)->obs) |
| 2116 | { | ||
| 2117 |
2/4✓ Branch 1 taken 83358 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 83358 times.
✗ Branch 5 not taken.
|
83358 | measKeys[obsType].emplace_back(satSigId, Rover, obsType); |
| 2118 |
2/4✓ Branch 1 taken 83358 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 83358 times.
✗ Branch 5 not taken.
|
83358 | measKeys[obsType].emplace_back(satSigId, Base, obsType); |
| 2119 | } | ||
| 2120 | } | ||
| 2121 | |||
| 2122 | 601 | unordered_map<GnssObs::ObservationType, KeyedMatrixXd<RTK::Meas::SingleObs<ReceiverType>>> Rtilde; | |
| 2123 |
2/2✓ Branch 6 taken 1803 times.
✓ Branch 7 taken 601 times.
|
2404 | for (const auto& [obsType, keys] : measKeys) |
| 2124 | { | ||
| 2125 |
3/6✓ Branch 4 taken 1803 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1803 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1803 times.
✗ Branch 11 not taken.
|
1803 | Rtilde[obsType] = KeyedMatrixXd<RTK::Meas::SingleObs<ReceiverType>>(Eigen::MatrixXd::Zero(static_cast<int>(keys.size()), static_cast<int>(keys.size())), keys); |
| 2126 | } | ||
| 2127 | |||
| 2128 |
2/2✓ Branch 7 taken 27786 times.
✓ Branch 8 taken 601 times.
|
28387 | for (const auto& [satSigId, observation] : observations.signals) |
| 2129 | { | ||
| 2130 |
1/2✓ Branch 1 taken 27786 times.
✗ Branch 2 not taken.
|
27786 | const auto& baseObservations = observation.recvObs.at(Base)->obs; |
| 2131 |
1/2✓ Branch 1 taken 27786 times.
✗ Branch 2 not taken.
|
27786 | const auto& roverObservations = observation.recvObs.at(Rover)->obs; |
| 2132 | |||
| 2133 |
2/2✓ Branch 6 taken 83358 times.
✓ Branch 7 taken 27786 times.
|
111144 | for (const auto& [obsType, baseObs] : baseObservations) |
| 2134 | { | ||
| 2135 |
1/2✓ Branch 1 taken 83358 times.
✗ Branch 2 not taken.
|
83358 | const auto& roverObs = roverObservations.at(obsType); |
| 2136 | |||
| 2137 | 83358 | RTK::Meas::SingleObs<ReceiverType> roverKey(satSigId, Rover, obsType); | |
| 2138 | 83358 | RTK::Meas::SingleObs<ReceiverType> baseKey(satSigId, Base, obsType); | |
| 2139 | |||
| 2140 |
2/4✓ Branch 1 taken 83358 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 83358 times.
✗ Branch 5 not taken.
|
83358 | Rtilde.at(obsType)(roverKey, roverKey) = roverObs.measVar; |
| 2141 |
2/4✓ Branch 1 taken 83358 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 83358 times.
✗ Branch 5 not taken.
|
83358 | Rtilde.at(obsType)(baseKey, baseKey) = baseObs.measVar; |
| 2142 | } | ||
| 2143 | } | ||
| 2144 | |||
| 2145 | // for ([[maybe_unused]] const auto& [obsType, R] : Rtilde) | ||
| 2146 | // { | ||
| 2147 | // LOG_DATA("{}: R_tilde({}) =\n{}", nameId(), obsType, R); | ||
| 2148 | // } | ||
| 2149 | |||
| 2150 | 1202 | return Rtilde; | |
| 2151 | 601 | } | |
| 2152 | |||
| 2153 | 601 | void RealTimeKinematic::calcKalmanUpdateMatrices(const Observations& observations, const Differences& doubleDifferences, | |
| 2154 | const unordered_map<GnssObs::ObservationType, KeyedMatrixXd<RTK::Meas::SingleObs<RealTimeKinematic::ReceiverType>>>& Rtilde) | ||
| 2155 | { | ||
| 2156 | // Update the Measurement sensitivity Matrix (𝐇), the Measurement noise covariance matrix (𝐑) and the Measurement vector (𝐳) | ||
| 2157 | |||
| 2158 | 601 | std::vector<Meas::MeasKeyTypes> measKeys; | |
| 2159 |
1/2✓ Branch 4 taken 601 times.
✗ Branch 5 not taken.
|
601 | measKeys.reserve(doubleDifferences.size() * _obsFilter.getUsedObservationTypes().size()); |
| 2160 |
2/2✓ Branch 0 taken 1803 times.
✓ Branch 1 taken 601 times.
|
2404 | for (size_t i = 0; i < GnssObs::ObservationType_COUNT; i++) |
| 2161 | { | ||
| 2162 |
4/4✓ Branch 0 taken 1202 times.
✓ Branch 1 taken 601 times.
✓ Branch 2 taken 601 times.
✓ Branch 3 taken 601 times.
|
1803 | GnssObs::ObservationType oType = i == 0 ? GnssObs::Carrier : (i == 1 ? GnssObs::Pseudorange : GnssObs::Doppler); |
| 2163 | |||
| 2164 |
2/2✓ Branch 7 taken 70737 times.
✓ Branch 8 taken 1803 times.
|
72540 | for (const auto& [satSigId, doubleDiff] : doubleDifferences) |
| 2165 | { | ||
| 2166 |
2/2✓ Branch 7 taken 212211 times.
✓ Branch 8 taken 70737 times.
|
282948 | for (const auto& [obsType, diff] : doubleDiff) |
| 2167 | { | ||
| 2168 |
2/2✓ Branch 0 taken 70737 times.
✓ Branch 1 taken 141474 times.
|
212211 | if (obsType == oType) |
| 2169 | { | ||
| 2170 |
3/5✓ Branch 0 taken 23579 times.
✓ Branch 1 taken 23579 times.
✓ Branch 2 taken 23579 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
70737 | switch (obsType) |
| 2171 | { | ||
| 2172 | 23579 | case GnssObs::Pseudorange: | |
| 2173 |
1/2✓ Branch 1 taken 23579 times.
✗ Branch 2 not taken.
|
23579 | measKeys.emplace_back(Meas::PsrDD{ satSigId }); |
| 2174 | 23579 | break; | |
| 2175 | 23579 | case GnssObs::Carrier: | |
| 2176 |
1/2✓ Branch 1 taken 23579 times.
✗ Branch 2 not taken.
|
23579 | measKeys.emplace_back(Meas::CarrierDD{ satSigId }); |
| 2177 | 23579 | break; | |
| 2178 | 23579 | case GnssObs::Doppler: | |
| 2179 |
1/2✓ Branch 1 taken 23579 times.
✗ Branch 2 not taken.
|
23579 | measKeys.emplace_back(Meas::DopplerDD{ satSigId }); |
| 2180 | 23579 | break; | |
| 2181 | ✗ | case GnssObs::ObservationType_COUNT: | |
| 2182 | ✗ | LOG_CRITICAL("{}: ObservationType_COUNT is not a valid value", nameId()); | |
| 2183 | break; | ||
| 2184 | } | ||
| 2185 | } | ||
| 2186 | } | ||
| 2187 | } | ||
| 2188 | } | ||
| 2189 | |||
| 2190 | LOG_DATA("{}: Setting measurement keys: {}", nameId(), joinToString(measKeys)); | ||
| 2191 |
1/2✓ Branch 2 taken 601 times.
✗ Branch 3 not taken.
|
601 | _kalmanFilter.setMeasurements(measKeys); |
| 2192 | |||
| 2193 | // R matrix | ||
| 2194 |
2/2✓ Branch 7 taken 1803 times.
✓ Branch 8 taken 601 times.
|
2404 | for (const auto& [obsType, R] : Rtilde) |
| 2195 | { | ||
| 2196 | 1803 | std::vector<Meas::MeasKeyTypes> obsTypeMeasKeys; | |
| 2197 |
2/2✓ Branch 5 taken 212211 times.
✓ Branch 6 taken 1803 times.
|
214014 | for (const auto& key : measKeys) |
| 2198 | { | ||
| 2199 |
7/8✓ Branch 1 taken 70737 times.
✓ Branch 2 taken 141474 times.
✓ Branch 3 taken 23579 times.
✓ Branch 4 taken 47158 times.
✓ Branch 5 taken 23579 times.
✓ Branch 6 taken 188632 times.
✓ Branch 8 taken 23579 times.
✗ Branch 9 not taken.
|
212211 | if (std::holds_alternative<Meas::PsrDD>(key) && obsType == GnssObs::Pseudorange) { obsTypeMeasKeys.push_back(key); } |
| 2200 |
7/8✓ Branch 1 taken 70737 times.
✓ Branch 2 taken 117895 times.
✓ Branch 3 taken 23579 times.
✓ Branch 4 taken 47158 times.
✓ Branch 5 taken 23579 times.
✓ Branch 6 taken 165053 times.
✓ Branch 8 taken 23579 times.
✗ Branch 9 not taken.
|
188632 | else if (std::holds_alternative<Meas::CarrierDD>(key) && obsType == GnssObs::Carrier) { obsTypeMeasKeys.push_back(key); } |
| 2201 |
7/8✓ Branch 1 taken 70737 times.
✓ Branch 2 taken 94316 times.
✓ Branch 3 taken 23579 times.
✓ Branch 4 taken 47158 times.
✓ Branch 5 taken 23579 times.
✓ Branch 6 taken 141474 times.
✓ Branch 8 taken 23579 times.
✗ Branch 9 not taken.
|
165053 | else if (std::holds_alternative<Meas::DopplerDD>(key) && obsType == GnssObs::Doppler) { obsTypeMeasKeys.push_back(key); } |
| 2202 | } | ||
| 2203 |
1/2✓ Branch 3 taken 1803 times.
✗ Branch 4 not taken.
|
1803 | KeyedMatrixXd<RTK::Meas::MeasKeyTypes, RTK::Meas::SingleObs<ReceiverType>> J(Eigen::MatrixXd::Zero(static_cast<int>(obsTypeMeasKeys.size()), R.rows()), |
| 2204 |
1/2✓ Branch 4 taken 1803 times.
✗ Branch 5 not taken.
|
3606 | obsTypeMeasKeys, R.rowKeys()); |
| 2205 | |||
| 2206 |
2/2✓ Branch 4 taken 70737 times.
✓ Branch 5 taken 1803 times.
|
72540 | for (const auto& rowKey : obsTypeMeasKeys) |
| 2207 | { | ||
| 2208 |
1/2✓ Branch 1 taken 70737 times.
✗ Branch 2 not taken.
|
70737 | SatSigId satSigId_s; |
| 2209 |
3/5✓ Branch 0 taken 23579 times.
✓ Branch 1 taken 23579 times.
✓ Branch 2 taken 23579 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
70737 | switch (obsType) |
| 2210 | { | ||
| 2211 | 23579 | case GnssObs::Pseudorange: | |
| 2212 |
1/2✓ Branch 1 taken 23579 times.
✗ Branch 2 not taken.
|
23579 | satSigId_s = std::get<Meas::PsrDD>(rowKey).satSigId; |
| 2213 | 23579 | break; | |
| 2214 | 23579 | case GnssObs::Carrier: | |
| 2215 |
1/2✓ Branch 1 taken 23579 times.
✗ Branch 2 not taken.
|
23579 | satSigId_s = std::get<Meas::CarrierDD>(rowKey).satSigId; |
| 2216 | 23579 | break; | |
| 2217 | 23579 | case GnssObs::Doppler: | |
| 2218 |
1/2✓ Branch 1 taken 23579 times.
✗ Branch 2 not taken.
|
23579 | satSigId_s = std::get<Meas::DopplerDD>(rowKey).satSigId; |
| 2219 | 23579 | break; | |
| 2220 | ✗ | case GnssObs::ObservationType_COUNT: | |
| 2221 | ✗ | LOG_CRITICAL("{}: ObservationType_COUNT is not a valid value", nameId()); | |
| 2222 | break; | ||
| 2223 | } | ||
| 2224 | |||
| 2225 |
1/2✓ Branch 2 taken 70737 times.
✗ Branch 3 not taken.
|
70737 | const auto& satSigId_1 = _pivotSatellites.at({ satSigId_s.code, obsType }).satSigId; |
| 2226 | |||
| 2227 |
1/2✓ Branch 2 taken 70737 times.
✗ Branch 3 not taken.
|
70737 | J(rowKey, RTK::Meas::SingleObs<ReceiverType>(satSigId_s, Rover, obsType)) = 1; |
| 2228 |
1/2✓ Branch 2 taken 70737 times.
✗ Branch 3 not taken.
|
70737 | J(rowKey, RTK::Meas::SingleObs<ReceiverType>(satSigId_s, Base, obsType)) = -1; |
| 2229 |
1/2✓ Branch 2 taken 70737 times.
✗ Branch 3 not taken.
|
70737 | J(rowKey, RTK::Meas::SingleObs<ReceiverType>(satSigId_1, Rover, obsType)) = -1; |
| 2230 |
1/2✓ Branch 2 taken 70737 times.
✗ Branch 3 not taken.
|
70737 | J(rowKey, RTK::Meas::SingleObs<ReceiverType>(satSigId_1, Base, obsType)) = 1; |
| 2231 | } | ||
| 2232 | // LOG_DATA("{}: J({}) =\n{}", nameId(), obsType, J); | ||
| 2233 | |||
| 2234 |
5/10✓ Branch 2 taken 1803 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 1803 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1803 times.
✗ Branch 11 not taken.
✓ Branch 15 taken 1803 times.
✗ Branch 16 not taken.
✓ Branch 18 taken 1803 times.
✗ Branch 19 not taken.
|
1803 | _kalmanFilter.R(obsTypeMeasKeys, obsTypeMeasKeys) = J(all, all) * R(all, all) * J(all, all).transpose(); |
| 2235 | 1803 | } | |
| 2236 | LOG_DATA("{}: R =\n{}", nameId(), _kalmanFilter.R); | ||
| 2237 | |||
| 2238 |
2/2✓ Branch 7 taken 23579 times.
✓ Branch 8 taken 601 times.
|
24180 | for (const auto& [satSigId_s, doubleDiff] : doubleDifferences) |
| 2239 | { | ||
| 2240 |
1/2✓ Branch 1 taken 23579 times.
✗ Branch 2 not taken.
|
23579 | const auto& obs_s = observations.signals.at(satSigId_s); |
| 2241 | |||
| 2242 |
2/4✓ Branch 1 taken 23579 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 23579 times.
✗ Branch 6 not taken.
|
23579 | double lambda_j = InsConst::C / satSigId_s.freq().getFrequency(obs_s.freqNum()); |
| 2243 | |||
| 2244 |
2/4✓ Branch 1 taken 23579 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 23579 times.
✗ Branch 7 not taken.
|
23579 | const auto& e_pLOS_s = obs_s.recvObs.at(Rover)->e_pLOS(_receiver[Rover].e_posMarker); |
| 2245 | |||
| 2246 |
2/2✓ Branch 7 taken 70737 times.
✓ Branch 8 taken 23579 times.
|
94316 | for (const auto& [obsType, obs] : doubleDiff) |
| 2247 | { | ||
| 2248 |
1/2✓ Branch 2 taken 70737 times.
✗ Branch 3 not taken.
|
70737 | const auto& satSigId_1 = _pivotSatellites.at({ satSigId_s.code, obsType }).satSigId; |
| 2249 |
1/2✓ Branch 1 taken 70737 times.
✗ Branch 2 not taken.
|
70737 | const auto& obs_1 = observations.signals.at(satSigId_1); |
| 2250 |
2/4✓ Branch 1 taken 70737 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 70737 times.
✗ Branch 7 not taken.
|
70737 | const auto& e_pLOS_1 = obs_1.recvObs.at(Rover)->e_pLOS(_receiver[Rover].e_posMarker); |
| 2251 | |||
| 2252 |
3/5✓ Branch 0 taken 23579 times.
✓ Branch 1 taken 23579 times.
✓ Branch 2 taken 23579 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
70737 | switch (obsType) |
| 2253 | { | ||
| 2254 | 23579 | case GnssObs::Pseudorange: | |
| 2255 |
1/2✓ Branch 2 taken 23579 times.
✗ Branch 3 not taken.
|
23579 | _kalmanFilter.z(Meas::PsrDD{ satSigId_s }) = obs.measurement - obs.estimate; |
| 2256 |
4/8✓ Branch 1 taken 23579 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 23579 times.
✗ Branch 5 not taken.
✓ Branch 9 taken 23579 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 23579 times.
✗ Branch 13 not taken.
|
23579 | _kalmanFilter.H.block<3>(Meas::PsrDD{ satSigId_s }, States::Pos) = (e_pLOS_1 - e_pLOS_s).transpose(); |
| 2257 | 23579 | break; | |
| 2258 | 23579 | case GnssObs::Carrier: | |
| 2259 |
1/2✓ Branch 2 taken 23579 times.
✗ Branch 3 not taken.
|
23579 | _kalmanFilter.z(Meas::CarrierDD{ satSigId_s }) = obs.measurement - obs.estimate; |
| 2260 |
4/8✓ Branch 1 taken 23579 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 23579 times.
✗ Branch 5 not taken.
✓ Branch 9 taken 23579 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 23579 times.
✗ Branch 13 not taken.
|
23579 | _kalmanFilter.H.block<3>(Meas::CarrierDD{ satSigId_s }, States::Pos) = (e_pLOS_1 - e_pLOS_s).transpose(); |
| 2261 |
1/2✓ Branch 4 taken 23579 times.
✗ Branch 5 not taken.
|
23579 | _kalmanFilter.H(Meas::CarrierDD{ satSigId_s }, States::AmbiguityDD{ satSigId_s }) = lambda_j; |
| 2262 | 23579 | break; | |
| 2263 | 23579 | case GnssObs::Doppler: | |
| 2264 | { | ||
| 2265 |
1/2✓ Branch 2 taken 23579 times.
✗ Branch 3 not taken.
|
23579 | _kalmanFilter.z(Meas::DopplerDD{ satSigId_s }) = obs.measurement - obs.estimate; |
| 2266 |
2/4✓ Branch 1 taken 23579 times.
✗ Branch 2 not taken.
✓ Branch 7 taken 23579 times.
✗ Branch 8 not taken.
|
23579 | const auto& e_vLOS_1 = obs_1.recvObs.at(Rover)->e_vLOS(_receiver[Rover].e_posMarker, _receiver[Rover].e_vel); |
| 2267 |
2/4✓ Branch 1 taken 23579 times.
✗ Branch 2 not taken.
✓ Branch 7 taken 23579 times.
✗ Branch 8 not taken.
|
23579 | const auto& e_vLOS_s = obs_s.recvObs.at(Rover)->e_vLOS(_receiver[Rover].e_posMarker, _receiver[Rover].e_vel); |
| 2268 | |||
| 2269 |
1/2✓ Branch 3 taken 23579 times.
✗ Branch 4 not taken.
|
23579 | _kalmanFilter.H.block<3>(Meas::DopplerDD{ satSigId_s }, States::Pos) = Eigen::RowVector3d( |
| 2270 |
20/40✓ Branch 1 taken 23579 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 23579 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 23579 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 23579 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 23579 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 23579 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 23579 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 23579 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 23579 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 23579 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 23579 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 23579 times.
✗ Branch 35 not taken.
✓ Branch 37 taken 23579 times.
✗ Branch 38 not taken.
✓ Branch 40 taken 23579 times.
✗ Branch 41 not taken.
✓ Branch 43 taken 23579 times.
✗ Branch 44 not taken.
✓ Branch 46 taken 23579 times.
✗ Branch 47 not taken.
✓ Branch 49 taken 23579 times.
✗ Branch 50 not taken.
✓ Branch 52 taken 23579 times.
✗ Branch 53 not taken.
✓ Branch 55 taken 23579 times.
✗ Branch 56 not taken.
✓ Branch 58 taken 23579 times.
✗ Branch 59 not taken.
|
23579 | -e_vLOS_1.x() * e_pLOS_1.x() * e_pLOS_1.x() + e_vLOS_1.x() + e_vLOS_s.x() * e_pLOS_s.x() * e_pLOS_s.x() - e_vLOS_s.x() - e_vLOS_1.y() * e_pLOS_1.x() * e_pLOS_1.y() + e_vLOS_s.y() * e_pLOS_s.x() * e_pLOS_s.y() - e_vLOS_1.z() * e_pLOS_1.x() * e_pLOS_1.z() + e_vLOS_s.z() * e_pLOS_s.x() * e_pLOS_s.z(), |
| 2271 |
20/40✓ Branch 1 taken 23579 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 23579 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 23579 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 23579 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 23579 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 23579 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 23579 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 23579 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 23579 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 23579 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 23579 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 23579 times.
✗ Branch 35 not taken.
✓ Branch 37 taken 23579 times.
✗ Branch 38 not taken.
✓ Branch 40 taken 23579 times.
✗ Branch 41 not taken.
✓ Branch 43 taken 23579 times.
✗ Branch 44 not taken.
✓ Branch 46 taken 23579 times.
✗ Branch 47 not taken.
✓ Branch 49 taken 23579 times.
✗ Branch 50 not taken.
✓ Branch 52 taken 23579 times.
✗ Branch 53 not taken.
✓ Branch 55 taken 23579 times.
✗ Branch 56 not taken.
✓ Branch 58 taken 23579 times.
✗ Branch 59 not taken.
|
23579 | -e_vLOS_1.x() * e_pLOS_1.x() * e_pLOS_1.y() + e_vLOS_s.x() * e_pLOS_s.x() * e_pLOS_s.y() - e_vLOS_1.y() * e_pLOS_1.y() * e_pLOS_1.y() + e_vLOS_1.y() + e_vLOS_s.y() * e_pLOS_s.y() * e_pLOS_s.y() - e_vLOS_s.y() - e_vLOS_1.z() * e_pLOS_1.y() * e_pLOS_1.z() + e_vLOS_s.z() * e_pLOS_s.y() * e_pLOS_s.z(), |
| 2272 |
22/44✓ Branch 1 taken 23579 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 23579 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 23579 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 23579 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 23579 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 23579 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 23579 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 23579 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 23579 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 23579 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 23579 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 23579 times.
✗ Branch 35 not taken.
✓ Branch 37 taken 23579 times.
✗ Branch 38 not taken.
✓ Branch 40 taken 23579 times.
✗ Branch 41 not taken.
✓ Branch 43 taken 23579 times.
✗ Branch 44 not taken.
✓ Branch 46 taken 23579 times.
✗ Branch 47 not taken.
✓ Branch 49 taken 23579 times.
✗ Branch 50 not taken.
✓ Branch 52 taken 23579 times.
✗ Branch 53 not taken.
✓ Branch 55 taken 23579 times.
✗ Branch 56 not taken.
✓ Branch 58 taken 23579 times.
✗ Branch 59 not taken.
✓ Branch 61 taken 23579 times.
✗ Branch 62 not taken.
✓ Branch 64 taken 23579 times.
✗ Branch 65 not taken.
|
47158 | -e_vLOS_1.x() * e_pLOS_1.x() * e_pLOS_1.z() + e_vLOS_s.x() * e_pLOS_s.x() * e_pLOS_s.z() - e_vLOS_1.y() * e_pLOS_1.y() * e_pLOS_1.z() + e_vLOS_s.y() * e_pLOS_s.y() * e_pLOS_s.z() - e_vLOS_1.z() * e_pLOS_1.z() * e_pLOS_1.z() + e_vLOS_1.z() + e_vLOS_s.z() * e_pLOS_s.z() * e_pLOS_s.z() - e_vLOS_s.z()); |
| 2273 |
4/8✓ Branch 1 taken 23579 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 23579 times.
✗ Branch 5 not taken.
✓ Branch 9 taken 23579 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 23579 times.
✗ Branch 13 not taken.
|
23579 | _kalmanFilter.H.block<3>(Meas::DopplerDD{ satSigId_s }, States::Vel) = (e_pLOS_1 - e_pLOS_s).transpose(); |
| 2274 | 23579 | break; | |
| 2275 | } | ||
| 2276 | ✗ | case GnssObs::ObservationType_COUNT: | |
| 2277 | ✗ | LOG_CRITICAL("{}: ObservationType_COUNT is not a valid value", nameId()); | |
| 2278 | break; | ||
| 2279 | } | ||
| 2280 | } | ||
| 2281 | } | ||
| 2282 | |||
| 2283 | LOG_DATA("{}: z =\n{}", nameId(), _kalmanFilter.z.transposed()); | ||
| 2284 | LOG_DATA("{}: H =\n{}", nameId(), _kalmanFilter.H); | ||
| 2285 | 601 | } | |
| 2286 | |||
| 2287 | ✗ | std::vector<RealTimeKinematic::OutlierInfo> RealTimeKinematic::removeOutlier(Observations& observations, const std::shared_ptr<RtkSolution>& rtkSol) | |
| 2288 | { | ||
| 2289 | ✗ | KeyedVectorXd<RTK::Meas::MeasKeyTypes> normInno((_kalmanFilter.z(all).array().abs() | |
| 2290 | ✗ | / _kalmanFilter.S(all, all).diagonal().array().abs().sqrt()) | |
| 2291 | ✗ | .matrix(), | |
| 2292 | ✗ | _kalmanFilter.z.rowKeys()); | |
| 2293 | ✗ | if (normInno(all).hasNaN()) | |
| 2294 | { | ||
| 2295 | ✗ | LOG_ERROR("{}: NIS check has NaN values. Skipping check this epoch.", nameId()); | |
| 2296 | LOG_DATA("{}: normInno has NaN", nameId()); | ||
| 2297 | LOG_DATA("{}: S =\n{}", nameId(), _kalmanFilter.S); | ||
| 2298 | ✗ | return {}; | |
| 2299 | } | ||
| 2300 | |||
| 2301 | LOG_DATA("{}: normInno = \n{}", nameId(), normInno.transposed()); | ||
| 2302 | LOG_DATA("{}: dz (post-fit) = \n{}", nameId(), _kalmanFilter.z.transposed()); | ||
| 2303 | LOG_DATA("{}: dz (innovation) = \n{}", nameId(), _kalmanFilter.savedPreUpdate().z.transposed()); | ||
| 2304 | |||
| 2305 | // Check pivot satellites for outlier by calculating mean / stdev | ||
| 2306 | ✗ | OutlierInfo faultyMeas; | |
| 2307 | |||
| 2308 | ✗ | for (auto& [pivotCodeObsType, pivot] : _pivotSatellites) | |
| 2309 | { | ||
| 2310 | ✗ | auto code = pivotCodeObsType.first; | |
| 2311 | ✗ | auto obsType = pivotCodeObsType.second; | |
| 2312 | ✗ | auto filterMeasurements = [&](const Meas::MeasKeyTypes& key) -> bool { | |
| 2313 | ✗ | SatSigId satSigId; | |
| 2314 | ✗ | GnssObs::ObservationType obsType2 = GnssObs::ObservationType_COUNT; | |
| 2315 | ✗ | if (const auto* meas = std::get_if<Meas::PsrDD>(&key)) | |
| 2316 | { | ||
| 2317 | ✗ | obsType2 = GnssObs::Pseudorange; | |
| 2318 | ✗ | satSigId = meas->satSigId; | |
| 2319 | } | ||
| 2320 | ✗ | else if (const auto* meas = std::get_if<Meas::CarrierDD>(&key)) | |
| 2321 | { | ||
| 2322 | ✗ | obsType2 = GnssObs::Carrier; | |
| 2323 | ✗ | satSigId = meas->satSigId; | |
| 2324 | } | ||
| 2325 | ✗ | else if (const auto* meas = std::get_if<Meas::DopplerDD>(&key)) | |
| 2326 | { | ||
| 2327 | ✗ | obsType2 = GnssObs::Doppler; | |
| 2328 | ✗ | satSigId = meas->satSigId; | |
| 2329 | } | ||
| 2330 | ✗ | return satSigId.code != code || obsType2 != obsType; | |
| 2331 | ✗ | }; | |
| 2332 | ✗ | double mean = 0.0; | |
| 2333 | ✗ | size_t amount = 0; | |
| 2334 | ✗ | for (const auto& key : _kalmanFilter.savedPreUpdate().z.rowKeys()) | |
| 2335 | { | ||
| 2336 | ✗ | if (filterMeasurements(key)) { continue; } | |
| 2337 | ✗ | mean += _kalmanFilter.savedPreUpdate().z(key); | |
| 2338 | ✗ | amount++; | |
| 2339 | } | ||
| 2340 | ✗ | if (amount <= 1) { continue; } | |
| 2341 | ✗ | mean /= static_cast<double>(amount); | |
| 2342 | |||
| 2343 | ✗ | double variance = 0.0; | |
| 2344 | ✗ | for (const auto& key : _kalmanFilter.savedPreUpdate().z.rowKeys()) | |
| 2345 | { | ||
| 2346 | ✗ | if (filterMeasurements(key)) { continue; } | |
| 2347 | ✗ | variance += std::pow(std::abs(_kalmanFilter.savedPreUpdate().z(key) - mean), 2); | |
| 2348 | } | ||
| 2349 | ✗ | variance *= 1.0 / (static_cast<double>(amount) - 1.0); | |
| 2350 | |||
| 2351 | LOG_DATA("{}: pivot [{}][{}]: {:.3f} / {:.3f} = {:.3f}", nameId(), code, obsType, mean, std::sqrt(variance), mean / std::sqrt(variance)); | ||
| 2352 | |||
| 2353 | ✗ | if (std::abs(mean / std::sqrt(variance)) > 5.0) // 3 or 5 sigma (but t-distributed https://en.wikipedia.org/wiki/Student%27s_t-distribution) | |
| 2354 | { | ||
| 2355 | ✗ | switch (obsType) | |
| 2356 | { | ||
| 2357 | ✗ | case GnssObs::Pseudorange: | |
| 2358 | ✗ | faultyMeas.key = Meas::PsrDD{ pivot.satSigId }; | |
| 2359 | ✗ | break; | |
| 2360 | ✗ | case GnssObs::Carrier: | |
| 2361 | ✗ | faultyMeas.key = Meas::CarrierDD{ pivot.satSigId }; | |
| 2362 | ✗ | break; | |
| 2363 | ✗ | case GnssObs::Doppler: | |
| 2364 | ✗ | faultyMeas.key = Meas::DopplerDD{ pivot.satSigId }; | |
| 2365 | ✗ | break; | |
| 2366 | ✗ | case GnssObs::ObservationType_COUNT: | |
| 2367 | ✗ | break; | |
| 2368 | } | ||
| 2369 | ✗ | faultyMeas.obsType = obsType; | |
| 2370 | ✗ | faultyMeas.satSigId = pivot.satSigId; | |
| 2371 | ✗ | faultyMeas.pivot = RtkSolution::PivotChange::Reason::PivotOutlier; | |
| 2372 | |||
| 2373 | ✗ | break; // Pivot is problematic throw out and then break. Only one pivot change at a time | |
| 2374 | } | ||
| 2375 | } | ||
| 2376 | ✗ | if (faultyMeas.obsType == GnssObs::ObservationType_COUNT) | |
| 2377 | { | ||
| 2378 | ✗ | int maxIdx = 0; | |
| 2379 | ✗ | normInno(all).maxCoeff(&maxIdx); | |
| 2380 | ✗ | faultyMeas.key = normInno.rowKeys().at(static_cast<size_t>(maxIdx)); | |
| 2381 | LOG_DATA("{}: Largest post-fit innovation: {} ({:.1f})", nameId(), faultyMeas.key, normInno(faultyMeas.key)); | ||
| 2382 | |||
| 2383 | ✗ | if (const auto* meas = std::get_if<Meas::PsrDD>(&faultyMeas.key)) | |
| 2384 | { | ||
| 2385 | ✗ | faultyMeas.obsType = GnssObs::Pseudorange; | |
| 2386 | ✗ | faultyMeas.satSigId = meas->satSigId; | |
| 2387 | } | ||
| 2388 | ✗ | else if (const auto* meas = std::get_if<Meas::CarrierDD>(&faultyMeas.key)) | |
| 2389 | { | ||
| 2390 | ✗ | faultyMeas.obsType = GnssObs::Carrier; | |
| 2391 | ✗ | faultyMeas.satSigId = meas->satSigId; | |
| 2392 | } | ||
| 2393 | ✗ | else if (const auto* meas = std::get_if<Meas::DopplerDD>(&faultyMeas.key)) | |
| 2394 | { | ||
| 2395 | ✗ | faultyMeas.obsType = GnssObs::Doppler; | |
| 2396 | ✗ | faultyMeas.satSigId = meas->satSigId; | |
| 2397 | } | ||
| 2398 | |||
| 2399 | ✗ | auto& pivot = _pivotSatellites.at({ faultyMeas.satSigId.code, faultyMeas.obsType }); | |
| 2400 | LOG_DATA("{}: Pivot for [{}] is [{}]", nameId(), faultyMeas.satSigId, pivot.satSigId); | ||
| 2401 | ✗ | if (pivot.satSigId == faultyMeas.satSigId) | |
| 2402 | { | ||
| 2403 | ✗ | faultyMeas.pivot = RtkSolution::PivotChange::Reason::PivotOutlier; | |
| 2404 | LOG_DATA("{}: Pivot [{}][{}] with outlier.", nameId(), pivot.satSigId, faultyMeas.obsType); | ||
| 2405 | } | ||
| 2406 | } | ||
| 2407 | |||
| 2408 | LOG_DATA("{}: faultyMeas = {}", nameId(), faultyMeas.key); | ||
| 2409 | |||
| 2410 | ✗ | if (faultyMeas.obsType == GnssObs::Pseudorange && _outlierMinPsrObsKeep != 0 && observations.nObservables[GnssObs::Pseudorange] <= _outlierMinPsrObsKeep) | |
| 2411 | { | ||
| 2412 | ✗ | addEventToGui(rtkSol, "Stopped doing NIS check, as minimum amount of pseudorange observables reached."); | |
| 2413 | ✗ | return {}; | |
| 2414 | } | ||
| 2415 | |||
| 2416 | ✗ | std::vector<RealTimeKinematic::OutlierInfo> faulty{ faultyMeas }; | |
| 2417 | // Exclude Pseudorange and Carrier toghether | ||
| 2418 | ✗ | if (faultyMeas.obsType == GnssObs::Pseudorange && _kalmanFilter.z.hasRow(Meas::CarrierDD{ faultyMeas.satSigId })) | |
| 2419 | { | ||
| 2420 | LOG_DATA("{}: Also flagging [{}][{}] as outlier.", nameId(), faultyMeas.satSigId, GnssObs::Carrier); | ||
| 2421 | ✗ | faulty.push_back(faultyMeas); | |
| 2422 | ✗ | faulty.back().obsType = GnssObs::Carrier; | |
| 2423 | ✗ | faulty.back().key = Meas::CarrierDD{ faulty.back().satSigId }; | |
| 2424 | } | ||
| 2425 | ✗ | else if (faultyMeas.obsType == GnssObs::Carrier && _kalmanFilter.z.hasRow(Meas::PsrDD{ faultyMeas.satSigId })) | |
| 2426 | { | ||
| 2427 | LOG_DATA("{}: Also flagging [{}][{}] as outlier.", nameId(), faultyMeas.satSigId, GnssObs::Pseudorange); | ||
| 2428 | ✗ | faulty.push_back(faultyMeas); | |
| 2429 | ✗ | faulty.back().obsType = GnssObs::Pseudorange; | |
| 2430 | ✗ | faulty.back().key = Meas::PsrDD{ faulty.back().satSigId }; | |
| 2431 | } | ||
| 2432 | ✗ | if (faulty.size() == 2) | |
| 2433 | { | ||
| 2434 | ✗ | if (auto pivotKey = std::make_pair(faulty.back().satSigId.code, faulty.back().obsType); | |
| 2435 | ✗ | _pivotSatellites.contains(pivotKey) && _pivotSatellites.at(pivotKey).satSigId == faulty.back().satSigId) | |
| 2436 | { | ||
| 2437 | ✗ | faulty.back().pivot = RtkSolution::PivotChange::Reason::PivotOutlier; | |
| 2438 | } | ||
| 2439 | else | ||
| 2440 | { | ||
| 2441 | ✗ | faulty.back().pivot.reset(); | |
| 2442 | } | ||
| 2443 | } | ||
| 2444 | |||
| 2445 | ✗ | printPivotSatellites(); | |
| 2446 | ✗ | for (const auto& faultyMeas : faulty) | |
| 2447 | { | ||
| 2448 | LOG_DATA("{}: Erasing receiver observation [{}][{}], because outlier (NIS check).", nameId(), faultyMeas.satSigId, faultyMeas.obsType); | ||
| 2449 | |||
| 2450 | ✗ | if (faultyMeas.pivot) | |
| 2451 | { | ||
| 2452 | LOG_DATA("{}: Removing pivot satellite [{}][{}] because outlier detected.", nameId(), faultyMeas.satSigId, faultyMeas.obsType); | ||
| 2453 | ✗ | updatePivotSatellite(faultyMeas.satSigId.code, faultyMeas.obsType, observations, rtkSol, faultyMeas.pivot.value()); | |
| 2454 | } | ||
| 2455 | else | ||
| 2456 | { | ||
| 2457 | ✗ | if (States::AmbiguityDD ambStateKey{ faultyMeas.satSigId }; | |
| 2458 | ✗ | faultyMeas.obsType == GnssObs::Carrier && _kalmanFilter.hasState(ambStateKey)) | |
| 2459 | { | ||
| 2460 | LOG_TRACE("{}: Removing state [{}]", nameId(), ambStateKey); | ||
| 2461 | ✗ | _kalmanFilter.removeState(ambStateKey); | |
| 2462 | ✗ | _ambiguitiesHold.erase(faultyMeas.satSigId); | |
| 2463 | ✗ | if (_outputStateEvents) | |
| 2464 | { | ||
| 2465 | ✗ | addEventToGui(rtkSol, fmt::format("State [{}] removed (because outlier detected in carrier measurement [{}])", ambStateKey, faultyMeas.key)); | |
| 2466 | } | ||
| 2467 | } | ||
| 2468 | ✗ | _kalmanFilter.removeMeasurement(faultyMeas.key); | |
| 2469 | |||
| 2470 | LOG_DATA("{}: x = \n{}", nameId(), _kalmanFilter.x.transposed()); | ||
| 2471 | LOG_DATA("{}: z = \n{}", nameId(), _kalmanFilter.z.transposed()); | ||
| 2472 | } | ||
| 2473 | |||
| 2474 | ✗ | observations.removeSignal(faultyMeas.satSigId, faultyMeas.obsType, nameId()); | |
| 2475 | ✗ | _obsFilter.excludeSignalTemporarily(faultyMeas.satSigId, _outlierRemoveEpochs - 1); | |
| 2476 | ✗ | addEventToGui(rtkSol, fmt::format("Erasing receiver observation [{}][{}], because outlier (NIS check)", faultyMeas.satSigId, faultyMeas.obsType)); | |
| 2477 | |||
| 2478 | ✗ | _nMeasExcludedNIS++; | |
| 2479 | ✗ | rtkSol->nisRemovedCnt++; | |
| 2480 | ✗ | rtkSol->outliers.emplace_back(RtkSolution::Outlier::Type::NIS, faultyMeas.satSigId, faultyMeas.obsType); | |
| 2481 | } | ||
| 2482 | ✗ | return faulty; | |
| 2483 | ✗ | } | |
| 2484 | |||
| 2485 | 601 | RealTimeKinematic::UpdateStatus RealTimeKinematic::kalmanFilterUpdate(const std::shared_ptr<RtkSolution>& rtkSol) | |
| 2486 | { | ||
| 2487 | 601 | UpdateStatus solutionValid; | |
| 2488 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 601 times.
|
601 | if (_kalmanFilter.z.rows() == 0) |
| 2489 | { | ||
| 2490 | ✗ | solutionValid.valid = false; | |
| 2491 | } | ||
| 2492 | else | ||
| 2493 | { | ||
| 2494 |
1/2✓ Branch 3 taken 601 times.
✗ Branch 4 not taken.
|
601 | auto dt = static_cast<double>((_receiver[Rover].gnssObs->insTime - _lastUpdate).count()); |
| 2495 | ✗ | solutionValid.threshold = std::max({ 50.0, | |
| 2496 |
1/2✓ Branch 2 taken 601 times.
✗ Branch 3 not taken.
|
601 | 10.0 * _receiver[Rover].e_vel.norm() * dt, |
| 2497 |
5/10✓ Branch 3 taken 601 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 601 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 601 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 601 times.
✗ Branch 13 not taken.
✓ Branch 15 taken 601 times.
✗ Branch 16 not taken.
|
601 | _kalmanFilter.P(States::Pos, States::Pos).diagonal().cwiseSqrt().maxCoeff() }); |
| 2498 | |||
| 2499 | 601 | _kalmanFilter.savePreUpdate(); | |
| 2500 | 601 | _kalmanFilter.correctWithMeasurementInnovation(); | |
| 2501 | |||
| 2502 | LOG_DATA("{}: x (a posteriori, t = {}) =\n{}", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), _kalmanFilter.x.transposed()); | ||
| 2503 | LOG_DATA("{}: P (a posteriori, t = {}) =\n{}", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), _kalmanFilter.P); | ||
| 2504 | |||
| 2505 | LOG_DATA("{}: dx_ecef (a posteriori - a priori ) = {}", nameId(), (_kalmanFilter.x.segment<3>(States::Pos) - _receiver[Rover].e_posMarker).transpose()); | ||
| 2506 | LOG_DATA("{}: dv_ecef (a posteriori - a priori ) = {}", nameId(), (_kalmanFilter.x.segment<3>(States::Vel) - _receiver[Rover].e_vel).transpose()); | ||
| 2507 | |||
| 2508 |
3/6✓ Branch 3 taken 601 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 601 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 601 times.
✗ Branch 10 not taken.
|
601 | solutionValid.dx = (_kalmanFilter.x.segment<3>(States::Pos) - _receiver[Rover].e_posMarker).norm(); |
| 2509 | 601 | solutionValid.valid = solutionValid.dx < solutionValid.threshold; | |
| 2510 | } | ||
| 2511 | |||
| 2512 |
1/2✓ Branch 0 taken 601 times.
✗ Branch 1 not taken.
|
601 | if (solutionValid.valid) |
| 2513 | { | ||
| 2514 |
2/4✓ Branch 2 taken 601 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 601 times.
✗ Branch 7 not taken.
|
601 | _receiver[Rover].e_posMarker = _kalmanFilter.x.segment<3>(States::Pos); |
| 2515 |
2/4✓ Branch 2 taken 601 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 601 times.
✗ Branch 7 not taken.
|
601 | _receiver[Rover].e_vel = _kalmanFilter.x.segment<3>(States::Vel); |
| 2516 |
1/2✓ Branch 2 taken 601 times.
✗ Branch 3 not taken.
|
601 | _receiver[Rover].lla_posMarker = trafo::ecef2lla_WGS84(_receiver[Rover].e_posMarker); |
| 2517 | |||
| 2518 |
1/2✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
|
601 | if (_kalmanFilter.isPreUpdateSaved()) { _kalmanFilter.discardPreUpdate(); } |
| 2519 | } | ||
| 2520 | else | ||
| 2521 | { | ||
| 2522 | ✗ | std::string text = _kalmanFilter.z.rows() == 0 | |
| 2523 | ✗ | ? std::string("Update rejected - no observations left") | |
| 2524 | ✗ | : fmt::format("Update rejected - position change due to update is {:.2f} [m] > {:.2f} [m]", solutionValid.dx, solutionValid.threshold); | |
| 2525 | |||
| 2526 | ✗ | LOG_WARN("{}: [{}] {}", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), text); | |
| 2527 | ✗ | addEventToGui(rtkSol, text); | |
| 2528 | ✗ | if (_kalmanFilter.isPreUpdateSaved()) { _kalmanFilter.restorePreUpdate(); } | |
| 2529 | ✗ | } | |
| 2530 | |||
| 2531 | 601 | return solutionValid; | |
| 2532 | } | ||
| 2533 | |||
| 2534 | 601 | bool RealTimeKinematic::resolveAmbiguities(size_t nCarrierMeasUniqueSatellite, const std::shared_ptr<RtkSolution>& rtkSol) | |
| 2535 | { | ||
| 2536 | 601 | std::vector<States::StateKeyType> ambKeys; | |
| 2537 | 601 | std::vector<Meas::MeasKeyTypes> ambMeasKeys; | |
| 2538 |
2/2✓ Branch 2 taken 23579 times.
✓ Branch 3 taken 601 times.
|
24180 | for (size_t i = States::KFStates_COUNT; i < _kalmanFilter.x.rowKeys().size(); i++) // 0-2 Pos, 3-5 Vel |
| 2539 | { | ||
| 2540 |
2/4✓ Branch 2 taken 23579 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 23579 times.
✗ Branch 6 not taken.
|
23579 | if (const auto* ambDD = std::get_if<States::AmbiguityDD>(&_kalmanFilter.x.rowKeys().at(i))) |
| 2541 | { | ||
| 2542 |
1/2✓ Branch 1 taken 23579 times.
✗ Branch 2 not taken.
|
23579 | ambKeys.emplace_back(*ambDD); |
| 2543 |
1/2✓ Branch 1 taken 23579 times.
✗ Branch 2 not taken.
|
23579 | ambMeasKeys.emplace_back(*ambDD); |
| 2544 | } | ||
| 2545 | } | ||
| 2546 | 601 | size_t nAmbs = ambKeys.size(); | |
| 2547 | |||
| 2548 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 601 times.
|
601 | if (nCarrierMeasUniqueSatellite < _nMinSatForAmbFix) |
| 2549 | { | ||
| 2550 | LOG_TRACE("{}: [{}] Not fixing ambiguities because only {} satellites but minimum {} needed.", | ||
| 2551 | nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), nCarrierMeasUniqueSatellite, _nMinSatForAmbFix); | ||
| 2552 | ✗ | if (!_nMinSatForAmbFixTriggered) | |
| 2553 | { | ||
| 2554 | ✗ | addEventToGui(rtkSol, fmt::format("Not fixing ambiguities anymore because\nonly {} satellites but minimum {} needed.", nCarrierMeasUniqueSatellite, _nMinSatForAmbFix)); | |
| 2555 | } | ||
| 2556 | ✗ | _nMinSatForAmbFixTriggered = true; | |
| 2557 | |||
| 2558 | ✗ | return false; | |
| 2559 | } | ||
| 2560 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 601 times.
|
601 | if (_nMinSatForAmbFixTriggered) |
| 2561 | { | ||
| 2562 | ✗ | _nMinSatForAmbFixTriggered = false; | |
| 2563 | ✗ | addEventToGui(rtkSol, fmt::format("Resuming ambiguity fixing as\nminimum amount of satellites reached.")); | |
| 2564 | } | ||
| 2565 | |||
| 2566 |
3/6✓ Branch 3 taken 601 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 601 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 601 times.
✗ Branch 10 not taken.
|
601 | if (double posVar = _kalmanFilter.P(States::Pos, States::Pos).diagonal().sum() / 3.0; |
| 2567 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 601 times.
|
601 | posVar > _maxPosVar) |
| 2568 | { | ||
| 2569 | LOG_TRACE("{}: [{}] Not fixing ambiguities because position variance is {:.4f}m which is higher than {:.4f}m", | ||
| 2570 | nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), posVar, _maxPosVar); | ||
| 2571 | ✗ | if (!_maxPosVarTriggered) | |
| 2572 | { | ||
| 2573 | ✗ | addEventToGui(rtkSol, fmt::format("Not fixing ambiguities anymore because\nposition variance is {:.4f}m^2 which is higher than {:.4f}m^2.", posVar, _maxPosVar)); | |
| 2574 | } | ||
| 2575 | ✗ | _maxPosVarTriggered = true; | |
| 2576 | ✗ | return false; | |
| 2577 | } | ||
| 2578 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 601 times.
|
601 | else if (_maxPosVarTriggered) // NOLINT(readability-else-after-return,llvm-else-after-return) |
| 2579 | { | ||
| 2580 | ✗ | _maxPosVarTriggered = false; | |
| 2581 | ✗ | addEventToGui(rtkSol, fmt::format("Resuming ambiguity fixing as\nposition variance smaller than limit.")); | |
| 2582 | } | ||
| 2583 | |||
| 2584 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 601 times.
|
601 | if (ambKeys.empty()) { return false; } // No ambiguities to estimate |
| 2585 | 1202 | if (nCarrierMeasUniqueSatellite >= _nMinSatForAmbHold | |
| 2586 |
6/8✓ Branch 0 taken 601 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 601 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 598 times.
✓ Branch 7 taken 3 times.
✓ Branch 8 taken 598 times.
✓ Branch 9 taken 3 times.
|
601 | && _ambiguityResolutionStrategy == AmbiguityResolutionStrategy::FixAndHold && ambKeys.size() == _ambiguitiesHold.size()) // No new ambiguities to estimate |
| 2587 | { | ||
| 2588 | #if LOG_LEVEL <= LOG_LEVEL_DATA | ||
| 2589 | { | ||
| 2590 | std::set<SatSigId> ambStates; | ||
| 2591 | for (const auto& key : ambKeys) | ||
| 2592 | { | ||
| 2593 | auto amb = std::get<States::AmbiguityDD>(key); | ||
| 2594 | if (ambStates.contains(amb.satSigId)) | ||
| 2595 | { | ||
| 2596 | LOG_CRITICAL("{}: The KF state has [{}] more than once.", nameId(), amb); | ||
| 2597 | } | ||
| 2598 | ambStates.insert(amb.satSigId); | ||
| 2599 | } | ||
| 2600 | LOG_DATA("{}: ambKeys: [{}]", nameId(), fmt::join(ambStates, ", ")); | ||
| 2601 | |||
| 2602 | std::set<SatSigId> ambHold; | ||
| 2603 | for (const auto& hold : _ambiguitiesHold) | ||
| 2604 | { | ||
| 2605 | if (ambHold.contains(hold.first)) | ||
| 2606 | { | ||
| 2607 | LOG_CRITICAL("{}: _ambiguitiesHold has [{}] more than once.", nameId(), hold.first); | ||
| 2608 | } | ||
| 2609 | ambHold.insert(hold.first); | ||
| 2610 | } | ||
| 2611 | LOG_DATA("{}: ambHold: [{}]", nameId(), fmt::join(ambHold, ", ")); | ||
| 2612 | |||
| 2613 | if (ambStates != ambHold) | ||
| 2614 | { | ||
| 2615 | std::vector<SatSigId> diff; | ||
| 2616 | std::set_difference(ambStates.begin(), ambStates.end(), ambHold.begin(), ambHold.end(), std::back_inserter(diff)); // NOLINT(boost-use-ranges,modernize-use-ranges) // There is no ranges::set_difference | ||
| 2617 | LOG_DATA("{}: Amb not in hold: [{}]", nameId(), fmt::join(diff, ", ")); | ||
| 2618 | diff.clear(); | ||
| 2619 | std::set_difference(ambHold.begin(), ambHold.end(), ambStates.begin(), ambStates.end(), std::back_inserter(diff)); // NOLINT(boost-use-ranges,modernize-use-ranges) // There is no ranges::set_difference | ||
| 2620 | LOG_DATA("{}: Amb not in states: [{}]", nameId(), fmt::join(diff, ", ")); | ||
| 2621 | } | ||
| 2622 | } | ||
| 2623 | #endif | ||
| 2624 |
2/4✓ Branch 2 taken 598 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 598 times.
✗ Branch 6 not taken.
|
598 | Eigen::VectorXd fixedAmb = Eigen::VectorXd::Zero(static_cast<int>(ambKeys.size())); |
| 2625 | // Apply exact integers (after update it is still float in the late digits) | ||
| 2626 |
2/2✓ Branch 1 taken 23462 times.
✓ Branch 2 taken 598 times.
|
24060 | for (size_t k = 0; k < ambKeys.size(); k++) |
| 2627 | { | ||
| 2628 |
1/2✓ Branch 1 taken 23462 times.
✗ Branch 2 not taken.
|
23462 | const auto& key = ambKeys.at(k); |
| 2629 |
1/2✓ Branch 1 taken 23462 times.
✗ Branch 2 not taken.
|
23462 | auto satSigId = std::get<States::AmbiguityDD>(key).satSigId; |
| 2630 | LOG_DATA("{}: FixAndHold: [{}] Holding {} to {} (after update)", nameId(), satSigId, _kalmanFilter.x.hasRow(key), _ambiguitiesHold.contains(satSigId)); | ||
| 2631 | LOG_DATA("{}: FixAndHold: [{}] Holding {} to {} (after update)", nameId(), satSigId, _kalmanFilter.x(key), _ambiguitiesHold.at(satSigId)); | ||
| 2632 |
2/4✓ Branch 1 taken 23462 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 23462 times.
✗ Branch 5 not taken.
|
23462 | fixedAmb(static_cast<int>(k)) = _ambiguitiesHold.at(satSigId); |
| 2633 | } | ||
| 2634 | 598 | rtkSol->nAmbiguitiesFixed = ambKeys.size() + 1; // + 1 to also count the pivot | |
| 2635 |
1/2✓ Branch 1 taken 598 times.
✗ Branch 2 not taken.
|
598 | applyFixedAmbiguities(fixedAmb, ambKeys, ambMeasKeys); |
| 2636 | |||
| 2637 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 598 times.
|
598 | if (_nMinSatForAmbHoldTriggered) |
| 2638 | { | ||
| 2639 | ✗ | _nMinSatForAmbHoldTriggered = false; | |
| 2640 | ✗ | addEventToGui(rtkSol, fmt::format("Resuming ambiguity holding as\nminimum amount of satellites reached.")); | |
| 2641 | } | ||
| 2642 | |||
| 2643 | 598 | return true; | |
| 2644 | 598 | } | |
| 2645 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
3 | if (nCarrierMeasUniqueSatellite < _nMinSatForAmbHold && _ambiguityResolutionStrategy == AmbiguityResolutionStrategy::FixAndHold) |
| 2646 | { | ||
| 2647 | LOG_TRACE("{}: [{}] Not holding ambiguities because only {} satellites but minimum {} needed.", | ||
| 2648 | nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), nCarrierMeasUniqueSatellite, _nMinSatForAmbHold); | ||
| 2649 | ✗ | if (!_nMinSatForAmbHoldTriggered) | |
| 2650 | { | ||
| 2651 | ✗ | addEventToGui(rtkSol, fmt::format("Not holding ambiguities anymore because\nonly {} satellites but minimum {} needed.", nCarrierMeasUniqueSatellite, _nMinSatForAmbHold)); | |
| 2652 | } | ||
| 2653 | ✗ | _nMinSatForAmbHoldTriggered = true; | |
| 2654 | } | ||
| 2655 | |||
| 2656 | LOG_DATA("{}: [{}] Estimating ambiguities", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST)); | ||
| 2657 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | auto floatKeys = States::PosVel; |
| 2658 | 3 | size_t partialFixTries = 0; | |
| 2659 | do { | ||
| 2660 | LOG_DATA("{}: [{}] floatKeys = {}", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), fmt::join(floatKeys, ", ")); | ||
| 2661 | LOG_DATA("{}: [{}] ambKeys = {}", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), fmt::join(ambKeys, ", ")); | ||
| 2662 |
2/4✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
|
6 | auto fixed = ResolveAmbiguities(_kalmanFilter.x(ambKeys), _kalmanFilter.P(ambKeys, ambKeys), |
| 2663 |
2/4✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
|
6 | _kalmanFilter.x(floatKeys), _kalmanFilter.P(floatKeys, floatKeys), |
| 2664 |
2/4✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
|
6 | _kalmanFilter.P(ambKeys, floatKeys), _kalmanFilter.P(floatKeys, ambKeys), |
| 2665 |
2/4✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
|
9 | _ambiguityResolutionParameters, nameId()); |
| 2666 | 3 | rtkSol->ambiguityResolutionFailure = fixed.failure; | |
| 2667 | 3 | rtkSol->ambiguityCriticalValueRatio = fixed.ambiguityCriticalValueRatio; | |
| 2668 | |||
| 2669 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | if (fixed.failure == AmbiguityResolutionFailure::None) |
| 2670 | { | ||
| 2671 |
2/4✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
|
3 | _kalmanFilter.x(floatKeys) = fixed.b; |
| 2672 | LOG_DATA("{}: x(fixed.b) =\n{}", nameId(), _kalmanFilter.x.transposed()); | ||
| 2673 | |||
| 2674 |
2/4✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
|
3 | if (_applyFixedAmbiguitiesWithUpdate) { applyFixedAmbiguities(fixed.fixedAmb.front().a, ambKeys, ambMeasKeys); } |
| 2675 | else | ||
| 2676 | { | ||
| 2677 | ✗ | _kalmanFilter.x(ambKeys) = fixed.fixedAmb.front().a; | |
| 2678 | // _kalmanFilter.P(floatKeys, floatKeys) = fixed.Qb; | ||
| 2679 | // LOG_DATA("{}: P(fixed.Qb) =\n{}", nameId(), _kalmanFilter.P); | ||
| 2680 | } | ||
| 2681 | |||
| 2682 | LOG_DATA("{}: dx_ecef (fix, update - fix ) = {}", nameId(), (_kalmanFilter.x.segment<3>(States::Pos) - _receiver[Rover].e_posMarker).transpose()); | ||
| 2683 | LOG_DATA("{}: dv_ecef (fix, update - fix ) = {}", nameId(), (_kalmanFilter.x.segment<3>(States::Vel) - _receiver[Rover].e_vel).transpose()); | ||
| 2684 |
2/4✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
|
3 | _receiver[Rover].e_posMarker = _kalmanFilter.x.segment<3>(States::Pos); |
| 2685 |
2/4✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
|
3 | _receiver[Rover].e_vel = _kalmanFilter.x.segment<3>(States::Vel); |
| 2686 |
1/2✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
3 | _receiver[Rover].lla_posMarker = trafo::ecef2lla_WGS84(_receiver[Rover].e_posMarker); |
| 2687 | |||
| 2688 | 3 | rtkSol->ambiguityCriticalValueRatio = fixed.ambiguityCriticalValueRatio; | |
| 2689 | 3 | rtkSol->nAmbiguitiesFixed = fixed.nFixed + 1; // + 1 to also count the pivot | |
| 2690 | |||
| 2691 |
2/2✓ Branch 4 taken 117 times.
✓ Branch 5 taken 3 times.
|
120 | for (const auto& key_ : ambKeys) |
| 2692 | { | ||
| 2693 |
1/2✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
|
117 | const auto key = std::get<States::AmbiguityDD>(key_); |
| 2694 | |||
| 2695 | 234 | if (_ambiguityResolutionStrategy == AmbiguityResolutionStrategy::FixAndHold | |
| 2696 |
3/4✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 73 times.
✓ Branch 4 taken 44 times.
|
117 | && _ambiguitiesHold.contains(key.satSigId) |
| 2697 |
3/6✓ Branch 2 taken 73 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 73 times.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 73 times.
|
73 | && std::abs(_kalmanFilter.x(key) - _ambiguitiesHold.at(key.satSigId)) > 0.1 |
| 2698 |
2/8✓ Branch 0 taken 117 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 117 times.
|
234 | && fixed.nFixed == nAmbs && partialFixTries == 0) |
| 2699 | { | ||
| 2700 | ✗ | LOG_WARN("{}: Ambiguity [{}] changed from {} to {} (despite being FixAndHold)", nameId(), key.satSigId, | |
| 2701 | _kalmanFilter.x(key), _ambiguitiesHold.at(key.satSigId)); | ||
| 2702 | } | ||
| 2703 |
2/4✓ Branch 2 taken 117 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 117 times.
✗ Branch 6 not taken.
|
117 | _ambiguitiesHold[key.satSigId] = _kalmanFilter.x(key); |
| 2704 | } | ||
| 2705 | |||
| 2706 | 3 | return partialFixTries == 0; | |
| 2707 | } | ||
| 2708 | LOG_DATA("{}: Fixing failed. partialFixTries = {}", nameId(), partialFixTries); | ||
| 2709 | |||
| 2710 | ✗ | if (_ambiguityResolutionParameters.partialFixing) | |
| 2711 | { | ||
| 2712 | ✗ | int maxAmb = 0; | |
| 2713 | ✗ | _kalmanFilter.P(ambKeys, ambKeys).diagonal().maxCoeff(&maxAmb); | |
| 2714 | |||
| 2715 | LOG_DATA("{}: [{}] Trying partial fixing by not fixing [{}] with highest variance of {}", | ||
| 2716 | nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), | ||
| 2717 | ambKeys.at(static_cast<size_t>(maxAmb)), | ||
| 2718 | _kalmanFilter.P(ambKeys.at(static_cast<size_t>(maxAmb)), ambKeys.at(static_cast<size_t>(maxAmb)))); | ||
| 2719 | ✗ | floatKeys.push_back(ambKeys.at(static_cast<size_t>(maxAmb))); | |
| 2720 | ✗ | ambKeys.erase(std::next(ambKeys.begin(), maxAmb)); | |
| 2721 | ✗ | ambMeasKeys.erase(std::next(ambMeasKeys.begin(), maxAmb)); | |
| 2722 | ✗ | partialFixTries++; | |
| 2723 | } | ||
| 2724 |
1/10✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
|
3 | } while (_ambiguityResolutionParameters.partialFixing && ambKeys.size() > 1 && partialFixTries < 6); |
| 2725 | |||
| 2726 | ✗ | if (_ambiguityResolutionParameters.searchAlgorithm != AmbiguityResolutionParameters::SearchAlgorithm::None) | |
| 2727 | { | ||
| 2728 | LOG_TRACE("{}: [{}] Ambiguity resolution failed", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST)); | ||
| 2729 | ✗ | _ambiguitiesHold.clear(); | |
| 2730 | } | ||
| 2731 | |||
| 2732 | ✗ | return false; | |
| 2733 | 601 | } | |
| 2734 | |||
| 2735 | 601 | void RealTimeKinematic::applyFixedAmbiguities(const Eigen::VectorXd& fixedAmb, const std::vector<States::StateKeyType>& ambKeys, const std::vector<Meas::MeasKeyTypes>& ambMeasKeys) | |
| 2736 | { | ||
| 2737 | #if LOG_LEVEL <= LOG_LEVEL_DATA | ||
| 2738 | if (((_kalmanFilter.x(ambKeys).array() * 1e2).round() * 1e-2).matrix() != fixedAmb) | ||
| 2739 | { | ||
| 2740 | auto ambPrint = KeyedMatrixXd<States::StateKeyType>( | ||
| 2741 | (Eigen::MatrixXd(static_cast<int>(ambKeys.size()), 2) << _kalmanFilter.x(ambKeys), fixedAmb).finished(), | ||
| 2742 | ambKeys, std::vector<States::StateKeyType>{ States::AmbiguityDD(SatSigId(Code::G1C, 200)), States::AmbiguityDD(SatSigId(Code::G1C, 201)) }); | ||
| 2743 | LOG_DATA("{}: [{}] Ambiguity estimate changed (relative to pivot) (200 = prev, 201 = new)\n{}", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), ambPrint.transposed()); | ||
| 2744 | LOG_DATA("P(amb) =\n{}", _kalmanFilter.P(ambKeys, ambKeys)); | ||
| 2745 | } | ||
| 2746 | #endif | ||
| 2747 | |||
| 2748 | // Make a KF update with the fixed ambiguities as observation in order to adapt the P matrix | ||
| 2749 |
1/2✓ Branch 2 taken 601 times.
✗ Branch 3 not taken.
|
601 | _kalmanFilter.setMeasurements(ambMeasKeys); |
| 2750 | |||
| 2751 | // Update the Measurement sensitivity Matrix (𝐇), the Measurement noise covariance matrix (𝐑) and the Measurement vector (𝐳) | ||
| 2752 |
4/8✓ Branch 2 taken 601 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 601 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 601 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 601 times.
✗ Branch 13 not taken.
|
601 | _kalmanFilter.z.segment(ambMeasKeys) = fixedAmb - _kalmanFilter.x(ambKeys); |
| 2753 |
2/4✓ Branch 3 taken 601 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 601 times.
✗ Branch 7 not taken.
|
601 | _kalmanFilter.H(ambMeasKeys, ambKeys).setIdentity(); |
| 2754 | |||
| 2755 | // R matrix | ||
| 2756 |
2/4✓ Branch 2 taken 601 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 601 times.
✗ Branch 6 not taken.
|
601 | _kalmanFilter.R(all, all).diagonal().setConstant(_ambFixUpdateVariance); |
| 2757 | |||
| 2758 | LOG_DATA("{}: z =\n{}", nameId(), _kalmanFilter.z.transposed()); | ||
| 2759 | LOG_DATA("{}: H =\n{}", nameId(), _kalmanFilter.H); | ||
| 2760 | LOG_DATA("{}: R =\n{}", nameId(), _kalmanFilter.R); | ||
| 2761 | |||
| 2762 | 601 | _kalmanFilter.correctWithMeasurementInnovation(); | |
| 2763 | |||
| 2764 | LOG_DATA("{}: x (fix, update , t = {}) = (Ambiguity update)\n{}", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), _kalmanFilter.x.transposed()); | ||
| 2765 | // Apply the integer values, otherwise values not exact | ||
| 2766 |
2/4✓ Branch 2 taken 601 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 601 times.
✗ Branch 6 not taken.
|
601 | _kalmanFilter.x(ambKeys) = fixedAmb; |
| 2767 | LOG_DATA("{}: x (fix, update , t = {}) = (Ambiguity integers)\n{}", nameId(), _receiver[Rover].gnssObs->insTime.toYMDHMS(GPST), _kalmanFilter.x.transposed()); | ||
| 2768 | 601 | } | |
| 2769 | |||
| 2770 | 1202 | const char* to_string(const RealTimeKinematic::ReceiverType& receiver) | |
| 2771 | { | ||
| 2772 |
2/4✓ Branch 0 taken 601 times.
✓ Branch 1 taken 601 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
1202 | switch (receiver) |
| 2773 | { | ||
| 2774 | 601 | case RealTimeKinematic::ReceiverType::Base: | |
| 2775 | 601 | return "Base"; | |
| 2776 | 601 | case RealTimeKinematic::ReceiverType::Rover: | |
| 2777 | 601 | return "Rover"; | |
| 2778 | ✗ | case RealTimeKinematic::ReceiverType::ReceiverType_COUNT: | |
| 2779 | ✗ | return ""; | |
| 2780 | } | ||
| 2781 | ✗ | return ""; | |
| 2782 | } | ||
| 2783 | |||
| 2784 | } // namespace NAV | ||
| 2785 |