0.5.1
Loading...
Searching...
No Matches
UbloxGnssOrbitCollector.cpp
Go to the documentation of this file.
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
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"
19
20#include <fmt/format.h>
22
29
40
45
47{
48 return "UbloxGnssOrbitCollector";
49}
50
52{
53 return typeStatic();
54}
55
57{
58 return "Converter";
59}
60
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
144{
145 LOG_TRACE("{}: called", nameId());
146 if (!_postProcessingLock.has_value())
147 {
149 _gnssNavInfo.reset();
150 }
151 else
152 {
153 _gnssNavInfo.reset();
154 }
155 _ephemerisBuilder.clear();
156 _lastAccessedBuilder.clear();
158
159 if (inputPins.at(INPUT_PORT_INDEX_UBLOX_OBS).isPinLinked()
161 && !_postProcessingLock.has_value())
162 {
163 LOG_TRACE("{}: Setting post-processing lock", nameId());
165 }
166
167 return true;
168}
169
170void 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 if (_postProcessingLock.has_value())
175 {
176 _postProcessingLock.reset();
177 }
178}
179
181{
182 LOG_DATA("{}: Searching for [{}] at [{}]", nameId(), satId, insTime.toYMDHMS(GPST));
183 if (IOD != 0)
184 {
185 _lastAccessedBuilder[satId] = IOD;
186 }
187
188 auto iter = std::ranges::find_if(_ephemerisBuilder, [&](const auto& builder) {
189 return builder.satId == satId && builder.navData->refTime == insTime;
190 });
191 if (iter == _ephemerisBuilder.end())
192 {
193 LOG_DATA("{}: Constructing new builder", nameId());
194
195 std::shared_ptr<SatNavData> satNavData = nullptr;
196 switch (SatelliteSystem_(satId.satSys))
197 {
198 case GPS:
199 satNavData = std::make_shared<GPSEphemeris>(insTime);
200 break;
201 case GAL:
202 satNavData = std::make_shared<GalileoEphemeris>(insTime);
203 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 return _ephemerisBuilder.emplace_back(satId, satNavData);
226 }
227
228 LOG_DATA("{}: Found builder", nameId());
229 return *iter;
230}
231
232std::optional<std::reference_wrapper<NAV::UbloxGnssOrbitCollector::EphemerisBuilder>>
234{
235 LOG_DATA("{}: Searching for [{}] at Issue of Data [{}]", nameId(), satId, IOD);
236 _lastAccessedBuilder[satId] = IOD;
237
238 auto iter = std::ranges::find_if(_ephemerisBuilder, [&](const auto& builder) {
239 if (builder.satId == satId)
240 {
241 if (builder.navData->type == SatNavData::GPSEphemeris)
242 {
243 auto ephemeris = std::dynamic_pointer_cast<GPSEphemeris>(builder.navData);
244 return ephemeris && ephemeris->IODE == IOD;
245 }
246 if (builder.navData->type == SatNavData::GalileoEphemeris)
247 {
248 auto ephemeris = std::dynamic_pointer_cast<GalileoEphemeris>(builder.navData);
249 return ephemeris && ephemeris->IODnav == IOD;
250 }
251 }
252 return false;
253 });
254 if (iter != _ephemerisBuilder.end())
255 {
256 LOG_DATA("{}: Found builder", nameId());
257 return *iter;
258 }
259 LOG_DATA("{}: Could not find builder. Ignoring subframe.", nameId());
260 return std::nullopt;
261}
262
263std::optional<std::reference_wrapper<NAV::UbloxGnssOrbitCollector::EphemerisBuilder>>
265{
266 LOG_DATA("{}: Searching the last builder for [{}]", nameId(), satId);
267 if (_lastAccessedBuilder.contains(satId))
268 {
269 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
276{
277 [[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 if (ubloxObs->msgClass == ubx::UBX_CLASS_RXM)
283 {
284 if (static_cast<ubx::UbxRxmMessages>(ubloxObs->msgId) == ubx::UbxRxmMessages::UBX_RXM_SFRBX)
285 {
286 const auto& sfrbx = std::get<ubx::UbxRxmSfrbx>(ubloxObs->data);
287
288 SatelliteSystem satSys = ubx::getSatSys(sfrbx.gnssId);
289 SatId satId(satSys, sfrbx.svId);
290 LOG_DATA("{}: [{}][{}] Converting message", nameId(), ubloxObs->insTime.toYMDHMS(GPST), satId);
291
292 switch (SatelliteSystem_(satSys))
293 {
294 case GPS:
295 decryptGPS(satId, sfrbx, ubloxObs->insTime);
296 break;
297 case GAL:
298 decryptGalileo(satId, sfrbx, ubloxObs->insTime);
299 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 if (_postProcessingLock.has_value()
323 && inputPins.at(INPUT_PORT_INDEX_UBLOX_OBS).isPinLinked()
324 && 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}
330
331void 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 if (sfrbx.numWords != 10)
345 {
346 LOG_ERROR("{}: [{}] Received {} instead of 10 words", nameId(), satId, sfrbx.numWords);
347 return;
348 }
349
350 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 constexpr uint8_t TLM_PREAMBLE = 0b10001011;
360 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 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 w++;
373 LOG_DATA("{}: [{}] how: {} {}", nameId(), satId, std::bitset<2>(sfrbx.dwrd.at(w) >> 30), std::bitset<30>(sfrbx.dwrd.at(w)));
374 auto subFrameId = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 8) & 0b111);
375 LOG_DATA("{}: [{}] subFrameId: {}", nameId(), satId, subFrameId);
376
377 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 auto gpsWeekToW = insTime.toGPSweekTow(GPST);
384
385 auto finishSubFrame = [&]<size_t N>(const std::shared_ptr<SatNavData>& ephemeris, size_t subFrameId, std::bitset<N>& subframesFound) {
386 if (1 <= subFrameId && subFrameId <= 3)
387 {
388 subframesFound.set(subFrameId - 1);
389
390 LOG_DATA("{}: [{}] subframesFound: {}", nameId(), satId, subframesFound);
391 if (subframesFound.count() == 3)
392 {
393 LOG_DATA("{}: [{}] [{}] All subframes found. Updating gnnsNavInfo", nameId(), satId, ephemeris->refTime.toYMDHMS(GPST));
394 if (!_gnssNavInfo.searchNavigationData(satId, ephemeris->refTime))
395 {
396 LOG_DEBUG("{}: Adding new ephemeris data [{}] [{}]", nameId(), satId, ephemeris->refTime.toYMDHMS(GPST));
397 }
398 std::unique_lock guard(outputPins.at(OUTPUT_PORT_INDEX_GNSS_NAV_INFO).dataAccessMutex, std::defer_lock);
399 if (!_postProcessingLock.has_value())
400 {
401 guard.lock();
402 }
403 _gnssNavInfo.satelliteSystems |= satId.satSys;
404 _gnssNavInfo.addSatelliteNavData(satId, ephemeris);
405 }
406 }
407 };
408
409 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 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 [[maybe_unused]] auto WN = static_cast<uint16_t>((sfrbx.dwrd.at(w) >> 20) & 0b1111111111); // 10 BITS - Transmission Week Number
420 auto L2ChannelCodes = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 18) & 0b11); // 2 BITS
421 auto uraIndex = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 14) & 0b1111); // 4 BITS
422 auto svHealth = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 8) & 0b111111); // 6 BITS
423 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 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 auto L2DataFlagPCode = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 29) & 0b1); // 1 BIT
429 LOG_DATA("{}: [{}] L2DataFlagPCode {}", nameId(), satId, L2DataFlagPCode);
430
431 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 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 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 auto T_GD = static_cast<int8_t>((sfrbx.dwrd.at(w) >> 6) & 0b11111111);
440 LOG_DATA("{}: [{}] T_GD {}", nameId(), satId, T_GD);
441
442 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 IODC |= static_cast<uint16_t>((sfrbx.dwrd.at(w) >> 22) & 0b11111111); // 8 LSBs - 10 BITS TOTAL
445 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 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 auto af2 = static_cast<int8_t>((sfrbx.dwrd.at(w) >> 22) & 0b11111111); // 8 BITS
451 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 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 auto af0 = math::interpretAs<int32_t, 22>(sfrbx.dwrd.at(w) >> 8); // 22 BITS
457 LOG_DATA("{}: [{}] af0 {}", nameId(), satId, af0);
458
459 InsTime insTimeToc(gpsWeekToW.gpsCycle, gpsWeekToW.gpsWeek, toc * std::pow(2, 4), GPST);
460
461 auto& ephemerisBuilder = getEphemerisBuilder(satId, insTimeToc);
462 auto ephemeris = std::dynamic_pointer_cast<GPSEphemeris>(ephemerisBuilder.navData);
463 ephemeris->toc = insTimeToc;
464
465 ephemeris->L2ChannelCodes = L2ChannelCodes;
466 ephemeris->svAccuracy = gpsUraIdx2Val(uraIndex);
467 ephemeris->svHealth = svHealth;
468 ephemeris->IODC = IODC;
469 ephemeris->L2DataFlagPCode = L2DataFlagPCode;
470 ephemeris->T_GD = T_GD * std::pow(2, -31);
471 ephemeris->refTime = ephemeris->toc;
472 ephemeris->a = {
473 af0 * std::pow(2, -31),
474 af1 * std::pow(2, -43),
475 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 finishSubFrame(ephemeris, subFrameId, ephemerisBuilder.subframes);
483 }
484 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 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 auto IODE = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 22) & 0b11111111); // 8 BITS
495 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 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 auto delta_n = static_cast<int16_t>((sfrbx.dwrd.at(w) >> 14) & 0b1111111111111111); // 16 BITS
501 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 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 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 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 auto Cuc = static_cast<int16_t>((sfrbx.dwrd.at(w) >> 14) & 0b1111111111111111); // 16 BITS
512 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 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 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 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 auto Cus = static_cast<int16_t>((sfrbx.dwrd.at(w) >> 14) & 0b1111111111111111); // 16 BITS
523 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 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 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 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 auto toe = static_cast<uint16_t>((sfrbx.dwrd.at(w) >> 14) & 0b1111111111111111); // 16 BITS
534 auto fitInterval = static_cast<bool>((sfrbx.dwrd.at(w) >> 13) & 0b1); // 1 BIT
535 [[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 InsTime insTimeToe(gpsWeekToW.gpsCycle, gpsWeekToW.gpsWeek, toe * std::pow(2, 4), GPST);
539
540 auto& ephemerisBuilder = getEphemerisBuilder(satId, insTimeToe);
541 auto ephemeris = std::dynamic_pointer_cast<GPSEphemeris>(ephemerisBuilder.navData);
542 ephemeris->toe = insTimeToe;
543
544 ephemeris->IODE = IODE;
545 ephemeris->Crs = Crs * std::pow(2, -5);
546 ephemeris->delta_n = semicircles2rad(delta_n * std::pow(2, -43));
547 ephemeris->M_0 = semicircles2rad(M_0 * std::pow(2, -31));
548 ephemeris->Cuc = Cuc * std::pow(2, -29);
549 ephemeris->e = e * std::pow(2, -33);
550 ephemeris->Cus = Cus * std::pow(2, -29);
551 ephemeris->sqrt_A = sqrt_A * std::pow(2, -19);
552 ephemeris->toe = InsTime(gpsWeekToW.gpsCycle, gpsWeekToW.gpsWeek, toe * std::pow(2, 4), GPST);
553 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 finishSubFrame(ephemeris, subFrameId, ephemerisBuilder.subframes);
562 }
563 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 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 auto Cic = static_cast<int16_t>((sfrbx.dwrd.at(w) >> 14) & 0b1111111111111111); // 16 BITS
574 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 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 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 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 auto Cis = static_cast<int16_t>((sfrbx.dwrd.at(w) >> 14) & 0b1111111111111111); // 16 BITS
585 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 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 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 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 auto Crc = static_cast<int16_t>((sfrbx.dwrd.at(w) >> 14) & 0b1111111111111111); // 16 BITS
596 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 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 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 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 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 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 auto IODE = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 22) & 0b11111111); // 8 BITS
612 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 auto ephemerisBuilder = getEphemerisBuilder(satId, IODE);
616 if (!ephemerisBuilder.has_value())
617 {
618 LOG_DATA("{}: [{}] Could not find Ephemeris builder for IODE {}", nameId(), satId, IODE);
619 return;
620 }
621 auto ephemeris = std::dynamic_pointer_cast<GPSEphemeris>(ephemerisBuilder->get().navData);
622
623 ephemeris->Cic = Cic * std::pow(2, -29);
624 ephemeris->Omega_0 = semicircles2rad(Omega_0 * std::pow(2, -31));
625 ephemeris->Cis = Cis * std::pow(2, -29);
626 ephemeris->i_0 = semicircles2rad(i_0 * std::pow(2, -31));
627 ephemeris->Crc = Crc * std::pow(2, -5);
628 ephemeris->omega = semicircles2rad(omega * std::pow(2, -31));
629 ephemeris->Omega_dot = semicircles2rad(Omega_dot * std::pow(2, -43));
630 ephemeris->IODE = IODE;
631 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 finishSubFrame(ephemeris, subFrameId, ephemerisBuilder->get().subframes);
639 }
640 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 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 [[maybe_unused]] auto dataId = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 28) & 0b11); // 2 BITS
659 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 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 auto alpha0 = static_cast<int8_t>((sfrbx.dwrd.at(w) >> 14) & 0b11111111); // 8 BITS
671 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 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 auto alpha2 = static_cast<int8_t>((sfrbx.dwrd.at(w) >> 22) & 0b11111111); // 8 BITS
677 auto alpha3 = static_cast<int8_t>((sfrbx.dwrd.at(w) >> 14) & 0b11111111); // 8 BITS
678 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 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 auto beta1 = static_cast<int8_t>((sfrbx.dwrd.at(w) >> 22) & 0b11111111); // 8 BITS
684 auto beta2 = static_cast<int8_t>((sfrbx.dwrd.at(w) >> 14) & 0b11111111); // 8 BITS
685 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 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 auto A1 = math::interpretAs<int32_t, 24>(sfrbx.dwrd.at(w) >> 6); // 24 BITS
691 LOG_DATA("{}: [{}] A1 {}", nameId(), satId, A1);
692
693 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 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 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 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 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 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 std::unique_lock guard(outputPins.at(OUTPUT_PORT_INDEX_GNSS_NAV_INFO).dataAccessMutex, std::defer_lock);
710 if (!_postProcessingLock.has_value())
711 {
712 guard.lock();
713 }
714 if (!_gnssNavInfo.ionosphericCorrections.contains(satId.satSys, IonosphericCorrections::Alpha))
715 {
716 if (auto ephemerisBuilder = getLastEphemerisBuilder(satId);
717 ephemerisBuilder.has_value())
718 {
719 auto ephemeris = std::dynamic_pointer_cast<GPSEphemeris>(ephemerisBuilder->get().navData);
720 LOG_DEBUG("{}: [{}] Received Ionospheric and Time system corrections [{}]", nameId(), satId.satSys, ephemeris->refTime.toYMDHMS(GPST));
721 }
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 alpha0 * std::pow(2, -30) /* [s] */,
731 alpha1 * std::pow(2, -27) /* [s/semi-circle] */,
732 alpha2 * std::pow(2, -24) /* [s/semi-circle^2] */,
733 alpha3 * std::pow(2, -24) /* [s/semi-circle^3] */,
734 });
735 _gnssNavInfo.ionosphericCorrections.insert(satId.satSys, IonosphericCorrections::Beta,
736 {
737 beta0 * std::pow(2, 11) /* [s] */,
738 beta1 * std::pow(2, 14) /* [s/semi-circle] */,
739 beta2 * std::pow(2, 16) /* [s/semi-circle^2] */,
740 beta3 * std::pow(2, 16) /* [s/semi-circle^3] */,
741 });
743 .a0 = A0 * std::pow(2, -30),
744 .a1 = A1 * std::pow(2, -50),
745 };
746 }
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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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
800void 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 if (sfrbx.numWords != 8)
823 {
824 LOG_ERROR("{}: [{}] Received {} instead of 8 words", nameId(), satId, sfrbx.numWords);
825 return;
826 }
827
828 size_t w = 0; // Word counter
829
830 auto even = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 31) & 0b1);
831 auto pageTypeEven = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 30) & 0b1);
832 auto odd = static_cast<uint8_t>((sfrbx.dwrd.at(4) >> 31) & 0b1);
833 auto pageTypeOdd = static_cast<uint8_t>((sfrbx.dwrd.at(4) >> 30) & 0b1);
834 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 if (even != 0 || pageTypeEven == 1
838 || odd != 1 || pageTypeOdd == 1)
839 {
840 LOG_DATA("{}: [{}] Ignoring message, because one of the page types is alert page", nameId(), satId);
841 return;
842 }
843
844 auto gpsWeekToW = insTime.toGPSweekTow(GST);
845
846 auto finishWord = [&]<size_t N>(const std::shared_ptr<SatNavData>& ephemeris, size_t wordType, std::bitset<N>& subframesFound) {
847 if (1 <= wordType && wordType <= 5)
848 {
849 subframesFound.set(wordType - 1);
850
851 LOG_DATA("{}: [{}] subframesFound: {}", nameId(), satId, subframesFound);
852 if (subframesFound.count() == 5)
853 {
854 LOG_DATA("{}: [{}] [{}] All words found. Updating gnnsNavInfo", nameId(), satId, ephemeris->refTime.toYMDHMS(GPST));
855 if (!_gnssNavInfo.searchNavigationData(satId, ephemeris->refTime))
856 {
857 LOG_DEBUG("{}: Adding new ephemeris data [{}] [{}]", nameId(), satId, ephemeris->refTime.toYMDHMS(GPST));
858 }
859 std::unique_lock guard(outputPins.at(OUTPUT_PORT_INDEX_GNSS_NAV_INFO).dataAccessMutex, std::defer_lock);
860 if (!_postProcessingLock.has_value())
861 {
862 guard.lock();
863 }
864 _gnssNavInfo.satelliteSystems |= satId.satSys;
865 _gnssNavInfo.addSatelliteNavData(satId, ephemeris);
866 }
867 }
868 };
869
870 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 auto IODnav = static_cast<uint16_t>((sfrbx.dwrd.at(w) >> 14) & 0b1111111111);
884 auto toe = static_cast<uint16_t>(sfrbx.dwrd.at(w) & 0b11111111111111);
885 LOG_DATA("{}: [{}] IODnav {}, toe {}", nameId(), satId, IODnav, toe);
886
887 w++;
888 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 2 */, std::bitset<32>(sfrbx.dwrd.at(w)));
889 auto M_0 = static_cast<int32_t>(sfrbx.dwrd.at(w));
890 LOG_DATA("{}: [{}] M_0 {}", nameId(), satId, M_0);
891
892 w++;
893 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 3 */, std::bitset<32>(sfrbx.dwrd.at(w)));
894 uint32_t e = sfrbx.dwrd.at(w);
895 LOG_DATA("{}: [{}] e {}", nameId(), satId, e);
896
897 w++;
898 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 4 */, std::bitset<32>(sfrbx.dwrd.at(w)));
899 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 w++;
903 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 5 */, std::bitset<32>(sfrbx.dwrd.at(w)));
904 sqrt_A |= (sfrbx.dwrd.at(w) >> 16) & 0b11111111111111;
905 LOG_DATA("{}: [{}] sqrt_A {} ({})", nameId(), satId, sqrt_A, std::bitset<32>(sqrt_A));
906
907 InsTime insTimeToe(gpsWeekToW.gpsCycle, gpsWeekToW.gpsWeek, toe * 60.0, GST);
908
909 auto& ephemerisBuilder = getEphemerisBuilder(satId, insTimeToe, IODnav);
910 auto ephemeris = std::dynamic_pointer_cast<GalileoEphemeris>(ephemerisBuilder.navData);
911 ephemeris->toe = insTimeToe;
912
913 ephemeris->IODnav = IODnav;
914 ephemeris->M_0 = semicircles2rad(M_0 * std::pow(2.0, -31));
915 ephemeris->e = e * std::pow(2.0, -33);
916 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 finishWord(ephemeris, wordType, ephemerisBuilder.subframes);
922 }
923 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 auto IODnav = static_cast<uint16_t>((sfrbx.dwrd.at(w) >> 14) & 0b1111111111);
937 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 w++;
941 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 2 */, std::bitset<32>(sfrbx.dwrd.at(w)));
942 Omega_0 |= static_cast<int32_t>((sfrbx.dwrd.at(w) >> 14) & 0b111111111111111111);
943 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 w++;
947 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 3 */, std::bitset<32>(sfrbx.dwrd.at(w)));
948 i_0 |= static_cast<int32_t>((sfrbx.dwrd.at(w) >> 14) & 0b111111111111111111);
949 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 w++;
953 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 4 */, std::bitset<32>(sfrbx.dwrd.at(w)));
954 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 w++;
958 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 5 */, std::bitset<32>(sfrbx.dwrd.at(w)));
959 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 auto ephemerisBuilder = getEphemerisBuilder(satId, IODnav);
963 if (!ephemerisBuilder.has_value())
964 {
965 LOG_DATA("{}: [{}] Could not find Ephemeris builder for IODnav {}", nameId(), satId, IODnav);
966 return;
967 }
968 auto ephemeris = std::dynamic_pointer_cast<GalileoEphemeris>(ephemerisBuilder->get().navData);
969
970 ephemeris->Omega_0 = semicircles2rad(Omega_0 * std::pow(2.0, -31));
971 ephemeris->i_0 = semicircles2rad(i_0 * std::pow(2.0, -31));
972 ephemeris->omega = semicircles2rad(omega * std::pow(2.0, -31));
973 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 finishWord(ephemeris, wordType, ephemerisBuilder->get().subframes);
979 }
980 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 auto IODnav = static_cast<uint16_t>((sfrbx.dwrd.at(w) >> 14) & 0b1111111111);
994 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 w++;
998 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 2 */, std::bitset<32>(sfrbx.dwrd.at(w)));
999 Omega_dot |= static_cast<int32_t>((sfrbx.dwrd.at(w) >> 22) & 0b1111111111);
1000 Omega_dot = math::interpretAs<int32_t, 24>(Omega_dot);
1001 auto delta_n = static_cast<int16_t>((sfrbx.dwrd.at(w) >> 6) & 0b1111111111111111);
1002 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 w++;
1007 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 3 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1008 Cuc |= static_cast<int16_t>((sfrbx.dwrd.at(w) >> 22) & 0b1111111111); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
1009 auto Cus = static_cast<int16_t>((sfrbx.dwrd.at(w) >> 6) & 0b1111111111111111);
1010 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 w++;
1014 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 4 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1015 Crc |= static_cast<int16_t>((sfrbx.dwrd.at(w) >> 22) & 0b1111111111); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
1016 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 w++;
1020 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 5 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1021 Crs |= static_cast<int16_t>((sfrbx.dwrd.at(w) >> 22) & 0b11111111); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
1022 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 auto ephemerisBuilder = getEphemerisBuilder(satId, IODnav);
1026 if (!ephemerisBuilder.has_value())
1027 {
1028 LOG_DATA("{}: [{}] Could not find Ephemeris builder for IODnav {}", nameId(), satId, IODnav);
1029 return;
1030 }
1031 auto ephemeris = std::dynamic_pointer_cast<GalileoEphemeris>(ephemerisBuilder->get().navData);
1032
1033 ephemeris->Omega_dot = semicircles2rad(Omega_dot * std::pow(2.0, -43));
1034 ephemeris->delta_n = semicircles2rad(delta_n * std::pow(2.0, -43));
1035 ephemeris->Cuc = Cuc * std::pow(2.0, -29);
1036 ephemeris->Cus = Cus * std::pow(2.0, -29);
1037 ephemeris->Crc = Crc * std::pow(2.0, -5);
1038 ephemeris->Crs = Crs * std::pow(2.0, -5);
1039 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 finishWord(ephemeris, wordType, ephemerisBuilder->get().subframes);
1045 }
1046 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 auto IODnav = static_cast<uint16_t>((sfrbx.dwrd.at(w) >> 14) & 0b1111111111);
1061 [[maybe_unused]] auto SVID = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 8) & 0b111111);
1062 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 w++;
1066 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 2 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1067 Cic |= static_cast<int16_t>((sfrbx.dwrd.at(w) >> 24) & 0b11111111); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
1068 auto Cis = static_cast<int16_t>((sfrbx.dwrd.at(w) >> 8) & 0b1111111111111111);
1069 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 w++;
1073 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 3 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1074 toc |= static_cast<uint16_t>((sfrbx.dwrd.at(w) >> 26) & 0b111111);
1075 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 w++;
1079 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 4 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1080 af0 |= static_cast<int32_t>((sfrbx.dwrd.at(w) >> 27) & 0b11111);
1082 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 w++;
1086 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 5 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1087 af1 |= static_cast<int32_t>((sfrbx.dwrd.at(w) >> 22) & 0b11111111);
1089 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 auto ephemerisBuilder = getEphemerisBuilder(satId, IODnav);
1093 if (!ephemerisBuilder.has_value())
1094 {
1095 LOG_DATA("{}: [{}] Could not find Ephemeris builder for IODnav {}", nameId(), satId, IODnav);
1096 return;
1097 }
1098 auto ephemeris = std::dynamic_pointer_cast<GalileoEphemeris>(ephemerisBuilder->get().navData);
1099
1100 InsTime insTimeToc(gpsWeekToW.gpsCycle, gpsWeekToW.gpsWeek, toc * 60.0, GST);
1101
1102 ephemeris->Cic = Cic * std::pow(2.0, -29);
1103 ephemeris->Cis = Cis * std::pow(2.0, -29);
1104 ephemeris->toc = insTimeToc;
1105 ephemeris->refTime = ephemeris->toc;
1106 ephemeris->a = {
1107 af0 * std::pow(2, -34),
1108 af1 * std::pow(2, -46),
1109 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 finishWord(ephemeris, wordType, ephemerisBuilder->get().subframes);
1117 }
1118 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 auto ai0 = static_cast<uint16_t>((sfrbx.dwrd.at(w) >> 13) & 0b11111111111);
1133 auto ai1 = math::interpretAs<int16_t, 11>((sfrbx.dwrd.at(w) >> 2) & 0b11111111111);
1134 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 w++;
1139 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 2 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1140 ai2 |= static_cast<int16_t>((sfrbx.dwrd.at(w) >> 20) & 0b111111111111); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
1142 auto BGD_E1_E5a = math::interpretAs<int16_t, 10>((sfrbx.dwrd.at(w) >> 5) & 0b1111111111);
1143 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 w++;
1147 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 3 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1148 BGD_E1_E5b |= static_cast<int16_t>((sfrbx.dwrd.at(w) >> 27) & 0b11111); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
1149 BGD_E1_E5b = math::interpretAs<int16_t, 10>(BGD_E1_E5b);
1150 auto E5b_HS = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 25) & 0b11);
1151 auto E1b_HS = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 23) & 0b11);
1152 auto E5b_DVS = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 22) & 0b1);
1153 auto E1b_DVS = static_cast<uint8_t>((sfrbx.dwrd.at(w) >> 21) & 0b1);
1154 [[maybe_unused]] auto WN = static_cast<uint16_t>((sfrbx.dwrd.at(w) >> 9) & 0b111111111111);
1155 [[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 w++;
1160 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 4 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1161 TOW |= (sfrbx.dwrd.at(w) >> 21) & 0b11111111111;
1162 LOG_DATA("{}: [{}] TOW {} ({})", nameId(), satId, TOW, std::bitset<32>(TOW));
1163
1164 w++;
1165 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 5 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1166
1167 auto ephemerisBuilder = getLastEphemerisBuilder(satId);
1168 if (!ephemerisBuilder.has_value())
1169 {
1170 LOG_DATA("{}: [{}] Could not find any ephemeris builder", nameId(), satId);
1171 return;
1172 }
1173 auto ephemeris = std::dynamic_pointer_cast<GalileoEphemeris>(ephemerisBuilder->get().navData);
1174
1175 ephemeris->BGD_E1_E5a = BGD_E1_E5a * std::pow(2.0, -32);
1176 ephemeris->BGD_E1_E5b = BGD_E1_E5b * std::pow(2.0, -32);
1177 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 ephemeris->dataSource[0] = true; // I/NAV E1-B
1186 ephemeris->dataSource[9] = true; // af0-af2, Toc, SISA are for E5b,E1
1187
1188 {
1189 std::unique_lock guard(outputPins.at(OUTPUT_PORT_INDEX_GNSS_NAV_INFO).dataAccessMutex, std::defer_lock);
1190 if (!_postProcessingLock.has_value())
1191 {
1192 guard.lock();
1193 }
1194
1195 if (!_gnssNavInfo.ionosphericCorrections.contains(satId.satSys, IonosphericCorrections::Alpha))
1196 {
1197 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 ai0 * std::pow(2.0, -2) /* [sfu] */,
1204 ai1 * std::pow(2.0, -8) /* [sfu/degree] */,
1205 ai2 * std::pow(2.0, -15) /* [sfu/degree^2] */,
1206 0.0,
1207 });
1208 }
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 finishWord(ephemeris, wordType, ephemerisBuilder->get().subframes);
1213 }
1214 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 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 w++;
1231 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 2 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1232 A0 |= static_cast<int32_t>((sfrbx.dwrd.at(w) >> 24) & 0b11111111);
1233 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 w++;
1237 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 3 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1238
1239 w++;
1240 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 4 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1241
1242 w++;
1243 LOG_DATA("{}: [{}] word {:2}: {}", nameId(), satId, w + 1 /* 5 */, std::bitset<32>(sfrbx.dwrd.at(w)));
1244
1245 std::unique_lock guard(outputPins.at(OUTPUT_PORT_INDEX_GNSS_NAV_INFO).dataAccessMutex, std::defer_lock);
1246 if (!_postProcessingLock.has_value())
1247 {
1248 guard.lock();
1249 }
1250
1251 auto key = std::make_pair(satId.satSys.getTimeSystem(), UTC);
1252 if (!_gnssNavInfo.timeSysCorr.contains(key))
1253 {
1254 if (auto ephemerisBuilder = getLastEphemerisBuilder(satId);
1255 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 LOG_DEBUG("{}: [{}] Received Time system corrections", nameId(), satId.satSys);
1263 }
1264 }
1265
1267 .a0 = A0 * std::pow(2, -30),
1268 .a1 = A1 * std::pow(2, -50),
1269 };
1270 }
1271}
1272
1273void 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
1311void 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
1341void 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
1357void 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
1366void 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}
BDS Ephemeris information.
Save/Load the Nodes.
Galileo Ephemeris information.
GNSS helper functions.
GPS Ephemeris information.
Galileo Ephemeris information.
Utility class for logging to console and file.
#define LOG_CRITICAL(...)
Critical Event, which causes the program to work entirely and throws an exception.
Definition Logger.hpp:75
#define LOG_DEBUG
Debug information. Should not be called on functions which receive observations (spamming)
Definition Logger.hpp:67
#define LOG_DATA
All output which occurs repeatedly every time observations are received.
Definition Logger.hpp:29
#define LOG_ERROR
Error occurred, which stops part of the program to work, but not everything.
Definition Logger.hpp:73
#define LOG_WARN
Error occurred, but a fallback option exists and program continues to work normally.
Definition Logger.hpp:71
#define LOG_TRACE
Detailled info to trace the execution of the program. Should not be called on functions which receive...
Definition Logger.hpp:65
Algorithms concerning the STL containers.
Collects UBX-RXM-SFRBX messages and provides the Orbit information.
static std::string type()
Returns the type of the data class.
Input pins of nodes.
Definition Pin.hpp:491
TsDeque< std::shared_ptr< const NAV::NodeData > > NodeDataQueue
Node data queue type.
Definition Pin.hpp:707
The class is responsible for all time-related tasks.
Definition InsTime.hpp:710
constexpr InsTime_GPSweekTow toGPSweekTow(TimeSystem timesys=GPST) const
Converts this time object into a different format.
Definition InsTime.hpp:854
constexpr InsTime_YMDHMS toYMDHMS(TimeSystem timesys=UTC, int digits=-1) const
Converts this time object into a different format.
Definition InsTime.hpp:871
@ Beta
Coefficients of a cubic equation representing the period of the model.
@ Alpha
Coefficients of a cubic equation representing the amplitude of the vertical delay.
bool isDisabled() const
Checks if the node is disabled.
Definition Node.cpp:569
std::vector< OutputPin > outputPins
List of output pins.
Definition Node.hpp:511
Node(std::string name)
Constructor.
Definition Node.cpp:29
std::vector< InputPin > inputPins
List of input pins.
Definition Node.hpp:509
OutputPin * CreateOutputPin(const char *name, Pin::Type pinType, const std::vector< std::string > &dataIdentifier, OutputPin::PinData data=static_cast< void * >(nullptr), int idx=-1)
Create an Output Pin object.
Definition Node.cpp:278
@ POST_PROCESSING
Node running in post-processing mode.
Definition Node.hpp:192
std::string nameId() const
Node name and id.
Definition Node.cpp:323
Mode getMode() const
Get the current mode of the node.
Definition Node.cpp:365
std::string name
Name of the Node.
Definition Node.hpp:507
InputPin * CreateInputPin(const char *name, Pin::Type pinType, const std::vector< std::string > &dataIdentifier={}, InputPin::Callback callback=static_cast< InputPin::FlowFirableCallbackFunc >(nullptr), InputPin::FlowFirableCheckFunc firable=nullptr, int priority=0, int idx=-1)
Create an Input Pin object.
Definition Node.cpp:252
std::scoped_lock< std::mutex > requestOutputValueLock(size_t pinIdx)
Blocks the thread till the output values was read by all connected nodes.
Definition Node.cpp:132
bool _hasConfig
Flag if the config window should be shown.
Definition Node.hpp:525
Output pins of nodes.
Definition Pin.hpp:338
@ GalileoEphemeris
Galileo Broadcast Ephemeris.
@ GPSEphemeris
GPS Broadcast Ephemeris.
auto extract_front()
Returns a copy of the first element in the container and removes it from the container.
Definition TsDeque.hpp:494
std::optional< std::reference_wrapper< EphemerisBuilder > > getLastEphemerisBuilder(const SatId &satId)
Searches the most recent ephemeris builder for the given satellite.
static std::string typeStatic()
String representation of the Class Type.
std::vector< EphemerisBuilder > _ephemerisBuilder
Map of ephemeris build helper for each satellite.
GnssNavInfo _gnssNavInfo
Data object to share over the output pin.
void receiveObs(InputPin::NodeDataQueue &queue, size_t pinIdx)
Data receive function.
static std::string category()
String representation of the Class Category.
void decryptGPS(const SatId &satId, const ubx::UbxRxmSfrbx &sfrbx, const InsTime &insTime)
Decrypt the GPS SFRBX message.
void guiConfig() override
ImGui config window which is shown on double click.
void decryptSBAS(const SatId &satId, const ubx::UbxRxmSfrbx &sfrbx, const InsTime &insTime)
Decrypt the SBAS SFRBX message.
void decryptGalileo(const SatId &satId, const ubx::UbxRxmSfrbx &sfrbx, const InsTime &insTime)
Decrypt the Galileo SFRBX message.
bool initialize() override
Initialize the node.
void decryptGLONASS(const SatId &satId, const ubx::UbxRxmSfrbx &sfrbx, const InsTime &insTime)
Decrypt the GLONASS SFRBX message.
static constexpr size_t INPUT_PORT_INDEX_UBLOX_OBS
Flow (UbloxObs)
void decryptQZSS(const SatId &satId, const ubx::UbxRxmSfrbx &sfrbx, const InsTime &insTime)
Decrypt the QZSS SFRBX message.
EphemerisBuilder & getEphemerisBuilder(const SatId &satId, const InsTime &insTime, size_t IOD=0)
Searches the ephemeris builder for the given satellite and time. If nothing found returns a new insta...
void decryptIRNSS(const SatId &satId, const ubx::UbxRxmSfrbx &sfrbx, const InsTime &insTime)
Decrypt the IRNSS SFRBX message.
std::set< SatelliteSystem > _warningsNotImplemented
List of satellite systems to emit warnings because conversion is not implemented yet.
static constexpr size_t OUTPUT_PORT_INDEX_GNSS_NAV_INFO
Object.
UbloxGnssOrbitCollector()
Default constructor.
std::unordered_map< SatId, size_t > _lastAccessedBuilder
List of IOD for each satellite.
void onDeleteLink(OutputPin &startPin, InputPin &endPin) override
Called when a link is to be deleted.
~UbloxGnssOrbitCollector() override
Destructor.
std::string type() const override
String representation of the Class Type.
void decryptBeiDou(const SatId &satId, const ubx::UbxRxmSfrbx &sfrbx, const InsTime &insTime)
Decrypt the BeiDou SFRBX message.
std::optional< std::unique_lock< std::mutex > > _postProcessingLock
Mutex to lock if the connected ublox obs provider is a file reader.
static std::string type()
Returns the type of the data class.
Definition UbloxObs.hpp:30
constexpr Out interpretAs(In in)
Interprets the input integer with certain amount of Bits as Output type. Takes care of sign extension...
Definition Math.hpp:83
std::string getStringFromMsgId(UbxClass msgClass, uint8_t msgId)
Get the a string from a UBX Msg Id.
SatelliteSystem getSatSys(uint8_t gnssId)
Get the GNSS Satellite System from gnssId.
@ UBX_CLASS_RXM
Receiver Manager Messages: Satellite Status, RTC Status.
UbxRxmMessages
The available RXM Messages.
std::string getStringFromMsgClass(UbxClass msgClass)
Get the a string from a UBX Msg Class.
@ GST
Galileo System Time.
@ GPST
GPS Time.
@ UTC
Coordinated Universal Time.
double galSisaIdx2Val(uint8_t idx)
Converts a GALILEO SISA (Signal in space accuracy) index to it's value.
Definition Functions.cpp:38
double gpsUraIdx2Val(uint8_t idx)
Converts a GPS URA (user range accuracy) index to it's value.
Definition Functions.cpp:54
constexpr auto semicircles2rad(const T &semicircles)
Convert Semicircles to Radians.
Definition Units.hpp:57
SatelliteSystem_
Satellite System enumeration.
@ GPS
Global Positioning System.
@ QZSS
Quasi-Zenith Satellite System.
@ GLO
Globalnaja nawigazionnaja sputnikowaja sistema (GLONASS)
@ GAL
Galileo.
@ SBAS
Satellite Based Augmentation System.
@ BDS
Beidou.
@ SatSys_None
No Satellite system.
@ IRNSS
Indian Regional Navigation Satellite System.
DataValidityStatus
Navigation Data Validity.
SignalHealthStatus
Signal Health Status.
Time system correction parameters.
@ Flow
NodeData Trigger.
Definition Pin.hpp:52
@ Object
Generic Object.
Definition Pin.hpp:57
Identifies a satellite (satellite system and number)
SatelliteSystem satSys
Satellite system (GPS, GLONASS, GALILEO, QZSS, BDS, IRNSS, SBAS)
Satellite System type.
TimeSystem getTimeSystem() const
Get the Time System of this Satellite System.
Ephemeris builder to store unfinished ephemeris data till all subframes are collected.
Broadcast Navigation Data Subframe.
std::vector< uint32_t > dwrd
The data words.
uint8_t numWords
The number of data words contained in this message (0..16)