INSTINCT Code Coverage Report


Directory: src/
File: Nodes/Converter/GNSS/UbloxGnssOrbitCollector.cpp
Date: 2025-07-19 10:51:51
Exec Total Coverage
Lines: 470 599 78.5%
Functions: 16 25 64.0%
Branches: 319 844 37.8%

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 "UbloxGnssOrbitCollector.hpp"
10
11 #include <algorithm>
12 #include <chrono>
13 #include <cstddef>
14 #include <imgui.h>
15 #include <imgui_internal.h>
16
17 #include "util/Logger.hpp"
18 #include "util/Container/STL.hpp"
19
20 #include "internal/NodeManager.hpp"
21 #include <fmt/format.h>
22 namespace nm = NAV::NodeManager;
23 #include "internal/FlowManager.hpp"
24
25 #include "Navigation/GNSS/Satellite/Ephemeris/GPSEphemeris.hpp"
26 #include "Navigation/GNSS/Satellite/Ephemeris/GalileoEphemeris.hpp"
27 #include "Navigation/GNSS/Satellite/Ephemeris/GLONASSEphemeris.hpp"
28 #include "Navigation/GNSS/Satellite/Ephemeris/BDSEphemeris.hpp"
29 #include "Navigation/GNSS/Functions.hpp"
30 #include "Navigation/Transformations/Units.hpp"
31
32 115 NAV::UbloxGnssOrbitCollector::UbloxGnssOrbitCollector()
33
2/4
✓ Branch 1 taken 115 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 115 times.
✗ Branch 5 not taken.
115 : Node(typeStatic())
34 {
35 LOG_TRACE("{}: called", name);
36 115 _hasConfig = true;
37
38
4/8
✓ Branch 1 taken 115 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 115 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 115 times.
✓ Branch 9 taken 115 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
345 nm::CreateInputPin(this, "UbloxObs", Pin::Type::Flow, { NAV::UbloxObs::type() }, &UbloxGnssOrbitCollector::receiveObs);
39
40
5/10
✓ Branch 2 taken 115 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 115 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 115 times.
✗ Branch 11 not taken.
✓ Branch 14 taken 115 times.
✓ Branch 15 taken 115 times.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
460 nm::CreateOutputPin(this, GnssNavInfo::type().c_str(), Pin::Type::Object, { GnssNavInfo::type() }, &_gnssNavInfo);
41 345 }
42
43 232 NAV::UbloxGnssOrbitCollector::~UbloxGnssOrbitCollector()
44 {
45 LOG_TRACE("{}: called", nameId());
46 232 }
47
48 229 std::string NAV::UbloxGnssOrbitCollector::typeStatic()
49 {
50
1/2
✓ Branch 1 taken 229 times.
✗ Branch 2 not taken.
458 return "UbloxGnssOrbitCollector";
51 }
52
53 std::string NAV::UbloxGnssOrbitCollector::type() const
54 {
55 return typeStatic();
56 }
57
58 114 std::string NAV::UbloxGnssOrbitCollector::category()
59 {
60
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
228 return "Converter";
61 }
62
63 void NAV::UbloxGnssOrbitCollector::guiConfig()
64 {
65 bool isDisabled = ImGui::GetCurrentContext()->CurrentItemFlags & ImGuiItemFlags_Disabled;
66
67 if (isDisabled) { ImGui::EndDisabled(); }
68 if (ImGui::BeginTable(fmt::format("{} UbloxGnssOrbitCollector", size_t(id)).c_str(), 4,
69 ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX))
70 {
71 ImGui::TableSetupColumn("");
72 ImGui::TableSetupColumn("Received");
73 ImGui::TableSetupColumn("Iono");
74 ImGui::TableSetupColumn("Building");
75 ImGui::TableHeadersRow();
76
77 for (uint64_t sys = 0xFF; sys < static_cast<uint64_t>(0xFF) << (7 * 8); sys = sys << 8UL)
78 {
79 ImGui::TableNextRow();
80 ImGui::TableNextColumn();
81
82 ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ImGui::GetColorU32(ImGuiCol_TableHeaderBg));
83 SatelliteSystem satSys{ SatelliteSystem_(sys) };
84 ImGui::TextUnformatted(fmt::format("{}", satSys).c_str());
85
86 ImGui::TableNextColumn();
87 size_t i = 0;
88 constexpr size_t ITEMS_PER_ROW = 4;
89 for (const auto& [satId, sat] : _gnssNavInfo.satellites())
90 {
91 if (satId.satSys != satSys) { continue; }
92 if (i % ITEMS_PER_ROW != 0)
93 {
94 ImGui::SameLine();
95 ImGui::SetCursorPosX(ImGui::GetCursorPosX() - ImGui::GetStyle().ItemSpacing.x);
96 ImGui::TextUnformatted(", ");
97 ImGui::SameLine();
98 }
99 ImGui::TextUnformatted(fmt::format("{}", satId).c_str());
100 if (ImGui::IsItemHovered())
101 {
102 ImGui::BeginTooltip();
103 for (const auto& satNavData : sat.getNavigationData())
104 {
105 ImGui::TextUnformatted(fmt::format("{}", satNavData->refTime.toYMDHMS(GPST)).c_str());
106 }
107 ImGui::EndTooltip();
108 }
109 i++;
110 }
111
112 ImGui::TableNextColumn();
113 std::string iono;
114 for (const auto& correction : _gnssNavInfo.ionosphericCorrections.data())
115 {
116 if (correction.satSys != satSys) { continue; }
117 if (!iono.empty()) { iono += ", "; }
118 iono += correction.alphaBeta == IonosphericCorrections::Alpha ? "α" : "β";
119 }
120 ImGui::TextUnformatted(iono.c_str());
121
122 ImGui::TableNextColumn();
123 for (const auto& builder : _ephemerisBuilder)
124 {
125 if (builder.satId.satSys != satSys
126 || std::ranges::any_of(_gnssNavInfo.satellites(), [&](const auto& sat) {
127 if (builder.satId != sat.first) { return false; }
128 return std::ranges::any_of(sat.second.getNavigationData(), [&](const std::shared_ptr<NAV::SatNavData>& satNavData) {
129 return satNavData->refTime == builder.navData->refTime;
130 });
131 }))
132 {
133 continue;
134 }
135
136 ImGui::TextUnformatted(fmt::format("{} ({}) frames: {}", builder.satId, builder.navData->refTime.toYMDHMS(GPST), builder.subframes).c_str());
137 }
138 }
139
140 ImGui::EndTable();
141 }
142 if (isDisabled) { ImGui::BeginDisabled(); }
143 }
144
145 3 bool NAV::UbloxGnssOrbitCollector::initialize()
146 {
147 LOG_TRACE("{}: called", nameId());
148
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 if (!_postProcessingLock.has_value())
149 {
150
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 auto guard = requestOutputValueLock(OUTPUT_PORT_INDEX_GNSS_NAV_INFO);
151 3 _gnssNavInfo.reset();
152 3 }
153 else
154 {
155 _gnssNavInfo.reset();
156 }
157 3 _ephemerisBuilder.clear();
158 3 _lastAccessedBuilder.clear();
159 3 _warningsNotImplemented.clear();
160
161 3 if (inputPins.at(INPUT_PORT_INDEX_UBLOX_OBS).isPinLinked()
162
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
3 && getMode() == Mode::POST_PROCESSING
163
4/6
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 2 times.
6 && !_postProcessingLock.has_value())
164 {
165 LOG_TRACE("{}: Setting post-processing lock", nameId());
166 1 _postProcessingLock.emplace(outputPins.at(OUTPUT_PORT_INDEX_GNSS_NAV_INFO).dataAccessMutex);
167 }
168
169 3 return true;
170 }
171
172 1 void NAV::UbloxGnssOrbitCollector::onDeleteLink([[maybe_unused]] OutputPin& startPin, [[maybe_unused]] InputPin& endPin)
173 {
174 LOG_TRACE("{}: called for {} ==> {}", nameId(), size_t(startPin.id), size_t(endPin.id));
175
176
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (_postProcessingLock.has_value())
177 {
178 1 _postProcessingLock.reset();
179 }
180 1 }
181
182 718 NAV::UbloxGnssOrbitCollector::EphemerisBuilder& NAV::UbloxGnssOrbitCollector::getEphemerisBuilder(const SatId& satId, const InsTime& insTime, size_t IOD)
183 {
184 LOG_DATA("{}: Searching for [{}] at [{}]", nameId(), satId, insTime.toYMDHMS(GPST));
185
2/2
✓ Branch 0 taken 227 times.
✓ Branch 1 taken 491 times.
718 if (IOD != 0)
186 {
187
1/2
✓ Branch 1 taken 227 times.
✗ Branch 2 not taken.
227 _lastAccessedBuilder[satId] = IOD;
188 }
189
190
1/2
✓ Branch 1 taken 718 times.
✗ Branch 2 not taken.
718 auto iter = std::ranges::find_if(_ephemerisBuilder, [&](const auto& builder) {
191
4/4
✓ Branch 1 taken 875 times.
✓ Branch 2 taken 8649 times.
✓ Branch 5 taken 671 times.
✓ Branch 6 taken 204 times.
9524 return builder.satId == satId && builder.navData->refTime == insTime;
192 });
193
2/2
✓ Branch 2 taken 47 times.
✓ Branch 3 taken 671 times.
718 if (iter == _ephemerisBuilder.end())
194 {
195 LOG_DATA("{}: Constructing new builder", nameId());
196
197 47 std::shared_ptr<SatNavData> satNavData = nullptr;
198
2/9
✓ Branch 1 taken 19 times.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
47 switch (SatelliteSystem_(satId.satSys))
199 {
200 19 case GPS:
201
1/2
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
19 satNavData = std::make_shared<GPSEphemeris>(insTime);
202 19 break;
203 28 case GAL:
204
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 satNavData = std::make_shared<GalileoEphemeris>(insTime);
205 28 break;
206 case GLO:
207 // satNavData = std::make_shared<GLONASSEphemeris>(insTime);
208 LOG_CRITICAL("{}: GLONASS not implemented yet.", nameId()); // TODO: Not yet supported
209 break;
210 case BDS:
211 // satNavData = std::make_shared<BDSEphemeris>(insTime);
212 LOG_CRITICAL("{}: BeiDou not implemented yet.", nameId()); // TODO: Not yet supported
213 break;
214 case QZSS:
215 LOG_CRITICAL("{}: QZSS not implemented yet.", nameId()); // TODO: Not yet supported
216 break;
217 case IRNSS:
218 LOG_CRITICAL("{}: IRNSS not implemented yet.", nameId()); // TODO: Not yet supported
219 break;
220 case SBAS:
221 LOG_CRITICAL("{}: SBAS not implemented yet.", nameId()); // TODO: Not yet supported
222 break;
223 case SatSys_None:
224 LOG_CRITICAL("{}: Satellite system cannot be none.", nameId());
225 break;
226 }
227
1/2
✓ Branch 1 taken 47 times.
✗ Branch 2 not taken.
47 return _ephemerisBuilder.emplace_back(satId, satNavData);
228 47 }
229
230 LOG_DATA("{}: Found builder", nameId());
231 671 return *iter;
232 }
233
234 std::optional<std::reference_wrapper<NAV::UbloxGnssOrbitCollector::EphemerisBuilder>>
235 1184 NAV::UbloxGnssOrbitCollector::getEphemerisBuilder(const SatId& satId, size_t IOD)
236 {
237 LOG_DATA("{}: Searching for [{}] at Issue of Data [{}]", nameId(), satId, IOD);
238
1/2
✓ Branch 1 taken 1184 times.
✗ Branch 2 not taken.
1184 _lastAccessedBuilder[satId] = IOD;
239
240
1/2
✓ Branch 1 taken 1184 times.
✗ Branch 2 not taken.
1184 auto iter = std::ranges::find_if(_ephemerisBuilder, [&](const auto& builder) {
241
2/2
✓ Branch 1 taken 1828 times.
✓ Branch 2 taken 20327 times.
22155 if (builder.satId == satId)
242 {
243
2/2
✓ Branch 1 taken 262 times.
✓ Branch 2 taken 1566 times.
1828 if (builder.navData->type == SatNavData::GPSEphemeris)
244 {
245 262 auto ephemeris = std::dynamic_pointer_cast<GPSEphemeris>(builder.navData);
246
3/4
✓ Branch 1 taken 262 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 246 times.
✓ Branch 5 taken 16 times.
262 return ephemeris && ephemeris->IODE == IOD;
247 262 }
248
1/2
✓ Branch 1 taken 1566 times.
✗ Branch 2 not taken.
1566 if (builder.navData->type == SatNavData::GalileoEphemeris)
249 {
250 1566 auto ephemeris = std::dynamic_pointer_cast<GalileoEphemeris>(builder.navData);
251
3/4
✓ Branch 1 taken 1566 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 876 times.
✓ Branch 5 taken 690 times.
1566 return ephemeris && ephemeris->IODnav == IOD;
252 1566 }
253 }
254 20327 return false;
255 });
256
2/2
✓ Branch 2 taken 1122 times.
✓ Branch 3 taken 62 times.
1184 if (iter != _ephemerisBuilder.end())
257 {
258 LOG_DATA("{}: Found builder", nameId());
259 1122 return *iter;
260 }
261 LOG_DATA("{}: Could not find builder. Ignoring subframe.", nameId());
262 62 return std::nullopt;
263 }
264
265 std::optional<std::reference_wrapper<NAV::UbloxGnssOrbitCollector::EphemerisBuilder>>
266 238 NAV::UbloxGnssOrbitCollector::getLastEphemerisBuilder(const SatId& satId)
267 {
268 LOG_DATA("{}: Searching the last builder for [{}]", nameId(), satId);
269
1/2
✓ Branch 1 taken 238 times.
✗ Branch 2 not taken.
238 if (_lastAccessedBuilder.contains(satId))
270 {
271 238 return getEphemerisBuilder(satId, _lastAccessedBuilder.at(satId));
272 }
273 LOG_DATA("{}: Could not find last accessed builder. Ignoring subframe.", nameId());
274 return std::nullopt;
275 }
276
277 8207 void NAV::UbloxGnssOrbitCollector::receiveObs(NAV::InputPin::NodeDataQueue& queue, size_t /* pinIdx */)
278 {
279
1/2
✓ Branch 1 taken 8207 times.
✗ Branch 2 not taken.
8207 [[maybe_unused]] auto ubloxObs = std::static_pointer_cast<const UbloxObs>(queue.extract_front());
280 LOG_DATA("{}: [{}] Received UbloxObs {}-{}", nameId(), ubloxObs->insTime.toYMDHMS(GPST),
281 vendor::ublox::getStringFromMsgClass(ubloxObs->msgClass),
282 vendor::ublox::getStringFromMsgId(ubloxObs->msgClass, ubloxObs->msgId));
283
284
1/2
✓ Branch 1 taken 8207 times.
✗ Branch 2 not taken.
8207 if (ubloxObs->msgClass == ubx::UBX_CLASS_RXM)
285 {
286
2/2
✓ Branch 1 taken 6607 times.
✓ Branch 2 taken 1600 times.
8207 if (static_cast<ubx::UbxRxmMessages>(ubloxObs->msgId) == ubx::UbxRxmMessages::UBX_RXM_SFRBX)
287 {
288
1/2
✓ Branch 2 taken 6607 times.
✗ Branch 3 not taken.
6607 const auto& sfrbx = std::get<ubx::UbxRxmSfrbx>(ubloxObs->data);
289
290
1/2
✓ Branch 1 taken 6607 times.
✗ Branch 2 not taken.
6607 SatelliteSystem satSys = ubx::getSatSys(sfrbx.gnssId);
291 6607 SatId satId(satSys, sfrbx.svId);
292 LOG_DATA("{}: [{}][{}] Converting message", nameId(), ubloxObs->insTime.toYMDHMS(GPST), satId);
293
294
2/9
✓ Branch 1 taken 2396 times.
✓ Branch 2 taken 4211 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
6607 switch (SatelliteSystem_(satSys))
295 {
296 2396 case GPS:
297
1/2
✓ Branch 2 taken 2396 times.
✗ Branch 3 not taken.
2396 decryptGPS(satId, sfrbx, ubloxObs->insTime);
298 2396 break;
299 4211 case GAL:
300
1/2
✓ Branch 2 taken 4211 times.
✗ Branch 3 not taken.
4211 decryptGalileo(satId, sfrbx, ubloxObs->insTime);
301 4211 break;
302 case GLO:
303 decryptGLONASS(satId, sfrbx, ubloxObs->insTime);
304 break;
305 case BDS:
306 decryptBeiDou(satId, sfrbx, ubloxObs->insTime);
307 break;
308 case QZSS:
309 decryptQZSS(satId, sfrbx, ubloxObs->insTime);
310 break;
311 case IRNSS:
312 decryptIRNSS(satId, sfrbx, ubloxObs->insTime);
313 break;
314 case SBAS:
315 decryptSBAS(satId, sfrbx, ubloxObs->insTime);
316 break;
317 case SatSys_None:
318 LOG_CRITICAL("{}: Satellite system cannot be none.", nameId());
319 break;
320 }
321 }
322 }
323
324 8207 if (_postProcessingLock.has_value()
325
3/6
✓ Branch 1 taken 8207 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8207 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 8207 times.
✗ Branch 7 not taken.
8207 && inputPins.at(INPUT_PORT_INDEX_UBLOX_OBS).isPinLinked()
326
5/10
✓ Branch 0 taken 8207 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 8207 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 8207 times.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 8207 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 8207 times.
16414 && inputPins.at(INPUT_PORT_INDEX_UBLOX_OBS).link.getConnectedPin()->noMoreDataAvailable)
327 {
328 LOG_TRACE("{}: Post-processing lock cleared as all data read.", nameId());
329 _postProcessingLock.reset();
330 }
331 8207 }
332
333 2396 void NAV::UbloxGnssOrbitCollector::decryptGPS(const SatId& satId, const ubx::UbxRxmSfrbx& sfrbx, const InsTime& insTime)
334 {
335 // u-blox 8 / u-blox M8: Receiver description - Including protocol specification, ch. 10.2, p. 30f
336 // > For GPS L1C/A signals, there is a fairly straightforward mapping between the reported subframe
337 // > and the structure of subframe and words described in the GPS ICD. Each subframe comprises ten
338 // > data words, which are reported in the same order they are received.
339 // >
340 // > MSB LSB
341 // > 1 to 10: Pad (2 bits) Data (24 bits) Parity (6 bits)
342 // >
343 // > Note that as the GPS data words only comprise 30 bits, the 2 most significant bits in each word
344 // > reported by UBX-RXM-SFRBX are padding and should be ignored.
345
346
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2396 times.
2396 if (sfrbx.numWords != 10)
347 {
348 LOG_ERROR("{}: [{}] Received {} instead of 10 words", nameId(), satId, sfrbx.numWords);
349 1169 return;
350 }
351
352 2396 size_t w = 0; // Word counter
353
354 // Telemetry Word
355 // 30 bits long, occurs every 6 seconds ┌ Integrity Status Flag
356 // Preamble │ ┌ Reserved
357 // 1 0 0 0 1 0 1 1 MSB TLM Message LSB │ │ Parity
358 // ┌──────┴──────┐ ┌───────────────┴──────────────────────┐ │ │ ┌──────┴────────┐
359 // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
360 LOG_DATA("{}: [{}] tlm: {} {}", nameId(), satId, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
361 2396 constexpr uint8_t TLM_PREAMBLE = 0b10001011;
362
3/4
✓ Branch 1 taken 2396 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1169 times.
✓ Branch 4 taken 1227 times.
2396 if (static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 22) & 0b11111111) != TLM_PREAMBLE) // Preamble found after the 2 padding bits
363 {
364 LOG_DATA("{}: [{}] Wrong telemetry word preamble. Ignoring SFRBX message.", nameId(), satId);
365 1169 return;
366 }
367
368 // Handover Word (HOW)
369 // 30 bits long, occurs every 6 seconds anti-spoof (A-S) flag
370 // alert flag │
371 // MSB TOW-Count Message LSB │ │ Subframe ID Parity
372 // ┌──────────────────┴────────────────────┐ │ │ ┌───┴──┐ ┌──────┴────────┐
373 // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
374 1227 w++;
375 LOG_DATA("{}: [{}] how: {} {}", nameId(), satId, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
376
1/2
✓ Branch 1 taken 1227 times.
✗ Branch 2 not taken.
1227 auto subFrameId = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 8) & 0b111);
377 LOG_DATA("{}: [{}] subFrameId: {}", nameId(), satId, subFrameId);
378
379
2/4
✓ Branch 0 taken 1227 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1227 times.
1227 if (subFrameId == 0 || subFrameId > 5)
380 {
381 LOG_ERROR("{}: [{}] The subFrameId has to be in the range [1..=5] but it is {}", nameId(), satId, subFrameId);
382 return;
383 }
384
385
1/2
✓ Branch 2 taken 1227 times.
✗ Branch 3 not taken.
1227 auto gpsWeekToW = insTime.toGPSweekTow(GPST);
386
387 736 auto finishSubFrame = [&]<size_t N>(const std::shared_ptr<SatNavData>& ephemeris, size_t subFrameId, std::bitset<N>& subframesFound) {
388
2/4
✓ Branch 0 taken 736 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 736 times.
✗ Branch 3 not taken.
736 if (1 <= subFrameId && subFrameId <= 3)
389 {
390 736 subframesFound.set(subFrameId - 1);
391
392 LOG_DATA("{}: [{}] subframesFound: {}", nameId(), satId, subframesFound);
393
2/2
✓ Branch 1 taken 698 times.
✓ Branch 2 taken 38 times.
736 if (subframesFound.count() == 3)
394 {
395 LOG_DATA("{}: [{}] [{}] All subframes found. Updating gnnsNavInfo", nameId(), satId, ephemeris->refTime.toYMDHMS(GPST));
396
3/4
✓ Branch 2 taken 698 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 11 times.
✓ Branch 7 taken 687 times.
698 if (!_gnssNavInfo.searchNavigationData(satId, ephemeris->refTime))
397 {
398
4/8
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 11 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 11 times.
✗ Branch 10 not taken.
✓ Branch 13 taken 11 times.
✗ Branch 14 not taken.
11 LOG_DEBUG("{}: Adding new ephemeris data [{}] [{}]", nameId(), satId, ephemeris->refTime.toYMDHMS(GPST));
399 }
400
1/2
✓ Branch 1 taken 698 times.
✗ Branch 2 not taken.
698 std::unique_lock guard(outputPins.at(OUTPUT_PORT_INDEX_GNSS_NAV_INFO).dataAccessMutex, std::defer_lock);
401
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 698 times.
698 if (!_postProcessingLock.has_value())
402 {
403 guard.lock();
404 }
405 698 _gnssNavInfo.satelliteSystems |= satId.satSys;
406
1/2
✓ Branch 1 taken 698 times.
✗ Branch 2 not taken.
698 _gnssNavInfo.addSatelliteNavData(satId, ephemeris);
407 698 }
408 }
409 736 };
410
411
2/2
✓ Branch 0 taken 245 times.
✓ Branch 1 taken 982 times.
1227 if (subFrameId == 1) // Third through tenth words of subframe 1 shall each contain 6 parity bits as their LSBs
412 {
413 // IS-GPS-200M: Figure 20-1. Data Format (sheet 1 of 11), p. 79
414 // IS-GPS-200M: 20.3.3.3 Subframe 1, p. 91ff
415 // IS-GPS-200M: Table 20-I. Subframe 1 Parameters, p. 96
416
417 // T_GD, af2, af1, af0 shall be two's complement, with the sign bit (+ or -) occupying the MSB
418
419 245 w++;
420 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 3 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
421
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 [[maybe_unused]] auto WN = static_cast<uint16_t>((sfrbx.dwrd.at(w) >> 20) & 0b1111111111); // 10 BITS - Transmission Week Number
422
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 auto L2ChannelCodes = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 18) & 0b11); // 2 BITS
423
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 auto uraIndex = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 14) & 0b1111); // 4 BITS
424
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 auto svHealth = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 8) & 0b111111); // 6 BITS
425
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 auto IODC = static_cast<uint16_t>(((sfrbx.dwrd.at(w) >> 6) & 0b11) << 8); // 2 MSBs - 10 BITS TOTAL
426 LOG_DATA("{}: [{}] WN {}, L2ChannelCodes {}, uraIndex {}, svHealth {}, IODC {}", nameId(), satId, WN, L2ChannelCodes, uraIndex, svHealth, std::bitset<10>(IODC));
427
428 245 w++;
429 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 4 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
430
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 auto L2DataFlagPCode = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 29) & 0b1); // 1 BIT
431 LOG_DATA("{}: [{}] L2DataFlagPCode {}", nameId(), satId, L2DataFlagPCode);
432
433 245 w++;
434 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 5 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
435
436 245 w++;
437 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 6 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
438
439 245 w++;
440 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 7 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
441
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 auto T_GD = static_cast<int8_t>((sfrbx.dwrd.at(w) >> 6) & 0b11111111);
442 LOG_DATA("{}: [{}] T_GD {}", nameId(), satId, T_GD);
443
444 245 w++;
445 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 8 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
446
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 IODC |= static_cast<uint16_t>((sfrbx.dwrd.at(w) >> 22) & 0b11111111); // 8 LSBs - 10 BITS TOTAL
447
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 auto toc = static_cast<uint16_t>((sfrbx.dwrd.at(w) >> 6) & 0b1111111111111111); // 16 LSBs
448 LOG_DATA("{}: [{}] IODC {} ({}), toc {}", nameId(), satId, IODC, std::bitset<10>(IODC), toc);
449
450 245 w++;
451 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 9 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
452
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 auto af2 = static_cast<int8_t>((sfrbx.dwrd.at(w) >> 22) & 0b11111111); // 8 BITS
453
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 auto af1 = static_cast<int16_t>((sfrbx.dwrd.at(w) >> 6) & 0b1111111111111111); // 16 BITS
454 LOG_DATA("{}: [{}] af2 {}, af1 {}", nameId(), satId, af2, af1);
455
456 245 w++;
457 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 10 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
458
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 auto af0 = math::interpretAs<int32_t, 22>(sfrbx.dwrd.at(w) >> 8); // 22 BITS
459 LOG_DATA("{}: [{}] af0 {}", nameId(), satId, af0);
460
461
1/2
✓ Branch 3 taken 245 times.
✗ Branch 4 not taken.
245 InsTime insTimeToc(gpsWeekToW.gpsCycle, gpsWeekToW.gpsWeek, toc * std::pow(2, 4), GPST);
462
463
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 auto& ephemerisBuilder = getEphemerisBuilder(satId, insTimeToc);
464 245 auto ephemeris = std::dynamic_pointer_cast<GPSEphemeris>(ephemerisBuilder.navData);
465 245 ephemeris->toc = insTimeToc;
466
467 245 ephemeris->L2ChannelCodes = L2ChannelCodes;
468
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 ephemeris->svAccuracy = gpsUraIdx2Val(uraIndex);
469 245 ephemeris->svHealth = svHealth;
470 245 ephemeris->IODC = IODC;
471 245 ephemeris->L2DataFlagPCode = L2DataFlagPCode;
472 245 ephemeris->T_GD = T_GD * std::pow(2, -31);
473 245 ephemeris->refTime = ephemeris->toc;
474 245 ephemeris->a = {
475 245 af0 * std::pow(2, -31),
476 245 af1 * std::pow(2, -43),
477 245 af2 * std::pow(2, -55),
478 };
479 LOG_DATA("{}: [{}] svAccuracy [{} m], svHealth [{}], IODC [{}], tgd [{:.3e} s]", nameId(), satId,
480 ephemeris->svAccuracy, ephemeris->svHealth, ephemeris->IODC, ephemeris->T_GD);
481 LOG_DATA("{}: [{}] toc [{}], a0 [{:.3e} s], a1 [{:.3e} s/s], a2 [{:.3e} s/s^2]", nameId(), satId,
482 ephemeris->toc.toYMDHMS(GPST), ephemeris->a[0], ephemeris->a[1], ephemeris->a[2]);
483
484
1/2
✓ Branch 2 taken 245 times.
✗ Branch 3 not taken.
245 finishSubFrame(ephemeris, subFrameId, ephemerisBuilder.subframes);
485 245 }
486
2/2
✓ Branch 0 taken 246 times.
✓ Branch 1 taken 736 times.
982 else if (subFrameId == 2) // The third through tenth words of subframes 2 and 3 shall each contain six parity bits as their LSBs
487 {
488 // IS-GPS-200M: Figure 20-1. Data Format (sheet 2 of 11), p. 80
489 // IS-GPS-200M: 20.3.3.4 Subframes 2 and 3, p. 101ff
490 // IS-GPS-200M: Table 20-III. Ephemeris Parameters, p. 105
491
492 // Crs, delta_n, M_0, Cuc, Cus shall be two's complement, with the sign bit (+ or -) occupying the MSB
493
494 246 w++;
495 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 3 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
496
1/2
✓ Branch 1 taken 246 times.
✗ Branch 2 not taken.
246 auto IODE = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 22) & 0b11111111); // 8 BITS
497
1/2
✓ Branch 1 taken 246 times.
✗ Branch 2 not taken.
246 auto Crs = static_cast<int16_t>((sfrbx.dwrd.at(w) >> 6) & 0b1111111111111111); // 16 BITS
498 LOG_DATA("{}: [{}] IODE {}, Crs {}", nameId(), satId, IODE, Crs);
499
500 246 w++;
501 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 4 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
502
1/2
✓ Branch 1 taken 246 times.
✗ Branch 2 not taken.
246 auto delta_n = static_cast<int16_t>((sfrbx.dwrd.at(w) >> 14) & 0b1111111111111111); // 16 BITS
503
1/2
✓ Branch 1 taken 246 times.
✗ Branch 2 not taken.
246 auto M_0 = static_cast<int32_t>(((sfrbx.dwrd.at(w) >> 6) & 0b11111111) << 24); // 8 MSBs - 32 BITS TOTAL
504 LOG_DATA("{}: [{}] delta_n {}, M_0 {}", nameId(), satId, delta_n, std::bitset<32>(static_cast<uint32_t>(M_0)));
505
506 246 w++;
507 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 5 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
508
1/2
✓ Branch 1 taken 246 times.
✗ Branch 2 not taken.
246 M_0 |= static_cast<int32_t>((sfrbx.dwrd.at(w) >> 6) & 0b111111111111111111111111); // 24 LSBs - 32 BITS TOTAL
509 LOG_DATA("{}: [{}] M_0 {} ({})", nameId(), satId, M_0, std::bitset<32>(static_cast<uint32_t>(M_0)));
510
511 246 w++;
512 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 6 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
513
1/2
✓ Branch 1 taken 246 times.
✗ Branch 2 not taken.
246 auto Cuc = static_cast<int16_t>((sfrbx.dwrd.at(w) >> 14) & 0b1111111111111111); // 16 BITS
514
1/2
✓ Branch 1 taken 246 times.
✗ Branch 2 not taken.
246 uint32_t e = ((sfrbx.dwrd.at(w) >> 6) & 0b11111111) << 24; // 8 MSBs - 32 BITS TOTAL
515 LOG_DATA("{}: [{}] Cuc {}, e {}", nameId(), satId, Cuc, std::bitset<32>(e));
516
517 246 w++;
518 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 7 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
519
1/2
✓ Branch 1 taken 246 times.
✗ Branch 2 not taken.
246 e |= (sfrbx.dwrd.at(w) >> 6) & 0b111111111111111111111111; // 24 LSBs - 32 BITS TOTAL
520 LOG_DATA("{}: [{}] e {} ({})", nameId(), satId, e, std::bitset<32>(e));
521
522 246 w++;
523 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 8 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
524
1/2
✓ Branch 1 taken 246 times.
✗ Branch 2 not taken.
246 auto Cus = static_cast<int16_t>((sfrbx.dwrd.at(w) >> 14) & 0b1111111111111111); // 16 BITS
525
1/2
✓ Branch 1 taken 246 times.
✗ Branch 2 not taken.
246 uint32_t sqrt_A = ((sfrbx.dwrd.at(w) >> 6) & 0b11111111) << 24; // 8 MSBs - 32 BITS TOTAL
526 LOG_DATA("{}: [{}] Cus {}, sqrt_A {}", nameId(), satId, Cus, std::bitset<32>(sqrt_A));
527
528 246 w++;
529 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 9 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
530
1/2
✓ Branch 1 taken 246 times.
✗ Branch 2 not taken.
246 sqrt_A |= (sfrbx.dwrd.at(w) >> 6) & 0b111111111111111111111111; // 24 LSBs - 32 BITS TOTAL
531 LOG_DATA("{}: [{}] sqrt_A {} ({})", nameId(), satId, sqrt_A, std::bitset<32>(sqrt_A));
532
533 246 w++;
534 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 10 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
535
1/2
✓ Branch 1 taken 246 times.
✗ Branch 2 not taken.
246 auto toe = static_cast<uint16_t>((sfrbx.dwrd.at(w) >> 14) & 0b1111111111111111); // 16 BITS
536
1/2
✓ Branch 1 taken 246 times.
✗ Branch 2 not taken.
246 auto fitInterval = static_cast<bool>((sfrbx.dwrd.at(w) >> 13) & 0b1); // 1 BIT
537
1/2
✓ Branch 1 taken 246 times.
✗ Branch 2 not taken.
246 [[maybe_unused]] auto AODO = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 8) & 0b11111); // 5 BITS
538 LOG_DATA("{}: [{}] toe {}, fitInterval {}, AODO {}", nameId(), satId, toe, fitInterval, AODO);
539
540
1/2
✓ Branch 3 taken 246 times.
✗ Branch 4 not taken.
246 InsTime insTimeToe(gpsWeekToW.gpsCycle, gpsWeekToW.gpsWeek, toe * std::pow(2, 4), GPST);
541
542
1/2
✓ Branch 1 taken 246 times.
✗ Branch 2 not taken.
246 auto& ephemerisBuilder = getEphemerisBuilder(satId, insTimeToe);
543 246 auto ephemeris = std::dynamic_pointer_cast<GPSEphemeris>(ephemerisBuilder.navData);
544 246 ephemeris->toe = insTimeToe;
545
546 246 ephemeris->IODE = IODE;
547 246 ephemeris->Crs = Crs * std::pow(2, -5);
548 246 ephemeris->delta_n = semicircles2rad(delta_n * std::pow(2, -43));
549 246 ephemeris->M_0 = semicircles2rad(M_0 * std::pow(2, -31));
550 246 ephemeris->Cuc = Cuc * std::pow(2, -29);
551 246 ephemeris->e = e * std::pow(2, -33);
552 246 ephemeris->Cus = Cus * std::pow(2, -29);
553 246 ephemeris->sqrt_A = sqrt_A * std::pow(2, -19);
554
1/2
✓ Branch 3 taken 246 times.
✗ Branch 4 not taken.
246 ephemeris->toe = InsTime(gpsWeekToW.gpsCycle, gpsWeekToW.gpsWeek, toe * std::pow(2, 4), GPST);
555
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 246 times.
246 ephemeris->fitInterval = fitInterval ? 8.0 : 4.0;
556 // AODO
557
558 LOG_DATA("{}: [{}] IODE [{}], Crs [{:.3e} m], delta_n [{:.3e} rad/s], M_0 [{:.3e} rad], Cuc [{:.3e} rad]", nameId(), satId,
559 ephemeris->IODE, ephemeris->Crs, ephemeris->delta_n, ephemeris->M_0, ephemeris->Cuc);
560 LOG_DATA("{}: [{}] e [{:.3e}], Cus [{:.3e} rad], sqrt_A [{:.3e} m^(1/2)], toe [{}], fitInterval [{:.1f}]", nameId(), satId,
561 ephemeris->e, ephemeris->Cus, ephemeris->sqrt_A, ephemeris->toe.toGPSweekTow(GPST), ephemeris->fitInterval);
562
563
1/2
✓ Branch 2 taken 246 times.
✗ Branch 3 not taken.
246 finishSubFrame(ephemeris, subFrameId, ephemerisBuilder.subframes);
564 246 }
565
2/2
✓ Branch 0 taken 245 times.
✓ Branch 1 taken 491 times.
736 else if (subFrameId == 3) // The third through tenth words of subframes 2 and 3 shall each contain six parity bits as their LSBs
566 {
567 // IS-GPS-200M: Figure 20-1. Data Format (sheet 3 of 11), p. 81
568 // IS-GPS-200M: 20.3.3.4 Subframes 2 and 3, p. 101ff
569 // IS-GPS-200M: Table 20-III. Ephemeris Parameters, p. 105
570
571 // Cic, Omega_0, Cis, i_0, Crc, omega, Omega_dot, IDOT shall be two's complement, with the sign bit (+ or -) occupying the MSB
572
573 245 w++;
574 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 3 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
575
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 auto Cic = static_cast<int16_t>((sfrbx.dwrd.at(w) >> 14) & 0b1111111111111111); // 16 BITS
576
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 auto Omega_0 = static_cast<int32_t>(((sfrbx.dwrd.at(w) >> 6) & 0b11111111) << 24); // 8 MSBs - 32 BITS TOTAL
577 LOG_DATA("{}: [{}] Cic {}, Omega_0 {}", nameId(), satId, Cic, std::bitset<32>(static_cast<uint32_t>(Omega_0)));
578
579 245 w++;
580 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 4 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
581
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 Omega_0 |= static_cast<int32_t>((sfrbx.dwrd.at(w) >> 6) & 0b111111111111111111111111); // 24 LSBs - 32 BITS TOTAL
582 LOG_DATA("{}: [{}] Omega_0 {} ({})", nameId(), satId, Omega_0, std::bitset<32>(static_cast<uint32_t>(Omega_0)));
583
584 245 w++;
585 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 5 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
586
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 auto Cis = static_cast<int16_t>((sfrbx.dwrd.at(w) >> 14) & 0b1111111111111111); // 16 BITS
587
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 auto i_0 = static_cast<int32_t>(((sfrbx.dwrd.at(w) >> 6) & 0b11111111) << 24); // 8 MSBs - 32 BITS TOTAL
588 LOG_DATA("{}: [{}] Cis {}, i_0 {}", nameId(), satId, Cis, std::bitset<32>(static_cast<uint32_t>(i_0)));
589
590 245 w++;
591 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 6 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
592
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 i_0 |= static_cast<int32_t>((sfrbx.dwrd.at(w) >> 6) & 0b111111111111111111111111); // 24 LSBs - 32 BITS TOTAL
593 LOG_DATA("{}: [{}] i_0 {} ({})", nameId(), satId, i_0, std::bitset<32>(static_cast<uint32_t>(i_0)));
594
595 245 w++;
596 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 7 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
597
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 auto Crc = static_cast<int16_t>((sfrbx.dwrd.at(w) >> 14) & 0b1111111111111111); // 16 BITS
598
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 auto omega = static_cast<int32_t>(((sfrbx.dwrd.at(w) >> 6) & 0b11111111) << 24); // 8 MSBs - 32 BITS TOTAL
599 LOG_DATA("{}: [{}] Crc {}, omega {}", nameId(), satId, Crc, std::bitset<32>(static_cast<uint32_t>(omega)));
600
601 245 w++;
602 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 8 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
603
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 omega |= static_cast<int32_t>((sfrbx.dwrd.at(w) >> 6) & 0b111111111111111111111111); // 24 LSBs - 32 BITS TOTAL
604 LOG_DATA("{}: [{}] omega {} ({})", nameId(), satId, omega, std::bitset<32>(static_cast<uint32_t>(omega)));
605
606 245 w++;
607 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 9 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
608
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 auto Omega_dot = math::interpretAs<int32_t, 24>(sfrbx.dwrd.at(w) >> 6); // 24 BITS
609 LOG_DATA("{}: [{}] Omega_dot {} ({})", nameId(), satId, Omega_dot, std::bitset<32>(static_cast<uint32_t>(Omega_dot)));
610
611 245 w++;
612 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 10 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
613
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 auto IODE = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 22) & 0b11111111); // 8 BITS
614
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 auto i_dot = math::interpretAs<int16_t, 14>(sfrbx.dwrd.at(w) >> 8); // 14 BITS
615 LOG_DATA("{}: [{}] IODE {}, i_dot {} ({})", nameId(), satId, IODE, i_dot, std::bitset<16>(static_cast<uint16_t>(i_dot)));
616
617
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 auto ephemerisBuilder = getEphemerisBuilder(satId, IODE);
618
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 245 times.
245 if (!ephemerisBuilder.has_value())
619 {
620 LOG_DATA("{}: [{}] Could not find Ephemeris builder for IODE {}", nameId(), satId, IODE);
621 return;
622 }
623 245 auto ephemeris = std::dynamic_pointer_cast<GPSEphemeris>(ephemerisBuilder->get().navData);
624
625 245 ephemeris->Cic = Cic * std::pow(2, -29);
626 245 ephemeris->Omega_0 = semicircles2rad(Omega_0 * std::pow(2, -31));
627 245 ephemeris->Cis = Cis * std::pow(2, -29);
628 245 ephemeris->i_0 = semicircles2rad(i_0 * std::pow(2, -31));
629 245 ephemeris->Crc = Crc * std::pow(2, -5);
630 245 ephemeris->omega = semicircles2rad(omega * std::pow(2, -31));
631 245 ephemeris->Omega_dot = semicircles2rad(Omega_dot * std::pow(2, -43));
632 245 ephemeris->IODE = IODE;
633 245 ephemeris->i_dot = semicircles2rad(i_dot * std::pow(2, -43));
634
635 LOG_DATA("{}: [{}] Cic [{:.3e} rad], Omega_0 [{:.3e} rad], Cis [{:.3e} rad], i_0 [{:.3e} rad], Crc [{:.3e} m]", nameId(), satId,
636 ephemeris->Cic, ephemeris->Omega_0, ephemeris->Cis, ephemeris->i_0, ephemeris->Crc);
637 LOG_DATA("{}: [{}] omega [{:.3e} rad], Omega_dot [{:.3e} rad/s], IODE [{}], i_dot [{:.3e} rad/s]", nameId(), satId,
638 ephemeris->omega, ephemeris->Omega_dot, ephemeris->IODE, ephemeris->i_dot);
639
640
1/2
✓ Branch 4 taken 245 times.
✗ Branch 5 not taken.
245 finishSubFrame(ephemeris, subFrameId, ephemerisBuilder->get().subframes);
641 245 }
642
2/2
✓ Branch 0 taken 237 times.
✓ Branch 1 taken 254 times.
491 else if (subFrameId == 4) // Words three through ten of each page contain six parity bits as their LSBs
643 {
644 // Page | SV ID | Description
645 // ------------------------ | ------------------------------ | -----------
646 // 1, 6, 11, 16, 21: | 57 | Reserved
647 // 2, 3, 4, 5, 7, 8, 9, 10: | 25, 26, 27, 28, 29, 30, 31, 32 | almanac data for SV 25 through 32 respectively
648 // 12, 19, 20, 22, 23, 24: | 62, 58, 59, 60, 61, 62 | Reserved
649 // 13: | 52 | NMCT
650 // 14, 15: | 53, 54 | Reserved for system use
651 // 17: | 55 | Special messages
652 // 18: | 56 | Ionospheric and UTC data
653 // 25: | 63 | A-S flags/SV configurations for 32 SVs, plus SV health for SV 25 through 32
654 //
655 // IS-GPS-200M: Table 20-V. Data IDs and SV IDs in Subframes 4 and 5, p. 114
656 // IS-GPS-200M: 20.3.3.5 Subframes 4 and 5, p. 112ff
657
658 237 w++;
659 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 3 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
660
1/2
✓ Branch 1 taken 237 times.
✗ Branch 2 not taken.
237 [[maybe_unused]] auto dataId = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 28) & 0b11); // 2 BITS
661
1/2
✓ Branch 1 taken 237 times.
✗ Branch 2 not taken.
237 auto svId = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 22) & 0b111111); // 6 BITS
662 LOG_DATA("{}: [{}] dataId {}, svId {}", nameId(), satId, dataId, svId);
663
664
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 229 times.
237 if (svId == 56) // IS-GPS-200M: Figure 20-1. Data Format (sheet 8 of 11), p. 86 - for page 18
665 {
666 // IS-GPS-200M: 20.3.3.5.1.6 Coordinated Universal Time (UTC) Parameters, p. 121
667 // IS-GPS-200M: Table 20-IX. UTC Parameters, p. 122
668
669 // IS-GPS-200M: 20.3.3.5.1.7 Ionospheric Data, p. 121
670 // IS-GPS-200M: Table 20-X. Ionospheric Parameters, p. 123
671
672
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 auto alpha0 = static_cast<int8_t>((sfrbx.dwrd.at(w) >> 14) & 0b11111111); // 8 BITS
673
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 auto alpha1 = static_cast<int8_t>((sfrbx.dwrd.at(w) >> 6) & 0b11111111); // 8 BITS
674 LOG_DATA("{}: [{}] alpha0 {}, alpha1 {}", nameId(), satId, alpha0, alpha1);
675
676 8 w++;
677 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 4 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
678
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 auto alpha2 = static_cast<int8_t>((sfrbx.dwrd.at(w) >> 22) & 0b11111111); // 8 BITS
679
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 auto alpha3 = static_cast<int8_t>((sfrbx.dwrd.at(w) >> 14) & 0b11111111); // 8 BITS
680
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 auto beta0 = static_cast<int8_t>((sfrbx.dwrd.at(w) >> 6) & 0b11111111); // 8 BITS
681 LOG_DATA("{}: [{}] alpha2 {}, alpha3 {}, beta0 {}", nameId(), satId, alpha2, alpha3, beta0);
682
683 8 w++;
684 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 5 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
685
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 auto beta1 = static_cast<int8_t>((sfrbx.dwrd.at(w) >> 22) & 0b11111111); // 8 BITS
686
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 auto beta2 = static_cast<int8_t>((sfrbx.dwrd.at(w) >> 14) & 0b11111111); // 8 BITS
687
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 auto beta3 = static_cast<int8_t>((sfrbx.dwrd.at(w) >> 6) & 0b11111111); // 8 BITS
688 LOG_DATA("{}: [{}] beta1 {}, beta2 {}, beta3 {}", nameId(), satId, beta1, beta2, beta3);
689
690 8 w++;
691 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 6 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
692
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 auto A1 = math::interpretAs<int32_t, 24>(sfrbx.dwrd.at(w) >> 6); // 24 BITS
693 LOG_DATA("{}: [{}] A1 {}", nameId(), satId, A1);
694
695 8 w++;
696 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 7 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
697
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 auto A0 = static_cast<int32_t>(((sfrbx.dwrd.at(w) >> 6) & 0b111111111111111111111111) << 8); // 24 MSBs - 32 BITS TOTAL
698 LOG_DATA("{}: [{}] A0 {}", nameId(), satId, std::bitset<32>(static_cast<uint32_t>(A0)));
699
700 8 w++;
701 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 8 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
702
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 A0 |= static_cast<int32_t>((sfrbx.dwrd.at(w) >> 22) & 0b11111111); // 8 LSBs - 32 BITS TOTAL
703 LOG_DATA("{}: [{}] A0 {} ({})", nameId(), satId, A0, std::bitset<32>(static_cast<uint32_t>(A0)));
704
705 8 w++;
706 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 9 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
707
708 8 w++;
709 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 10 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
710
711
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 std::unique_lock guard(outputPins.at(OUTPUT_PORT_INDEX_GNSS_NAV_INFO).dataAccessMutex, std::defer_lock);
712
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if (!_postProcessingLock.has_value())
713 {
714 guard.lock();
715 }
716
3/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 7 times.
8 if (!_gnssNavInfo.ionosphericCorrections.contains(satId.satSys, IonosphericCorrections::Alpha))
717 {
718
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
2 if (auto ephemerisBuilder = getLastEphemerisBuilder(satId);
719 1 ephemerisBuilder.has_value())
720 {
721 1 auto ephemeris = std::dynamic_pointer_cast<GPSEphemeris>(ephemerisBuilder->get().navData);
722
4/8
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
1 LOG_DEBUG("{}: [{}] Received Ionospheric and Time system corrections [{}]", nameId(), satId.satSys, ephemeris->refTime.toYMDHMS(GPST));
723 1 }
724 else
725 {
726 LOG_DEBUG("{}: [{}] Received Ionospheric and Time system corrections", nameId(), satId.satSys);
727 }
728 }
729
730 _gnssNavInfo.ionosphericCorrections.insert(satId.satSys, IonosphericCorrections::Alpha,
731 {
732 8 alpha0 * std::pow(2, -30) /* [s] */,
733 8 alpha1 * std::pow(2, -27) /* [s/semi-circle] */,
734 8 alpha2 * std::pow(2, -24) /* [s/semi-circle^2] */,
735
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 alpha3 * std::pow(2, -24) /* [s/semi-circle^3] */,
736 });
737 _gnssNavInfo.ionosphericCorrections.insert(satId.satSys, IonosphericCorrections::Beta,
738 {
739 8 beta0 * std::pow(2, 11) /* [s] */,
740 8 beta1 * std::pow(2, 14) /* [s/semi-circle] */,
741 8 beta2 * std::pow(2, 16) /* [s/semi-circle^2] */,
742
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 beta3 * std::pow(2, 16) /* [s/semi-circle^3] */,
743 });
744
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
8 _gnssNavInfo.timeSysCorr[{ satId.satSys.getTimeSystem(), UTC }] = GnssNavInfo::TimeSystemCorrections{
745 8 .a0 = A0 * std::pow(2, -30),
746 8 .a1 = A1 * std::pow(2, -50),
747 };
748 8 }
749 else
750 {
751 // IS-GPS-200M: Figure 20-1. Data Format (sheet 6 of 11), p. 84 - for pages 1, 6, 11, 16 and 21
752 // IS-GPS-200M: Figure 20-1. Data Format (sheet 7 of 11), p. 85 - for pages 12, 19, 20, 22, 23 and 24
753 // IS-GPS-200M: Figure 20-1. Data Format (sheet 9 of 11), p. 87 - for page 25
754 // IS-GPS-200M: Figure 20-1. Data Format (sheet 10 of 11), p. 88 - for page 13
755 // IS-GPS-200M: Figure 20-1. Data Format (sheet 11 of 11), p. 89 - for pages 14, 15 and 17
756
757 229 w++;
758 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 4 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
759 229 w++;
760 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 5 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
761 229 w++;
762 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 6 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
763 229 w++;
764 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 7 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
765 229 w++;
766 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 8 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
767 229 w++;
768 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 9 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
769 229 w++;
770 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 10 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
771 }
772 }
773
1/2
✓ Branch 0 taken 254 times.
✗ Branch 1 not taken.
254 else if (subFrameId == 5) // Words three through ten of each page contain six parity bits as their LSBs
774 {
775 // Page description
776 // 1 through 24: almanac data for SV 1 through 24
777 // 25: SV health data for SV 1 through 24, the almanac reference time, the almanac reference week number
778 //
779 // IS-GPS-200M: Figure 20-1. Data Format (sheet 4 of 11), p. 82 - for page 1 through 24
780 // IS-GPS-200M: Figure 20-1. Data Format (sheet 5 of 11), p. 83 - for page 25
781 // IS-GPS-200M: 20.3.3.5 Subframes 4 and 5, p. 112ff
782
783 254 w++;
784 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 3 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
785 254 w++;
786 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 4 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
787 254 w++;
788 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 5 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
789 254 w++;
790 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 6 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
791 254 w++;
792 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 7 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
793 254 w++;
794 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 8 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
795 254 w++;
796 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 9 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
797 254 w++;
798 LOG_DATA("{}: [{}] word {:2}: {} {}", nameId(), satId, w + 1 /* 10 */, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
799 }
800 }
801
802 4211 void NAV::UbloxGnssOrbitCollector::decryptGalileo(const SatId& satId, const ubx::UbxRxmSfrbx& sfrbx, const InsTime& insTime)
803 {
804 // u-blox 8 / u-blox M8: Receiver description - Including protocol specification, ch. 10.5, p. 32f
805 // > The Galileo E1OS and E5b signals both transmit the I/NAV message but in different configurations.
806 // > For Galileo E1OS signals, each reported subframe contains a pair of I/NAV pages as described in
807 // > the Galileo ICD.
808 // > Galileo pages can either be "Nominal" or "Alert" pages. For Nominal pages the eight words are
809 // > arranged as follows:
810 // >
811 // > MSB LSB
812 // > 1: Even/Odd (1 bit) Page type (1 bit) Word type (6 bits) Data (122 - 99) (24 bits)
813 // > 2: Data (98 - 67) (32 bits)
814 // > 3: Data (66 - 35) (32 bits)
815 // > 4: Data (34 - 17) (18 bits) Tail (6 bits) Pad (8 bits)
816 // > 5: Even/Odd (1 bit) Page type (1 bit) Data (16 - 1) (16 bits) Reserved 1 (40 - 27) (14 bits)
817 // > 6: Reserved 2 (26 - 1) (26 bits) SAR (22 - 17) (6 bits)
818 // > 7: SAR (16 - 1) (16 bits) Spare (2 bits) CRC (24 - 11) (14 bits)
819 // > 8: CRC (10 - 1) (10 bits) Reserved 2 (8 bits) Tail (6 bits) Pad (8 bits)
820 // >
821 // > Alert pages are reported in very similar manner, but the page type bits will have value 1 and the
822 // > structure of the eight words will be slightly different (as indicated by the Galileo ICD).
823
824
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4211 times.
4211 if (sfrbx.numWords != 8)
825 {
826 LOG_ERROR("{}: [{}] Received {} instead of 8 words", nameId(), satId, sfrbx.numWords);
827 695 return;
828 }
829
830 4211 size_t w = 0; // Word counter
831
832
1/2
✓ Branch 1 taken 4211 times.
✗ Branch 2 not taken.
4211 auto even = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 31) & 0b1);
833
1/2
✓ Branch 1 taken 4211 times.
✗ Branch 2 not taken.
4211 auto pageTypeEven = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 30) & 0b1);
834
1/2
✓ Branch 1 taken 4211 times.
✗ Branch 2 not taken.
4211 auto odd = static_cast<uint8_t>((sfrbx.dwrd.at(4) >> 31) & 0b1);
835
1/2
✓ Branch 1 taken 4211 times.
✗ Branch 2 not taken.
4211 auto pageTypeOdd = static_cast<uint8_t>((sfrbx.dwrd.at(4) >> 30) & 0b1);
836
1/2
✓ Branch 1 taken 4211 times.
✗ Branch 2 not taken.
4211 auto wordType = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 24) & 0b111111);
837 LOG_DATA("{}: [{}] wordType: {:2}, even {} ({} pageType), odd {} ({} pageType)", nameId(), satId, wordType, even, pageTypeEven, odd, pageTypeOdd);
838
839
2/4
✓ Branch 0 taken 4211 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4211 times.
✗ Branch 3 not taken.
4211 if (even != 0 || pageTypeEven == 1
840
4/4
✓ Branch 0 taken 3814 times.
✓ Branch 1 taken 397 times.
✓ Branch 2 taken 237 times.
✓ Branch 3 taken 3577 times.
4211 || odd != 1 || pageTypeOdd == 1)
841 {
842 LOG_DATA("{}: [{}] Ignoring message, because one of the page types is alert page", nameId(), satId);
843 634 return;
844 }
845
846
1/2
✓ Branch 2 taken 3577 times.
✗ Branch 3 not taken.
3577 auto gpsWeekToW = insTime.toGPSweekTow(GST);
847
848 1103 auto finishWord = [&]<size_t N>(const std::shared_ptr<SatNavData>& ephemeris, size_t wordType, std::bitset<N>& subframesFound) {
849
2/4
✓ Branch 0 taken 1103 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1103 times.
✗ Branch 3 not taken.
1103 if (1 <= wordType && wordType <= 5)
850 {
851 1103 subframesFound.set(wordType - 1);
852
853 LOG_DATA("{}: [{}] subframesFound: {}", nameId(), satId, subframesFound);
854
2/2
✓ Branch 1 taken 989 times.
✓ Branch 2 taken 114 times.
1103 if (subframesFound.count() == 5)
855 {
856 LOG_DATA("{}: [{}] [{}] All words found. Updating gnnsNavInfo", nameId(), satId, ephemeris->refTime.toYMDHMS(GPST));
857
3/4
✓ Branch 2 taken 989 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 11 times.
✓ Branch 7 taken 978 times.
989 if (!_gnssNavInfo.searchNavigationData(satId, ephemeris->refTime))
858 {
859
4/8
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 11 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 11 times.
✗ Branch 10 not taken.
✓ Branch 13 taken 11 times.
✗ Branch 14 not taken.
11 LOG_DEBUG("{}: Adding new ephemeris data [{}] [{}]", nameId(), satId, ephemeris->refTime.toYMDHMS(GPST));
860 }
861
1/2
✓ Branch 1 taken 989 times.
✗ Branch 2 not taken.
989 std::unique_lock guard(outputPins.at(OUTPUT_PORT_INDEX_GNSS_NAV_INFO).dataAccessMutex, std::defer_lock);
862
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 989 times.
989 if (!_postProcessingLock.has_value())
863 {
864 guard.lock();
865 }
866 989 _gnssNavInfo.satelliteSystems |= satId.satSys;
867
1/2
✓ Branch 1 taken 989 times.
✗ Branch 2 not taken.
989 _gnssNavInfo.addSatelliteNavData(satId, ephemeris);
868 989 }
869 }
870 1103 };
871
872
2/2
✓ Branch 0 taken 227 times.
✓ Branch 1 taken 3350 times.
3577 if (wordType == 1) // Ephemeris (1/4)
873 {
874 // Galileo-OS-SIS-ICD-v2.0: ch. 4.3.5, Table 40 Bits Allocation for I/NAV Word Type 1, p. 37
875 //
876 // Even/Odd PageType WordType IODnav toe M_0 e sqrt_A Tail Pad Even/Odd PageType sqrt_A Reserved Reserved 1
877 // 1 1 6 10 14 32 32 18 6 8 1 1 14 2 14
878 // └───────────────────┬───────────────────┘ │ │ └───────┬───────┘ └───────────────────────┬──────────────────────┘
879 // Ublox 1 2 3 4 5
880
881 // Galileo-OS-SIS-ICD-v2.0: ch. 5.1.1, Table 60 Ephemeris Parameters, p. 43f
882 // > M_0 shall be two's complement, with the sign bit (+ or -) occupying the MSB
883
884 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 1 */, std::bitset<32>(sfrbx.dwrd.at(w)));
885
1/2
✓ Branch 1 taken 227 times.
✗ Branch 2 not taken.
227 auto IODnav = static_cast<uint16_t>((sfrbx.dwrd.at(w) >> 14) & 0b1111111111);
886
1/2
✓ Branch 1 taken 227 times.
✗ Branch 2 not taken.
227 auto toe = static_cast<uint16_t>(sfrbx.dwrd.at(w) & 0b11111111111111);
887 LOG_DATA("{}: [{}] IODnav {}, toe {}", nameId(), satId, IODnav, toe);
888
889 227 w++;
890 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 2 */, std::bitset<32>(sfrbx.dwrd.at(w)));
891
1/2
✓ Branch 1 taken 227 times.
✗ Branch 2 not taken.
227 auto M_0 = static_cast<int32_t>(sfrbx.dwrd.at(w));
892 LOG_DATA("{}: [{}] M_0 {}", nameId(), satId, M_0);
893
894 227 w++;
895 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 3 */, std::bitset<32>(sfrbx.dwrd.at(w)));
896
1/2
✓ Branch 1 taken 227 times.
✗ Branch 2 not taken.
227 uint32_t e = sfrbx.dwrd.at(w);
897 LOG_DATA("{}: [{}] e {}", nameId(), satId, e);
898
899 227 w++;
900 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 4 */, std::bitset<32>(sfrbx.dwrd.at(w)));
901
1/2
✓ Branch 1 taken 227 times.
✗ Branch 2 not taken.
227 uint32_t sqrt_A = ((sfrbx.dwrd.at(w) >> 14) & 0b111111111111111111) << 14;
902 LOG_DATA("{}: [{}] sqrt_A {}", nameId(), satId, std::bitset<32>(sqrt_A));
903
904 227 w++;
905 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 5 */, std::bitset<32>(sfrbx.dwrd.at(w)));
906
1/2
✓ Branch 1 taken 227 times.
✗ Branch 2 not taken.
227 sqrt_A |= (sfrbx.dwrd.at(w) >> 16) & 0b11111111111111;
907 LOG_DATA("{}: [{}] sqrt_A {} ({})", nameId(), satId, sqrt_A, std::bitset<32>(sqrt_A));
908
909
1/2
✓ Branch 2 taken 227 times.
✗ Branch 3 not taken.
227 InsTime insTimeToe(gpsWeekToW.gpsCycle, gpsWeekToW.gpsWeek, toe * 60.0, GST);
910
911
1/2
✓ Branch 1 taken 227 times.
✗ Branch 2 not taken.
227 auto& ephemerisBuilder = getEphemerisBuilder(satId, insTimeToe, IODnav);
912 227 auto ephemeris = std::dynamic_pointer_cast<GalileoEphemeris>(ephemerisBuilder.navData);
913 227 ephemeris->toe = insTimeToe;
914
915 227 ephemeris->IODnav = IODnav;
916 227 ephemeris->M_0 = semicircles2rad(M_0 * std::pow(2.0, -31));
917 227 ephemeris->e = e * std::pow(2.0, -33);
918 227 ephemeris->sqrt_A = sqrt_A * std::pow(2.0, -19);
919
920 LOG_DATA("{}: [{}] IODnav [{}], toe [{}], M_0 [{:.3e} rad], e [{:.3e}], sqrt_A [{:.3e} m^(1/2)]", nameId(), satId,
921 ephemeris->IODnav, ephemeris->toe.toYMDHMS(GPST), ephemeris->M_0, ephemeris->e, ephemeris->sqrt_A);
922
923
1/2
✓ Branch 2 taken 227 times.
✗ Branch 3 not taken.
227 finishWord(ephemeris, wordType, ephemerisBuilder.subframes);
924 227 }
925
2/2
✓ Branch 0 taken 236 times.
✓ Branch 1 taken 3114 times.
3350 else if (wordType == 2) // Ephemeris (2/4)
926 {
927 // Galileo-OS-SIS-ICD-v2.0: ch. 4.3.5, Table 41 Bits Allocation for I/NAV Word Type 2, p. 37
928 //
929 // Even/Odd PageType WordType IODnav Omega_0 Omega_0 i_0 i_0 omega omega Tail Pad Even/Odd PageType i_dot Reserved Reserved 1
930 // 1 1 6 10 14 18 14 18 14 18 6 8 1 1 14 2 14
931 // └──────────────────────┬────────────────────┘ └────┬─────┘ └────┬───┘ └───────┬────────┘ └──────────────────────┬──────────────────────┘
932 // Ublox 1 2 3 4 5
933
934 // Galileo-OS-SIS-ICD-v2.0: ch. 5.1.1, Table 60 Ephemeris Parameters, p. 43f
935 // > Omega_0, i_0, omega, i_dot shall be two's complement, with the sign bit (+ or -) occupying the MSB
936
937 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 1 */, std::bitset<32>(sfrbx.dwrd.at(w)));
938
1/2
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
236 auto IODnav = static_cast<uint16_t>((sfrbx.dwrd.at(w) >> 14) & 0b1111111111);
939
1/2
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
236 auto Omega_0 = static_cast<int32_t>((sfrbx.dwrd.at(w) & 0b11111111111111) << 18);
940 LOG_DATA("{}: [{}] IODnav {}, Omega_0 {}", nameId(), satId, IODnav, std::bitset<32>(static_cast<uint32_t>(Omega_0)));
941
942 236 w++;
943 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 2 */, std::bitset<32>(sfrbx.dwrd.at(w)));
944
1/2
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
236 Omega_0 |= static_cast<int32_t>((sfrbx.dwrd.at(w) >> 14) & 0b111111111111111111);
945
1/2
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
236 auto i_0 = static_cast<int32_t>((sfrbx.dwrd.at(w) & 0b11111111111111) << 18);
946 LOG_DATA("{}: [{}] Omega_0 {} ({}), i_0 {}", nameId(), satId, Omega_0, std::bitset<32>(static_cast<uint32_t>(Omega_0)), std::bitset<32>(static_cast<uint32_t>(i_0)));
947
948 236 w++;
949 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 3 */, std::bitset<32>(sfrbx.dwrd.at(w)));
950
1/2
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
236 i_0 |= static_cast<int32_t>((sfrbx.dwrd.at(w) >> 14) & 0b111111111111111111);
951
1/2
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
236 auto omega = static_cast<int32_t>((sfrbx.dwrd.at(w) & 0b11111111111111) << 18);
952 LOG_DATA("{}: [{}] i_0 {} ({}), omega {}", nameId(), satId, i_0, std::bitset<32>(static_cast<uint32_t>(i_0)), std::bitset<32>(static_cast<uint32_t>(omega)));
953
954 236 w++;
955 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 4 */, std::bitset<32>(sfrbx.dwrd.at(w)));
956
1/2
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
236 omega |= static_cast<int32_t>((sfrbx.dwrd.at(w) >> 14) & 0b111111111111111111);
957 LOG_DATA("{}: [{}] omega {} ({})", nameId(), satId, omega, std::bitset<32>(static_cast<uint32_t>(omega)));
958
959 236 w++;
960 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 5 */, std::bitset<32>(sfrbx.dwrd.at(w)));
961
1/2
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
236 auto i_dot = math::interpretAs<int16_t, 14>(sfrbx.dwrd.at(w) >> 16);
962 LOG_DATA("{}: [{}] i_dot {} ({})", nameId(), satId, i_dot, std::bitset<16>(static_cast<uint16_t>(i_dot)));
963
964
1/2
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
236 auto ephemerisBuilder = getEphemerisBuilder(satId, IODnav);
965
2/2
✓ Branch 1 taken 27 times.
✓ Branch 2 taken 209 times.
236 if (!ephemerisBuilder.has_value())
966 {
967 LOG_DATA("{}: [{}] Could not find Ephemeris builder for IODnav {}", nameId(), satId, IODnav);
968 27 return;
969 }
970 209 auto ephemeris = std::dynamic_pointer_cast<GalileoEphemeris>(ephemerisBuilder->get().navData);
971
972 209 ephemeris->Omega_0 = semicircles2rad(Omega_0 * std::pow(2.0, -31));
973 209 ephemeris->i_0 = semicircles2rad(i_0 * std::pow(2.0, -31));
974 209 ephemeris->omega = semicircles2rad(omega * std::pow(2.0, -31));
975 209 ephemeris->i_dot = semicircles2rad(i_dot * std::pow(2.0, -43));
976
977 LOG_DATA("{}: [{}] IODnav [{}], Omega_0 [{} rad], i_0 [{:.3e} rad], omega [{:.3e} rad], i_dot [{:.3e} rad/s]", nameId(), satId,
978 ephemeris->IODnav, ephemeris->Omega_0, ephemeris->i_0, ephemeris->omega, ephemeris->i_dot);
979
980
1/2
✓ Branch 4 taken 209 times.
✗ Branch 5 not taken.
209 finishWord(ephemeris, wordType, ephemerisBuilder->get().subframes);
981 209 }
982
2/2
✓ Branch 0 taken 228 times.
✓ Branch 1 taken 2886 times.
3114 else if (wordType == 3) // Ephemeris (3/4) and SISA
983 {
984 // Galileo-OS-SIS-ICD-v2.0: ch. 4.3.5, Table 42 Bits Allocation for I/NAV Word Type 3, p. 38
985 //
986 // Even/Odd PageType WordType IODnav Omega_dot Omega_dot delta_n Cuc Cuc Cus Crc Crc Crs Tail Pad Even/Odd PageType Crs SISA(E1,E5b) Reserved 1
987 // 1 1 6 10 14 10 16 6 10 16 6 10 8 6 8 1 1 8 8 14
988 // └───────────────────────┬─────────────────────┘ └──────────┬──────────┘ └─────┬─────┘ └────────┬────────┘ └───────────────────────┬───────────────────────┘
989 // Ublox 1 2 3 4 5
990
991 // Galileo-OS-SIS-ICD-v2.0: ch. 5.1.1, Table 60 Ephemeris Parameters, p. 43f
992 // > Omega_dot, delta_n, Cuc, Cus, Crc, Crs shall be two's complement, with the sign bit (+ or -) occupying the MSB
993
994 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 1 */, std::bitset<32>(sfrbx.dwrd.at(w)));
995
1/2
✓ Branch 1 taken 228 times.
✗ Branch 2 not taken.
228 auto IODnav = static_cast<uint16_t>((sfrbx.dwrd.at(w) >> 14) & 0b1111111111);
996
1/2
✓ Branch 1 taken 228 times.
✗ Branch 2 not taken.
228 auto Omega_dot = static_cast<int32_t>((sfrbx.dwrd.at(w) & 0b11111111111111) << 10);
997 LOG_DATA("{}: [{}] IODnav {}, Omega_dot {}", nameId(), satId, IODnav, std::bitset<32>(static_cast<uint32_t>(Omega_dot)));
998
999 228 w++;
1000 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 2 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1001
1/2
✓ Branch 1 taken 228 times.
✗ Branch 2 not taken.
228 Omega_dot |= static_cast<int32_t>((sfrbx.dwrd.at(w) >> 22) & 0b1111111111);
1002 228 Omega_dot = math::interpretAs<int32_t, 24>(Omega_dot);
1003
1/2
✓ Branch 1 taken 228 times.
✗ Branch 2 not taken.
228 auto delta_n = static_cast<int16_t>((sfrbx.dwrd.at(w) >> 6) & 0b1111111111111111);
1004
1/2
✓ Branch 1 taken 228 times.
✗ Branch 2 not taken.
228 auto Cuc = static_cast<int16_t>((sfrbx.dwrd.at(w) & 0b111111) << 10);
1005 LOG_DATA("{}: [{}] Omega_dot {} ({}), delta_n {}, Cuc {}", nameId(), satId,
1006 Omega_dot, std::bitset<32>(static_cast<uint32_t>(Omega_dot)), delta_n, std::bitset<16>(static_cast<uint16_t>(Cuc)));
1007
1008 228 w++;
1009 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 3 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1010
1/2
✓ Branch 1 taken 228 times.
✗ Branch 2 not taken.
228 Cuc |= static_cast<int16_t>((sfrbx.dwrd.at(w) >> 22) & 0b1111111111); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
1011
1/2
✓ Branch 1 taken 228 times.
✗ Branch 2 not taken.
228 auto Cus = static_cast<int16_t>((sfrbx.dwrd.at(w) >> 6) & 0b1111111111111111);
1012
1/2
✓ Branch 1 taken 228 times.
✗ Branch 2 not taken.
228 auto Crc = static_cast<int16_t>((sfrbx.dwrd.at(w) & 0b111111) << 10);
1013 LOG_DATA("{}: [{}] Cuc {} ({}), Cus {}, Crc {}", nameId(), satId, Cuc, std::bitset<16>(static_cast<uint16_t>(Cuc)), Cus, std::bitset<16>(static_cast<uint16_t>(Crc)));
1014
1015 228 w++;
1016 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 4 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1017
1/2
✓ Branch 1 taken 228 times.
✗ Branch 2 not taken.
228 Crc |= static_cast<int16_t>((sfrbx.dwrd.at(w) >> 22) & 0b1111111111); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
1018
1/2
✓ Branch 1 taken 228 times.
✗ Branch 2 not taken.
228 auto Crs = static_cast<int16_t>(((sfrbx.dwrd.at(w) >> 14) & 0b11111111) << 8);
1019 LOG_DATA("{}: [{}] Crc {} ({}), Crs {}", nameId(), satId, Crc, std::bitset<16>(static_cast<uint16_t>(Crc)), std::bitset<16>(static_cast<uint16_t>(Crs)));
1020
1021 228 w++;
1022 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 5 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1023
1/2
✓ Branch 1 taken 228 times.
✗ Branch 2 not taken.
228 Crs |= static_cast<int16_t>((sfrbx.dwrd.at(w) >> 22) & 0b11111111); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
1024
1/2
✓ Branch 1 taken 228 times.
✗ Branch 2 not taken.
228 auto SISA = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 14) & 0b11111111);
1025 LOG_DATA("{}: [{}] Crs {} ({}), SISA {}", nameId(), satId, Crs, std::bitset<16>(static_cast<uint16_t>(Crs)), SISA);
1026
1027
1/2
✓ Branch 1 taken 228 times.
✗ Branch 2 not taken.
228 auto ephemerisBuilder = getEphemerisBuilder(satId, IODnav);
1028
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 225 times.
228 if (!ephemerisBuilder.has_value())
1029 {
1030 LOG_DATA("{}: [{}] Could not find Ephemeris builder for IODnav {}", nameId(), satId, IODnav);
1031 3 return;
1032 }
1033 225 auto ephemeris = std::dynamic_pointer_cast<GalileoEphemeris>(ephemerisBuilder->get().navData);
1034
1035 225 ephemeris->Omega_dot = semicircles2rad(Omega_dot * std::pow(2.0, -43));
1036 225 ephemeris->delta_n = semicircles2rad(delta_n * std::pow(2.0, -43));
1037 225 ephemeris->Cuc = Cuc * std::pow(2.0, -29);
1038 225 ephemeris->Cus = Cus * std::pow(2.0, -29);
1039 225 ephemeris->Crc = Crc * std::pow(2.0, -5);
1040 225 ephemeris->Crs = Crs * std::pow(2.0, -5);
1041
1/2
✓ Branch 1 taken 225 times.
✗ Branch 2 not taken.
225 ephemeris->signalAccuracy = galSisaIdx2Val(SISA);
1042
1043 LOG_DATA("{}: [{}] IODnav [{}], Omega_dot [{} rad/s], delta_n [{:.3e} rad/s], Cuc [{:.3e} rad], Cus [{:.3e} rad/s], Crc [{:.3e} m], Crs [{:.3e} m], SISA [{:.3e} m]", nameId(), satId,
1044 ephemeris->IODnav, ephemeris->Omega_dot, ephemeris->delta_n, ephemeris->Cuc, ephemeris->Cus, ephemeris->Crc, ephemeris->Crs, ephemeris->signalAccuracy);
1045
1046
1/2
✓ Branch 4 taken 225 times.
✗ Branch 5 not taken.
225 finishWord(ephemeris, wordType, ephemerisBuilder->get().subframes);
1047 225 }
1048
2/2
✓ Branch 0 taken 237 times.
✓ Branch 1 taken 2649 times.
2886 else if (wordType == 4) // SVID, Ephemeris (4/4), and Clock correction parameters
1049 {
1050 // Galileo-OS-SIS-ICD-v2.0: ch. 4.3.5, Table 43 Bits Allocation for I/NAV Word Type 4, p. 38
1051 //
1052 // Even/Odd PageType WordType IODnav SVID Cic Cic Cis toc toc af0 af0 af1 Tail Pad Even/Odd PageType af1 af2 Spare Reserved 1
1053 // 1 1 6 10 6 8 8 16 8 6 26 5 13 6 8 1 1 8 6 2 14
1054 // └───────────────────────┬─────────────────────┘ └─────┬─────┘ └──┬───┘ └────────┬────────┘ └───────────────────────┬─────────────────────┘
1055 // Ublox 1 2 3 4 5
1056
1057 // Galileo-OS-SIS-ICD-v2.0: ch. 5.1.1, Table 60 Ephemeris Parameters, p. 43f
1058 // Galileo-OS-SIS-ICD-v2.0: ch. 5.1.3, Table 63 Galileo Clock Correction Parameters, p. 46
1059 // > Cic, Cis, af0, af1, af2 shall be two's complement, with the sign bit (+ or -) occupying the MSB
1060
1061 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 1 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1062
1/2
✓ Branch 1 taken 237 times.
✗ Branch 2 not taken.
237 auto IODnav = static_cast<uint16_t>((sfrbx.dwrd.at(w) >> 14) & 0b1111111111);
1063
1/2
✓ Branch 1 taken 237 times.
✗ Branch 2 not taken.
237 [[maybe_unused]] auto SVID = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 8) & 0b111111);
1064
1/2
✓ Branch 1 taken 237 times.
✗ Branch 2 not taken.
237 auto Cic = static_cast<int16_t>((sfrbx.dwrd.at(w) & 0b11111111) << 8);
1065 LOG_DATA("{}: [{}] IODnav {}, SVID {}, Cic {}", nameId(), satId, IODnav, SVID, std::bitset<16>(static_cast<uint16_t>(Cic)));
1066
1067 237 w++;
1068 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 2 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1069
1/2
✓ Branch 1 taken 237 times.
✗ Branch 2 not taken.
237 Cic |= static_cast<int16_t>((sfrbx.dwrd.at(w) >> 24) & 0b11111111); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
1070
1/2
✓ Branch 1 taken 237 times.
✗ Branch 2 not taken.
237 auto Cis = static_cast<int16_t>((sfrbx.dwrd.at(w) >> 8) & 0b1111111111111111);
1071
1/2
✓ Branch 1 taken 237 times.
✗ Branch 2 not taken.
237 auto toc = static_cast<uint16_t>((sfrbx.dwrd.at(w) & 0b11111111) << 6);
1072 LOG_DATA("{}: [{}] Cic {} ({}), Cis {}, toc {}", nameId(), satId, Cic, std::bitset<16>(static_cast<uint16_t>(Cic)), Cis, std::bitset<16>(toc));
1073
1074 237 w++;
1075 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 3 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1076
1/2
✓ Branch 1 taken 237 times.
✗ Branch 2 not taken.
237 toc |= static_cast<uint16_t>((sfrbx.dwrd.at(w) >> 26) & 0b111111);
1077
1/2
✓ Branch 1 taken 237 times.
✗ Branch 2 not taken.
237 auto af0 = static_cast<int32_t>((sfrbx.dwrd.at(w) & 0b11111111111111111111111111) << 5);
1078 LOG_DATA("{}: [{}] toc {} ({}), af0 {}", nameId(), satId, toc, std::bitset<16>(toc), std::bitset<32>(static_cast<uint32_t>(af0)));
1079
1080 237 w++;
1081 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 4 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1082
1/2
✓ Branch 1 taken 237 times.
✗ Branch 2 not taken.
237 af0 |= static_cast<int32_t>((sfrbx.dwrd.at(w) >> 27) & 0b11111);
1083 237 af0 = math::interpretAs<int32_t, 31>(af0);
1084
1/2
✓ Branch 1 taken 237 times.
✗ Branch 2 not taken.
237 auto af1 = static_cast<int32_t>(((sfrbx.dwrd.at(w) >> 14) & 0b1111111111111) << 8);
1085 LOG_DATA("{}: [{}] af0 {} ({}), af1 {}", nameId(), satId, af0, std::bitset<32>(static_cast<uint32_t>(af0)), std::bitset<32>(static_cast<uint32_t>(af1)));
1086
1087 237 w++;
1088 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 5 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1089
1/2
✓ Branch 1 taken 237 times.
✗ Branch 2 not taken.
237 af1 |= static_cast<int32_t>((sfrbx.dwrd.at(w) >> 22) & 0b11111111);
1090 237 af1 = math::interpretAs<int32_t, 21>(af1);
1091
1/2
✓ Branch 1 taken 237 times.
✗ Branch 2 not taken.
237 auto af2 = math::interpretAs<int8_t, 6>(sfrbx.dwrd.at(w) >> 16);
1092 LOG_DATA("{}: [{}] af1 {} ({}), af1 {}", nameId(), satId, af1, std::bitset<32>(static_cast<uint32_t>(af1)), std::bitset<8>(static_cast<uint8_t>(af2)));
1093
1094
1/2
✓ Branch 1 taken 237 times.
✗ Branch 2 not taken.
237 auto ephemerisBuilder = getEphemerisBuilder(satId, IODnav);
1095
2/2
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 209 times.
237 if (!ephemerisBuilder.has_value())
1096 {
1097 LOG_DATA("{}: [{}] Could not find Ephemeris builder for IODnav {}", nameId(), satId, IODnav);
1098 28 return;
1099 }
1100 209 auto ephemeris = std::dynamic_pointer_cast<GalileoEphemeris>(ephemerisBuilder->get().navData);
1101
1102
1/2
✓ Branch 2 taken 209 times.
✗ Branch 3 not taken.
209 InsTime insTimeToc(gpsWeekToW.gpsCycle, gpsWeekToW.gpsWeek, toc * 60.0, GST);
1103
1104 209 ephemeris->Cic = Cic * std::pow(2.0, -29);
1105 209 ephemeris->Cis = Cis * std::pow(2.0, -29);
1106 209 ephemeris->toc = insTimeToc;
1107 209 ephemeris->refTime = ephemeris->toc;
1108 209 ephemeris->a = {
1109 209 af0 * std::pow(2, -34),
1110 209 af1 * std::pow(2, -46),
1111 209 af2 * std::pow(2, -59),
1112 };
1113
1114 LOG_DATA("{}: [{}] IODnav [{}], Cic [{:.3e} rad], Cis [{:.3e} rad]", nameId(), satId, ephemeris->IODnav, ephemeris->Cic, ephemeris->Cis);
1115 LOG_DATA("{}: [{}] toc [{}], a0 [{:.3e} s], a1 [{:.3e} s/s], a2 [{:.3e} s/s^2]", nameId(), satId,
1116 ephemeris->toc.toYMDHMS(GPST), ephemeris->a[0], ephemeris->a[1], ephemeris->a[2]);
1117
1118
1/2
✓ Branch 4 taken 209 times.
✗ Branch 5 not taken.
209 finishWord(ephemeris, wordType, ephemerisBuilder->get().subframes);
1119 209 }
1120
2/2
✓ Branch 0 taken 236 times.
✓ Branch 1 taken 2413 times.
2649 else if (wordType == 5) // Ionospheric correction, BGD, signal health and data validity status and GST
1121 {
1122 // Galileo-OS-SIS-ICD-v2.0: ch. 4.3.5, Table 44 Bits Allocation for I/NAV Word Type 5, p. 38
1123 //
1124 // Even/Odd PageType WordType ai0 ai1 ai2 ai2 Region 1-5 BGD(E1,E5a) BGD(E1,E5b) BGD(E1,E5b) E5b_HS E1b_HS E5b_DVS E1b_DVS WN TOW TOW Spare Tail Pad Even/Odd PageType Spare Reserved 1
1125 // 1 1 6 11 11 2 12 5x1 10 5 5 2 2 1 1 12 9 11 7 13 6 1 1 16 14
1126 // └─────────────────────┬───────────────────┘ └───────────────────┬───────────────────┘ └─────────────────────────┬──────────────────────────┘ └─────────┬─────────┘ └──────────────────┬────────────────┘
1127 // Ublox 1 2 3 4 5
1128
1129 // Galileo-OS-SIS-ICD-v2.0: ch. 5.1.6, Table 67 Ionospheric Correction Parameters, p. 48
1130 // Galileo-OS-SIS-ICD-v2.0: ch. 5.1.5, Table 65 BGD Parameters, p. 47
1131 // > ai1, ai2, BGD(E1,E5a), BGD(E1,E5b) shall be two's complement, with the sign bit (+ or -) occupying the MSB
1132
1133 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 1 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1134
1/2
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
236 auto ai0 = static_cast<uint16_t>((sfrbx.dwrd.at(w) >> 13) & 0b11111111111);
1135
1/2
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
236 auto ai1 = math::interpretAs<int16_t, 11>((sfrbx.dwrd.at(w) >> 2) & 0b11111111111);
1136
1/2
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
236 auto ai2 = static_cast<int16_t>((sfrbx.dwrd.at(w) & 0b11) << 12);
1137 LOG_DATA("{}: [{}] ai0 {} ({}), ai1 {} ({}), ai2 {}", nameId(), satId,
1138 ai0, std::bitset<16>(ai0), ai1, std::bitset<16>(static_cast<uint16_t>(ai1)), std::bitset<16>(static_cast<uint16_t>(ai2)));
1139
1140 236 w++;
1141 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 2 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1142
1/2
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
236 ai2 |= static_cast<int16_t>((sfrbx.dwrd.at(w) >> 20) & 0b111111111111); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
1143 236 ai2 = math::interpretAs<int16_t, 14>(ai2);
1144
1/2
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
236 auto BGD_E1_E5a = math::interpretAs<int16_t, 10>((sfrbx.dwrd.at(w) >> 5) & 0b1111111111);
1145
1/2
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
236 auto BGD_E1_E5b = static_cast<int16_t>((sfrbx.dwrd.at(w) & 0b11111) << 5);
1146 LOG_DATA("{}: [{}] ai2 {} ({}), BGD_E1_E5a {}, BGD_E1_E5b {}", nameId(), satId, ai2, std::bitset<16>(static_cast<uint16_t>(ai2)), BGD_E1_E5a, std::bitset<16>(static_cast<uint16_t>(BGD_E1_E5b)));
1147
1148 236 w++;
1149 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 3 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1150
1/2
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
236 BGD_E1_E5b |= static_cast<int16_t>((sfrbx.dwrd.at(w) >> 27) & 0b11111); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
1151 236 BGD_E1_E5b = math::interpretAs<int16_t, 10>(BGD_E1_E5b);
1152
1/2
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
236 auto E5b_HS = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 25) & 0b11);
1153
1/2
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
236 auto E1b_HS = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 23) & 0b11);
1154
1/2
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
236 auto E5b_DVS = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 22) & 0b1);
1155
1/2
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
236 auto E1b_DVS = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 21) & 0b1);
1156
1/2
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
236 [[maybe_unused]] auto WN = static_cast<uint16_t>((sfrbx.dwrd.at(w) >> 9) & 0b111111111111);
1157
1/2
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
236 [[maybe_unused]] uint32_t TOW = (sfrbx.dwrd.at(w) & 0b111111111) << 11;
1158 LOG_DATA("{}: [{}] BGD_E1_E5b {} ({}), E5b_HS {}, E1b_HS {}, E5b_DVS {}, E1b_DVS {}, WN {}, TOW {}", nameId(), satId,
1159 BGD_E1_E5b, std::bitset<16>(static_cast<uint16_t>(BGD_E1_E5b)), E5b_HS, E1b_HS, E5b_DVS, E1b_DVS, WN, std::bitset<32>(TOW));
1160
1161 236 w++;
1162 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 4 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1163
1/2
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
236 TOW |= (sfrbx.dwrd.at(w) >> 21) & 0b11111111111;
1164 LOG_DATA("{}: [{}] TOW {} ({})", nameId(), satId, TOW, std::bitset<32>(TOW));
1165
1166 236 w++;
1167 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 5 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1168
1169
1/2
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
236 auto ephemerisBuilder = getLastEphemerisBuilder(satId);
1170
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 233 times.
236 if (!ephemerisBuilder.has_value())
1171 {
1172 LOG_DATA("{}: [{}] Could not find any ephemeris builder", nameId(), satId);
1173 3 return;
1174 }
1175 233 auto ephemeris = std::dynamic_pointer_cast<GalileoEphemeris>(ephemerisBuilder->get().navData);
1176
1177 233 ephemeris->BGD_E1_E5a = BGD_E1_E5a * std::pow(2.0, -32);
1178 233 ephemeris->BGD_E1_E5b = BGD_E1_E5b * std::pow(2.0, -32);
1179 233 ephemeris->svHealth = {
1180 .E5a_DataValidityStatus = {},
1181 .E5b_DataValidityStatus = static_cast<GalileoEphemeris::SvHealth::DataValidityStatus>(E5b_DVS),
1182 .E1B_DataValidityStatus = static_cast<GalileoEphemeris::SvHealth::DataValidityStatus>(E1b_DVS),
1183 .E5a_SignalHealthStatus = {},
1184 .E5b_SignalHealthStatus = static_cast<GalileoEphemeris::SvHealth::SignalHealthStatus>(E5b_HS),
1185 .E1B_SignalHealthStatus = static_cast<GalileoEphemeris::SvHealth::SignalHealthStatus>(E1b_HS),
1186 };
1187 233 ephemeris->dataSource[0] = true; // I/NAV E1-B
1188 233 ephemeris->dataSource[9] = true; // af0-af2, Toc, SISA are for E5b,E1
1189
1190 {
1191
1/2
✓ Branch 1 taken 233 times.
✗ Branch 2 not taken.
233 std::unique_lock guard(outputPins.at(OUTPUT_PORT_INDEX_GNSS_NAV_INFO).dataAccessMutex, std::defer_lock);
1192
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 233 times.
233 if (!_postProcessingLock.has_value())
1193 {
1194 guard.lock();
1195 }
1196
1197
3/4
✓ Branch 1 taken 233 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 232 times.
233 if (!_gnssNavInfo.ionosphericCorrections.contains(satId.satSys, IonosphericCorrections::Alpha))
1198 {
1199
4/8
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
1 LOG_DEBUG("{}: [{}] Received Ionospheric corrections [{}]", nameId(), satId.satSys, ephemeris->refTime.toYMDHMS(GPST));
1200 }
1201
1202 // ‘sfu’ (solar flux unit) is not a SI unit but can be converted as: 1 sfu = 10e-22 W/(m2*Hz)
1203 _gnssNavInfo.ionosphericCorrections.insert(satId.satSys, IonosphericCorrections::Alpha,
1204 {
1205 233 ai0 * std::pow(2.0, -2) /* [sfu] */,
1206 233 ai1 * std::pow(2.0, -8) /* [sfu/degree] */,
1207
1/2
✓ Branch 2 taken 233 times.
✗ Branch 3 not taken.
233 ai2 * std::pow(2.0, -15) /* [sfu/degree^2] */,
1208 0.0,
1209 });
1210 233 }
1211
1212 LOG_DATA("{}: [{}] BGD_E1_E5a [{:.3e} s], BGD_E1_E5b [{:.3e} rad]", nameId(), satId, ephemeris->BGD_E1_E5a, ephemeris->BGD_E1_E5b);
1213
1214
1/2
✓ Branch 4 taken 233 times.
✗ Branch 5 not taken.
233 finishWord(ephemeris, wordType, ephemerisBuilder->get().subframes);
1215 233 }
1216
2/2
✓ Branch 0 taken 237 times.
✓ Branch 1 taken 2176 times.
2413 else if (wordType == 6) // GST-UTC conversion parameters
1217 {
1218 // Galileo-OS-SIS-ICD-v2.0: ch. 4.3.5, Table 44 Bits Allocation for I/NAV Word Type 6, p. 38
1219 //
1220 // Even/Odd PageType WordType A0 A0 A1 dt_LS t0t WNot WN_LSF DN dt_LSF TOW Tail Pad Even/Odd PageType TOW Spare Reserved 1
1221 // 1 1 6 24 8 24 8 8 8 8 3 8 7 6 8 1 1 13 3 14
1222 // └───────────────┬──────────────┘ └──┬─┘ └──────────┬───────────┘ └────────────┬───────────┘ └────────────────────┬───────────────────┘
1223 // Ublox 1 2 3 4 5
1224
1225 // Galileo-OS-SIS-ICD-v2.0: ch. 5.1.7, Table 68 Ionospheric Correction Parameters, p. 49
1226 // > A0, A1, dt_LS, dt_LSF shall be two's complement, with the sign bit (+ or -) occupying the MSB
1227
1228 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 1 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1229
1/2
✓ Branch 1 taken 237 times.
✗ Branch 2 not taken.
237 auto A0 = static_cast<int32_t>((sfrbx.dwrd.at(w) & 0b111111111111111111111111) << 8);
1230 LOG_DATA("{}: [{}] A0 {}", nameId(), satId, std::bitset<32>(static_cast<uint32_t>(A0)));
1231
1232 237 w++;
1233 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 2 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1234
1/2
✓ Branch 1 taken 237 times.
✗ Branch 2 not taken.
237 A0 |= static_cast<int32_t>((sfrbx.dwrd.at(w) >> 24) & 0b11111111);
1235
1/2
✓ Branch 1 taken 237 times.
✗ Branch 2 not taken.
237 auto A1 = math::interpretAs<int32_t, 24>(sfrbx.dwrd.at(w) & 0b111111111111111111111111);
1236 LOG_DATA("{}: [{}] A0 {} ({}), A1 {} ({})", nameId(), satId, A0, std::bitset<32>(static_cast<uint32_t>(A0)), A1, std::bitset<32>(static_cast<uint32_t>(A1)));
1237
1238 237 w++;
1239 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 3 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1240
1241 237 w++;
1242 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 4 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1243
1244 237 w++;
1245 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 5 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1246
1247
1/2
✓ Branch 1 taken 237 times.
✗ Branch 2 not taken.
237 std::unique_lock guard(outputPins.at(OUTPUT_PORT_INDEX_GNSS_NAV_INFO).dataAccessMutex, std::defer_lock);
1248
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 237 times.
237 if (!_postProcessingLock.has_value())
1249 {
1250 guard.lock();
1251 }
1252
1253
1/2
✓ Branch 1 taken 237 times.
✗ Branch 2 not taken.
237 auto key = std::make_pair(satId.satSys.getTimeSystem(), UTC);
1254
3/4
✓ Branch 2 taken 237 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 236 times.
237 if (!_gnssNavInfo.timeSysCorr.contains(key))
1255 {
1256
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
2 if (auto ephemerisBuilder = getLastEphemerisBuilder(satId);
1257 1 ephemerisBuilder.has_value())
1258 {
1259 auto ephemeris = std::dynamic_pointer_cast<GalileoEphemeris>(ephemerisBuilder->get().navData);
1260 LOG_DEBUG("{}: [{}] Received Time system corrections [{}]", nameId(), satId.satSys, ephemeris->refTime.toYMDHMS(GPST));
1261 }
1262 else
1263 {
1264
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
1 LOG_DEBUG("{}: [{}] Received Time system corrections", nameId(), satId.satSys);
1265 }
1266 }
1267
1268
1/2
✓ Branch 2 taken 237 times.
✗ Branch 3 not taken.
237 _gnssNavInfo.timeSysCorr[key] = GnssNavInfo::TimeSystemCorrections{
1269 237 .a0 = A0 * std::pow(2, -30),
1270 237 .a1 = A1 * std::pow(2, -50),
1271 };
1272 237 }
1273 }
1274
1275 void NAV::UbloxGnssOrbitCollector::decryptGLONASS([[maybe_unused]] const SatId& satId, const ubx::UbxRxmSfrbx& /* sfrbx */, const InsTime& /* insTime */)
1276 {
1277 // u-blox 8 / u-blox M8: Receiver description - Including protocol specification, ch. 10.3, p. 31
1278 // > For GLONASS L1OF and L2OF signals, each reported subframe contains a string as described in
1279 // > the GLONASS ICD. This string comprises 85 data bits which are reported over three 32 bit words
1280 // > in the UBX-RXM-SFRBX message. Data bits 1 to 8 are always a hamming code, whilst bits 81 to 84
1281 // > are a string number and bit 85 is the idle chip, which should always have a value of zero. The
1282 // > meaning of other bits vary with string and frame number.
1283 // > The fourth and final 32 bit word in the UBX-RXM-SFRBX message contains frame and superframe
1284 // > numbers (where available). These values aren't actually transmitted by the SVs, but are deduced
1285 // > by the receiver and are included to aid decoding of the transmitted data. However, the receiver
1286 // > does not always know these values, in which case a value of zero is reported.
1287 // >
1288 // > MSB LSB
1289 // > 1: Idle chip (1 bit, always 0) String # (4 bits) Data (80 - 54) (27 bits)
1290 // > 2: Data (53 - 22) (32 bits)
1291 // > 3: Data (21 - 9) (13 bits) Hammering code (8 bits) Pad (11 bits)
1292 // > 4: Superframe # (16 bits) Pad (8 bits) Frame # (8 bits)
1293 // >
1294 // > In some circumstances, (especially on startup) the receiver may be able to decode data from a
1295 // > GLONASS SV before it can identify the SV. When this occurs UBX-RXM-SFRBX messages will be
1296 // > issued with an svId of 255 to indicate "unknown".
1297
1298 if (!_warningsNotImplemented.contains(satId.satSys))
1299 {
1300 LOG_WARN("{}: ubx orbit collector is not implemented for GLONASS yet.", nameId()); // TODO: not implemented yet.
1301 _warningsNotImplemented.insert(satId.satSys);
1302 }
1303
1304 // auto ephemeris = std::dynamic_pointer_cast<GLONASSEphemeris>(ephemerisBuilder->get().navData);
1305
1306 // sfrbx.freqId;
1307 // _gnssNavInfo.addSatelliteNavData({ satSys, satNum }, std::make_shared<GLONASSEphemeris>(epoch, tau_c,
1308 // -m_tau_n, gamma_n, static_cast<bool>(health),
1309 // pos, vel, accelLuniSolar,
1310 // frequencyNumber_accuracyCode));
1311 }
1312
1313 void NAV::UbloxGnssOrbitCollector::decryptBeiDou([[maybe_unused]] const SatId& satId, const ubx::UbxRxmSfrbx& /* sfrbx */, const InsTime& /* insTime */)
1314 {
1315 // u-blox 8 / u-blox M8: Receiver description - Including protocol specification, ch. 10.4, p. 32
1316 // > For BeiDou (B1I) signals, there is a fairly straightforward mapping between the reported subframe
1317 // > and the structure of subframe and words described in the BeiDou ICD. Each subframe comprises
1318 // > ten data words, which are reported in the same order they are received.
1319 // >
1320 // > MSB LSB
1321 // > 1 to 10: Pad (2 bits) Data (22 bits) Parity (8 bits)
1322 // >
1323 // > Note that as the BeiDou data words only comprise 30 bits, the 2 most significant bits in each word
1324 // > reported by UBX-RXM-SFRBX are padding and should be ignored.
1325
1326 if (!_warningsNotImplemented.contains(satId.satSys))
1327 {
1328 LOG_WARN("{}: ubx orbit collector is not implemented for BeiDou yet.", nameId()); // TODO: not implemented yet.
1329 _warningsNotImplemented.insert(satId.satSys);
1330 }
1331
1332 // auto ephemeris = std::dynamic_pointer_cast<BDSEphemeris>(ephemerisBuilder->get().navData);
1333
1334 // _gnssNavInfo.addSatelliteNavData({ satSys, satNum }, std::make_shared<BDSEphemeris>(epoch, toe,
1335 // IODE_IODnav_AODE_IODEC, fitInterval_AODC, a,
1336 // sqrt_A, e, i_0, Omega_0, omega, M_0,
1337 // delta_n, Omega_dot, i_dot, Cus, Cuc,
1338 // Cis, Cic, Crs, Crc,
1339 // signalAccuracy, svHealth,
1340 // tgd_bgd5a_TGD1, IODC_bgd5b_TGD2));
1341 }
1342
1343 void NAV::UbloxGnssOrbitCollector::decryptQZSS([[maybe_unused]] const SatId& satId, const ubx::UbxRxmSfrbx& /* sfrbx */, const InsTime& /* insTime */)
1344 {
1345 // u-blox 8 / u-blox M8: Receiver description - Including protocol specification, ch. 10.7, p. 34
1346 // > The structure of the data delivered by QZSS L1C/A signals is effectively identical to that of GPS
1347 // > (L1C/A). Similarly the QZSS L2C signal is effectively identical to the GPS (L2C).
1348 // > The QZSS (L1SAIF) signal is different and uses the same data block format as used by SBAS
1349 // > (L1C/A). QZSS (SAIF) signals can be distinguished from QZSS (L1C/A and L2C) by noting that they
1350 // > have 8 words, instead of 10 for QZSS (L1C/A and L2C).
1351
1352 if (!_warningsNotImplemented.contains(satId.satSys))
1353 {
1354 LOG_WARN("{}: ubx orbit collector is not implemented for QZSS yet.", nameId()); // TODO: not implemented yet.
1355 _warningsNotImplemented.insert(satId.satSys);
1356 }
1357 }
1358
1359 void NAV::UbloxGnssOrbitCollector::decryptIRNSS([[maybe_unused]] const SatId& satId, const ubx::UbxRxmSfrbx& /* sfrbx */, const InsTime& /* insTime */)
1360 {
1361 if (!_warningsNotImplemented.contains(satId.satSys))
1362 {
1363 LOG_WARN("{}: ubx orbit collector is not implemented for IRNSS yet.", nameId()); // TODO: not implemented yet.
1364 _warningsNotImplemented.insert(satId.satSys);
1365 }
1366 }
1367
1368 void NAV::UbloxGnssOrbitCollector::decryptSBAS([[maybe_unused]] const SatId& satId, const ubx::UbxRxmSfrbx& /* sfrbx */, const InsTime& /* insTime */)
1369 {
1370 // u-blox 8 / u-blox M8: Receiver description - Including protocol specification, ch. 10.6, p. 33f
1371
1372 if (!_warningsNotImplemented.contains(satId.satSys))
1373 {
1374 LOG_WARN("{}: ubx orbit collector is not implemented for SBAS yet.", nameId()); // TODO: not implemented yet.
1375 _warningsNotImplemented.insert(satId.satSys);
1376 }
1377 }
1378