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