INSTINCT Code Coverage Report


Directory: src/
File: Nodes/Utility/Combiner.hpp
Date: 2025-02-07 16:54:41
Exec Total Coverage
Lines: 15 21 71.4%
Functions: 2 2 100.0%
Branches: 23 60 38.3%

Line Branch Exec Source
1 // This file is part of INSTINCT, the INS Toolkit for Integrated
2 // Navigation Concepts and Training by the Institute of Navigation of
3 // the University of Stuttgart, Germany.
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
8
9 /// @file Combiner.hpp
10 /// @brief Calculates differences between signals
11 /// @author T. Topp (topp@ins.uni-stuttgart.de)
12 /// @date 2024-01-29
13
14 #pragma once
15
16 // <boost/asio.hpp> needs to be included before <winsock.h> (even though not used in this file)
17 // https://stackoverflow.com/questions/9750344/boostasio-winsock-and-winsock-2-compatibility-issue
18 #ifdef _WIN32
19 // Set the proper SDK version before including boost/Asio
20 #include <SDKDDKVer.h>
21 // Note boost/ASIO includes Windows.h.
22 #include <boost/asio.hpp>
23 #endif //_WIN32
24
25 #include <limits>
26 #include <memory>
27 #include <unordered_set>
28 #include <map>
29
30 #include "Navigation/Time/InsTime.hpp"
31 #include "NodeData/NodeData.hpp"
32 #include "internal/Node/Node.hpp"
33 #include "internal/gui/widgets/DynamicInputPins.hpp"
34
35 #include "Navigation/Math/PolynomialRegressor.hpp"
36
37 #include "NodeData/GNSS/GnssCombination.hpp"
38 #include "NodeData/GNSS/GnssObs.hpp"
39 #include "NodeData/GNSS/RtklibPosObs.hpp"
40 #include "NodeData/GNSS/SppSolution.hpp"
41 #include "NodeData/IMU/ImuObs.hpp"
42 #include "NodeData/IMU/ImuObsSimulated.hpp"
43 #include "NodeData/IMU/ImuObsWDelta.hpp"
44 #include "NodeData/IMU/KvhObs.hpp"
45 #include "NodeData/IMU/VectorNavBinaryOutput.hpp"
46 #include "NodeData/State/InsGnssLCKFSolution.hpp"
47 #include "NodeData/State/InsGnssTCKFSolution.hpp"
48 #include "NodeData/State/PosVelAtt.hpp"
49
50 #include "util/Logger/CommonLog.hpp"
51 #include "util/Container/ScrollingBuffer.hpp"
52 #include "util/Container/Unordered_map.hpp"
53
54 namespace NAV
55 {
56 /// @brief Calculates differences between signals
57 class Combiner : public Node, public CommonLog
58 {
59 public:
60 /// @brief Default constructor
61 Combiner();
62 /// @brief Destructor
63 ~Combiner() override;
64 /// @brief Copy constructor
65 Combiner(const Combiner&) = delete;
66 /// @brief Move constructor
67 Combiner(Combiner&&) = delete;
68 /// @brief Copy assignment operator
69 Combiner& operator=(const Combiner&) = delete;
70 /// @brief Move assignment operator
71 Combiner& operator=(Combiner&&) = delete;
72
73 /// @brief String representation of the Class Type
74 [[nodiscard]] static std::string typeStatic();
75
76 /// @brief String representation of the Class Type
77 [[nodiscard]] std::string type() const override;
78
79 /// @brief String representation of the Class Category
80 [[nodiscard]] static std::string category();
81
82 /// @brief ImGui config window which is shown on double click
83 /// @attention Don't forget to set _hasConfig to true in the constructor of the node
84 void guiConfig() override;
85
86 /// @brief Saves the node into a json object
87 [[nodiscard]] json save() const override;
88
89 /// @brief Restores the node from a json object
90 /// @param[in] j Json object with the node state
91 void restore(const json& j) override;
92
93 private:
94 constexpr static size_t OUTPUT_PORT_INDEX_DYN_DATA = 0; ///< @brief Flow (DynamicData)
95
96 /// Possible data identifiers to connect
97 static inline std::vector<std::string> _dataIdentifier = { Pos::type(),
98 PosVel::type(),
99 PosVelAtt::type(),
100 InsGnssLCKFSolution::type(),
101 InsGnssTCKFSolution::type(),
102 GnssCombination::type(),
103 GnssObs::type(),
104 SppSolution::type(),
105 RtklibPosObs::type(),
106 ImuObs::type(),
107 ImuObsWDelta::type(),
108 ImuObsSimulated::type(),
109 KvhObs::type(),
110 VectorNavBinaryOutput::type() };
111
112 /// Combination of data
113 struct Combination
114 {
115 /// Term of a combination equation
116 struct Term
117 {
118 double factor = 1.0; ///< Factor to multiply the term with
119 size_t pinIndex = 0; ///< Pin Index
120 std::variant<size_t, std::string> dataSelection = size_t(0); ///< Data Index or Data identifier
121
122 PolynomialRegressor<double> polyReg{ 1, 2 }; ///< Polynomial Regressor to interpolate data
123 ScrollingBuffer<std::shared_ptr<const NodeData>> rawData{ 2 }; ///< Last raw data to add if we send
124
125 /// @brief Get a string description of the combination
126 /// @param node Combiner node pointer
127 /// @param descriptors Data descriptors
128 581328 [[nodiscard]] std::string description(const Combiner* node, const std::vector<std::string>& descriptors) const
129 {
130
3/6
✓ Branch 1 taken 581328 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 581328 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 581328 times.
✗ Branch 8 not taken.
581328 if (std::holds_alternative<size_t>(dataSelection) && std::get<size_t>(dataSelection) < descriptors.size())
131 {
132
7/14
✓ Branch 1 taken 290664 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 290664 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 290664 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 290664 times.
✓ Branch 10 taken 290664 times.
✓ Branch 11 taken 290664 times.
✓ Branch 12 taken 290664 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
2034648 return fmt::format("{} {} ({})", factor == 1.0 ? "+" : (factor == -1.0 ? "-" : fmt::format("{:.2f}", factor)),
133
2/2
✓ Branch 3 taken 290664 times.
✓ Branch 4 taken 290664 times.
1743984 descriptors.at(std::get<size_t>(dataSelection)), node->inputPins.at(pinIndex).name);
134 }
135 if (std::holds_alternative<std::string>(dataSelection))
136 {
137 return fmt::format("{} {} ({})", factor == 1.0 ? "+" : (factor == -1.0 ? "-" : fmt::format("{:.2f}", factor)),
138 std::get<std::string>(dataSelection), node->inputPins.at(pinIndex).name);
139 }
140 return fmt::format("N/A ({})", node->inputPins.at(pinIndex).name);
141 }
142 };
143
144 /// List of terms making up the combination
145 std::vector<Term> terms{ Term{ .factor = +1.0, .pinIndex = 0 },
146 Term{ .factor = -1.0, .pinIndex = 1 } };
147
148 /// @brief Get a string description of the combination
149 /// @param node Combiner node pointer
150 145332 [[nodiscard]] std::string description(const Combiner* node) const
151 {
152 145332 std::string desc;
153
2/2
✓ Branch 5 taken 290664 times.
✓ Branch 6 taken 145332 times.
435996 for (const auto& term : terms)
154 {
155
1/2
✓ Branch 1 taken 290664 times.
✗ Branch 2 not taken.
290664 auto descriptors = node->getDataDescriptors(term.pinIndex);
156
1/2
✓ Branch 1 taken 290664 times.
✗ Branch 2 not taken.
290664 auto termDescription = term.description(node, descriptors);
157
158
2/2
✓ Branch 1 taken 145332 times.
✓ Branch 2 taken 145332 times.
290664 if (!desc.empty())
159 {
160
3/6
✓ Branch 1 taken 145332 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 145332 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 145332 times.
✗ Branch 7 not taken.
145332 if (termDescription.starts_with("+ ") || termDescription.starts_with("- "))
161 {
162
1/2
✓ Branch 1 taken 145332 times.
✗ Branch 2 not taken.
145332 desc += " ";
163 }
164 else
165 {
166 desc += " + ";
167 }
168 }
169
1/2
✓ Branch 1 taken 290664 times.
✗ Branch 2 not taken.
290664 desc += termDescription;
170 290664 }
171
172 145332 return desc;
173 }
174 };
175
176 /// Combinations to calculate
177 std::vector<Combination> _combinations{ Combination() };
178
179 /// Pin data
180 struct PinData
181 {
182 /// Time of the last observation processed
183 InsTime lastTime;
184 /// Min time between messages
185 double minTimeStep = std::numeric_limits<double>::infinity();
186 /// Extra data descriptors for dynamic data
187 std::vector<std::string> dynDataDescriptors;
188 };
189
190 /// Data per pin
191 std::vector<PinData> _pinData;
192
193 /// Reference pin
194 size_t _refPinIdx = 0;
195
196 /// Output missing combinations with NaN instead of removing
197 bool _outputMissingAsNaN = false;
198
199 bool _noOutputIfTimeDiffLarge = true; ///< Wether to not output a term if the interpolation time point is too far away
200 double _maxTimeDiffMultiplierFrequency = 2.1; ///< Multiply frequency with this to get maximum allowed time difference to interpolate to
201
202 bool _noOutputIfTimeStepLarge = true; ///< Wether to not output a term if the time step to interpolate in between is large
203 double _maxTimeStepMultiplierFrequency = 5.0; ///< Multiply frequency with this to get maximum allowed time step of incoming observations
204
205 /// Send request information
206 struct SendRequest
207 {
208 size_t combIndex = 0; ///< Combination Index
209 std::unordered_set<size_t> termIndices; ///< Term indices, which are already calculated
210 double result = 0.0; ///< Calculation result
211 std::vector<std::pair<std::string, std::shared_ptr<const NodeData>>> rawData; ///< List of the raw data of all terms contributing to the result
212 };
213
214 /// Chronological list of send request
215 std::map<InsTime, std::vector<SendRequest>> _sendRequests;
216
217 /// @brief Function to call to add a new pin
218 /// @param[in, out] node Pointer to this node
219 static void pinAddCallback(Node* node);
220 /// @brief Function to call to delete a pin
221 /// @param[in, out] node Pointer to this node
222 /// @param[in] pinIdx Input pin index to delete
223 static void pinDeleteCallback(Node* node, size_t pinIdx);
224
225 /// @brief Dynamic input pins
226 /// @attention This should always be the last variable in the header, because it accesses others through the function callbacks
227 gui::widgets::DynamicInputPins _dynamicInputPins{ 0, this, pinAddCallback, pinDeleteCallback };
228
229 /// @brief Initialize the node
230 bool initialize() override;
231
232 /// @brief Deinitialize the node
233 void deinitialize() override;
234
235 /// @brief Returns a list of descriptors for the pin
236 /// @param pinIndex Pin Index to look for the descriptor
237 [[nodiscard]] std::vector<std::string> getDataDescriptors(size_t pinIndex) const;
238
239 /// @brief Receive Data Function
240 /// @param[in] queue Queue with all the received data messages
241 /// @param[in] pinIdx Index of the pin the data is received on
242 void receiveData(InputPin::NodeDataQueue& queue, size_t pinIdx);
243
244 /// @brief Write info to a json object
245 /// @param[out] j Json output
246 /// @param[in] data Object to read info from
247 friend void to_json(json& j, const Combination& data);
248 /// @brief Read info from a json object
249 /// @param[in] j Json variable to read info from
250 /// @param[out] data Output object
251 friend void from_json(const json& j, Combination& data);
252 /// @brief Write info to a json object
253 /// @param[out] j Json output
254 /// @param[in] data Object to read info from
255 friend void to_json(json& j, const Combination::Term& data);
256 /// @brief Read info from a json object
257 /// @param[in] j Json variable to read info from
258 /// @param[out] data Output object
259 friend void from_json(const json& j, Combination::Term& data);
260 };
261
262 } // namespace NAV
263