INSTINCT Code Coverage Report


Directory: src/
File: Nodes/Converter/GNSS/UbloxGnssOrbitCollector.cpp
Date: 2025-11-25 23:34:18
Exec Total Coverage
Lines: 470 599 78.5%
Functions: 16 25 64.0%
Branches: 319 820 38.9%

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