0.5.1
Loading...
Searching...
No Matches
GnssAnalyzer.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 "GnssAnalyzer.hpp"
10
11#include <imgui_internal.h>
12
13#include "util/Logger.hpp"
14
16
18
22
25
26namespace NAV
27{
28
29/// @brief Write info to a json object
30/// @param[out] j Json output
31/// @param[in] data Object to read info from
33{
34 j = json{
35 { "obsType", data.obsType },
36 { "satSigId", data.satSigId },
37 { "sign", data.sign },
38 };
39}
40/// @brief Read info from a json object
41/// @param[in] j Json variable to read info from
42/// @param[out] data Output object
44{
45 if (j.contains("obsType")) { j.at("obsType").get_to(data.obsType); }
46 if (j.contains("satSigId")) { j.at("satSigId").get_to(data.satSigId); }
47 if (j.contains("sign")) { j.at("sign").get_to(data.sign); }
48}
49
50/// @brief Write info to a json object
51/// @param[out] j Json output
52/// @param[in] data Object to read info from
54{
55 j = json{
56 { "description", data.description() },
57 { "terms", data.terms },
58 { "unit", data.unit },
59 { "polynomialCycleSlipDetector", data.polynomialCycleSlipDetector },
60 { "polynomialCycleSlipDetector.thresholdPercentage", data.polynomialCycleSlipDetectorThresholdPercentage },
61 { "polynomialCycleSlipDetector.outputWhenWindowSizeNotReached", data.polynomialCycleSlipDetectorOutputWhenWindowSizeNotReached },
62 { "polynomialCycleSlipDetector.outputPolynomials", data.polynomialCycleSlipDetectorOutputPolynomials },
63 };
64}
65/// @brief Read info from a json object
66/// @param[in] j Json variable to read info from
67/// @param[out] data Output object
69{
70 if (j.contains("terms"))
71 {
72 j.at("terms").get_to(data.terms);
73 }
74 if (j.contains("unit"))
75 {
76 j.at("unit").get_to(data.unit);
77 }
78 if (j.contains("polynomialCycleSlipDetector"))
79 {
80 j.at("polynomialCycleSlipDetector").get_to(data.polynomialCycleSlipDetector);
81 }
82 if (j.contains("polynomialCycleSlipDetector.thresholdPercentage"))
83 {
84 j.at("polynomialCycleSlipDetector.thresholdPercentage").get_to(data.polynomialCycleSlipDetectorThresholdPercentage);
85 }
86 if (j.contains("polynomialCycleSlipDetector.outputWhenWindowSizeNotReached"))
87 {
88 j.at("polynomialCycleSlipDetector.outputWhenWindowSizeNotReached").get_to(data.polynomialCycleSlipDetectorOutputWhenWindowSizeNotReached);
89 }
90 if (j.contains("polynomialCycleSlipDetector.outputPolynomials"))
91 {
92 j.at("polynomialCycleSlipDetector.outputPolynomials").get_to(data.polynomialCycleSlipDetectorOutputPolynomials);
93 }
94}
95
96} // namespace NAV
97
110
112{
113 LOG_TRACE("{}: called", nameId());
114}
115
117{
118 return "Gnss Analyzer";
119}
120
121std::string NAV::GnssAnalyzer::type() const
122{
123 return typeStatic();
124}
125
127{
128 return "Data Processor";
129}
130
132{
133 ImGui::TextUnformatted("Create combinations of GNSS measurements by adding or subtracting signals.");
134
135 std::vector<size_t> combToDelete;
136 for (size_t c = 0; c < _combinations.size(); c++)
137 {
138 auto& comb = _combinations.at(c);
139
140 bool keepCombination = true;
141 if (ImGui::CollapsingHeader(fmt::format("Combination {}##id{}", c, size_t(id)).c_str(),
142 _combinations.size() > 1 ? &keepCombination : nullptr, ImGuiTreeNodeFlags_DefaultOpen))
143 {
144 if (ImGui::Button(fmt::format("Add Term##id{} c{}", size_t(id), c).c_str()))
145 {
147 comb.terms.emplace_back();
148 if (comb.terms.size() != 2) { comb.polynomialCycleSlipDetector.setEnabled(false); }
149 }
150
151 ImGui::SameLine();
152 int selected = comb.unit == Combination::Unit::Meters ? 0 : 1;
153 ImGui::SetNextItemWidth(100.0F);
154 if (ImGui::Combo(fmt::format("Output unit##id{} c{}", size_t(id), c).c_str(), &selected, "Meters\0Cycles\0\0"))
155 {
157 comb.unit = selected == 0 ? Combination::Unit::Meters : Combination::Unit::Cycles;
158 }
159
160 ImGui::SameLine();
161 if (ImGui::Button(fmt::format("Cycle-slip detector##id{} c{}", size_t(id), c).c_str()))
162 {
163 ImGui::OpenPopup(fmt::format("Cycle-slip detector##Popup - id{} c{}", size_t(id), c).c_str());
164 }
165 if (ImGui::BeginPopup(fmt::format("Cycle-slip detector##Popup - id{} c{}", size_t(id), c).c_str()))
166 {
167 constexpr float WIDTH = 145.0F;
168 if (PolynomialCycleSlipDetectorGui(fmt::format("Cycle-slip detector id{} c{}", size_t(id), c).c_str(),
169 comb.polynomialCycleSlipDetector, WIDTH))
170 {
172 }
173
174 ImGui::SetNextItemWidth(WIDTH);
175 if (double val = comb.polynomialCycleSlipDetectorThresholdPercentage * 100.0;
176 ImGui::DragDouble(fmt::format("Threshold##id{} c{}", size_t(id), c).c_str(), &val, 1.0F,
177 1.0, std::numeric_limits<double>::max(), "%.2f %%"))
178 {
179 comb.polynomialCycleSlipDetectorThresholdPercentage = val / 100.0;
181 }
182 ImGui::SameLine();
183 std::string description = "As percentage of the smallest wavelength of the combination terms.";
184 if (auto maxF = std::ranges::max_element(comb.terms, [](const Combination::Term& a, const Combination::Term& b) {
185 return a.satSigId.freq().getFrequency(a.freqNum) < b.satSigId.freq().getFrequency(b.freqNum);
186 });
187 maxF != comb.terms.end())
188 {
189 double lambda = InsConst::C / maxF->satSigId.freq().getFrequency(maxF->freqNum);
190 double threshold = comb.polynomialCycleSlipDetectorThresholdPercentage * lambda;
191 description += fmt::format("\nFor [{} {}] the wavelength is λ = {:.3f} [m].\nThe threshold is then {:.3f} [m].",
192 maxF->satSigId.toSatId().satSys, maxF->satSigId.freq(), lambda, threshold);
193 }
194 gui::widgets::HelpMarker(description.c_str());
195
196 if (!comb.polynomialCycleSlipDetector.isEnabled()) { ImGui::BeginDisabled(); }
197 if (ImGui::Checkbox(fmt::format("Output when insufficient points##id{} c{}", size_t(id), c).c_str(), &comb.polynomialCycleSlipDetectorOutputWhenWindowSizeNotReached))
198 {
200 }
201 if (ImGui::Checkbox(fmt::format("Output polynomials##id{} c{}", size_t(id), c).c_str(), &comb.polynomialCycleSlipDetectorOutputPolynomials))
202 {
204 }
205 if (!comb.polynomialCycleSlipDetector.isEnabled()) { ImGui::EndDisabled(); }
206
207 ImGui::EndPopup();
208 }
209
210 std::vector<size_t> termToDelete;
211 if (ImGui::BeginTable(fmt::format("##Table id{} c{}", size_t(id), c).c_str(), 3 * static_cast<int>(comb.terms.size()) + 1,
212 ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX,
213 ImVec2(0, 70.0F)))
214 {
215 ImGui::TableNextRow();
216
217 for (size_t t = 0; t < comb.terms.size(); t++)
218 {
219 auto& term = comb.terms.at(t);
220
221 ImGui::TableSetColumnIndex(static_cast<int>(t) * 3);
222 int selected = term.sign == 1 ? 0 : 1;
223 ImGui::SetNextItemWidth(50.0F);
224 if (ImGui::Combo(fmt::format("##Sign id{} c{} t{}", size_t(id), c, t).c_str(), &selected, "+1\0-1\0\0"))
225 {
227 term.sign = selected == 0 ? +1 : -1;
228 }
229
230 ImGui::TableSetColumnIndex(static_cast<int>(t) * 3 + 1);
231 selected = term.obsType == Combination::Term::ObservationType::Pseudorange ? 0 : 1;
232 ImGui::SetNextItemWidth(62.0F);
233 if (ImGui::Combo(fmt::format("##ObsType id{} c{} t{}", size_t(id), c, t).c_str(), &selected, comb.unit == Combination::Unit::Cycles ? "P\0Φ\0\0" : "p\0φ\0\0"))
234 {
237 }
238
239 ImGui::TableSetColumnIndex(static_cast<int>(t) * 3 + 2);
240 ImGui::SetNextItemWidth(62.0F);
241 if (ShowCodeSelector(fmt::format("##Code id{} c{} t{}", size_t(id), c, t).c_str(), term.satSigId.code, Freq_All, true))
242 {
244 }
245 ImGui::SameLine();
246 ImGui::Dummy(ImVec2(10.0F, 0.0F));
247 }
248 ImGui::TableNextColumn();
249 ImGui::TextUnformatted("= Combined Frequency");
250
251 ImGui::TableNextRow();
252 for (size_t t = 0; t < comb.terms.size(); t++)
253 {
254 auto& term = comb.terms.at(t);
255
256 ImGui::TableSetColumnIndex(static_cast<int>(t) * 3);
257 ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 8.0F);
258 float radius = 8.0F;
259 ImGui::GetWindowDrawList()->AddCircleFilled(ImGui::GetCursorScreenPos() + ImVec2(radius, ImGui::GetTextLineHeight() / 2.0F + 2.0F), radius,
260 term.receivedDuringRun ? IM_COL32(0, 255, 0, 255) : IM_COL32(255, 0, 0, 255));
261 ImGui::Dummy(ImVec2(radius * 2.0F, ImGui::GetTextLineHeight()));
262 if (ImGui::IsItemHovered()) { ImGui::SetTooltip(term.receivedDuringRun ? "Signal was received" : "Signal was not received"); }
263
264 if (comb.terms.size() > 1)
265 {
266 ImGui::SameLine();
267 if (ImGui::Button(fmt::format("X##id{} c{} t{}", size_t(id), c, t).c_str()))
268 {
270 termToDelete.push_back(t);
271 }
272 if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Remove Term?"); }
273 }
274
275 ImGui::TableSetColumnIndex(static_cast<int>(t) * 3 + 1);
276 double f = term.satSigId.freq().getFrequency(term.freqNum);
277 ImGui::TextUnformatted(fmt::format("{:.2f}", f * 1e-6).c_str());
278 if (ImGui::IsItemHovered()) { ImGui::SetTooltip("%s", fmt::format("Frequency in [Mhz]\nλ = {:.3f}m", InsConst::C / f).c_str()); }
279
280 ImGui::TableSetColumnIndex(static_cast<int>(t) * 3 + 2);
281 ImGui::SetNextItemWidth(62.0F);
282 SatId satId = SatId(term.satSigId.toSatId().satSys, term.satSigId.satNum);
283 if (ShowSatelliteSelector(fmt::format("##SatNum id{} c{} t{}", size_t(id), c, t).c_str(), satId, satId.satSys, true))
284 {
285 term.satSigId.satNum = satId.satNum;
287 }
288 }
289 ImGui::TableNextColumn();
290 double f = comb.calcCombinationFrequency();
291 ImGui::TextUnformatted(fmt::format(" {:.2f} MHz", f * 1e-6).c_str());
292 if (ImGui::IsItemHovered()) { ImGui::SetTooltip("%s", fmt::format("λ = {:.3f}m", InsConst::C / f).c_str()); }
293
294 ImGui::EndTable();
295 }
296
297 for (const auto& t : termToDelete) { comb.terms.erase(std::next(comb.terms.begin(), static_cast<std::ptrdiff_t>(t))); }
298 }
299
300 if (!keepCombination) { combToDelete.push_back(c); }
301 }
302 for (const auto& c : combToDelete) { _combinations.erase(std::next(_combinations.begin(), static_cast<std::ptrdiff_t>(c))); }
303
304 ImGui::Separator();
305 if (ImGui::Button(fmt::format("Add Combination##id{}", size_t(id)).c_str()))
306 {
308 _combinations.emplace_back();
309 _combinations.back().polynomialCycleSlipDetector.setResetAfterCycleSlip(false);
310 }
311}
312
313[[nodiscard]] json NAV::GnssAnalyzer::save() const
314{
315 LOG_TRACE("{}: called", nameId());
316
317 json j;
318
319 j["combinations"] = _combinations;
320
321 return j;
322}
323
325{
326 LOG_TRACE("{}: called", nameId());
327
328 if (j.contains("combinations"))
329 {
330 j.at("combinations").get_to(_combinations);
331 }
332}
333
335{
336 LOG_TRACE("{}: called", nameId());
337
338 for (auto& comb : _combinations)
339 {
340 comb.polynomialCycleSlipDetector.clear();
341 comb.polynomials.clear();
342 for (auto& term : comb.terms)
343 {
344 term.receivedDuringRun = false;
345 }
346 }
347
348 return true;
349}
350
352{
353 LOG_TRACE("{}: called", nameId());
354
355 for (auto& comb : _combinations)
356 {
357 for (auto& term : comb.terms)
358 {
359 term.receivedDuringRun = false;
360 }
361 }
362}
363
365{
366 auto gnssObs = std::static_pointer_cast<const GnssObs>(queue.extract_front());
367 LOG_DATA("{}: Received GnssObs for [{}]", nameId(), gnssObs->insTime);
368
369 auto gnssComb = std::make_shared<GnssCombination>();
370 gnssComb->insTime = gnssObs->insTime;
371
372 for (size_t c = 0; c < _combinations.size(); c++)
373 {
374 auto& comb = _combinations.at(c);
376 combination.description = comb.description();
377 for (size_t i = 0; i < _combinations.size(); i++)
378 {
379 if (i == c) { continue; }
380 if (combination.description == _combinations.at(i).description())
381 {
382 combination.description += fmt::format(" - {}", c);
383 break;
384 }
385 }
386 LOG_DATA("{}: {}", nameId(), combination.description);
387
388 double result = 0.0;
389 double lambdaMin = 100.0;
390 size_t termsFound = 0;
391 for (auto& term : comb.terms)
392 {
394 oTerm.sign = term.sign;
395 oTerm.satSigId = term.satSigId;
399 LOG_DATA("{}: {}[{}][{}]", nameId(), oTerm.sign == 1 ? "+" : "-", oTerm.satSigId, oTerm.obsType);
400
401 double freq = term.satSigId.freq().getFrequency(term.freqNum);
402 double lambda = InsConst::C / freq;
403 lambdaMin = std::min(lambdaMin, lambda);
404
405 if (auto obs = (*gnssObs)(term.satSigId))
406 {
408 {
409 if (auto psr = obs->get().pseudorange)
410 {
411 term.receivedDuringRun = true;
412
413 double value = psr->value;
414 result += static_cast<double>(term.sign) * value;
415
416 if (comb.unit == Combination::Unit::Cycles)
417 {
418 value /= lambda;
419 }
420 oTerm.value = value;
421
422 termsFound++;
423 }
424 else
425 {
426 LOG_DATA("{}: Resetting cycle-slip detector as no pseudorange in measurement", nameId());
427 comb.polynomialCycleSlipDetector.reset(comb.description());
428 }
429 }
430 else // if (term.obsType == Combination::Term::ObservationType::Carrier)
431 {
432 if (auto carrier = obs->get().carrierPhase)
433 {
434 term.receivedDuringRun = true;
435
436 double value = carrier->value * lambda;
437 result += static_cast<double>(term.sign) * value;
438
439 if (comb.unit == Combination::Unit::Cycles)
440 {
441 value /= lambda;
442 }
443 oTerm.value = value;
444
445 termsFound++;
446 }
447 else
448 {
449 LOG_DATA("{}: Resetting cycle-slip detector as no carrier-phase in measurement", nameId());
450 comb.polynomialCycleSlipDetector.reset(comb.description());
451 }
452 }
453 }
454
455 combination.terms.push_back(oTerm);
456 }
457 LOG_DATA("{}: Found {}/{}", nameId(), termsFound, comb.terms.size());
458 if (termsFound == comb.terms.size())
459 {
460 auto lambda = InsConst::C / comb.calcCombinationFrequency();
461 double resultCycles = result / lambda;
462 combination.result = comb.unit == Combination::Unit::Cycles ? resultCycles : result;
463
464 if (comb.polynomialCycleSlipDetector.isEnabled())
465 {
466 auto key = comb.description();
467 LOG_DATA("{}: Polynomial {}/{} data points ({} needed for calculation)", nameId(),
468 comb.polynomialCycleSlipDetector.getDataSize(key), comb.polynomialCycleSlipDetector.getWindowSize(),
469 comb.polynomialCycleSlipDetector.getPolynomialDegree() + 1);
470 combination.cycleSlipPrediction = comb.polynomialCycleSlipDetector.predictValue(key, gnssComb->insTime);
471 if (combination.cycleSlipPrediction.has_value())
472 {
473 combination.cycleSlipMeasMinPred = *combination.result - *combination.cycleSlipPrediction;
474 LOG_DATA("{}: Predicting: meas - pred = {}", nameId(), *combination.cycleSlipMeasMinPred);
475 }
476
477 if (comb.polynomialCycleSlipDetectorOutputPolynomials)
478 {
479 if (auto polynomial = comb.polynomialCycleSlipDetector.calcPolynomial(key))
480 {
481 comb.polynomials.emplace_back(gnssComb->insTime, *polynomial);
482 }
483 if (auto relTime = comb.polynomialCycleSlipDetector.calcRelativeTime(key, gnssComb->insTime))
484 {
485 for (const auto& poly : comb.polynomials)
486 {
487 double value = poly.second.f(*relTime);
488 LOG_DATA("f({:.2f}) = {:.2f} ({})", *relTime, value, poly.second.toString());
489 combination.cycleSlipPolynomials.emplace_back(poly.first, poly.second, value);
490 }
491 }
492 }
493 double threshold = comb.polynomialCycleSlipDetectorThresholdPercentage * lambdaMin;
494 if (comb.unit == Combination::Unit::Cycles) { threshold /= lambda; }
495
496 // This adds the measurement to the polynomial
497 combination.cycleSlipResult = comb.polynomialCycleSlipDetector.checkForCycleSlip(key, gnssComb->insTime, *combination.result, threshold);
498
499 if (!comb.polynomialCycleSlipDetectorOutputWhenWindowSizeNotReached
501 {
502 combination.cycleSlipPrediction.reset();
503 combination.cycleSlipMeasMinPred.reset();
504 }
505 }
506 }
507
508 gnssComb->combinations.push_back(combination);
509 }
510
512}
Code definitions.
Holds all Constants.
Save/Load the Nodes.
nlohmann::json json
json namespace
Allows creation of GNSS measurement combinations.
GNSS measurement combinations.
GNSS Observation messages.
Text Help Marker (?) with Tooltip.
Utility class for logging to console and file.
#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
Structs identifying a unique satellite.
static std::string typeStatic()
String representation of the Class Type.
static std::string category()
String representation of the Class Category.
~GnssAnalyzer() override
Destructor.
std::vector< Combination > _combinations
Combinations to calculate.
void receiveGnssObs(InputPin::NodeDataQueue &queue, size_t pinIdx)
Receive Gnss observation.
void deinitialize() override
Deinitialize the node.
void guiConfig() override
ImGui config window which is shown on double click.
void restore(const json &j) override
Restores the node from a json object.
static constexpr size_t OUTPUT_PORT_INDEX_GNSS_COMBINATION
Flow (GnssCombination)
std::string type() const override
String representation of the Class Type.
GnssAnalyzer()
Default constructor.
bool initialize() override
Initialize the node.
json save() const override
Saves the node into a json object.
static std::string type()
Returns the type of the data class.
@ Carrier
Carrier-Phase.
Definition GnssObs.hpp:40
@ Pseudorange
Pseudorange.
Definition GnssObs.hpp:39
static std::string type()
Returns the type of the data class.
Definition GnssObs.hpp:151
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
Node(std::string name)
Constructor.
Definition Node.cpp:29
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
bool _hasConfig
Flag if the config window should be shown.
Definition Node.hpp:525
auto extract_front()
Returns a copy of the first element in the container and removes it from the container.
Definition TsDeque.hpp:494
bool DragDouble(const char *label, double *v, float v_speed, double v_min, double v_max, const char *format, ImGuiSliderFlags flags)
Shows a Drag GUI element for 'double'.
Definition imgui_ex.cpp:19
void ApplyChanges()
Signals that there have been changes to the flow.
void HelpMarker(const char *desc, const char *symbol="(?)")
Text Help Marker, e.g. '(?)', with Tooltip.
void to_json(json &j, const Node &node)
Converts the provided node into a json object.
Definition Node.cpp:1060
@ LessDataThanWindowSize
Less data than the specified window size (cannot predict cycle-slip yet)
constexpr Frequency_ Freq_All
All Frequencies.
bool ShowCodeSelector(const char *label, Code &code, const Frequency &filterFreq, bool singleSelect)
Shows a ComboBox to select signal codes.
Definition Code.cpp:1310
void from_json(const json &j, Node &node)
Converts the provided json object into a node object.
Definition Node.cpp:1077
bool PolynomialCycleSlipDetectorGui(const char *label, PolynomialCycleSlipDetector< Key > &polynomialCycleSlipDetector, float width=0.0F)
Shows a GUI for advanced configuration of the PolynomialCycleSlipDetector.
bool ShowSatelliteSelector(const char *label, std::vector< SatId > &satellites, SatelliteSystem filterSys, bool displayOnlyNumber)
Shows a ComboBox to select satellites.
Term of a combination equation.
SatSigId satSigId
SignalId and satellite number.
ObservationType obsType
Observation Type.
Combination of GNSS measurements.
PolynomialCycleSlipDetector< std::string > polynomialCycleSlipDetector
Cycle-slip detector.
std::string description() const
Get a string description of the combination.
Unit unit
Unit to calculate the combination in.
std::vector< Term > terms
List of terms making up the combination.
bool polynomialCycleSlipDetectorOutputPolynomials
Whether the polynomials should be outputted.
bool polynomialCycleSlipDetectorOutputWhenWindowSizeNotReached
Whether to output the prediction even when the window size is not reached.
double polynomialCycleSlipDetectorThresholdPercentage
Threshold to categorize a measurement as cycle slip [% of smallest wavelength].
Term of a combination equation.
std::optional< double > value
Measurement (if present)
SatSigId satSigId
SignalId and satellite number.
GnssObs::ObservationType obsType
Observation Type.
Combination of GNSS measurements.
std::optional< double > cycleSlipMeasMinPred
Measurement minus predicted value from the cycle-slip detector.
std::optional< double > cycleSlipPrediction
Predicted value from the cycle-slip detector (polynomial fit)
std::optional< PolynomialCycleSlipDetectorResult > cycleSlipResult
Cycle-slip result.
std::vector< std::tuple< InsTime, Polynomial< double >, double > > cycleSlipPolynomials
Polynomial fits.
std::string description
String describing the combination.
std::optional< double > result
Calculated combination (only set if all terms where present)
std::vector< Term > terms
List of terms making up the combination.
@ 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.