INSTINCT Code Coverage Report


Directory: src/
File: Nodes/Plotting/Plot.hpp
Date: 2025-06-02 15:19:59
Exec Total Coverage
Lines: 7 22 31.8%
Functions: 6 25 24.0%
Branches: 8 34 23.5%

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 Plot.hpp
10 /// @brief Plots data into ImPlot Windows
11 /// @author T. Topp (topp@ins.uni-stuttgart.de)
12 /// @date 2021-01-09
13
14 #pragma once
15
16 #include <array>
17 #include <imgui.h>
18 #include <implot.h>
19
20 #include <map>
21 #include <memory>
22 #include <mutex>
23 #include <unordered_set>
24
25 #include "NodeData/NodeData.hpp"
26 #include "internal/Node/Node.hpp"
27 #include "internal/gui/widgets/DynamicInputPins.hpp"
28 #include "internal/gui/widgets/PositionInput.hpp"
29
30 #include "util/Container/ScrollingBuffer.hpp"
31 #include "util/Container/Vector.hpp"
32 #include "util/Plot/PlotEventTooltip.hpp"
33 #include "util/Plot/PlotItemStyle.hpp"
34 #include "util/Plot/PlotTooltip.hpp"
35 #include "util/Logger/CommonLog.hpp"
36
37 #include "NodeData/General/DynamicData.hpp"
38 #include "NodeData/GNSS/GnssCombination.hpp"
39 #include "NodeData/GNSS/GnssObs.hpp"
40 #include "NodeData/GNSS/RtklibPosObs.hpp"
41 #include "NodeData/GNSS/SppSolution.hpp"
42 #include "NodeData/IMU/ImuObs.hpp"
43 #include "NodeData/IMU/ImuObsSimulated.hpp"
44 #include "NodeData/IMU/ImuObsWDelta.hpp"
45 #include "NodeData/IMU/KvhObs.hpp"
46 #include "NodeData/IMU/VectorNavBinaryOutput.hpp"
47 #include "NodeData/State/InsGnssLCKFSolution.hpp"
48 #include "NodeData/State/PosVelAtt.hpp"
49 #include "NodeData/State/InsGnssTCKFSolution.hpp"
50 #include "NodeData/WiFi/WiFiPositioningSolution.hpp"
51 #include "NodeData/Baro/BaroPressObs.hpp"
52 #include "NodeData/Baro/BaroHgt.hpp"
53
54 namespace NAV
55 {
56 /// @brief Plot node which plots all kind of observations
57 class Plot : public Node, public CommonLog
58 {
59 public:
60 /// @brief Default constructor
61 Plot();
62 /// @brief Destructor
63 ~Plot() override;
64 /// @brief Copy constructor
65 Plot(const Plot&) = delete;
66 /// @brief Move constructor
67 Plot(Plot&&) = delete;
68 /// @brief Copy assignment operator
69 Plot& operator=(const Plot&) = delete;
70 /// @brief Move assignment operator
71 Plot& operator=(Plot&&) = 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 /// @brief Called when a new link was established
94 /// @param[in] startPin Pin where the link starts
95 /// @param[in] endPin Pin where the link ends
96 void afterCreateLink(OutputPin& startPin, InputPin& endPin) override;
97
98 /// @brief Information needed to plot the data on a certain pin
99 struct PinData
100 {
101 /// @brief Stores the actual data coming from a pin
102 struct PlotData
103 {
104 /// @brief Default constructor (needed to make serialization with json working)
105
1/2
✓ Branch 2 taken 4730 times.
✗ Branch 3 not taken.
4730 PlotData() = default;
106
107 /// @brief Constructor
108 /// @param[in] displayName Display name of the contained data
109 /// @param[in] size Size of the buffer
110 PlotData(std::string displayName, size_t size);
111
112 /// Display name of the contained data
113 std::string displayName;
114 /// Buffer for the data
115 ScrollingBuffer<double> buffer;
116 /// Flag if data was received, as the buffer contains std::nan("") otherwise
117 bool hasData = false;
118
119 /// When connecting a new link. All data is flagged for delete and only those who are also present in the new link are kept
120 bool markedForDelete = false;
121 /// Bool to show if dynamic data
122 bool isDynamic = false;
123 };
124
125 /// @brief Possible Pin types
126 enum class PinType : uint8_t
127 {
128 Flow, ///< NodeData Trigger
129 Bool, ///< Boolean
130 Int, ///< Integer Number
131 Float, ///< Floating Point Number
132 Matrix, ///< Matrix Object
133 };
134
135 /// @brief Constructor
136
1/2
✓ Branch 3 taken 254 times.
✗ Branch 4 not taken.
254 PinData() = default;
137 /// @brief Destructor
138 372 ~PinData() = default;
139 /// @brief Copy constructor
140 /// @param[in] other The other element to copy
141 PinData(const PinData& other);
142
143 /// @brief Move constructor
144 /// @param[in] other The other element to move
145 PinData(PinData&& other) noexcept;
146
147 /// @brief Copy assignment operator
148 /// @param[in] rhs The other element to copy
149 PinData& operator=(const PinData& rhs);
150 /// @brief Move assignment operator
151 /// @param[in] rhs The other element to move
152 PinData& operator=(PinData&& rhs) noexcept;
153
154 /// @brief Adds a plotData Element to the list
155 /// @param[in] dataIndex Index where to add the data to
156 /// @param[in] displayName Display name of the contained data
157 void addPlotDataItem(size_t dataIndex, const std::string& displayName);
158
159 /// Size of all buffers of the plotData elements
160 int size = 0;
161 /// Data Identifier of the connected pin
162 std::string dataIdentifier;
163 /// List with all the data
164 std::vector<PlotData> plotData;
165 /// List with the raw data received
166 ScrollingBuffer<std::shared_ptr<const NodeData>> rawNodeData;
167 /// Pin Type
168 PinType pinType = PinType::Flow;
169 /// Amount of points to skip for plotting
170 int stride = 1;
171 /// Mutex to lock the buffer so that the GUI thread and the calculation threads don't cause a data race
172 std::mutex mutex;
173 /// Dynamic data start index
174 int dynamicDataStartIndex = -1;
175 /// Events with relative time, absolute time, tooltip text and data Index (-1 means all)
176 std::vector<std::tuple<double, InsTime, std::string, int32_t>> events;
177 };
178
179 /// @brief Information specifying the look of each plot
180 struct PlotInfo
181 {
182 /// Info needed to draw a data set
183 struct PlotItem
184 {
185 /// @brief Default constructor (needed to make serialization with json working)
186
4/8
✓ Branch 14 taken 289 times.
✗ Branch 15 not taken.
✓ Branch 17 taken 289 times.
✗ Branch 18 not taken.
✓ Branch 20 taken 289 times.
✗ Branch 21 not taken.
✓ Branch 23 taken 289 times.
✗ Branch 24 not taken.
289 PlotItem() = default;
187
188 /// @brief Constructor
189 /// @param[in] pinIndex Index of the pin where the data came in
190 /// @param[in] dataIndex Index of the data on the pin
191 /// @param[in] displayName Display name of the data
192 PlotItem(size_t pinIndex, size_t dataIndex, std::string displayName)
193 : pinIndex(pinIndex), dataIndex(dataIndex), displayName(std::move(displayName))
194 {
195 style.colormapMaskDataCmpIdx = dataIndex;
196 style.markerColormapMaskDataCmpIdx = dataIndex;
197 }
198
199 /// @brief Constructor
200 /// @param[in] pinIndex Index of the pin where the data came in
201 /// @param[in] dataIndex Index of the data on the pin
202 /// @param[in] displayName Display name of the data
203 /// @param[in] axis Axis to plot the data on (Y1, Y2, Y3)
204 PlotItem(size_t pinIndex, size_t dataIndex, std::string displayName, ImAxis axis)
205 : PlotItem(pinIndex, dataIndex, std::move(displayName))
206 {
207 this->axis = axis; // NOLINT(cppcoreguidelines-prefer-member-initializer)
208 }
209
210 /// @brief Equal comparison operator (needed to search the vector with std::find)
211 /// @param[in] rhs Right-hand-side of the operator
212 /// @return True if the pin and data indices match
213 constexpr bool operator==(const PlotItem& rhs) const
214 {
215 return pinIndex == rhs.pinIndex && dataIndex == rhs.dataIndex && displayName == rhs.displayName;
216 }
217
218 size_t pinIndex{}; ///< Index of the pin where the data came in
219 size_t dataIndex{}; ///< Index of the data on the pin
220 std::string displayName; ///< Display name of the data (not changing and unique)
221 ImAxis axis{ ImAxis_Y1 }; ///< Axis to plot the data on (Y1, Y2, Y3)
222 PlotItemStyle style{}; ///< Defines how the data should be plotted
223 /// Buffer for the colormap mask
224 ScrollingBuffer<ImU32> colormapMaskColors = ScrollingBuffer<ImU32>(0);
225 /// Colormap version (to track updates of the colormap)
226 size_t colormapMaskVersion = 0;
227 /// Buffer for the colormap mask
228 ScrollingBuffer<ImU32> markerColormapMaskColors = ScrollingBuffer<ImU32>(0);
229 /// Colormap version (to track updates of the colormap)
230 size_t markerColormapMaskVersion = 0;
231
232 /// Buffer for event markers
233 ScrollingBuffer<double> eventMarker = ScrollingBuffer<double>(0);
234
235 /// Error bounds lower and upper data
236 std::array<ScrollingBuffer<double>, 2> errorBoundsData;
237
238 /// List of tooltips (x,y, tooltip)
239 std::vector<std::tuple<double, double, PlotEventTooltip>> eventTooltips;
240 };
241
242 /// @brief Default constructor
243 153 PlotInfo() = default;
244
245 /// @brief Constructor
246 /// @param[in] title Title of the ImPlot
247 /// @param[in] nInputPins Amount of inputPins
248 153 PlotInfo(const std::string& title, size_t nInputPins)
249
2/4
✓ Branch 3 taken 153 times.
✗ Branch 4 not taken.
✓ Branch 10 taken 153 times.
✗ Branch 11 not taken.
306 : title(title), headerText(title), selectedXdata(nInputPins, 1) {}
250
251 /// Size of the plot
252 ImVec2 size{ -1, 300 };
253
254 /// Title of the ImPlot
255 std::string title;
256 /// Title of the CollapsingHeader
257 std::string headerText;
258 /// Flag, whether to override the x axis label
259 bool overrideXAxisLabel = false;
260 /// X axis label
261 std::string xAxisLabel;
262 /// Y1 axis label
263 std::string y1AxisLabel;
264 /// Y2 axis label
265 std::string y2AxisLabel;
266 /// Y3 axis label
267 std::string y3AxisLabel;
268 /// Selected pin in the GUI for the Drag & Drop Data
269 size_t selectedPin = 0;
270 /// Flags which are passed to the plot
271 int plotFlags = 0;
272 /// Flags for the x-Axis
273 ImPlotAxisFlags xAxisFlags = ImPlotAxisFlags_AutoFit;
274 /// Flags for the y-Axes
275 ImPlotAxisFlags yAxisFlags = ImPlotAxisFlags_AutoFit;
276 /// Scale for the x-Axis
277 ImPlotScale xAxisScale = ImPlotScale_Linear;
278 /// Scale for the y-Axes
279 std::array<ImPlotScale, 3> yAxesScale = { ImPlotScale_Linear, ImPlotScale_Linear, ImPlotScale_Linear };
280 /// Line Flags for all items (each item can override the selection)
281 ImPlotLineFlags lineFlags = ImPlotLineFlags_NoClip | ImPlotLineFlags_SkipNaN;
282
283 /// @brief Key: PinIndex, Value: plotData to use for x-Axis
284 std::vector<size_t> selectedXdata;
285
286 /// List containing all elements which should be plotted
287 std::vector<PlotItem> plotItems;
288
289 /// Width of plot Data content
290 float leftPaneWidth = 180.0F;
291 /// Width of the plot
292 float rightPaneWidth = 400.0F;
293
294 /// Flag whether the whole plot is visible. If not, then it should be deleted
295 bool visible = true;
296
297 /// List of tooltip windows to show
298 std::vector<PlotTooltip> tooltips;
299 };
300
301 private:
302 /// @brief Initialize the node
303 bool initialize() override;
304
305 /// @brief Deinitialize the node
306 void deinitialize() override;
307
308 /// @brief Adds/Deletes Plots depending on the variable nPlots
309 void updateNumberOfPlots();
310
311 /// @brief Function to call to add a new pin
312 /// @param[in, out] node Pointer to this node
313 static void pinAddCallback(Node* node);
314 /// @brief Function to call to delete a pin
315 /// @param[in, out] node Pointer to this node
316 /// @param[in] pinIdx Input pin index to delete
317 static void pinDeleteCallback(Node* node, size_t pinIdx);
318
319 /// Index of the GPST data (unix timestamp)
320 size_t GPST_PLOT_IDX = 1;
321
322 /// Data storage for each pin
323 std::vector<PinData> _pinData;
324
325 /// Info for each plot window
326 std::vector<PlotInfo> _plots;
327
328 /// Amount of plot windows (should equal _plots.size())
329 size_t _nPlots = 0;
330 /// Possible data identifiers to connect
331 std::vector<std::string> _dataIdentifier = {
332 // General
333 DynamicData::type(),
334 // GNSS
335 GnssCombination::type(),
336 GnssObs::type(),
337 RtklibPosObs::type(),
338 SppSolution::type(),
339 // IMU
340 ImuObs::type(),
341 ImuObsSimulated::type(),
342 ImuObsWDelta::type(),
343 KvhObs::type(),
344 VectorNavBinaryOutput::type(),
345 // State
346 InsGnssLCKFSolution::type(),
347 Pos::type(),
348 PosVel::type(),
349 PosVelAtt::type(),
350 InsGnssTCKFSolution::type(),
351 // WiFi
352 WiFiPositioningSolution::type(),
353 // Barometer
354 BaroHgt::type(),
355 BaroPressObs::type(),
356
357 };
358
359 /// Index of the Collapsible Header currently being dragged
360 int _dragAndDropHeaderIndex = -1;
361
362 size_t _screenshotFrameCnt = 0; ///< Frame counter for taking screenshots (> 0 when screenshot in progress)
363 size_t _screenShotPlotIdx = 0; ///< Plot index a screenshot is taken of
364
365 /// Values to force the x axis range to and a set of plotIdx to force
366 std::pair<std::unordered_set<size_t>, ImPlotRange> _forceXaxisRange;
367
368 /// Start position for the calculation of relative North-South and East-West
369 std::optional<gui::widgets::PositionWithFrame> _originPosition;
370
371 /// Flag, whether to override the North/East startValues in the GUI
372 bool _overridePositionStartValues = false;
373
374 /// @brief Dynamic input pins
375 /// @attention This should always be the last variable in the header, because it accesses others through the function callbacks
376 gui::widgets::DynamicInputPins _dynamicInputPins{ 0, this, pinAddCallback, pinDeleteCallback, 1 };
377
378 /// @brief Adds a event to a certain point in time
379 /// @param[in] pinIndex Index of the input pin where the data was received
380 /// @param insTime Absolute time
381 /// @param text Text to display
382 /// @param dataIndex Data Index to add the event for (-1 means all)
383 void addEvent(size_t pinIndex, InsTime insTime, const std::string& text, int32_t dataIndex);
384
385 /// @brief Add Data to the buffer of the pin
386 /// @param[in] pinIndex Index of the input pin where the data was received
387 /// @param[in] dataIndex Index of the data to insert
388 /// @param[in] value The value to insert
389 void addData(size_t pinIndex, size_t dataIndex, double value);
390
391 /// @brief Add Data to the buffer of the pin
392 /// @param[in] pinIndex Index of the input pin where the data was received
393 /// @param[in] displayName Display name of the data
394 /// @param[in] value The value to insert
395 /// @return Data Index where data were inserted
396 size_t addData(size_t pinIndex, std::string displayName, double value);
397
398 /// @brief Calculate the local position offset from the plot origin
399 /// @param[in] lla_position [𝜙, λ, h] Latitude, Longitude, Altitude in [rad, rad, m]
400 /// @return Local positions in north/south and east/west directions in [m]
401 CommonLog::LocalPosition calcLocalPosition(const Eigen::Vector3d& lla_position);
402
403 /// @brief Plots the data on this port
404 /// @param[in] insTime Time the data was received
405 /// @param[in] pinIdx Index of the pin the data is received on
406 void plotBoolean(const InsTime& insTime, size_t pinIdx);
407
408 /// @brief Plots the data on this port
409 /// @param[in] insTime Time the data was received
410 /// @param[in] pinIdx Index of the pin the data is received on
411 void plotInteger(const InsTime& insTime, size_t pinIdx);
412
413 /// @brief Plots the data on this port
414 /// @param[in] insTime Time the data was received
415 /// @param[in] pinIdx Index of the pin the data is received on
416 void plotFloat(const InsTime& insTime, size_t pinIdx);
417
418 /// @brief Plots the data on this port
419 /// @param[in] insTime Time the data was received
420 /// @param[in] pinIdx Index of the pin the data is received on
421 void plotMatrix(const InsTime& insTime, size_t pinIdx);
422
423 /// @brief Plot the data on this port
424 /// @param[in] queue Queue with all the received data messages
425 /// @param[in] pinIdx Index of the pin the data is received on
426 void plotFlowData(InputPin::NodeDataQueue& queue, size_t pinIdx);
427
428 /// @brief Plot the data
429 /// @param[in] obs Observation to plot
430 /// @param[in] pinIndex Index of the input pin where the data was received
431 /// @param[in, out] plotIndex Index for inserting the data into the plot data vector
432 /// @param[in] startIndex Data descriptor start index
433 template<typename T>
434 void plotData(const std::shared_ptr<const T>& obs, size_t pinIndex, size_t& plotIndex, size_t startIndex = 0)
435 {
436 for (size_t i = startIndex; i < T::GetStaticDescriptorCount(); ++i)
437 {
438 addData(pinIndex, plotIndex++, obs->getValueAtOrNaN(i));
439 }
440 }
441 };
442
443 } // namespace NAV
444