0.4.1
Loading...
Searching...
No Matches
PolynomialCycleSlipDetector.hpp
Go to the documentation of this file.
1// This file is part of INSTINCT, the INS Toolkit for Integrated
2// Navigation Concepts and Training by the Institute of Navigation of
3// the University of Stuttgart, Germany.
4//
5// This Source Code Form is subject to the terms of the Mozilla Public
6// License, v. 2.0. If a copy of the MPL was not distributed with this
7// file, You can obtain one at https://mozilla.org/MPL/2.0/.
8
9/// @file PolynomialCycleSlipDetector.hpp
10/// @brief Polynomial Cycle-slip detection algorithm
11/// @author T. Topp (topp@ins.uni-stuttgart.de)
12/// @date 2023-10-30
13
14#pragma once
15
16#include <vector>
17#include <utility>
18#include <optional>
19
25
28
29namespace NAV
30{
31
32/// GnssAnalyzer forward declaration
33class GnssAnalyzer;
34/// CycleSlipDetector forward declaration
36
37/// Cycle-slip detection result type
39{
40 Disabled, ///< The cycle-slip detector is disabled
41 LessDataThanWindowSize, ///< Less data than the specified window size (cannot predict cycle-slip yet)
42 NoCycleSlip, ///< No cycle-slip found
43 CycleSlip, ///< Cycle-slip found
44};
45
46/// @brief Cycle-slip detection
47template<typename Key>
49{
50 public:
51 /// @brief Constructor
52 /// @param[in] windowSize Amount of points to use for the fit (sliding window)
53 /// @param[in] polyDegree Polynomial degree to fit
54 /// @param[in] enabled Whether the detector is enabled
55 explicit PolynomialCycleSlipDetector(size_t windowSize, size_t polyDegree, bool enabled = true)
56 : _enabled(enabled), _windowSize(windowSize), _polyDegree(polyDegree) {}
57
58 /// @brief Checks for a cycle slip
59 /// @param[in] key Key of the detector
60 /// @param[in] insTime Time of the measurement
61 /// @param[in] measurementDifference Measurement difference
62 /// @param[in] threshold Threshold to categorize a measurement as cycle slip
63 /// @return Cycle-slip result
64 [[nodiscard]] PolynomialCycleSlipDetectorResult checkForCycleSlip(const Key& key, InsTime insTime, double measurementDifference, double threshold)
65 {
67 if (!_detectors.contains(key))
68 {
69 addMeasurement(key, insTime, measurementDifference);
71 }
72
73 const auto& detector = _detectors.at(key);
74 if (!detector.polyReg.windowSizeReached())
75 {
76 addMeasurement(key, insTime, measurementDifference);
78 }
79
80 auto polynomial = detector.polyReg.calcPolynomial();
81 auto predictedValue = polynomial.f(calcRelativeTime(insTime, detector));
82
83 if (std::abs(measurementDifference - predictedValue) > threshold)
84 {
85 reset(key);
86 addMeasurement(key, insTime, measurementDifference);
88 }
89 addMeasurement(key, insTime, measurementDifference);
91 }
92
93 /// Empties the collected polynomials
94 void clear()
95 {
96 _detectors.clear();
97 }
98
99 /// @brief Reset the polynomial for the given combination
100 /// @param[in] key Key of the detector
101 void reset(const Key& key)
102 {
103 if (_detectors.contains(key))
104 {
105 _detectors.erase(key);
106 }
107 }
108
109 /// @brief Is the cycle-slip detector enabled?
110 [[nodiscard]] bool isEnabled() const
111 {
112 return _enabled;
113 }
114 /// @brief Sets the enabled state
115 /// @param[in] enabled Whether to enabled or not
116 void setEnabled(bool enabled)
117 {
118 _enabled = enabled;
119 }
120
121 /// @brief Get the window size for the polynomial fit
122 [[nodiscard]] size_t getWindowSize() const { return _windowSize; }
123 /// @brief Sets the amount of points used for the fit (sliding window)
124 /// @param[in] windowSize Amount of points to use for the fit
125 void setWindowSize(size_t windowSize)
126 {
127 _windowSize = windowSize;
128 for (auto& detector : _detectors)
129 {
130 detector.second.polyReg.setWindowSize(windowSize);
131 }
132 }
133
134 /// @brief Get the degree of the polynomial which is used for fitting
135 [[nodiscard]] size_t getPolynomialDegree() const { return _polyDegree; }
136 /// @brief Sets the degree of the polynomial which is used for fitting
137 /// @param[in] polyDegree Polynomial degree to fit
138 void setPolynomialDegree(size_t polyDegree)
139 {
140 _polyDegree = polyDegree;
141 for (auto& detector : _detectors)
142 {
143 detector.second.polyReg.setPolynomialDegree(polyDegree);
144 }
145 }
146
147 /// Strategies for fitting
149
150 /// @brief Get the strategy used for fitting
151 [[nodiscard]] Strategy getFitStrategy() const { return _strategy; }
152 /// @brief Sets the strategy used for fitting
153 /// @param[in] strategy Strategy for fitting
155 {
156 _strategy = strategy;
157 for (auto& detector : _detectors)
158 {
159 detector.second.polyReg.setStrategy(strategy);
160 }
161 }
162
163 private:
164 /// @brief Signal Detector struct
166 {
167 /// @brief Constructor
168 /// @param[in] startTime Time when the first message for this detector was received
169 /// @param[in] windowSize Window size for the sliding window
170 /// @param[in] polyDegree Polynomial degree to fit
171 /// @param[in] strategy Strategy for fitting
172 SignalDetector(InsTime startTime, size_t windowSize, size_t polyDegree, Strategy strategy)
173 : startTime(startTime), polyReg(polyDegree, windowSize, strategy) {}
174
175 InsTime startTime; ///< Time when the first message for this detector was received
176 PolynomialRegressor<double> polyReg; ///< Polynomial Regressor
177 };
178
179 bool _enabled = true; ///< Whether the cycle-slip detector is enabled
180 size_t _windowSize; ///< Window size for the sliding window
181 size_t _polyDegree = 2; ///< Polynomial degree to fit
182 Strategy _strategy = Strategy::HouseholderQR; ///< Strategy used for fitting
183 unordered_map<Key, SignalDetector> _detectors; ///< Detectors, one for each key
184
185 /// @brief Calculate the relative time to the start time of the detector
186 /// @param[in] insTime Time of the measurement
187 /// @param[in] detector Detector to use
188 [[nodiscard]] static double calcRelativeTime(const InsTime& insTime, const SignalDetector& detector)
189 {
190 return static_cast<double>((insTime - detector.startTime).count());
191 }
192 /// @brief Calculate the relative time to the start time of the detector
193 /// @param[in] key Key of the detector
194 /// @param[in] insTime Time of the measurement
195 [[nodiscard]] std::optional<double> calcRelativeTime(const Key& key, const InsTime& insTime) const
196 {
197 if (!_detectors.contains(key)) { return {}; }
198
199 return calcRelativeTime(insTime, _detectors.at(key));
200 }
201
202 /// @brief Predicts a value from the collected data and polynomial fit
203 /// @param[in] key Key of the detector
204 /// @param[in] insTime Time of the measurement
205 [[nodiscard]] std::optional<double> predictValue(const Key& key, const InsTime& insTime) const
206 {
207 if (!_detectors.contains(key)) { return {}; }
208
209 const auto& detector = _detectors.at(key);
210
211 auto polynomial = detector.polyReg.calcPolynomial();
212 return polynomial.f(calcRelativeTime(insTime, detector));
213 }
214
215 /// @brief Calculates the polynomial from the collected data
216 /// @param[in] key Key of the detector
217 [[nodiscard]] std::optional<Polynomial<double>> calcPolynomial(const Key& key) const
218 {
219 if (!_detectors.contains(key)) { return {}; }
220
221 return _detectors.at(key).polyReg.calcPolynomial();
222 }
223
224 /// @brief Add a measurement to the polynomial fit
225 /// @param[in] key Key of the detector
226 /// @param[in] insTime Time of the measurement
227 /// @param[in] measurementDifference Measurement difference
228 void addMeasurement(const Key& key, InsTime insTime, double measurementDifference)
229 {
230 if (!_enabled) { return; }
231 auto& detector = _detectors.insert({ key, SignalDetector(insTime, _windowSize, _polyDegree, _strategy) }).first->second;
232
233 detector.polyReg.push_back(calcRelativeTime(insTime, detector), measurementDifference);
234 }
235
236 friend class GnssAnalyzer;
237 friend class CycleSlipDetector;
238};
239
240/// @brief Shows a GUI for advanced configuration of the PolynomialCycleSlipDetector
241/// @param[in] label Label to show beside the combo box. This has to be a unique id for ImGui.
242/// @param[in] polynomialCycleSlipDetector Reference to the cycle-slip detector to configure
243/// @param[in] width Width of the widget
244template<typename Key>
245bool PolynomialCycleSlipDetectorGui(const char* label, PolynomialCycleSlipDetector<Key>& polynomialCycleSlipDetector, float width = 0.0F)
246{
247 bool changed = false;
248
249 bool enabled = polynomialCycleSlipDetector.isEnabled();
250 if (ImGui::Checkbox(fmt::format("Enabled##{}", label).c_str(), &enabled))
251 {
252 changed = true;
253 polynomialCycleSlipDetector.setEnabled(enabled);
254 }
255
256 if (!enabled) { ImGui::BeginDisabled(); }
257
258 ImGui::SetNextItemWidth(width);
259 if (int windowSize = static_cast<int>(polynomialCycleSlipDetector.getWindowSize());
260 ImGui::InputIntL(fmt::format("Window size##{}", label).c_str(), &windowSize,
261 std::max(1, static_cast<int>(polynomialCycleSlipDetector.getPolynomialDegree()) + 1)))
262 {
263 changed = true;
264 polynomialCycleSlipDetector.setWindowSize(static_cast<size_t>(windowSize));
265 }
266
267 ImGui::SetNextItemWidth(width);
268 if (int polyDegree = static_cast<int>(polynomialCycleSlipDetector.getPolynomialDegree());
269 ImGui::InputIntL(fmt::format("Polynomial Degree##{}", label).c_str(), &polyDegree,
270 0, std::min(static_cast<int>(polynomialCycleSlipDetector.getWindowSize()) - 1, std::numeric_limits<int>::max())))
271 {
272 changed = true;
273 polynomialCycleSlipDetector.setPolynomialDegree(static_cast<size_t>(polyDegree));
274 }
275
276 ImGui::SetNextItemWidth(width);
277 if (auto strategy = polynomialCycleSlipDetector.getFitStrategy();
278 gui::widgets::EnumCombo(fmt::format("Strategy##{}", label).c_str(), strategy))
279 {
280 changed = true;
281 polynomialCycleSlipDetector.setFitStrategy(strategy);
282 }
283
284 if (!enabled) { ImGui::EndDisabled(); }
285
286 return changed;
287}
288
289/// @brief Write info to a json object
290/// @param[out] j Json output
291/// @param[in] data Object to read info from
292template<typename Key>
294{
295 j = json{
296 { "enabled", data.isEnabled() },
297 { "windowSize", data.getWindowSize() },
298 { "polynomialDegree", data.getPolynomialDegree() },
299 { "strategy", data.getFitStrategy() },
300 };
301}
302/// @brief Read info from a json object
303/// @param[in] j Json variable to read info from
304/// @param[out] data Output object
305template<typename Key>
307{
308 if (j.contains("enabled"))
309 {
310 auto enabled = j.at("enabled").get<bool>();
311 data.setEnabled(enabled);
312 }
313 if (j.contains("windowSize"))
314 {
315 auto windowSize = j.at("windowSize").get<size_t>();
316 data.setWindowSize(windowSize);
317 }
318 if (j.contains("polynomialDegree"))
319 {
320 auto polynomialDegree = j.at("polynomialDegree").get<size_t>();
321 data.setPolynomialDegree(polynomialDegree);
322 }
323 if (j.contains("strategy"))
324 {
325 auto strategy = j.at("strategy").get<size_t>();
326 data.setFitStrategy(static_cast<typename PolynomialCycleSlipDetector<Key>::Strategy>(strategy));
327 }
328}
329
330} // namespace NAV
Combo representing an enumeration.
nlohmann::json json
json namespace
The class is responsible for all time-related tasks.
Utility functions for std::pair.
Polynomial curve fitting.
Structs identifying a unique satellite.
Unordered map wrapper.
ankerl::unordered_dense::map< Key, T > unordered_map
Unordered map type.
Cycle-slip detector.
Allows creation of GNSS measurement combinations.
The class is responsible for all time-related tasks.
Definition InsTime.hpp:710
size_t getPolynomialDegree() const
Get the degree of the polynomial which is used for fitting.
void setFitStrategy(Strategy strategy)
Sets the strategy used for fitting.
void setWindowSize(size_t windowSize)
Sets the amount of points used for the fit (sliding window)
void addMeasurement(const Key &key, InsTime insTime, double measurementDifference)
Add a measurement to the polynomial fit.
std::optional< double > predictValue(const Key &key, const InsTime &insTime) const
Predicts a value from the collected data and polynomial fit.
void setEnabled(bool enabled)
Sets the enabled state.
PolynomialRegressor<>::Strategy Strategy
Strategies for fitting.
bool isEnabled() const
Is the cycle-slip detector enabled?
std::optional< double > calcRelativeTime(const Key &key, const InsTime &insTime) const
Calculate the relative time to the start time of the detector.
void setPolynomialDegree(size_t polyDegree)
Sets the degree of the polynomial which is used for fitting.
size_t getWindowSize() const
Get the window size for the polynomial fit.
static double calcRelativeTime(const InsTime &insTime, const SignalDetector &detector)
Calculate the relative time to the start time of the detector.
Strategy _strategy
Strategy used for fitting.
std::optional< Polynomial< double > > calcPolynomial(const Key &key) const
Calculates the polynomial from the collected data.
PolynomialCycleSlipDetectorResult checkForCycleSlip(const Key &key, InsTime insTime, double measurementDifference, double threshold)
Checks for a cycle slip.
size_t _windowSize
Window size for the sliding window.
bool _enabled
Whether the cycle-slip detector is enabled.
Strategy getFitStrategy() const
Get the strategy used for fitting.
size_t _polyDegree
Polynomial degree to fit.
void reset(const Key &key)
Reset the polynomial for the given combination.
unordered_map< Key, SignalDetector > _detectors
Detectors, one for each key.
PolynomialCycleSlipDetector(size_t windowSize, size_t polyDegree, bool enabled=true)
Constructor.
void clear()
Empties the collected polynomials.
Polynomial Curve Fitting.
Strategy
Possible Fit strategies.
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 EnumCombo(const char *label, T &enumeration, size_t startIdx=0)
Combo representing an enumeration.
Definition EnumCombo.hpp:30
void to_json(json &j, const Node &node)
Converts the provided node into a json object.
Definition Node.cpp:990
PolynomialCycleSlipDetectorResult
Cycle-slip detection result type.
@ LessDataThanWindowSize
Less data than the specified window size (cannot predict cycle-slip yet)
@ Disabled
The cycle-slip detector is disabled.
void from_json(const json &j, Node &node)
Converts the provided json object into a node object.
Definition Node.cpp:1007
bool PolynomialCycleSlipDetectorGui(const char *label, PolynomialCycleSlipDetector< Key > &polynomialCycleSlipDetector, float width=0.0F)
Shows a GUI for advanced configuration of the PolynomialCycleSlipDetector.
SignalDetector(InsTime startTime, size_t windowSize, size_t polyDegree, Strategy strategy)
Constructor.
PolynomialRegressor< double > polyReg
Polynomial Regressor.
InsTime startTime
Time when the first message for this detector was received.