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 | #include "udpRecv.hpp" | ||
10 | |||
11 | #include "Nodes/DataLink/UdpUtil.hpp" | ||
12 | #include "NodeData/GNSS/GnssObs.hpp" | ||
13 | #include "NodeData/State/PosVelAtt.hpp" | ||
14 | #include "Navigation/GNSS/Core/SatelliteIdentifier.hpp" | ||
15 | #include "Navigation/Time/InsTime.hpp" | ||
16 | |||
17 | #include "internal/NodeManager.hpp" | ||
18 | #include "util/Assert.h" | ||
19 | namespace nm = NAV::NodeManager; | ||
20 | #include "internal/FlowManager.hpp" | ||
21 | |||
22 | #include "util/Logger.hpp" | ||
23 | #include "internal/gui/widgets/imgui_ex.hpp" | ||
24 | #include "internal/gui/widgets/EnumCombo.hpp" | ||
25 | #include "internal/gui/NodeEditorApplication.hpp" | ||
26 | |||
27 | #include <cstdint> | ||
28 | #include <cstring> | ||
29 | #include <memory> | ||
30 | #include <boost/system/detail/error_code.hpp> | ||
31 | |||
32 | 114 | NAV::UdpRecv::UdpRecv() | |
33 |
4/8✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 114 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 114 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 114 times.
✗ Branch 12 not taken.
|
114 | : Node(typeStatic()), _socket(_io_context) |
34 | { | ||
35 | LOG_TRACE("{}: called", name); | ||
36 | |||
37 | 114 | _onlyRealTime = true; | |
38 | 114 | _hasConfig = true; | |
39 | 114 | _guiConfigDefaultWindowSize = { 261, 95 }; | |
40 | |||
41 |
4/8✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 114 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 114 times.
✓ Branch 10 taken 114 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
|
456 | nm::CreateOutputPin(this, "Data", Pin::Type::Flow, { NAV::PosVelAtt::type() }); |
42 | 228 | } | |
43 | |||
44 | 228 | NAV::UdpRecv::~UdpRecv() | |
45 | { | ||
46 | LOG_TRACE("{}: called", nameId()); | ||
47 | 228 | } | |
48 | |||
49 | 228 | std::string NAV::UdpRecv::typeStatic() | |
50 | { | ||
51 |
1/2✓ Branch 1 taken 228 times.
✗ Branch 2 not taken.
|
456 | return "UdpRecv"; |
52 | } | ||
53 | |||
54 | ✗ | std::string NAV::UdpRecv::type() const | |
55 | { | ||
56 | ✗ | return typeStatic(); | |
57 | } | ||
58 | |||
59 | 114 | std::string NAV::UdpRecv::category() | |
60 | { | ||
61 |
1/2✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
|
228 | return "Data Link"; |
62 | } | ||
63 | |||
64 | ✗ | void NAV::UdpRecv::guiConfig() | |
65 | { | ||
66 | ✗ | ImGui::SetNextItemWidth(150 * gui::NodeEditorApplication::windowFontRatio()); | |
67 | ✗ | if (ImGui::InputIntL(fmt::format("Port##{}", size_t(id)).c_str(), &_port, UdpUtil::PORT_LIMITS[0], UdpUtil::PORT_LIMITS[1])) | |
68 | { | ||
69 | ✗ | flow::ApplyChanges(); | |
70 | } | ||
71 | ✗ | ImGui::SetNextItemWidth(150 * gui::NodeEditorApplication::windowFontRatio()); | |
72 | ✗ | if (gui::widgets::EnumCombo(fmt::format("Output Type##{}", size_t(id)).c_str(), _outputType)) | |
73 | { | ||
74 | ✗ | LOG_DEBUG("{}: Output Type changed to {}", nameId(), to_string(_outputType)); | |
75 | ✗ | if (_outputType == UdpUtil::MessageType::PosVelAtt) | |
76 | { | ||
77 | ✗ | outputPins.at(OUTPUT_PORT_INDEX_NODE_DATA).dataIdentifier = { NAV::PosVelAtt::type() }; | |
78 | ✗ | outputPins.at(OUTPUT_PORT_INDEX_NODE_DATA).name = NAV::PosVelAtt::type(); | |
79 | } | ||
80 | ✗ | else if (_outputType == UdpUtil::MessageType::PosVel) | |
81 | { | ||
82 | ✗ | outputPins.at(OUTPUT_PORT_INDEX_NODE_DATA).dataIdentifier = { NAV::PosVel::type() }; | |
83 | ✗ | outputPins.at(OUTPUT_PORT_INDEX_NODE_DATA).name = NAV::PosVel::type(); | |
84 | } | ||
85 | ✗ | else if (_outputType == UdpUtil::MessageType::Pos) | |
86 | { | ||
87 | ✗ | outputPins.at(OUTPUT_PORT_INDEX_NODE_DATA).dataIdentifier = { NAV::Pos::type() }; | |
88 | ✗ | outputPins.at(OUTPUT_PORT_INDEX_NODE_DATA).name = NAV::Pos::type(); | |
89 | } | ||
90 | ✗ | else if (_outputType == UdpUtil::MessageType::GnssObs) | |
91 | { | ||
92 | ✗ | outputPins.at(OUTPUT_PORT_INDEX_NODE_DATA).dataIdentifier = { NAV::GnssObs::type() }; | |
93 | ✗ | outputPins.at(OUTPUT_PORT_INDEX_NODE_DATA).name = NAV::GnssObs::type(); | |
94 | } | ||
95 | |||
96 | ✗ | for (auto& link : outputPins.front().links) | |
97 | { | ||
98 | ✗ | if (auto* connectedPin = link.getConnectedPin()) | |
99 | { | ||
100 | ✗ | outputPins.front().recreateLink(*connectedPin); | |
101 | } | ||
102 | } | ||
103 | |||
104 | ✗ | flow::ApplyChanges(); | |
105 | } | ||
106 | ✗ | } | |
107 | |||
108 | ✗ | bool NAV::UdpRecv::resetNode() | |
109 | { | ||
110 | ✗ | return true; | |
111 | } | ||
112 | |||
113 | ✗ | json NAV::UdpRecv::save() const | |
114 | { | ||
115 | LOG_TRACE("{}: called", nameId()); | ||
116 | |||
117 | ✗ | json j; | |
118 | ✗ | j["port"] = _port; | |
119 | ✗ | j["outputType"] = _outputType; | |
120 | |||
121 | ✗ | return j; | |
122 | ✗ | } | |
123 | |||
124 | ✗ | void NAV::UdpRecv::restore(json const& j) | |
125 | { | ||
126 | LOG_TRACE("{}: called", nameId()); | ||
127 | ✗ | if (j.contains("port")) | |
128 | { | ||
129 | ✗ | j.at("port").get_to(_port); | |
130 | } | ||
131 | ✗ | if (j.contains("outputType")) | |
132 | { | ||
133 | ✗ | j.at("outputType").get_to(_outputType); | |
134 | |||
135 | ✗ | if (!outputPins.empty()) | |
136 | { | ||
137 | ✗ | if (_outputType == UdpUtil::MessageType::PosVelAtt) | |
138 | { | ||
139 | ✗ | outputPins.at(OUTPUT_PORT_INDEX_NODE_DATA).dataIdentifier = { NAV::PosVelAtt::type() }; | |
140 | ✗ | outputPins.at(OUTPUT_PORT_INDEX_NODE_DATA).name = NAV::PosVelAtt::type(); | |
141 | } | ||
142 | ✗ | else if (_outputType == UdpUtil::MessageType::PosVel) | |
143 | { | ||
144 | ✗ | outputPins.at(OUTPUT_PORT_INDEX_NODE_DATA).dataIdentifier = { NAV::PosVel::type() }; | |
145 | ✗ | outputPins.at(OUTPUT_PORT_INDEX_NODE_DATA).name = NAV::PosVel::type(); | |
146 | } | ||
147 | ✗ | else if (_outputType == UdpUtil::MessageType::Pos) | |
148 | { | ||
149 | ✗ | outputPins.at(OUTPUT_PORT_INDEX_NODE_DATA).dataIdentifier = { NAV::Pos::type() }; | |
150 | ✗ | outputPins.at(OUTPUT_PORT_INDEX_NODE_DATA).name = NAV::Pos::type(); | |
151 | } | ||
152 | ✗ | else if (_outputType == UdpUtil::MessageType::GnssObs) | |
153 | { | ||
154 | ✗ | outputPins.at(OUTPUT_PORT_INDEX_NODE_DATA).dataIdentifier = { NAV::GnssObs::type() }; | |
155 | ✗ | outputPins.at(OUTPUT_PORT_INDEX_NODE_DATA).name = NAV::GnssObs::type(); | |
156 | } | ||
157 | } | ||
158 | } | ||
159 | ✗ | } | |
160 | |||
161 | ✗ | bool NAV::UdpRecv::initialize() | |
162 | { | ||
163 | LOG_TRACE("{}: called", nameId()); | ||
164 | |||
165 | try | ||
166 | { | ||
167 | ✗ | _socket = boost::asio::ip::udp::socket(_io_context, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), static_cast<uint16_t>(_port))); | |
168 | } | ||
169 | ✗ | catch (const std::exception& /* e */) | |
170 | { | ||
171 | ✗ | LOG_ERROR("{}: Port {} is already in use. Choose a different port for this instance.", nameId(), _port); | |
172 | ✗ | return false; | |
173 | ✗ | } | |
174 | |||
175 | ✗ | _running = true; | |
176 | |||
177 | ✗ | asyncReceive(); | |
178 | |||
179 | ✗ | if (_isStartup) | |
180 | { | ||
181 | ✗ | _recvThread = std::thread([this]() { | |
182 | ✗ | _io_context.run(); | |
183 | ✗ | }); | |
184 | } | ||
185 | else | ||
186 | { | ||
187 | ✗ | _recvThread = std::thread([this]() { | |
188 | ✗ | _io_context.restart(); | |
189 | ✗ | _io_context.run(); | |
190 | ✗ | }); | |
191 | } | ||
192 | |||
193 | ✗ | _isStartup = false; | |
194 | |||
195 | ✗ | return true; | |
196 | } | ||
197 | |||
198 | ✗ | void NAV::UdpRecv::deinitialize() | |
199 | { | ||
200 | ✗ | _running = false; | |
201 | ✗ | _io_context.stop(); | |
202 | ✗ | _recvThread.join(); | |
203 | ✗ | _socket.close(); | |
204 | |||
205 | LOG_TRACE("{}: called", nameId()); | ||
206 | ✗ | } | |
207 | |||
208 | ✗ | void NAV::UdpRecv::asyncReceive() | |
209 | { | ||
210 | ✗ | _socket.async_receive_from( | |
211 | ✗ | boost::asio::buffer(_charArray, UdpUtil::MAXIMUM_BYTES), _sender_endpoint, | |
212 | ✗ | [this](boost::system::error_code errorRcvd, std::size_t bytesRcvd) { | |
213 | ✗ | if ((!errorRcvd) && (bytesRcvd > 0)) | |
214 | { | ||
215 | ✗ | UdpUtil::MessageType msgType{}; | |
216 | ✗ | std::memcpy(&msgType, _charArray.data(), UdpUtil::Size::MSGTYPE); | |
217 | LOG_DATA("{}: Received {} bytes (message type {})", nameId(), bytesRcvd, fmt::underlying(msgType)); | ||
218 | |||
219 | ✗ | int32_t gpsCycle{}; | |
220 | ✗ | int32_t gpsWeek{}; | |
221 | ✗ | double gpsTow{}; | |
222 | |||
223 | ✗ | std::memcpy(&gpsCycle, _charArray.data() + UdpUtil::Offset::GPSCYCLE, UdpUtil::Size::GPSCYCLE); | |
224 | ✗ | std::memcpy(&gpsWeek, _charArray.data() + UdpUtil::Offset::GPSWEEK, UdpUtil::Size::GPSWEEK); | |
225 | ✗ | std::memcpy(&gpsTow, _charArray.data() + UdpUtil::Offset::GPSTOW, UdpUtil::Size::GPSTOW); | |
226 | |||
227 | ✗ | if (msgType == UdpUtil::MessageType::PosVelAtt) | |
228 | { | ||
229 | ✗ | if (outputPins.at(OUTPUT_PORT_INDEX_NODE_DATA).name != NAV::PosVelAtt::type()) | |
230 | { | ||
231 | ✗ | LOG_ERROR("{}: Change output type to 'PosVelAtt'!", nameId()); | |
232 | ✗ | return; | |
233 | } | ||
234 | ✗ | auto obs = std::make_shared<PosVelAtt>(); | |
235 | |||
236 | ✗ | obs->insTime = InsTime(gpsCycle, gpsWeek, gpsTow); | |
237 | |||
238 | // Position in LLA coordinates | ||
239 | ✗ | Eigen::Vector3d posLLA{}; | |
240 | ✗ | std::memcpy(posLLA.data(), _charArray.data() + UdpUtil::Offset::POS, UdpUtil::Size::POS); | |
241 | |||
242 | // Velocity in local frame | ||
243 | ✗ | Eigen::Vector3d vel_n{}; | |
244 | ✗ | std::memcpy(vel_n.data(), _charArray.data() + UdpUtil::Offset::VEL, UdpUtil::Size::VEL); | |
245 | |||
246 | // Attitude | ||
247 | ✗ | Eigen::Quaterniond n_Quat_b{}; | |
248 | ✗ | std::memcpy(&n_Quat_b.x(), _charArray.data() + UdpUtil::Offset::QUAT, UdpUtil::Size::QUAT); | |
249 | ✗ | std::memcpy(&n_Quat_b.y(), _charArray.data() + UdpUtil::Offset::QUAT + UdpUtil::Size::QUAT, UdpUtil::Size::QUAT); | |
250 | ✗ | std::memcpy(&n_Quat_b.z(), _charArray.data() + UdpUtil::Offset::QUAT + 2 * UdpUtil::Size::QUAT, UdpUtil::Size::QUAT); | |
251 | ✗ | std::memcpy(&n_Quat_b.w(), _charArray.data() + UdpUtil::Offset::QUAT + 3 * UdpUtil::Size::QUAT, UdpUtil::Size::QUAT); | |
252 | |||
253 | ✗ | obs->setPosVelAtt_n(posLLA, vel_n, n_Quat_b); | |
254 | |||
255 | ✗ | this->invokeCallbacks(OUTPUT_PORT_INDEX_NODE_DATA, obs); | |
256 | ✗ | } | |
257 | ✗ | else if (msgType == UdpUtil::MessageType::PosVel) | |
258 | { | ||
259 | ✗ | if (outputPins.at(OUTPUT_PORT_INDEX_NODE_DATA).name != NAV::PosVel::type()) | |
260 | { | ||
261 | ✗ | LOG_ERROR("{}: Change output type to 'PosVel'!", nameId()); | |
262 | ✗ | return; | |
263 | } | ||
264 | ✗ | auto obs = std::make_shared<PosVel>(); | |
265 | |||
266 | ✗ | obs->insTime = InsTime(gpsCycle, gpsWeek, gpsTow); | |
267 | |||
268 | // Position in LLA coordinates | ||
269 | ✗ | Eigen::Vector3d posLLA{}; | |
270 | ✗ | std::memcpy(posLLA.data(), _charArray.data() + UdpUtil::Offset::POS, UdpUtil::Size::POS); | |
271 | |||
272 | // Velocity in local frame | ||
273 | ✗ | Eigen::Vector3d vel_n{}; | |
274 | ✗ | std::memcpy(vel_n.data(), _charArray.data() + UdpUtil::Offset::VEL, UdpUtil::Size::VEL); | |
275 | |||
276 | ✗ | obs->setPosVel_n(posLLA, vel_n); | |
277 | |||
278 | ✗ | this->invokeCallbacks(OUTPUT_PORT_INDEX_NODE_DATA, obs); | |
279 | ✗ | } | |
280 | ✗ | else if (msgType == UdpUtil::MessageType::Pos) | |
281 | { | ||
282 | ✗ | if (outputPins.at(OUTPUT_PORT_INDEX_NODE_DATA).name != NAV::Pos::type()) | |
283 | { | ||
284 | ✗ | LOG_ERROR("{}: Change output type to 'Pos'!", nameId()); | |
285 | ✗ | return; | |
286 | } | ||
287 | ✗ | auto obs = std::make_shared<Pos>(); | |
288 | |||
289 | ✗ | obs->insTime = InsTime(gpsCycle, gpsWeek, gpsTow); | |
290 | |||
291 | // Position in LLA coordinates | ||
292 | ✗ | Eigen::Vector3d posLLA{}; | |
293 | ✗ | std::memcpy(posLLA.data(), _charArray.data() + UdpUtil::Offset::POS, UdpUtil::Size::POS); | |
294 | |||
295 | ✗ | obs->setPosition_lla(posLLA); | |
296 | |||
297 | ✗ | this->invokeCallbacks(OUTPUT_PORT_INDEX_NODE_DATA, obs); | |
298 | ✗ | } | |
299 | ✗ | else if (msgType == UdpUtil::MessageType::GnssObs) | |
300 | { | ||
301 | ✗ | if (outputPins.at(OUTPUT_PORT_INDEX_NODE_DATA).name != NAV::GnssObs::type()) | |
302 | { | ||
303 | ✗ | LOG_ERROR("{}: Change output type to 'GnssObs'!", nameId()); | |
304 | ✗ | return; | |
305 | } | ||
306 | ✗ | auto gnssObs = std::make_shared<GnssObs>(); | |
307 | ✗ | gnssObs->insTime = InsTime(gpsCycle, gpsWeek, gpsTow); | |
308 | |||
309 | ✗ | size_t byteSizeGnssData{}; | |
310 | ✗ | std::memcpy(&byteSizeGnssData, _charArray.data() + UdpUtil::Offset::SIZE, UdpUtil::Size::SIZE); | |
311 | ✗ | size_t sizeGnssData = byteSizeGnssData / UdpUtil::Size::SINGLE_OBSERVATION_DATA; | |
312 | ✗ | INS_ASSERT_USER_ERROR(byteSizeGnssData % UdpUtil::Size::SINGLE_OBSERVATION_DATA == 0, | |
313 | "The UdpRecv node received a not dividable amount of bytes for the GnssObs data."); | ||
314 | LOG_DATA("{}: {} GNSS signals ({} bytes)", nameId(), sizeGnssData, byteSizeGnssData); | ||
315 | ✗ | gnssObs->data.resize(sizeGnssData, GnssObs::ObservationData(SatSigId())); | |
316 | ✗ | std::memcpy(gnssObs->data.data(), _charArray.data() + UdpUtil::Offset::GNSSDATA, byteSizeGnssData); | |
317 | |||
318 | ✗ | this->invokeCallbacks(OUTPUT_PORT_INDEX_NODE_DATA, gnssObs); | |
319 | ✗ | } | |
320 | else | ||
321 | { | ||
322 | ✗ | LOG_ERROR("{}: Data type not receivable, yet.", nameId()); | |
323 | } | ||
324 | } | ||
325 | else | ||
326 | { | ||
327 | ✗ | LOG_ERROR("{}: Error receiving the UDP network stream: {}, Received bytes: {}", nameId(), errorRcvd.what(), bytesRcvd); | |
328 | } | ||
329 | |||
330 | ✗ | if (_running) | |
331 | { | ||
332 | ✗ | asyncReceive(); | |
333 | } | ||
334 | }); | ||
335 | ✗ | } | |
336 | |||
337 | ✗ | const char* NAV::to_string(NAV::UdpUtil::MessageType value) | |
338 | { | ||
339 | ✗ | switch (value) | |
340 | { | ||
341 | ✗ | case NAV::UdpUtil::MessageType::PosVelAtt: | |
342 | ✗ | return "PosVelAtt"; | |
343 | ✗ | case NAV::UdpUtil::MessageType::PosVel: | |
344 | ✗ | return "PosVel"; | |
345 | ✗ | case NAV::UdpUtil::MessageType::Pos: | |
346 | ✗ | return "Pos"; | |
347 | ✗ | case NAV::UdpUtil::MessageType::GnssObs: | |
348 | ✗ | return "GnssObs"; | |
349 | ✗ | case NAV::UdpUtil::MessageType::COUNT: | |
350 | ✗ | return ""; | |
351 | } | ||
352 | ✗ | return ""; | |
353 | } | ||
354 |