0.5.1
Loading...
Searching...
No Matches
ErrorModel.cpp
Go to the documentation of this file.
1// This file is part of INSTINCT, the INS Toolkit for Integrated
2// Navigation Concepts and Training by the Institute of Navigation of
3// the University of Stuttgart, Germany.
4//
5// This Source Code Form is subject to the terms of the Mozilla Public
6// License, v. 2.0. If a copy of the MPL was not distributed with this
7// file, You can obtain one at https://mozilla.org/MPL/2.0/.
8
9#include "ErrorModel.hpp"
10
11#include <algorithm>
12#include <cstdint>
13#include <imgui.h>
14#include <imgui_internal.h>
15#include <limits>
16#include <set>
17#include <type_traits>
18
19#include "NodeRegistry.hpp"
23
26
30
35
36#include "util/Eigen.hpp"
37#include "util/StringUtil.hpp"
38
39// ---------------------------------------------------------- Private variabels ------------------------------------------------------------
40
41namespace NAV
42{
43/// List of supported data identifiers
44const std::vector<std::string> supportedDataIdentifier{
48};
49
50} // namespace NAV
51
52// ---------------------------------------------------------- Member functions -------------------------------------------------------------
53
55 : Node(typeStatic())
56{
57 LOG_TRACE("{}: called", name);
58 _hasConfig = true;
59 _guiConfigDefaultWindowSize = { 812, 530 };
60
62
64
65 std::mt19937_64 gen(static_cast<uint64_t>(std::chrono::system_clock::now().time_since_epoch().count()));
66 std::uniform_int_distribution<uint64_t> dist(0, std::numeric_limits<uint64_t>::max() / 2);
67
68 _imuAccelerometerRng.seed = dist(gen);
69 _imuGyroscopeRng.seed = dist(gen);
70
71 _imuAccelerometerRWRng.seed = dist(gen);
72 _imuGyroscopeRWRng.seed = dist(gen);
73 _imuAccelerometerIRWRng.seed = dist(gen);
74 _imuGyroscopeIRWRng.seed = dist(gen);
75
76 _positionRng.seed = dist(gen);
77 _velocityRng.seed = dist(gen);
78 _attitudeRng.seed = dist(gen);
79
80 _pseudorangeRng.seed = dist(gen);
81 _carrierPhaseRng.seed = dist(gen);
82 _dopplerRng.seed = dist(gen);
83 _ambiguityRng.seed = dist(gen);
84 _cycleSlipRng.seed = dist(gen);
85}
86
88{
89 LOG_TRACE("{}: called", nameId());
90}
91
93{
94 return "ErrorModel";
95}
96
97std::string NAV::ErrorModel::type() const
98{
99 return typeStatic();
100}
101
103{
104 return "Data Processor";
105}
106
108{
109 if (outputPins.front().dataIdentifier.size() != 1
110 || !inputPins.front().isPinLinked())
111 {
112 ImGui::TextUnformatted("Please connect the input pin to show the options");
113 return;
114 }
115
116 float itemWidth = 470 * gui::NodeEditorApplication::windowFontRatio();
117 float unitWidth = 180 * gui::NodeEditorApplication::windowFontRatio();
118
119 auto inputDoubleWithUnit = [&](const char* title, double& data, auto& unit, const char* combo_items_separated_by_zeros, const char* format) {
120 if (auto response = gui::widgets::InputDoubleWithUnit(fmt::format("{}##{}", title, size_t(id)).c_str(), itemWidth, unitWidth,
121 &data, unit, combo_items_separated_by_zeros, 0.0, 0.0,
122 format, ImGuiInputTextFlags_CharsScientific))
123 {
124 if (response == gui::widgets::InputWithUnitChange_Input) { LOG_DEBUG("{}: {} changed to {}", nameId(), title, data); }
125 if (response == gui::widgets::InputWithUnitChange_Unit) { LOG_DEBUG("{}: {} unit changed to {}", nameId(), title, fmt::underlying(unit)); }
127 }
128 };
129
130 auto inputVector3WithUnit = [&](const char* title, Eigen::Vector3d& data, auto& unit, const char* combo_items_separated_by_zeros, const char* format) {
131 if (auto response = gui::widgets::InputDouble3WithUnit(fmt::format("{}##{}", title, size_t(id)).c_str(), itemWidth, unitWidth,
132 data.data(), unit, combo_items_separated_by_zeros,
133 format, ImGuiInputTextFlags_CharsScientific))
134 {
135 if (response == gui::widgets::InputWithUnitChange_Input) { LOG_DEBUG("{}: {} changed to {}", nameId(), title, data.transpose()); }
136 if (response == gui::widgets::InputWithUnitChange_Unit) { LOG_DEBUG("{}: {} unit changed to {}", nameId(), title, fmt::underlying(unit)); }
138 }
139 };
140
141 auto rngInput = [&](const char* title, RandomNumberGenerator& rng) {
142 float currentCursorX = ImGui::GetCursorPosX();
143 if (ImGui::Checkbox(fmt::format("##rng.useSeed {} {}", title, size_t(id)).c_str(), &rng.useSeed))
144 {
145 LOG_DEBUG("{}: {} rng.useSeed changed to {}", nameId(), title, rng.useSeed);
147 }
148 if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Use seed?"); }
149 ImGui::SameLine();
150 if (!rng.useSeed)
151 {
152 ImGui::BeginDisabled();
153 }
154 ImGui::SetNextItemWidth(itemWidth - (ImGui::GetCursorPosX() - currentCursorX));
155 if (ImGui::SliderULong(fmt::format("{} Seed##{}", title, size_t(id)).c_str(), &rng.seed, 0, std::numeric_limits<uint64_t>::max() / 2, "%lu"))
156 {
157 LOG_DEBUG("{}: {} rng.seed changed to {}", nameId(), title, rng.seed);
159 }
160 if (!rng.useSeed)
161 {
162 ImGui::EndDisabled();
163 }
164 };
165
166 auto noiseGuiInput = [&]<typename T>(const char* title, T& data, auto& unit, const char* combo_items_separated_by_zeros, const char* format, RandomNumberGenerator& rng) {
167 if constexpr (std::is_same_v<T, double>) { inputDoubleWithUnit(title, data, unit, combo_items_separated_by_zeros, format); }
168 else if constexpr (std::is_same_v<T, Eigen::Vector3d>) { inputVector3WithUnit(title, data, unit, combo_items_separated_by_zeros, format); }
169
170 rngInput(title, rng);
171 };
172
173 if (NAV::NodeRegistry::NodeDataTypeAnyIsChildOf(outputPins.front().dataIdentifier, { ImuObs::type() })
174 || NAV::NodeRegistry::NodeDataTypeAnyIsChildOf(outputPins.front().dataIdentifier, { Pos::type() }))
175 {
176 ImGui::TextUnformatted("Offsets:");
177 ImGui::Indent();
178 {
179 if (NAV::NodeRegistry::NodeDataTypeAnyIsChildOf(outputPins.front().dataIdentifier, { ImuObs::type() }))
180 {
181 inputVector3WithUnit("Accelerometer Bias (platform)", _imuAccelerometerBias_p, _imuAccelerometerBiasUnit, MakeComboItems<Units::ImuAccelerometerUnits>().c_str(), "%.2g");
182 inputVector3WithUnit("Gyroscope Bias (platform)", _imuGyroscopeBias_p, _imuGyroscopeBiasUnit, MakeComboItems<Units::ImuGyroscopeUnits>().c_str(), "%.2g");
183 }
184 else if (NAV::NodeRegistry::NodeDataTypeAnyIsChildOf(outputPins.front().dataIdentifier, { Pos::type() }))
185 {
186 inputVector3WithUnit(fmt::format("Position Bias ({})", _positionBiasUnit == PositionBiasUnits::meter ? "NED" : "LatLonAlt").c_str(),
187 _positionBias, _positionBiasUnit, "m, m, m\0\0", "%.2g");
188 if (NAV::NodeRegistry::NodeDataTypeAnyIsChildOf(outputPins.front().dataIdentifier, { PosVel::type() }))
189 {
190 inputVector3WithUnit("Velocity Bias (NED)", _velocityBias, _velocityBiasUnit, "m/s\0\0", "%.2g");
191 if (NAV::NodeRegistry::NodeDataTypeAnyIsChildOf(outputPins.front().dataIdentifier, { PosVelAtt::type() }))
192 {
193 inputVector3WithUnit("RollPitchYaw Bias", _attitudeBias, _attitudeBiasUnit, "rad\0deg\0\0", "%.2g");
194 }
195 }
196 }
197 }
198 ImGui::Unindent();
199 }
200
201 if (NAV::NodeRegistry::NodeDataTypeAnyIsChildOf(outputPins.front().dataIdentifier, { ImuObs::type() })
202 || NAV::NodeRegistry::NodeDataTypeAnyIsChildOf(outputPins.front().dataIdentifier, { Pos::type() })
203 || NAV::NodeRegistry::NodeDataTypeAnyIsChildOf(outputPins.front().dataIdentifier, { GnssObs::type() }))
204 {
205 ImGui::TextUnformatted("Measurement noise:");
206 ImGui::Indent();
207 {
208 if (NAV::NodeRegistry::NodeDataTypeAnyIsChildOf(outputPins.front().dataIdentifier, { ImuObs::type() }))
209 {
210 noiseGuiInput("Accelerometer Noise (Std. dev)", _imuAccelerometerNoise, _imuAccelerometerNoiseUnit, MakeComboItems<Units::ImuAccelerometerNoiseUnits>().c_str(), "%.2g", _imuAccelerometerRng);
211 noiseGuiInput("Gyroscope Noise (Std. dev)", _imuGyroscopeNoise, _imuGyroscopeNoiseUnit, MakeComboItems<Units::ImuGyroscopeNoiseUnits>().c_str(), "%.2g", _imuGyroscopeRng);
212 }
213 else if (NAV::NodeRegistry::NodeDataTypeAnyIsChildOf(outputPins.front().dataIdentifier, { Pos::type() }))
214 {
215 noiseGuiInput(fmt::format("Position Noise ({})", _positionNoiseUnit == PositionNoiseUnits::meter
216 ? "Standard deviation"
217 : "Variance")
218 .c_str(),
219 _positionNoise, _positionNoiseUnit, "m, m, m\0m^2, m^2, m^2\0\0",
220 "%.2g", _positionRng);
221 if (NAV::NodeRegistry::NodeDataTypeAnyIsChildOf(outputPins.front().dataIdentifier, { PosVel::type() }))
222 {
223 noiseGuiInput(fmt::format("Velocity Noise ({})", _velocityNoiseUnit == VelocityNoiseUnits::m_s ? "Standard deviation"
224 : "Variance")
225 .c_str(),
226 _velocityNoise, _velocityNoiseUnit, "m/s\0m^2/s^2\0\0", "%.2g", _velocityRng);
227 if (NAV::NodeRegistry::NodeDataTypeAnyIsChildOf(outputPins.front().dataIdentifier, { PosVelAtt::type() }))
228 {
229 noiseGuiInput(fmt::format("Attitude Noise ({})", _attitudeNoiseUnit == AttitudeNoiseUnits::rad || _attitudeNoiseUnit == AttitudeNoiseUnits::deg
230 ? "Standard deviation"
231 : "Variance")
232 .c_str(),
233 _attitudeNoise, _attitudeNoiseUnit, "rad\0deg\0rad^2\0deg^2\0\0", "%.2g", _attitudeRng);
234 }
235 }
236 }
237 else if (NAV::NodeRegistry::NodeDataTypeAnyIsChildOf(outputPins.front().dataIdentifier, { GnssObs::type() }))
238 {
239 noiseGuiInput("Pseudorange Noise", _gui_pseudorangeNoise, _gui_pseudorangeNoiseUnit, "m\0\0", "%.3g", _pseudorangeRng);
240 noiseGuiInput("Carrier-phase Noise", _gui_carrierPhaseNoise, _gui_carrierPhaseNoiseUnit, "m\0\0", "%.3g", _carrierPhaseRng);
241 noiseGuiInput("Doppler/Range-rate Noise", _gui_dopplerNoise, _gui_dopplerNoiseUnit, "m/s\0\0", "%.3g", _dopplerRng);
242 }
243 }
244 ImGui::Unindent();
245 }
246 if (NAV::NodeRegistry::NodeDataTypeAnyIsChildOf(outputPins.front().dataIdentifier, { ImuObs::type() }))
247 {
248 ImGui::TextUnformatted("Random walk noise:");
249 ImGui::Indent();
250 noiseGuiInput("Accelerometer RW (Std. dev)", _imuAccelerometerRW, _imuAccelerometerRWUnit, MakeComboItems<Units::ImuAccelerometerNoiseUnits>().c_str(), "%.2g", _imuAccelerometerRWRng);
251 noiseGuiInput("Gyroscope RW (Std. dev)", _imuGyroscopeRW, _imuGyroscopeRWUnit, MakeComboItems<Units::ImuGyroscopeNoiseUnits>().c_str(), "%.2g", _imuGyroscopeRWRng);
252 ImGui::Unindent();
253 ImGui::TextUnformatted("Integrated Random walk noise:");
254 ImGui::Indent();
255 noiseGuiInput("Accelerometer IRW (Std. dev)", _imuAccelerometerIRW, _imuAccelerometerIRWUnit, MakeComboItems<Units::ImuAccelerometerIRWUnits>().c_str(), "%.2g", _imuAccelerometerIRWRng);
256 noiseGuiInput("Gyroscope IRW (Std. dev)", _imuGyroscopeIRW, _imuGyroscopeIRWUnit, MakeComboItems<Units::ImuGyroscopeIRWUnits>().c_str(), "%.2g", _imuGyroscopeIRWRng);
257 ImGui::Unindent();
258 }
259
260 if (NAV::NodeRegistry::NodeDataTypeAnyIsChildOf(outputPins.front().dataIdentifier, { GnssObs::type() }))
261 {
262 ImGui::TextUnformatted("Ambiguities:");
263 ImGui::Indent();
264 {
265 ImGui::SetNextItemWidth((itemWidth - ImGui::GetStyle().ItemInnerSpacing.x) / 2.0F);
266 if (ImGui::InputIntL(fmt::format("##Ambiguity Bounds lower {}", size_t(id)).c_str(), _gui_ambiguityLimits.data(),
267 std::numeric_limits<int>::lowest(), _gui_ambiguityLimits[1], 0, 0))
268 {
269 LOG_DEBUG("{}: Ambiguity lower bound changed to {}", nameId(), _gui_ambiguityLimits[0]);
271 }
272 ImGui::SameLine();
273 ImGui::SetCursorPosX(ImGui::GetCursorPosX() - ImGui::GetStyle().ItemSpacing.x + ImGui::GetStyle().ItemInnerSpacing.x);
274 ImGui::SetNextItemWidth((itemWidth - ImGui::GetStyle().ItemInnerSpacing.x) / 2.0F);
275 if (ImGui::InputIntL(fmt::format("Ambiguity Bounds##Ambiguity Bounds upper{}", size_t(id)).c_str(), &_gui_ambiguityLimits[1],
276 _gui_ambiguityLimits[0], std::numeric_limits<int>::max(), 0, 0))
277 {
278 LOG_DEBUG("{}: Ambiguity upper bound changed to {}", nameId(), _gui_ambiguityLimits[1]);
280 }
281 rngInput("Ambiguity", _ambiguityRng);
282 }
283 ImGui::Unindent();
284
285 ImGui::TextUnformatted("Cycle-slip:");
286 ImGui::Indent();
287 {
288 noiseGuiInput("Frequency", _gui_cycleSlipFrequency, _gui_cycleSlipFrequencyUnit, "/ day\0/ hour\0/ minute\0\0", "%.2g", _cycleSlipRng);
289
290 if (auto response = gui::widgets::SliderDoubleWithUnit(fmt::format("Detection probability (LLI)##{}", size_t(id)).c_str(), itemWidth, unitWidth,
293 {
294 if (response == gui::widgets::InputWithUnitChange_Input) { LOG_DEBUG("{}: Detection probability (LLI) changed to {}", nameId(), _gui_cycleSlipDetectionProbability); }
295 if (response == gui::widgets::InputWithUnitChange_Unit) { LOG_DEBUG("{}: Detection probability (LLI) unit changed to {}", nameId(), fmt::underlying(_gui_cycleSlipDetectionProbabilityUnit)); }
297 }
298 ImGui::SameLine();
299 gui::widgets::HelpMarker("The chance that the Lock-of-Loss (LLI) indicator is set, when a cycle-slip occurs");
300
301 ImGui::SetNextItemWidth(itemWidth);
302 if (ImGui::DragInt(fmt::format("Ambiguity Range to slip to##{}", size_t(id)).c_str(), &_gui_cycleSlipRange,
303 1.0F, 1, std::numeric_limits<int>::max(), "+/- %d"))
304 {
305 LOG_DEBUG("{}: Cycle-slip ambiguity range changed to {}", nameId(), _gui_cycleSlipRange);
307 }
308 ImGui::SameLine();
309 gui::widgets::HelpMarker("If the ambiguity range is set, the cycle-slips are bound by it too");
310
311 ImGui::SetNextItemWidth(itemWidth);
312 if (ShowFrequencySelector(fmt::format("Satellite Frequencies##{}", size_t(id)).c_str(), _filterFreq))
313 {
315 }
316 ImGui::SameLine();
317 gui::widgets::HelpMarker("Frequency and code selection only affects the cycle-slip simulation");
318
319 ImGui::SetNextItemWidth(itemWidth);
320 if (ShowCodeSelector(fmt::format("Signal Codes##{}", size_t(id)).c_str(), _filterCode, _filterFreq))
321 {
323 }
324 ImGui::SetNextItemOpen(true, ImGuiCond_FirstUseEver);
325 if (ImGui::TreeNode(fmt::format("Manual Cycle-slips Tree##{}", size_t(id)).c_str()))
326 {
327 ImGui::BeginGroup();
328 {
329 ImGui::TextUnformatted("New:");
330
331 float groupWidth = 62.0F * 2 + ImGui::GetStyle().ItemSpacing.x;
332 ImGui::BeginHorizontal(fmt::format("Horizontal SatSigId {}", size_t(id)).c_str());
333 {
334 ImGui::SetNextItemWidth(62.0F);
335 if (ShowCodeSelector(fmt::format("##Manual cycle-slip code {}", size_t(id)).c_str(), _manualCycleSlipSignal.code, Freq_All, true))
336 {
338 }
339 ImGui::SetNextItemWidth(62.0F);
340 SatId satId = _manualCycleSlipSignal.toSatId();
341 if (ShowSatelliteSelector(fmt::format("##Manual cycle-slip sat{}", size_t(id)).c_str(), satId, satId.satSys, true))
342 {
343 _manualCycleSlipSignal.satNum = satId.satNum;
345 }
346 ImGui::EndHorizontal();
347 }
348
349 ImGui::SetNextItemWidth(groupWidth);
350 if (ImGui::InputInt(fmt::format("##Ambiguity value {}", size_t(id)).c_str(), &_manualCycleSlipAmbiguity)) { flow::ApplyChanges(); }
351 if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Ambiguity value"); }
352
353 ImGui::BeginHorizontal(fmt::format("Horizontal LLI/Button {}", size_t(id)).c_str(), ImVec2(groupWidth, 0.0F));
354 {
355 if (ImGui::Checkbox(fmt::format("LLI##Ambiguity LLI {}", size_t(id)).c_str(), &_manualCycleSlipSetLLI)) { flow::ApplyChanges(); }
356 ImGui::Spring();
357 if (ImGui::Button(fmt::format("Add##manual cycle-slip {}", size_t(id)).c_str()))
358 {
361 }
362 ImGui::EndHorizontal();
363 }
364 }
365 ImGui::EndGroup();
366
367 ImGui::SameLine();
368 if (gui::widgets::TimeEdit(fmt::format("Manual Cycle-slips Time##{}", size_t(id)).c_str(),
370 {
372 }
373
374 if (!_manualCycleSlips.empty()
375 && ImGui::BeginTable(fmt::format("Manual cycle-slips Table##{}", size_t(id)).c_str(), 5,
376 ImGuiTableFlags_Borders | ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollY,
377 ImVec2(0.0F, std::min(25.0F + static_cast<float>(_manualCycleSlips.size()) * 28.0F, 300.0F)
379 {
380 ImGui::TableSetupColumn("Time");
381 ImGui::TableSetupColumn("Signal");
382 ImGui::TableSetupColumn("Value");
383 ImGui::TableSetupColumn("LLI");
384 ImGui::TableSetupColumn("Delete");
385
386 ImGui::TableSetupScrollFreeze(1, 1);
387 ImGui::TableHeadersRow();
388
389 std::optional<std::pair<InsTime, SatSigId>> _toRemove;
390 for (auto& cycleSlip : _manualCycleSlips)
391 {
392 const auto& [insTime, satSigId] = cycleSlip.first;
393 auto& [ambValue, setLLI] = cycleSlip.second;
394 ImGui::TableNextRow();
395
396 ImGui::TableNextColumn();
397 ImGui::TextUnformatted(fmt::format("{}", insTime.toYMDHMS(GPST)).c_str());
398
399 ImGui::TableNextColumn();
400 {
401 ImGui::SetNextItemWidth(62.0F);
402 auto newSatSigId = satSigId;
403 if (ShowCodeSelector(fmt::format("##Manual cycle-slip code {} {} {}", size_t(id), insTime, satSigId).c_str(), newSatSigId.code, Freq_All, true))
404 {
406 }
407 ImGui::SameLine();
408 ImGui::SetNextItemWidth(62.0F);
409 SatId satId = newSatSigId.toSatId();
410 if (ShowSatelliteSelector(fmt::format("##Manual cycle-slip sat{} {} {}", size_t(id), insTime, satSigId).c_str(), satId, satId.satSys, true))
411 {
412 newSatSigId.satNum = satId.satNum;
414 }
415 if (newSatSigId != satSigId)
416 {
417 _toRemove = cycleSlip.first;
418 _manualCycleSlips[{ insTime, newSatSigId }] = { ambValue, setLLI };
419 }
420 }
421
422 ImGui::TableNextColumn();
423 ImGui::SetNextItemWidth(120.0F);
424 if (ImGui::InputInt(fmt::format("##Ambiguity value {} {} {}", size_t(id), insTime, satSigId).c_str(), &ambValue))
425 {
427 }
428
429 ImGui::TableNextColumn();
430 if (ImGui::Checkbox(fmt::format("##Ambiguity LLI {} {} {}", size_t(id), insTime, satSigId).c_str(), &setLLI))
431 {
433 }
434
435 ImGui::TableNextColumn();
436 if (ImGui::Button(fmt::format("X##Manual cycle remove {} {} {}", size_t(id), insTime, satSigId).c_str()))
437 {
438 _toRemove = cycleSlip.first;
439 }
440 }
441 if (_toRemove)
442 {
443 _manualCycleSlips.erase(*_toRemove);
445 }
446 ImGui::EndTable();
447 }
448
449 ImGui::TreePop();
450 }
451 }
452 ImGui::Unindent();
453
454 ImGui::SetNextItemOpen(true, ImGuiCond_FirstUseEver);
455 if (ImGui::TreeNode(fmt::format("Simulated Ambiguities and Cycle-slips##{}", size_t(id)).c_str()))
456 {
457 auto nAmb = static_cast<int>(_ambiguities.size());
458 if (nAmb > 0)
459 {
460 std::set<InsTime> ambiguityTimes;
461 for (int i = 0; i < nAmb; i++)
462 {
463 const auto& ambVec = std::next(_ambiguities.begin(), i)->second;
464 for (const auto& amb : ambVec)
465 {
466 ambiguityTimes.insert(amb.first);
467 }
468 }
469 if (ambiguityTimes.size() < 64 - 1)
470 {
471 ImGui::PushFont(Application::MonoFont());
472
473 if (ImGui::BeginTable(fmt::format("Ambiguities##{}", size_t(id)).c_str(), static_cast<int>(ambiguityTimes.size() + 1),
474 ImGuiTableFlags_Borders | ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY,
475 ImVec2(0.0F, 300.0F * gui::NodeEditorApplication::monoFontRatio())))
476 {
477 ImGui::TableSetupColumn("");
478 for (const auto& time : ambiguityTimes)
479 {
480 auto t = time.toYMDHMS(GPST);
481 t.sec = std::round(t.sec * 1e2) / 1e2;
482 ImGui::TableSetupColumn(str::replaceAll_copy(fmt::format("{}", t), " ", "\n").c_str());
483 }
484 ImGui::TableSetupScrollFreeze(1, 1);
485 ImGui::TableHeadersRow();
486
487 for (int i = 0; i < nAmb; i++)
488 {
489 ImGui::TableNextRow();
490 ImGui::TableNextColumn();
491 const auto& ambiguities = std::next(_ambiguities.begin(), i);
492 if (ambiguities == _ambiguities.end()) { break; }
493 ImGui::TextUnformatted(fmt::format("{}", ambiguities->first).c_str());
494 ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ImGui::GetColorU32(ImGui::GetStyle().Colors[ImGuiCol_TableHeaderBg]));
495
496 for (const auto& time : ambiguityTimes)
497 {
498 ImGui::TableNextColumn();
499 auto iter = std::ranges::find_if(ambiguities->second, [&time](const auto& timeAndAmb) {
500 return timeAndAmb.first == time;
501 });
502 if (iter != ambiguities->second.end())
503 {
504 ImVec4 color;
505 auto cycleIter = std::ranges::find_if(_cycleSlips, [&](const CycleSlipInfo& cycleSlip) {
506 return cycleSlip.time == time && cycleSlip.satSigId == ambiguities->first;
507 });
508 if (cycleIter != _cycleSlips.end()) { color = ImColor(240, 128, 128); }
509 else if (ambiguities->second.back().first == time) { color = ImGui::GetStyle().Colors[ImGuiCol_Text]; }
510 else { color = ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]; }
511 ImGui::TextColored(color, "%s%s",
512 fmt::format("{}", iter->second).c_str(),
513 cycleIter != _cycleSlips.end() && cycleIter->LLI ? " (LLI)" : "");
514 if (ImGui::IsItemHovered() && cycleIter != _cycleSlips.end()) { ImGui::SetTooltip("Cycle-slip"); }
515 }
516 else if (time < ambiguities->second.front().first)
517 {
518 ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ImColor(70, 70, 70));
519 }
520 }
521 }
522
523 ImGui::EndTable();
524 }
525 ImGui::PopFont();
526 }
527 else
528 {
529 ImGui::TextColored(ImColor(255, 0, 0), "More than 64 timestamps for ambiguities, which cannot be displayed in a table");
530 }
531 }
532
533 ImGui::TreePop();
534 }
535 }
536}
537
539{
540 LOG_TRACE("{}: called", nameId());
541
542 json j;
543
544 j["imuAccelerometerBiasUnit"] = _imuAccelerometerBiasUnit;
545 j["imuAccelerometerBias_p"] = _imuAccelerometerBias_p;
546 j["imuGyroscopeBiasUnit"] = _imuGyroscopeBiasUnit;
547 j["imuGyroscopeBias_p"] = _imuGyroscopeBias_p;
548 j["imuAccelerometerNoiseUnit"] = _imuAccelerometerNoiseUnit;
549 j["imuAccelerometerNoise"] = _imuAccelerometerNoise;
550 j["imuAccelerometerRng"] = _imuAccelerometerRng;
551 j["imuGyroscopeNoiseUnit"] = _imuGyroscopeNoiseUnit;
552 j["imuGyroscopeNoise"] = _imuGyroscopeNoise;
553 j["imuGyroscopeRng"] = _imuGyroscopeRng;
554 j["imuAccelerometerRWUnit"] = _imuAccelerometerRWUnit;
555 j["imuAccelerometerRW"] = _imuAccelerometerRW;
556 j["imuAccelerometerRWRng"] = _imuAccelerometerRWRng;
557
558 j["imuGyroscopeRWUnit"] = _imuGyroscopeRWUnit;
559 j["imuGyroscopeRW"] = _imuGyroscopeRW;
560 j["imuGyroscopeRWRng"] = _imuGyroscopeRWRng;
561 j["imuAccelerometerIRWUnit"] = _imuAccelerometerIRWUnit;
562 j["imuAccelerometerIRW"] = _imuAccelerometerIRW;
563 j["imuAccelerometerIRWRng"] = _imuAccelerometerIRWRng;
564 j["imuGyroscopeIRWUnit"] = _imuGyroscopeIRWUnit;
565 j["imuGyroscopeIRW"] = _imuGyroscopeIRW;
566 j["imuGyroscopeIRWRng"] = _imuGyroscopeIRWRng;
567
568 // #########################################################################################################################################
569 j["positionBiasUnit"] = _positionBiasUnit;
570 j["positionBias"] = _positionBias;
571 j["velocityBiasUnit"] = _velocityBiasUnit;
572 j["velocityBias"] = _velocityBias;
573 j["attitudeBiasUnit"] = _attitudeBiasUnit;
574 j["attitudeBias"] = _attitudeBias;
575 j["positionNoiseUnit"] = _positionNoiseUnit;
576 j["positionNoise"] = _positionNoise;
577 j["positionRng"] = _positionRng;
578 j["velocityNoiseUnit"] = _velocityNoiseUnit;
579 j["velocityNoise"] = _velocityNoise;
580 j["velocityRng"] = _velocityRng;
581 j["attitudeNoiseUnit"] = _attitudeNoiseUnit;
582 j["attitudeNoise"] = _attitudeNoise;
583 j["attitudeRng"] = _attitudeRng;
584 // #########################################################################################################################################
585 j["pseudorangeNoiseUnit"] = _gui_pseudorangeNoiseUnit;
586 j["pseudorangeNoise"] = _gui_pseudorangeNoise;
587 j["pseudorangeRng"] = _pseudorangeRng;
588 j["carrierPhaseNoiseUnit"] = _gui_carrierPhaseNoiseUnit;
589 j["carrierPhaseNoise"] = _gui_carrierPhaseNoise;
590 j["carrierPhaseRng"] = _carrierPhaseRng;
591 j["dopplerNoiseUnit"] = _gui_dopplerNoiseUnit;
592 j["dopplerNoise"] = _gui_dopplerNoise;
593 j["dopplerRng"] = _dopplerRng;
594 j["ambiguityLimits"] = _gui_ambiguityLimits;
595 j["ambiguityRng"] = _ambiguityRng;
596 j["manualAmbiguities"] = _manualCycleSlips;
597 j["manualCycleSlipTime"] = _manualCycleSlipTime;
598 j["manualCycleSlipTimeEditFormat"] = _manualCycleSlipTimeEditFormat;
599 j["manualCycleSlipSignal"] = _manualCycleSlipSignal;
600 j["manualCycleSlipAmbiguity"] = _manualCycleSlipAmbiguity;
601 j["manualCycleSlipSetLLI"] = _manualCycleSlipSetLLI;
602 j["cycleSlipFrequencyUnit"] = _gui_cycleSlipFrequencyUnit;
603 j["cycleSlipFrequency"] = _gui_cycleSlipFrequency;
604 j["cycleSlipRange"] = _gui_cycleSlipRange;
605 j["cycleSlipDetectionProbabilityUnit"] = _gui_cycleSlipDetectionProbabilityUnit;
606 j["cycleSlipDetectionProbability"] = _gui_cycleSlipDetectionProbability;
607 j["cycleSlipRng"] = _cycleSlipRng;
608
609 j["filterFreq"] = Frequency_(_filterFreq);
610 j["filterCode"] = _filterCode;
611
612 return j;
613}
614
616{
617 LOG_TRACE("{}: called", nameId());
618
619 if (j.contains("imuAccelerometerBiasUnit")) { j.at("imuAccelerometerBiasUnit").get_to(_imuAccelerometerBiasUnit); }
620 if (j.contains("imuAccelerometerBias_p")) { j.at("imuAccelerometerBias_p").get_to(_imuAccelerometerBias_p); }
621 if (j.contains("imuGyroscopeBiasUnit")) { j.at("imuGyroscopeBiasUnit").get_to(_imuGyroscopeBiasUnit); }
622 if (j.contains("imuGyroscopeBias_p")) { j.at("imuGyroscopeBias_p").get_to(_imuGyroscopeBias_p); }
623 if (j.contains("imuAccelerometerNoiseUnit")) { j.at("imuAccelerometerNoiseUnit").get_to(_imuAccelerometerNoiseUnit); }
624 if (j.contains("imuAccelerometerNoise")) { j.at("imuAccelerometerNoise").get_to(_imuAccelerometerNoise); }
625 if (j.contains("imuAccelerometerRng")) { j.at("imuAccelerometerRng").get_to(_imuAccelerometerRng); }
626 if (j.contains("imuGyroscopeNoiseUnit")) { j.at("imuGyroscopeNoiseUnit").get_to(_imuGyroscopeNoiseUnit); }
627 if (j.contains("imuGyroscopeNoise")) { j.at("imuGyroscopeNoise").get_to(_imuGyroscopeNoise); }
628 if (j.contains("imuGyroscopeRng")) { j.at("imuGyroscopeRng").get_to(_imuGyroscopeRng); }
629
630 if (j.contains("imuAccelerometerRWUnit")) { j.at("imuAccelerometerRWUnit").get_to(_imuAccelerometerRWUnit); }
631 if (j.contains("imuAccelerometerRW")) { j.at("imuAccelerometerRW").get_to(_imuAccelerometerRW); }
632 if (j.contains("imuAccelerometerRWRng")) { j.at("imuAccelerometerRWRng").get_to(_imuAccelerometerRWRng); }
633 if (j.contains("imuGyroscopeRWUnit")) { j.at("imuGyroscopeRWUnit").get_to(_imuGyroscopeRWUnit); }
634 if (j.contains("imuGyroscopeRW")) { j.at("imuGyroscopeRW").get_to(_imuGyroscopeRW); }
635 if (j.contains("imuGyroscopeRWRng")) { j.at("imuGyroscopeRWRng").get_to(_imuGyroscopeRWRng); }
636 if (j.contains("imuAccelerometerIRWUnit")) { j.at("imuAccelerometerIRWUnit").get_to(_imuAccelerometerIRWUnit); }
637 if (j.contains("imuAccelerometerIRW")) { j.at("imuAccelerometerIRW").get_to(_imuAccelerometerIRW); }
638 if (j.contains("imuAccelerometerIRWRng")) { j.at("imuAccelerometerIRWRng").get_to(_imuAccelerometerIRWRng); }
639 if (j.contains("imuGyroscopeIRWUnit")) { j.at("imuGyroscopeIRWUnit").get_to(_imuGyroscopeIRWUnit); }
640 if (j.contains("imuGyroscopeIRW")) { j.at("imuGyroscopeIRW").get_to(_imuGyroscopeIRW); }
641 if (j.contains("imuGyroscopeIRWRng")) { j.at("imuGyroscopeIRWRng").get_to(_imuGyroscopeIRWRng); }
642 // #########################################################################################################################################
643 if (j.contains("positionBiasUnit")) { j.at("positionBiasUnit").get_to(_positionBiasUnit); }
644 if (j.contains("positionBias")) { j.at("positionBias").get_to(_positionBias); }
645 if (j.contains("velocityBiasUnit")) { j.at("velocityBiasUnit").get_to(_velocityBiasUnit); }
646 if (j.contains("velocityBias")) { j.at("velocityBias").get_to(_velocityBias); }
647 if (j.contains("attitudeBiasUnit")) { j.at("attitudeBiasUnit").get_to(_attitudeBiasUnit); }
648 if (j.contains("attitudeBias")) { j.at("attitudeBias").get_to(_attitudeBias); }
649 if (j.contains("positionNoiseUnit")) { j.at("positionNoiseUnit").get_to(_positionNoiseUnit); }
650 if (j.contains("positionNoise")) { j.at("positionNoise").get_to(_positionNoise); }
651 if (j.contains("positionRng")) { j.at("positionRng").get_to(_positionRng); }
652 if (j.contains("velocityNoiseUnit")) { j.at("velocityNoiseUnit").get_to(_velocityNoiseUnit); }
653 if (j.contains("velocityNoise")) { j.at("velocityNoise").get_to(_velocityNoise); }
654 if (j.contains("velocityRng")) { j.at("velocityRng").get_to(_velocityRng); }
655 if (j.contains("attitudeNoiseUnit")) { j.at("attitudeNoiseUnit").get_to(_attitudeNoiseUnit); }
656 if (j.contains("attitudeNoise")) { j.at("attitudeNoise").get_to(_attitudeNoise); }
657 if (j.contains("attitudeRng")) { j.at("attitudeRng").get_to(_attitudeRng); }
658 // #########################################################################################################################################
659 if (j.contains("pseudorangeNoiseUnit")) { j.at("pseudorangeNoiseUnit").get_to(_gui_pseudorangeNoiseUnit); }
660 if (j.contains("pseudorangeNoise")) { j.at("pseudorangeNoise").get_to(_gui_pseudorangeNoise); }
661 if (j.contains("pseudorangeRng")) { j.at("pseudorangeRng").get_to(_pseudorangeRng); }
662 if (j.contains("carrierPhaseNoiseUnit")) { j.at("carrierPhaseNoiseUnit").get_to(_gui_carrierPhaseNoiseUnit); }
663 if (j.contains("carrierPhaseNoise")) { j.at("carrierPhaseNoise").get_to(_gui_carrierPhaseNoise); }
664 if (j.contains("carrierPhaseRng")) { j.at("carrierPhaseRng").get_to(_carrierPhaseRng); }
665 if (j.contains("dopplerNoiseUnit")) { j.at("dopplerNoiseUnit").get_to(_gui_dopplerNoiseUnit); }
666 if (j.contains("dopplerNoise")) { j.at("dopplerNoise").get_to(_gui_dopplerNoise); }
667 if (j.contains("dopplerRng")) { j.at("dopplerRng").get_to(_dopplerRng); }
668 if (j.contains("ambiguityLimits")) { j.at("ambiguityLimits").get_to(_gui_ambiguityLimits); }
669 if (j.contains("ambiguityRng")) { j.at("ambiguityRng").get_to(_ambiguityRng); }
670 if (j.contains("manualAmbiguities")) { j.at("manualAmbiguities").get_to(_manualCycleSlips); }
671 if (j.contains("manualCycleSlipTime")) { j.at("manualCycleSlipTime").get_to(_manualCycleSlipTime); }
672 if (j.contains("manualCycleSlipTimeEditFormat")) { j.at("manualCycleSlipTimeEditFormat").get_to(_manualCycleSlipTimeEditFormat); }
673 if (j.contains("manualCycleSlipSignal")) { j.at("manualCycleSlipSignal").get_to(_manualCycleSlipSignal); }
674 if (j.contains("manualCycleSlipAmbiguity")) { j.at("manualCycleSlipAmbiguity").get_to(_manualCycleSlipAmbiguity); }
675 if (j.contains("manualCycleSlipSetLLI")) { j.at("manualCycleSlipSetLLI").get_to(_manualCycleSlipSetLLI); }
676 if (j.contains("cycleSlipFrequencyUnit")) { j.at("cycleSlipFrequencyUnit").get_to(_gui_cycleSlipFrequencyUnit); }
677 if (j.contains("cycleSlipFrequency")) { j.at("cycleSlipFrequency").get_to(_gui_cycleSlipFrequency); }
678 if (j.contains("cycleSlipRange")) { j.at("cycleSlipRange").get_to(_gui_cycleSlipRange); }
679 if (j.contains("cycleSlipDetectionProbabilityUnit")) { j.at("cycleSlipDetectionProbabilityUnit").get_to(_gui_cycleSlipDetectionProbabilityUnit); }
680 if (j.contains("cycleSlipDetectionProbability")) { j.at("cycleSlipDetectionProbability").get_to(_gui_cycleSlipDetectionProbability); }
681 if (j.contains("cycleSlipRng")) { j.at("cycleSlipRng").get_to(_cycleSlipRng); }
682 if (j.contains("filterFreq"))
683 {
684 uint64_t value = 0;
685 j.at("filterFreq").get_to(value);
686 _filterFreq = Frequency_(value);
687 }
688 if (j.contains("filterCode")) { j.at("filterCode").get_to(_filterCode); }
689}
690
692{
693 LOG_TRACE("{}: called", nameId());
694
695 _lastObservationTime.reset();
696 _dt = 0.0;
697
698 // ImuObs
699 _imuAccelerometerRng.resetSeed(size_t(id));
700 _imuGyroscopeRng.resetSeed(size_t(id));
701 _imuAccelerometerRWRng.resetSeed(size_t(id));
702 _imuGyroscopeRWRng.resetSeed(size_t(id));
703 _imuAccelerometerIRWRng.resetSeed(size_t(id));
704 _imuGyroscopeIRWRng.resetSeed(size_t(id));
705 _randomWalkAccelerometer.setZero();
706 _randomWalkGyroscope.setZero();
711
712 // PosVelAtt
713 _positionRng.resetSeed(size_t(id));
714 _velocityRng.resetSeed(size_t(id));
715 _attitudeRng.resetSeed(size_t(id));
716
717 // GnssObs
718 _pseudorangeRng.resetSeed(size_t(id));
719 _carrierPhaseRng.resetSeed(size_t(id));
720 _dopplerRng.resetSeed(size_t(id));
721 _ambiguityRng.resetSeed(size_t(id));
722 _ambiguities.clear();
723 _cycleSlips.clear();
724 _cycleSlipRng.resetSeed(size_t(id));
726
727 return true;
728}
729
731{
732 LOG_TRACE("{}: called for {} ==> {}", nameId(), size_t(startPin.id), size_t(endPin.id));
733
734 if (endPin.parentNode->id != id)
735 {
736 return; // Link on Output Port
737 }
738
739 // Store previous output pin identifier
740 auto previousOutputPinDataIdentifier = outputPins.front().dataIdentifier;
741 // Overwrite output pin identifier with input pin identifier
742 outputPins.front().dataIdentifier = startPin.dataIdentifier;
743
744 if (previousOutputPinDataIdentifier != outputPins.front().dataIdentifier) // If the identifier changed
745 {
746 // Check if connected links on output port are still valid
747 for (auto& link : outputPins.front().links)
748 {
749 if (auto* endPin = link.getConnectedPin())
750 {
751 if (!outputPins.front().canCreateLink(*endPin))
752 {
753 // If the link is not valid anymore, delete it
754 outputPins.front().deleteLink(*endPin);
755 }
756 }
757 }
758
759 // Refresh all links connected to the output pin if the type changed
760 if (outputPins.front().dataIdentifier != previousOutputPinDataIdentifier)
761 {
762 for (auto& link : outputPins.front().links)
763 {
764 if (auto* connectedPin = link.getConnectedPin())
765 {
766 outputPins.front().recreateLink(*connectedPin);
767 }
768 }
769 }
770 }
771}
772
774{
775 LOG_TRACE("{}: called for {} ==> {}", nameId(), size_t(startPin.id), size_t(endPin.id));
776
777 if ((endPin.parentNode->id != id // Link on Output port is removed
778 && !inputPins.front().isPinLinked()) // and the Input port is not linked
779 || (startPin.parentNode->id != id // Link on Input port is removed
780 && !outputPins.front().isPinLinked())) // and the Output port is not linked
781 {
782 outputPins.front().dataIdentifier = supportedDataIdentifier;
783 }
784}
785
787{
788 auto obs = queue.extract_front();
789 if (!_lastObservationTime.empty()) { _dt = static_cast<double>((obs->insTime - _lastObservationTime).count()); }
790
791 // Accelerometer Bias in platform frame coordinates [m/s^2]
792 Eigen::Vector3d accelerometerBias_p = convertUnit(_imuAccelerometerBias_p, _imuAccelerometerBiasUnit);
793 LOG_DATA("{}: accelerometerBias_p = {} [m/s^2]", nameId(), accelerometerBias_p.transpose());
794
795 // Gyroscope Bias in platform frame coordinates [rad/s]
796 Eigen::Vector3d gyroscopeBias_p = convertUnit(_imuGyroscopeBias_p, _imuGyroscopeBiasUnit);
797 LOG_DATA("{}: gyroscopeBias_p = {} [rad/s]", nameId(), gyroscopeBias_p.transpose());
798
799 // #########################################################################################################################################
800
801 // Accelerometer Noise standard deviation in platform frame coordinates [m/s^2/sqrt(s)]
802 Eigen::Vector3d accelerometerNoiseStd = convertUnit(_imuAccelerometerNoise, _imuAccelerometerNoiseUnit);
803 LOG_DATA("{}: accelerometerNoiseStd = {} [m/s^2/sqrt(s)]", nameId(), accelerometerNoiseStd.transpose());
804
805 // Gyroscope Noise standard deviation in platform frame coordinates [rad/s/sqrt(s)]
806 Eigen::Vector3d gyroscopeNoiseStd = convertUnit(_imuGyroscopeNoise, _imuGyroscopeNoiseUnit);
807 LOG_DATA("{}: gyroscopeNoiseStd = {} [rad/s/sqrt(s)]", nameId(), gyroscopeNoiseStd.transpose());
808
809 // Select the correct data type and make a copy of the node data to modify
810 if (NAV::NodeRegistry::NodeDataTypeAnyIsChildOf(outputPins.front().dataIdentifier, { ImuObsSimulated::type() }))
811 {
813 receiveImuObsWDelta(std::make_shared<ImuObsSimulated>(*std::static_pointer_cast<const ImuObsSimulated>(obs)),
814 accelerometerBias_p,
815 gyroscopeBias_p,
816 accelerometerNoiseStd,
817 gyroscopeNoiseStd));
818 }
819 else if (NAV::NodeRegistry::NodeDataTypeAnyIsChildOf(outputPins.front().dataIdentifier, { ImuObsWDelta::type() }))
820 {
822 receiveImuObsWDelta(std::make_shared<ImuObsWDelta>(*std::static_pointer_cast<const ImuObsWDelta>(obs)),
823 accelerometerBias_p,
824 gyroscopeBias_p,
825 accelerometerNoiseStd,
826 gyroscopeNoiseStd));
827 }
828 else if (NAV::NodeRegistry::NodeDataTypeAnyIsChildOf(outputPins.front().dataIdentifier, { ImuObs::type() }))
829 {
831 receiveImuObs(std::make_shared<ImuObs>(*std::static_pointer_cast<const ImuObs>(obs)),
832 accelerometerBias_p,
833 gyroscopeBias_p,
834 accelerometerNoiseStd,
835 gyroscopeNoiseStd));
836 }
837 else if (NAV::NodeRegistry::NodeDataTypeAnyIsChildOf(outputPins.front().dataIdentifier, { PosVelAtt::type() }))
838 {
839 invokeCallbacks(OUTPUT_PORT_INDEX_FLOW, receivePosVelAtt(std::make_shared<PosVelAtt>(*std::static_pointer_cast<const PosVelAtt>(obs))));
840 }
841 else if (NAV::NodeRegistry::NodeDataTypeAnyIsChildOf(outputPins.front().dataIdentifier, { PosVel::type() }))
842 {
843 invokeCallbacks(OUTPUT_PORT_INDEX_FLOW, receivePosVel(std::make_shared<PosVel>(*std::static_pointer_cast<const PosVel>(obs))));
844 }
845 else if (NAV::NodeRegistry::NodeDataTypeAnyIsChildOf(outputPins.front().dataIdentifier, { Pos::type() }))
846 {
847 invokeCallbacks(OUTPUT_PORT_INDEX_FLOW, receivePos(std::make_shared<Pos>(*std::static_pointer_cast<const Pos>(obs))));
848 }
849 else if (NAV::NodeRegistry::NodeDataTypeAnyIsChildOf(outputPins.front().dataIdentifier, { GnssObs::type() }))
850 {
851 invokeCallbacks(OUTPUT_PORT_INDEX_FLOW, receiveGnssObs(std::make_shared<GnssObs>(*std::static_pointer_cast<const GnssObs>(obs))));
852 }
853
854 _lastObservationTime = obs->insTime;
855}
856
857std::shared_ptr<NAV::ImuObs> NAV::ErrorModel::receiveImuObs(const std::shared_ptr<ImuObs>& imuObs,
858 const Eigen::Vector3d& accelerometerBias_p,
859 const Eigen::Vector3d& gyroscopeBias_p,
860 const Eigen::Vector3d& accelerometerNoiseStd,
861 const Eigen::Vector3d& gyroscopeNoiseStd)
862{
863 if (!_lastObservationTime.empty())
864 {
865 {
866 // Accelerometer RW standard deviation in platform frame coordinates [m/s^2/sqrt(s)]
867 Eigen::Vector3d accelerometerRWStd = convertUnit(_imuAccelerometerRW, _imuAccelerometerRWUnit);
868 LOG_DATA("{}: accelerometerRWStd = {} [m/s^2/sqrt(s)]", nameId(), accelerometerRWStd.transpose());
869
870 _randomWalkAccelerometer += Eigen::Vector3d{
871 _imuAccelerometerRWRng.getRand_normalDist(0.0, accelerometerRWStd(0)),
872 _imuAccelerometerRWRng.getRand_normalDist(0.0, accelerometerRWStd(1)),
873 _imuAccelerometerRWRng.getRand_normalDist(0.0, accelerometerRWStd(2))
874 } * sqrt(_dt);
875 }
876 {
877 // Accelerometer IRW standard deviation in platform frame coordinates [m/s^3/sqrt(s)]
878 Eigen::Vector3d accelerometerIRWStd = convertUnit(_imuAccelerometerIRW, _imuAccelerometerIRWUnit);
879 LOG_DATA("{}: accelerometerIRWStd = {} [m/s^3/sqrt(s)]", nameId(), accelerometerIRWStd.transpose());
880
881 // compute velocity RW first
883 _imuAccelerometerIRWRng.getRand_normalDist(0.0, accelerometerIRWStd(0)),
884 _imuAccelerometerIRWRng.getRand_normalDist(0.0, accelerometerIRWStd(1)),
885 _imuAccelerometerIRWRng.getRand_normalDist(0.0, accelerometerIRWStd(2))
886 } * sqrt(_dt);
887
888 // then compute IRW
890 }
891 {
892 // Gyro RW standard deviation in platform frame coordinates [rad/s/sqrt(s)]
893 Eigen::Vector3d gyroscopeRWStd = convertUnit(_imuGyroscopeRW, _imuGyroscopeRWUnit);
894 LOG_DATA("{}: gyroscopeRWStd = {} [rad/s/sqrt(s)]", nameId(), gyroscopeRWStd.transpose());
895
896 _randomWalkGyroscope += Eigen::Vector3d{
897 _imuGyroscopeRWRng.getRand_normalDist(0.0, gyroscopeRWStd(0)),
898 _imuGyroscopeRWRng.getRand_normalDist(0.0, gyroscopeRWStd(1)),
899 _imuGyroscopeRWRng.getRand_normalDist(0.0, gyroscopeRWStd(2))
900 } * sqrt(_dt);
901 }
902 {
903 // Gyro RW standard deviation in platform frame coordinates [rad/s^2/sqrt(s)]
904 Eigen::Vector3d gyroscopeIRWStd = convertUnit(_imuGyroscopeIRW, _imuGyroscopeIRWUnit);
905 LOG_DATA("{}: gyroscopeIRWStd = {} [rad/s^2/sqrt(s)]", nameId(), gyroscopeIRWStd.transpose());
906
907 // compute velocity RW first
909 _imuGyroscopeIRWRng.getRand_normalDist(0.0, gyroscopeIRWStd(0)),
910 _imuGyroscopeIRWRng.getRand_normalDist(0.0, gyroscopeIRWStd(1)),
911 _imuGyroscopeIRWRng.getRand_normalDist(0.0, gyroscopeIRWStd(2))
912 } * sqrt(_dt);
913
914 // then compute IRW
916 }
917 }
918
919 // #########################################################################################################################################
920
921 imuObs->p_acceleration += accelerometerBias_p
924
925 imuObs->p_angularRate += gyroscopeBias_p
928 if (_dt > 1e-9)
929 {
930 imuObs->p_acceleration += Eigen::Vector3d{ _imuAccelerometerRng.getRand_normalDist(0.0, accelerometerNoiseStd(0)),
931 _imuAccelerometerRng.getRand_normalDist(0.0, accelerometerNoiseStd(1)),
932 _imuAccelerometerRng.getRand_normalDist(0.0, accelerometerNoiseStd(2)) }
933 / std::sqrt(_dt); // Scale with input frequency
934 imuObs->p_angularRate += Eigen::Vector3d{ _imuGyroscopeRng.getRand_normalDist(0.0, gyroscopeNoiseStd(0)),
935 _imuGyroscopeRng.getRand_normalDist(0.0, gyroscopeNoiseStd(1)),
936 _imuGyroscopeRng.getRand_normalDist(0.0, gyroscopeNoiseStd(2)) }
937 / std::sqrt(_dt); // Scale with input frequency
938 }
939
940 return imuObs;
941}
942
943std::shared_ptr<NAV::ImuObsWDelta> NAV::ErrorModel::receiveImuObsWDelta(const std::shared_ptr<ImuObsWDelta>& imuObsWDelta,
944 const Eigen::Vector3d& accelerometerBias_p,
945 const Eigen::Vector3d& gyroscopeBias_p,
946 const Eigen::Vector3d& accelerometerNoiseStd,
947 const Eigen::Vector3d& gyroscopeNoiseStd)
948{
949 receiveImuObs(imuObsWDelta, accelerometerBias_p, gyroscopeBias_p, accelerometerNoiseStd, gyroscopeNoiseStd);
950
951 double imuObsWDeltaAverageWindow = _dt != 0.0 ? _dt / imuObsWDelta->dtime : 1.0;
952
953 imuObsWDelta->dvel += imuObsWDelta->dtime * (accelerometerBias_p + _randomWalkAccelerometer + _integratedRandomWalkAccelerometer);
954 imuObsWDelta->dtheta += imuObsWDelta->dtime * (gyroscopeBias_p + _randomWalkGyroscope + _integratedRandomWalkGyroscope);
955 if (_dt > 1e-9)
956 {
957 imuObsWDelta->dvel += Eigen::Vector3d{ _imuAccelerometerRng.getRand_normalDist(0.0, accelerometerNoiseStd(0) / std::sqrt(imuObsWDeltaAverageWindow)),
958 _imuAccelerometerRng.getRand_normalDist(0.0, accelerometerNoiseStd(1) / std::sqrt(imuObsWDeltaAverageWindow)),
959 _imuAccelerometerRng.getRand_normalDist(0.0, accelerometerNoiseStd(2) / std::sqrt(imuObsWDeltaAverageWindow)) }
960 * imuObsWDelta->dtime / std::sqrt(_dt); // Scale with input frequency
961 imuObsWDelta->dtheta += Eigen::Vector3d{ _imuGyroscopeRng.getRand_normalDist(0.0, gyroscopeNoiseStd(0) / std::sqrt(imuObsWDeltaAverageWindow)),
962 _imuGyroscopeRng.getRand_normalDist(0.0, gyroscopeNoiseStd(1) / std::sqrt(imuObsWDeltaAverageWindow)),
963 _imuGyroscopeRng.getRand_normalDist(0.0, gyroscopeNoiseStd(2) / std::sqrt(imuObsWDeltaAverageWindow)) }
964 * imuObsWDelta->dtime / std::sqrt(_dt); // Scale with input frequency
965 }
966
967 return imuObsWDelta;
968}
969
970std::shared_ptr<NAV::Pos> NAV::ErrorModel::receivePos(const std::shared_ptr<Pos>& pos)
971{
972 // Position Bias in latLonAlt in [rad, rad, m]
973 Eigen::Vector3d lla_positionBias = Eigen::Vector3d::Zero();
974 switch (_positionBiasUnit)
975 {
977 {
978 Eigen::Vector3d e_positionBias = trafo::e_Quat_n(pos->latitude(), pos->longitude()) * _positionBias;
979 if (!e_positionBias.isZero())
980 {
981 lla_positionBias = trafo::ecef2lla_WGS84(pos->e_position() + e_positionBias) - pos->lla_position();
982 }
983 break;
984 }
985 }
986 LOG_DATA("{}: lla_positionBias = {} [rad, rad, m]", nameId(), lla_positionBias.transpose());
987
988 // Velocity bias in local-navigation coordinates in [m/s]
989 Eigen::Vector3d n_velocityBias = Eigen::Vector3d::Zero();
990 switch (_velocityBiasUnit)
991 {
993 n_velocityBias = _velocityBias;
994 break;
995 }
996 LOG_DATA("{}: n_velocityBias = {} [m/s]", nameId(), n_velocityBias.transpose());
997
998 // Roll, pitch, yaw bias in [rad]
999 Eigen::Vector3d attitudeBias = Eigen::Vector3d::Zero();
1000 switch (_attitudeBiasUnit)
1001 {
1003 attitudeBias = _attitudeBias;
1004 break;
1006 attitudeBias = deg2rad(_attitudeBias);
1007 break;
1008 }
1009 LOG_DATA("{}: attitudeBias = {} [rad]", nameId(), attitudeBias.transpose());
1010
1011 // #########################################################################################################################################
1012
1013 // Position Noise standard deviation in latitude, longitude and altitude [rad, rad, m]
1014 Eigen::Vector3d lla_positionNoiseStd = Eigen::Vector3d::Zero();
1015 Eigen::Vector3d NED_pos_variance = Eigen::Vector3d::Zero();
1016 switch (_positionNoiseUnit)
1017 {
1019 {
1020 Eigen::Vector3d e_positionNoiseStd = trafo::e_Quat_n(pos->latitude(), pos->longitude()) * _positionNoise;
1021 if (!e_positionNoiseStd.isZero())
1022 {
1023 lla_positionNoiseStd = trafo::ecef2lla_WGS84(pos->e_position() + e_positionNoiseStd) - pos->lla_position();
1024 }
1025 NED_pos_variance = _positionNoise.cwiseAbs2();
1026 break;
1027 }
1029 {
1030 Eigen::Vector3d e_positionNoiseStd = trafo::e_Quat_n(pos->latitude(), pos->longitude()) * _positionNoise.cwiseSqrt();
1031 if (!e_positionNoiseStd.isZero())
1032 {
1033 lla_positionNoiseStd = trafo::ecef2lla_WGS84(pos->e_position() + e_positionNoiseStd) - pos->lla_position();
1034 }
1035 NED_pos_variance = _positionNoise;
1036 break;
1037 }
1038 }
1039 LOG_DATA("{}: lla_positionNoiseStd = {} [rad, rad, m]", nameId(), lla_positionNoiseStd.transpose());
1040
1041 // #########################################################################################################################################
1042
1043 pos->setPositionAndCov_n(pos->lla_position()
1044 + lla_positionBias
1045 + Eigen::Vector3d{ _positionRng.getRand_normalDist(0.0, lla_positionNoiseStd(0)),
1046 _positionRng.getRand_normalDist(0.0, lla_positionNoiseStd(1)),
1047 _positionRng.getRand_normalDist(0.0, lla_positionNoiseStd(2)) },
1048 NED_pos_variance.asDiagonal().toDenseMatrix());
1049
1050 return pos;
1051}
1052
1053std::shared_ptr<NAV::PosVel> NAV::ErrorModel::receivePosVel(const std::shared_ptr<PosVel>& posVel)
1054{
1055 // Position Bias in latLonAlt in [rad, rad, m]
1056 Eigen::Vector3d lla_positionBias = Eigen::Vector3d::Zero();
1057 switch (_positionBiasUnit)
1058 {
1060 {
1061 Eigen::Vector3d e_positionBias = trafo::e_Quat_n(posVel->latitude(), posVel->longitude()) * _positionBias;
1062 if (!e_positionBias.isZero())
1063 {
1064 lla_positionBias = trafo::ecef2lla_WGS84(posVel->e_position() + e_positionBias) - posVel->lla_position();
1065 }
1066 break;
1067 }
1068 }
1069 LOG_DATA("{}: lla_positionBias = {} [rad, rad, m]", nameId(), lla_positionBias.transpose());
1070
1071 // Velocity bias in local-navigation coordinates in [m/s]
1072 Eigen::Vector3d n_velocityBias = Eigen::Vector3d::Zero();
1073 switch (_velocityBiasUnit)
1074 {
1076 n_velocityBias = _velocityBias;
1077 break;
1078 }
1079 LOG_DATA("{}: n_velocityBias = {} [m/s]", nameId(), n_velocityBias.transpose());
1080
1081 // #########################################################################################################################################
1082
1083 // Position Noise standard deviation in latitude, longitude and altitude [rad, rad, m]
1084 Eigen::Vector3d lla_positionNoiseStd = Eigen::Vector3d::Zero();
1085 Eigen::Vector3d NED_pos_variance = Eigen::Vector3d::Zero();
1086 Eigen::Vector3d NED_velocity_variance = Eigen::Vector3d::Zero();
1087 switch (_positionNoiseUnit)
1088 {
1090 {
1091 Eigen::Vector3d e_positionNoiseStd = trafo::e_Quat_n(posVel->latitude(), posVel->longitude()) * _positionNoise;
1092 if (!e_positionNoiseStd.isZero())
1093 {
1094 lla_positionNoiseStd = trafo::ecef2lla_WGS84(posVel->e_position() + e_positionNoiseStd) - posVel->lla_position();
1095 }
1096 NED_pos_variance = _positionNoise.cwiseAbs2();
1097 break;
1098 }
1100 {
1101 Eigen::Vector3d e_positionNoiseStd = trafo::e_Quat_n(posVel->latitude(), posVel->longitude()) * _positionNoise.cwiseSqrt();
1102 if (!e_positionNoiseStd.isZero())
1103 {
1104 lla_positionNoiseStd = trafo::ecef2lla_WGS84(posVel->e_position() + e_positionNoiseStd) - posVel->lla_position();
1105 }
1106 NED_pos_variance = _positionNoise;
1107 break;
1108 }
1109 }
1110 LOG_DATA("{}: lla_positionNoiseStd = {} [rad, rad, m]", nameId(), lla_positionNoiseStd.transpose());
1111
1112 // Velocity Noise standard deviation in local-navigation coordinates in [m/s]
1113 Eigen::Vector3d n_velocityNoiseStd = Eigen::Vector3d::Zero();
1114 switch (_velocityNoiseUnit)
1115 {
1117 n_velocityNoiseStd = _velocityNoise;
1118 NED_velocity_variance = _velocityNoise.cwiseAbs2();
1119 break;
1121 n_velocityNoiseStd = _velocityNoise.cwiseSqrt();
1122 NED_velocity_variance = _velocityNoise;
1123 break;
1124 }
1125 LOG_DATA("{}: n_velocityNoiseStd = {} [m/s]", nameId(), n_velocityNoiseStd.transpose());
1126
1127 // #########################################################################################################################################
1128
1129 Eigen::Matrix<double, 6, 6> cov_n = Eigen::Matrix<double, 6, 6>::Zero();
1130 cov_n.block<3, 3>(0, 0).diagonal() = NED_pos_variance;
1131 cov_n.block<3, 3>(3, 3).diagonal() = NED_velocity_variance;
1132
1133 posVel->setPosVelAndCov_n(posVel->lla_position()
1134 + lla_positionBias
1135 + Eigen::Vector3d{ _positionRng.getRand_normalDist(0.0, lla_positionNoiseStd(0)),
1136 _positionRng.getRand_normalDist(0.0, lla_positionNoiseStd(1)),
1137 _positionRng.getRand_normalDist(0.0, lla_positionNoiseStd(2)) },
1138 posVel->n_velocity()
1139 + n_velocityBias
1140 + Eigen::Vector3d{ _velocityRng.getRand_normalDist(0.0, n_velocityNoiseStd(0)),
1141 _velocityRng.getRand_normalDist(0.0, n_velocityNoiseStd(1)),
1142 _velocityRng.getRand_normalDist(0.0, n_velocityNoiseStd(2)) },
1143 cov_n);
1144
1145 return posVel;
1146}
1147
1148std::shared_ptr<NAV::PosVelAtt> NAV::ErrorModel::receivePosVelAtt(const std::shared_ptr<PosVelAtt>& posVelAtt)
1149{
1150 // Position Bias in latLonAlt in [rad, rad, m]
1151 Eigen::Vector3d lla_positionBias = Eigen::Vector3d::Zero();
1152 switch (_positionBiasUnit)
1153 {
1155 {
1156 Eigen::Vector3d e_positionBias = trafo::e_Quat_n(posVelAtt->latitude(), posVelAtt->longitude()) * _positionBias;
1157 if (!e_positionBias.isZero())
1158 {
1159 lla_positionBias = trafo::ecef2lla_WGS84(posVelAtt->e_position() + e_positionBias) - posVelAtt->lla_position();
1160 }
1161 break;
1162 }
1163 }
1164 LOG_DATA("{}: lla_positionBias = {} [rad, rad, m]", nameId(), lla_positionBias.transpose());
1165
1166 // Velocity bias in local-navigation coordinates in [m/s]
1167 Eigen::Vector3d n_velocityBias = Eigen::Vector3d::Zero();
1168 switch (_velocityBiasUnit)
1169 {
1171 n_velocityBias = _velocityBias;
1172 break;
1173 }
1174 LOG_DATA("{}: n_velocityBias = {} [m/s]", nameId(), n_velocityBias.transpose());
1175
1176 // Roll, pitch, yaw bias in [rad]
1177 Eigen::Vector3d attitudeBias = Eigen::Vector3d::Zero();
1178 switch (_attitudeBiasUnit)
1179 {
1181 attitudeBias = _attitudeBias;
1182 break;
1184 attitudeBias = deg2rad(_attitudeBias);
1185 break;
1186 }
1187 LOG_DATA("{}: attitudeBias = {} [rad]", nameId(), attitudeBias.transpose());
1188
1189 // #########################################################################################################################################
1190
1191 // Position Noise standard deviation in latitude, longitude and altitude [rad, rad, m]
1192 Eigen::Vector3d lla_positionNoiseStd = Eigen::Vector3d::Zero();
1193 Eigen::Vector3d NED_pos_variance = Eigen::Vector3d::Zero();
1194 Eigen::Vector3d NED_velocity_variance = Eigen::Vector3d::Zero();
1195 Eigen::Vector3d RPY_attitude_variance = Eigen::Vector3d::Zero();
1196 switch (_positionNoiseUnit)
1197 {
1199 {
1200 Eigen::Vector3d e_positionNoiseStd = trafo::e_Quat_n(posVelAtt->latitude(), posVelAtt->longitude()) * _positionNoise;
1201 if (!e_positionNoiseStd.isZero())
1202 {
1203 lla_positionNoiseStd = trafo::ecef2lla_WGS84(posVelAtt->e_position() + e_positionNoiseStd) - posVelAtt->lla_position();
1204 }
1205 NED_pos_variance = _positionNoise.cwiseAbs2();
1206 break;
1207 }
1209 {
1210 Eigen::Vector3d e_positionNoiseStd = trafo::e_Quat_n(posVelAtt->latitude(), posVelAtt->longitude()) * _positionNoise.cwiseSqrt();
1211 if (!e_positionNoiseStd.isZero())
1212 {
1213 lla_positionNoiseStd = trafo::ecef2lla_WGS84(posVelAtt->e_position() + e_positionNoiseStd) - posVelAtt->lla_position();
1214 }
1215 NED_pos_variance = _positionNoise;
1216 break;
1217 }
1218 }
1219 LOG_DATA("{}: lla_positionNoiseStd = {} [rad, rad, m]", nameId(), lla_positionNoiseStd.transpose());
1220
1221 // Velocity Noise standard deviation in local-navigation coordinates in [m/s]
1222 Eigen::Vector3d n_velocityNoiseStd = Eigen::Vector3d::Zero();
1223 switch (_velocityNoiseUnit)
1224 {
1226 n_velocityNoiseStd = _velocityNoise;
1227 NED_velocity_variance = _velocityNoise.cwiseAbs2();
1228 break;
1230 n_velocityNoiseStd = _velocityNoise.cwiseSqrt();
1231 NED_velocity_variance = _velocityNoise;
1232 break;
1233 }
1234 LOG_DATA("{}: n_velocityNoiseStd = {} [m/s]", nameId(), n_velocityNoiseStd.transpose());
1235
1236 // Attitude Noise standard deviation in [rad]
1237 Eigen::Vector3d attitudeNoiseStd = Eigen::Vector3d::Zero();
1238 switch (_attitudeNoiseUnit)
1239 {
1241 attitudeNoiseStd = _attitudeNoise;
1242 RPY_attitude_variance = _attitudeNoise.cwiseAbs2();
1243 break;
1245 attitudeNoiseStd = deg2rad(_attitudeNoise);
1246 RPY_attitude_variance = deg2rad(_attitudeNoise).cwiseAbs2();
1247 break;
1249 attitudeNoiseStd = _attitudeNoise.cwiseSqrt();
1250 RPY_attitude_variance = _attitudeNoise;
1251 break;
1253 attitudeNoiseStd = deg2rad(_attitudeNoise.cwiseSqrt());
1254 RPY_attitude_variance = deg2rad(deg2rad(_attitudeNoise));
1255 break;
1256 }
1257 LOG_DATA("{}: attitudeNoiseStd = {} [rad]", nameId(), attitudeNoiseStd.transpose());
1258
1259 // #########################################################################################################################################
1260
1261 Eigen::Matrix<double, 9, 9> cov_n = Eigen::Matrix<double, 9, 9>::Zero();
1262 cov_n.block<3, 3>(0, 0).diagonal() = NED_pos_variance;
1263 cov_n.block<3, 3>(3, 3).diagonal() = NED_velocity_variance;
1264 cov_n.block<3, 3>(6, 6).diagonal() = RPY_attitude_variance;
1265
1266 Eigen::Matrix<double, 10, 9> J = Eigen::Matrix<double, 10, 9>::Zero();
1267 J.topLeftCorner<6, 6>().setIdentity();
1268 J.bottomRightCorner<4, 3>() = trafo::covRPY2quatJacobian(posVelAtt->n_Quat_b());
1269
1270 posVelAtt->setPosVelAttAndCov_n(posVelAtt->lla_position()
1271 + lla_positionBias
1272 + Eigen::Vector3d{ _positionRng.getRand_normalDist(0.0, lla_positionNoiseStd(0)),
1273 _positionRng.getRand_normalDist(0.0, lla_positionNoiseStd(1)),
1274 _positionRng.getRand_normalDist(0.0, lla_positionNoiseStd(2)) },
1275 posVelAtt->n_velocity()
1276 + n_velocityBias
1277 + Eigen::Vector3d{ _velocityRng.getRand_normalDist(0.0, n_velocityNoiseStd(0)),
1278 _velocityRng.getRand_normalDist(0.0, n_velocityNoiseStd(1)),
1279 _velocityRng.getRand_normalDist(0.0, n_velocityNoiseStd(2)) },
1280 trafo::n_Quat_b(posVelAtt->rollPitchYaw()
1281 + attitudeBias
1282 + Eigen::Vector3d{ _attitudeRng.getRand_normalDist(0.0, attitudeNoiseStd(0)),
1283 _attitudeRng.getRand_normalDist(0.0, attitudeNoiseStd(1)),
1284 _attitudeRng.getRand_normalDist(0.0, attitudeNoiseStd(2)) }),
1285 J * cov_n * J.transpose());
1286
1287 return posVelAtt;
1288}
1289
1290std::shared_ptr<NAV::GnssObs> NAV::ErrorModel::receiveGnssObs(const std::shared_ptr<GnssObs>& gnssObs)
1291{
1292 LOG_DATA("{}: [{}] Simulating error on GnssObs", nameId(), gnssObs->insTime.toYMDHMS(GPST));
1293 double pseudorangeNoise{}; // [m]
1295 {
1297 pseudorangeNoise = _gui_pseudorangeNoise;
1298 break;
1299 }
1300 double carrierPhaseNoise{}; // [m]
1302 {
1304 carrierPhaseNoise = _gui_carrierPhaseNoise;
1305 break;
1306 }
1307 double dopplerNoise{}; // [m/s]
1308 switch (_gui_dopplerNoiseUnit)
1309 {
1311 dopplerNoise = _gui_dopplerNoise;
1312 break;
1313 }
1314
1315 double dtCycleSlipSeconds{}; // [s]
1317 {
1320 break;
1323 break;
1326 break;
1327 }
1328 auto dtCycleSlip = std::chrono::nanoseconds(static_cast<int64_t>(dtCycleSlipSeconds * 1e9));
1329
1330 double cycleSlipDetectionProbability{}; // [0, 1]
1332 {
1334 cycleSlipDetectionProbability = _gui_cycleSlipDetectionProbability / 100.0;
1335 break;
1336 }
1337
1338 if (_cycleSlipWindowStartTime.empty()
1339 || gnssObs->insTime >= _cycleSlipWindowStartTime + dtCycleSlip)
1340 {
1341 _cycleSlipWindowStartTime = gnssObs->insTime;
1342 LOG_DATA("{}: [{}] Starting new cycle-slip window", nameId(), _cycleSlipWindowStartTime.toYMDHMS(GPST));
1343 }
1344
1345 size_t nObs = 0;
1346 for (auto& obs : gnssObs->data)
1347 {
1348 if (obs.satSigId.freq() & _filterFreq
1349 && obs.satSigId.code & _filterCode)
1350 {
1351 nObs++;
1352 }
1353 }
1354
1355 for (auto& obs : gnssObs->data)
1356 {
1357 if (obs.pseudorange) { obs.pseudorange.value().value += _pseudorangeRng.getRand_normalDist(0.0, pseudorangeNoise); }
1358 if (obs.doppler) { obs.doppler.value() += rangeRate2doppler(_dopplerRng.getRand_normalDist(0.0, dopplerNoise), obs.satSigId.freq(), 0); } // TODO: Add frequency number here for GLONASS
1359
1360 if (obs.carrierPhase)
1361 {
1362 // ------------------------------------------- Noise ---------------------------------------------
1363 auto lambda = InsConst::C / obs.satSigId.freq().getFrequency(0); // wave-length [m] // TODO: Add frequency number here for GLONASS
1364 obs.carrierPhase.value().value += _carrierPhaseRng.getRand_normalDist(0.0, carrierPhaseNoise) / lambda;
1365
1366 // ---------------------------------------- Cycle-slip -------------------------------------------
1367
1368 bool manualCycleSlip = false;
1369 for (const auto& cycleSlip : _manualCycleSlips)
1370 {
1371 const auto& [insTime, satSigId] = cycleSlip.first;
1372 if (std::abs((insTime - gnssObs->insTime).count()) < 1e-4 && obs.satSigId == satSigId)
1373 {
1374 manualCycleSlip = true;
1375
1376 const auto& [ambValue, setLLI] = cycleSlip.second;
1377 _ambiguities[obs.satSigId].emplace_back(gnssObs->insTime, ambValue);
1378 obs.carrierPhase.value().LLI = static_cast<uint8_t>(setLLI);
1379 _cycleSlips.push_back(CycleSlipInfo{ .time = gnssObs->insTime, .satSigId = obs.satSigId, .LLI = obs.carrierPhase.value().LLI != 0 });
1380
1381 LOG_DEBUG("{}: [{}] Applied manual cycle-slip for satellite [{}] with LLI {}", nameId(), gnssObs->insTime.toYMDHMS(GPST),
1382 obs.satSigId, obs.carrierPhase.value().LLI);
1383 }
1384 }
1385
1386 if (!manualCycleSlip // If already manual cycle-slip applied do not do another
1387 && obs.satSigId.freq() & _filterFreq // GUI selected frequencies
1388 && obs.satSigId.code & _filterCode // GUI selected codes
1389 && _gui_cycleSlipFrequency != 0.0 // 0 Frequency means disabled
1390 && !_lastObservationTime.empty() // Do not apply a cycle slip on the first message
1391 && (_cycleSlips.empty() || _cycleSlips.back().time < _cycleSlipWindowStartTime)) // In the current window, there was no cycle-slip yet
1392 {
1393 double probabilityCycleSlip = _dt / (dtCycleSlipSeconds * static_cast<double>(nObs)) * 2.0; // Double chance, because often does not happen otherwise
1394 if (_cycleSlipRng.getRand_uniformRealDist(0.0, 1.0) <= probabilityCycleSlip
1395 || (gnssObs->insTime >= _cycleSlipWindowStartTime + dtCycleSlip - std::chrono::nanoseconds(static_cast<int64_t>((_dt + 0.001) * 1e9)))) // Last message this window
1396 {
1397 int newAmbiguity = 0;
1398 int oldAmbiguity = !_ambiguities[obs.satSigId].empty()
1399 ? _ambiguities[obs.satSigId].back().second
1400 : static_cast<int>(_ambiguityRng.getRand_uniformIntDist(_gui_ambiguityLimits[0], _gui_ambiguityLimits[1]));
1401 auto deltaAmbiguity = static_cast<int>(_cycleSlipRng.getRand_uniformIntDist(1, _gui_cycleSlipRange));
1402 auto signAmbiguity = _cycleSlipRng.getRand_uniformIntDist(0, 1) == 0.0 ? 1 : -1;
1403
1404 if (_gui_ambiguityLimits[0] == _gui_ambiguityLimits[1]) // Ambiguities disabled
1405 {
1406 newAmbiguity = oldAmbiguity + signAmbiguity * deltaAmbiguity;
1407 }
1408 else if (oldAmbiguity == _gui_ambiguityLimits[0])
1409 {
1410 newAmbiguity = std::min(oldAmbiguity + deltaAmbiguity, _gui_ambiguityLimits[1]);
1411 }
1412 else if (oldAmbiguity == _gui_ambiguityLimits[1])
1413 {
1414 newAmbiguity = std::max(oldAmbiguity - deltaAmbiguity, _gui_ambiguityLimits[0]);
1415 }
1416 else
1417 {
1418 newAmbiguity = std::clamp(oldAmbiguity + signAmbiguity * deltaAmbiguity, _gui_ambiguityLimits[0], _gui_ambiguityLimits[1]);
1419 }
1420
1421 _ambiguities[obs.satSigId].emplace_back(gnssObs->insTime, newAmbiguity);
1422
1423 if (_cycleSlipRng.getRand_uniformRealDist(0.0, 1.0) <= cycleSlipDetectionProbability)
1424 {
1425 obs.carrierPhase.value().LLI = 1;
1426 }
1427 _cycleSlips.push_back(CycleSlipInfo{ .time = gnssObs->insTime, .satSigId = obs.satSigId, .LLI = obs.carrierPhase.value().LLI != 0 });
1428 LOG_DEBUG("{}: [{}] Simulating cycle-slip for satellite [{}] with LLI {}", nameId(), gnssObs->insTime.toYMDHMS(GPST),
1429 obs.satSigId, obs.carrierPhase.value().LLI);
1430 }
1431 }
1432
1433 // ----------------------------------------- Ambiguity -------------------------------------------
1434 if (!_ambiguities.contains(obs.satSigId) && _gui_ambiguityLimits[0] != _gui_ambiguityLimits[1])
1435 {
1436 _ambiguities[obs.satSigId].emplace_back(gnssObs->insTime, _ambiguityRng.getRand_uniformIntDist(_gui_ambiguityLimits[0], _gui_ambiguityLimits[1]));
1437 }
1438 if (_ambiguities.contains(obs.satSigId))
1439 {
1440 obs.carrierPhase.value().value += _ambiguities.at(obs.satSigId).back().second;
1441 }
1442 }
1443 }
1444
1445 return gnssObs;
1446}
Transformation collection.
Vector space operations.
Adds errors (biases and noise) to measurements.
Save/Load the Nodes.
nlohmann::json json
json namespace
GNSS helper functions.
Text Help Marker (?) with Tooltip.
Units used by INS.
Data storage class for simulated IMU observations.
Data storage class for one VectorNavImu observation.
Defines Widgets which allow the input of values and the selection of the unit.
#define LOG_DEBUG
Debug information. Should not be called on functions which receive observations (spamming)
Definition Logger.hpp:67
#define LOG_DATA
All output which occurs repeatedly every time observations are received.
Definition Logger.hpp:29
#define LOG_TRACE
Detailled info to trace the execution of the program. Should not be called on functions which receive...
Definition Logger.hpp:65
Utility class which specifies available nodes.
Structs identifying a unique satellite.
Utility functions for working with std::strings.
Frequency _filterFreq
Frequencies used for calculation (GUI filter)
std::shared_ptr< PosVel > receivePosVel(const std::shared_ptr< PosVel > &posVel)
Callback when receiving an PosVelObs.
RandomNumberGenerator _imuAccelerometerRng
Random number generator for the accelerometer noise.
SatSigId _manualCycleSlipSignal
GUI input for new manual cycle-slips.
int _gui_cycleSlipRange
Ambiguity limits cycle-slip.
RandomNumberGenerator _carrierPhaseRng
Random number generator for the carrier-phase noise.
Units::ImuAccelerometerUnits _imuAccelerometerBiasUnit
Selected unit for the accelerometer bias in the GUI.
std::shared_ptr< ImuObs > receiveImuObs(const std::shared_ptr< ImuObs > &imuObs, const Eigen::Vector3d &accelerometerBias_p, const Eigen::Vector3d &gyroscopeBias_p, const Eigen::Vector3d &accelerometerNoiseStd, const Eigen::Vector3d &gyroscopeNoiseStd)
Callback when receiving an ImuObs.
VelocityBiasUnits _velocityBiasUnit
Selected unit for the velocity bias in the GUI.
bool _manualCycleSlipSetLLI
GUI input for new manual cycle-slips.
std::array< int, 2 > _gui_ambiguityLimits
Ambiguity limits.
CarrierPhaseNoiseUnits _gui_carrierPhaseNoiseUnit
Selected unit for the carrier-phase noise in the GUI.
RandomNumberGenerator _imuGyroscopeRng
Random number generator for the gyroscope noise.
RandomNumberGenerator _imuAccelerometerIRWRng
Random number generator for the accelerometer IRW noise.
Units::ImuGyroscopeIRWUnits _imuGyroscopeIRWUnit
Selected unit for the accelerometer IRW noise in the GUI.
RandomNumberGenerator _imuGyroscopeRWRng
Random number generator for the accelerometer RW noise.
RandomNumberGenerator _imuAccelerometerRWRng
Random number generator for the accelerometer RW noise.
~ErrorModel() override
Destructor.
PositionNoiseUnits _positionNoiseUnit
Selected unit for the position noise in the GUI.
Eigen::Vector3d _positionNoise
Noise of the position (Unit as selected)
Units::ImuGyroscopeUnits _imuGyroscopeBiasUnit
Selected unit for the gyroscope bias in the GUI.
static std::string typeStatic()
String representation of the Class Type.
Eigen::Vector3d _imuAccelerometerNoise
Noise of the accelerometer (Unit as selected)
static constexpr size_t OUTPUT_PORT_INDEX_FLOW
Flow.
VelocityNoiseUnits _velocityNoiseUnit
Selected unit for the velocity noise in the GUI.
void restore(const json &j) override
Restores the node from a json object.
void afterDeleteLink(OutputPin &startPin, InputPin &endPin) override
Called when a link was deleted.
Eigen::Vector3d _integratedRandomWalkGyroscope
3D array which allow to accumulate IRW for gyro
Units::ImuAccelerometerNoiseUnits _imuAccelerometerNoiseUnit
Selected unit for the accelerometer noise in the GUI.
std::shared_ptr< GnssObs > receiveGnssObs(const std::shared_ptr< GnssObs > &gnssObs)
Callback when receiving an GnssObs.
RandomNumberGenerator _attitudeRng
Random number generator for the attitude noise.
AttitudeBiasUnits _attitudeBiasUnit
Selected unit for the attitude bias in the GUI.
@ rad
[rad] (Standard deviation)
@ deg
[deg] (Standard deviation)
json save() const override
Saves the node into a json object.
std::string type() const override
String representation of the Class Type.
RandomNumberGenerator _imuGyroscopeIRWRng
Random number generator for the accelerometer RW noise.
Eigen::Vector3d _velocityBias
Bias of the velocity (Unit as selected)
InsTime _lastObservationTime
Last observation time.
RandomNumberGenerator _cycleSlipRng
Random number generator for the cycle-slip.
double _gui_cycleSlipDetectionProbability
The chance to detect a cycle slip and set the Loss-of-Lock indicator.
Eigen::Vector3d _integratedRandomWalkGyroscope_velocity
3D array which allow to accumulate IRW veloctiy noise for gyro
CycleSlipDetectionProbabilityUnits _gui_cycleSlipDetectionProbabilityUnit
Selected unit for the cycle-slip detection probability in the GUI.
Eigen::Vector3d _velocityNoise
Noise of the velocity (Unit as selected)
InsTime _manualCycleSlipTime
GUI input for new manual cycle-slips.
std::shared_ptr< Pos > receivePos(const std::shared_ptr< Pos > &pos)
Callback when receiving an PosObs.
CycleSlipFrequencyUnits _gui_cycleSlipFrequencyUnit
Selected unit for the cycle-slip frequency in the GUI.
Eigen::Vector3d _imuAccelerometerRW
RW noise of the accelerometer (Unit as selected)
double _gui_pseudorangeNoise
Noise of the pseudorange (Unit as selected)
double _gui_carrierPhaseNoise
Noise of the carrier-phase (Unit as selected)
double _dt
Time interval of the messages [s].
RandomNumberGenerator _pseudorangeRng
Random number generator for the pseudorange noise.
Eigen::Vector3d _integratedRandomWalkAccelerometer
3D array which allow to accumulate IRW for accelerometer
std::shared_ptr< PosVelAtt > receivePosVelAtt(const std::shared_ptr< PosVelAtt > &posVelAtt)
Callback when receiving an PosVelAttObs.
Units::ImuGyroscopeNoiseUnits _imuGyroscopeRWUnit
Selected unit for the accelerometer RW noise in the GUI.
@ meter2
NED [m^2 m^2 m^2] (Variance)
@ meter
NED [m m m] (Standard deviation)
DopplerNoiseUnits _gui_dopplerNoiseUnit
Selected unit for the range-rate noise in the GUI.
RandomNumberGenerator _ambiguityRng
Random number generator for the ambiguity.
@ m_s
[m/s] (Standard deviation)
Eigen::Vector3d _imuAccelerometerBias_p
Bias of the accelerometer in platform coordinates (Unit as selected)
std::map< std::pair< InsTime, SatSigId >, std::pair< int, bool > > _manualCycleSlips
Cycle-slips set by the user.
RandomNumberGenerator _positionRng
Random number generator for the position noise.
Eigen::Vector3d _imuGyroscopeBias_p
Bias of the gyroscope in platform coordinates (Unit as selected)
Code _filterCode
Codes used for calculation (GUI filter)
double _gui_dopplerNoise
Noise of the range-rate (Unit as selected)
void receiveObs(InputPin::NodeDataQueue &queue, size_t pinIdx)
Callback when receiving data on a port.
Units::ImuAccelerometerNoiseUnits _imuAccelerometerRWUnit
Selected unit for the accelerometer RW noise in the GUI.
Eigen::Vector3d _imuGyroscopeNoise
Noise of the gyroscope (Unit as selected)
static std::string category()
String representation of the Class Category.
Eigen::Vector3d _positionBias
Bias of the position (Unit as selected)
void afterCreateLink(OutputPin &startPin, InputPin &endPin) override
Called when a new link was established.
gui::widgets::TimeEditFormat _manualCycleSlipTimeEditFormat
GUI input for new manual cycle-slips.
@ meter
[m] (Standard deviation)
Eigen::Vector3d _imuGyroscopeIRW
RW noise of the accelerometer (Unit as selected)
Units::ImuAccelerometerIRWUnits _imuAccelerometerIRWUnit
Selected unit for the accelerometer IRW noise in the GUI.
Eigen::Vector3d _randomWalkGyroscope
3D array which allow to accumulate RW noise for gyro
Eigen::Vector3d _attitudeBias
Bias of the attitude (Unit as selected)
void guiConfig() override
ImGui config window which is shown on double click.
Eigen::Vector3d _imuAccelerometerIRW
IRW noise of the accelerometer (Unit as selected)
PositionBiasUnits _positionBiasUnit
Selected unit for the position bias in the GUI.
Eigen::Vector3d _imuGyroscopeRW
RW noise of the accelerometer (Unit as selected)
AttitudeNoiseUnits _attitudeNoiseUnit
Selected unit for the attitude noise in the GUI.
ErrorModel()
Default constructor.
Eigen::Vector3d _randomWalkAccelerometer
3D array which allow to accumulate RW noise for accelerometer
@ m_s
[m/s] (Standard deviation)
Eigen::Vector3d _integratedRandomWalkAccelerometer_velocity
3D array which allow to accumulate IRW veloctiy noise for accelerometer
InsTime _cycleSlipWindowStartTime
The time frame which is considered for a cycle slip.
double _gui_cycleSlipFrequency
The cycle-slip frequency (Unit as selected)
std::shared_ptr< ImuObsWDelta > receiveImuObsWDelta(const std::shared_ptr< ImuObsWDelta > &imuObs, const Eigen::Vector3d &accelerometerBias_p, const Eigen::Vector3d &gyroscopeBias_p, const Eigen::Vector3d &accelerometerNoiseStd, const Eigen::Vector3d &gyroscopeNoiseStd)
Callback when receiving an ImuObsWDelta.
RandomNumberGenerator _velocityRng
Random number generator for the velocity noise.
std::vector< CycleSlipInfo > _cycleSlips
List of produced cycle-slips.
RandomNumberGenerator _dopplerRng
Random number generator for the range-rate noise.
Units::ImuGyroscopeNoiseUnits _imuGyroscopeNoiseUnit
Selected unit for the gyroscope noise in the GUI.
Eigen::Vector3d _attitudeNoise
Noise of the attitude (Unit as selected)
std::map< SatSigId, std::vector< std::pair< InsTime, int > > > _ambiguities
Ambiguity map.
int _manualCycleSlipAmbiguity
GUI input for new manual cycle-slips.
bool resetNode() override
Resets the node. It is guaranteed that the node is initialized when this is called.
PseudorangeNoiseUnits _gui_pseudorangeNoiseUnit
Selected unit for the pseudorange noise in the GUI.
static std::string type()
Returns the type of the data class.
Definition GnssObs.hpp:151
static std::string type()
Returns the type of the data class.
static std::string type()
Returns the type of the data class.
Definition ImuObs.hpp:33
Input pins of nodes.
Definition Pin.hpp:491
TsDeque< std::shared_ptr< const NAV::NodeData > > NodeDataQueue
Node data queue type.
Definition Pin.hpp:707
static constexpr double C
Speed of light [m/s].
Definition Constants.hpp:34
ImVec2 _guiConfigDefaultWindowSize
Definition Node.hpp:522
std::vector< OutputPin > outputPins
List of output pins.
Definition Node.hpp:511
Node(std::string name)
Constructor.
Definition Node.cpp:29
std::vector< InputPin > inputPins
List of input pins.
Definition Node.hpp:509
OutputPin * CreateOutputPin(const char *name, Pin::Type pinType, const std::vector< std::string > &dataIdentifier, OutputPin::PinData data=static_cast< void * >(nullptr), int idx=-1)
Create an Output Pin object.
Definition Node.cpp:278
std::string nameId() const
Node name and id.
Definition Node.cpp:323
std::string name
Name of the Node.
Definition Node.hpp:507
InputPin * CreateInputPin(const char *name, Pin::Type pinType, const std::vector< std::string > &dataIdentifier={}, InputPin::Callback callback=static_cast< InputPin::FlowFirableCallbackFunc >(nullptr), InputPin::FlowFirableCheckFunc firable=nullptr, int priority=0, int idx=-1)
Create an Input Pin object.
Definition Node.cpp:252
void invokeCallbacks(size_t portIndex, const std::shared_ptr< const NodeData > &data)
Calls all registered callbacks on the specified output port.
Definition Node.cpp:179
ax::NodeEditor::NodeId id
Unique Id of the Node.
Definition Node.hpp:503
bool _hasConfig
Flag if the config window should be shown.
Definition Node.hpp:525
Output pins of nodes.
Definition Pin.hpp:338
Node * parentNode
Reference to the parent node.
Definition Pin.hpp:307
ax::NodeEditor::PinId id
Unique Id of the Pin.
Definition Pin.hpp:297
std::vector< std::string > dataIdentifier
One or multiple Data Identifiers (Unique name which is used for data flows)
Definition Pin.hpp:305
static std::string type()
Returns the type of the data class.
Definition PosVelAtt.hpp:29
static std::string type()
Returns the type of the data class.
Definition PosVel.hpp:27
static std::string type()
Returns the type of the data class.
Definition Pos.hpp:36
Manages a thread which calls a specified function at a specified interval.
auto extract_front()
Returns a copy of the first element in the container and removes it from the container.
Definition TsDeque.hpp:494
static float windowFontRatio()
Ratio to multiply for GUI window elements.
static float monoFontRatio()
Ratio to multiply for log output GUI elements.
ImGui extensions.
bool InputIntL(const char *label, int *v, int v_min, int v_max, int step, int step_fast, ImGuiInputTextFlags flags)
Shows a value limited InputText GUI element for 'int'.
Definition imgui_ex.cpp:242
bool SliderULong(const char *label, uint64_t *v, uint64_t v_min, uint64_t v_max, const char *format, ImGuiSliderFlags flags)
Shows a Slider GUI element for 'uint64'.
Definition imgui_ex.cpp:129
constexpr int32_t SECONDS_PER_DAY
Seconds / Day.
Definition InsTime.hpp:60
constexpr int32_t SECONDS_PER_HOUR
Seconds / Hour.
Definition InsTime.hpp:59
constexpr int32_t SECONDS_PER_MINUTE
Seconds / Minute.
Definition InsTime.hpp:58
bool NodeDataTypeAnyIsChildOf(const std::vector< std::string > &childTypes, const std::vector< std::string > &parentTypes)
Checks if any of the provided child types is a child of any of the provided parent types.
void ApplyChanges()
Signals that there have been changes to the flow.
InputWithUnitChange InputDoubleWithUnit(const char *label, float itemWidth, float unitWidth, double *v, U &combo_current_item, const char *combo_items_separated_by_zeros, double step=0.0, double step_fast=0.0, const char *format="%.6f", ImGuiInputTextFlags flags=0, int combo_popup_max_height_in_items=-1)
Shows an InputText GUI element to modify the provided value and also set its unit.
InputWithUnitChange SliderDoubleWithUnit(const char *label, float itemWidth, float unitWidth, double *v, double v_min, double v_max, U &combo_current_item, const char *combo_items_separated_by_zeros, const char *format="%.6f", ImGuiSliderFlags flags=0, int combo_popup_max_height_in_items=-1)
Shows an Slider GUI element to modify the provided value and also set its unit.
bool TimeEdit(const char *str_id, InsTime &insTime, TimeEditFormat &timeEditFormat, float itemWidth=170.0F, int columns=1)
Inputs to edit an InsTime object.
Definition TimeEdit.cpp:52
@ InputWithUnitChange_Unit
The Unit changed.
@ InputWithUnitChange_Input
The Input changed.
InputWithUnitChange InputDouble3WithUnit(const char *label, float itemWidth, float unitWidth, double v[3], U &combo_current_item, const char *combo_items_separated_by_zeros, const char *format="%.6f", ImGuiInputTextFlags flags=0, int combo_popup_max_height_in_items=-1)
Shows an InputText GUI element to modify the provided value and also set its unit.
void HelpMarker(const char *desc, const char *symbol="(?)")
Text Help Marker, e.g. '(?)', with Tooltip.
static std::string replaceAll_copy(std::string str, const std::string &from, const std::string &to, CaseSensitivity cs)
Replaces all occurrence of a search pattern with another sequence.
Eigen::Vector3< typename Derived::Scalar > ecef2lla_WGS84(const Eigen::MatrixBase< Derived > &e_position)
Converts Earth-centered-Earth-fixed coordinates into latitude, longitude and altitude using WGS84.
Eigen::Quaternion< Scalar > e_Quat_n(const Scalar &latitude, const Scalar &longitude)
Quaternion for rotations from navigation to Earth-fixed frame.
Eigen::Matrix< typename Derived::Scalar, 4, 3 > covRPY2quatJacobian(const Eigen::QuaternionBase< Derived > &n_quat_b)
Calculates the Jacobian to convert an attitude represented in Euler angels (roll, pitch,...
Eigen::Quaternion< Scalar > n_Quat_b(Scalar roll, Scalar pitch, Scalar yaw)
Quaternion for rotations from body to navigation frame.
@ GPST
GPS Time.
constexpr Frequency_ Freq_All
All Frequencies.
Frequency_
Enumerate for GNSS frequencies.
Definition Frequency.hpp:26
bool ShowFrequencySelector(const char *label, Frequency &frequency, bool singleSelect)
Shows a ComboBox to select GNSS frequencies.
bool ShowCodeSelector(const char *label, Code &code, const Frequency &filterFreq, bool singleSelect)
Shows a ComboBox to select signal codes.
Definition Code.cpp:1310
double convertUnit(const double &value, Units::PositionUncertaintyUnits unit)
Converts the value depending on the unit provided.
Definition Units.cpp:135
constexpr auto deg2rad(const T &deg)
Convert Degree to Radians.
Definition Units.hpp:21
std::string MakeComboItems()
Units separated by '\0' and terminated by double '\0'.
const std::vector< std::string > supportedDataIdentifier
List of supported data identifiers.
Definition udpSend.cpp:30
bool ShowSatelliteSelector(const char *label, std::vector< SatId > &satellites, SatelliteSystem filterSys, bool displayOnlyNumber)
Shows a ComboBox to select satellites.
double rangeRate2doppler(double rangeRate, Frequency freq, int8_t num)
Transforms a range-rate into a doppler-shift.
Definition Functions.cpp:19
Cycle-slip information.
InsTime time
Time of the cycle-slip.
SatSigId satSigId
Satellite Signal identifier.
@ Flow
NodeData Trigger.
Definition Pin.hpp:52
Identifies a satellite (satellite system and number)
SatelliteSystem satSys
Satellite system (GPS, GLONASS, GALILEO, QZSS, BDS, IRNSS, SBAS)
uint16_t satNum
Number of the satellite.