QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
UBXDecoder.cc
Go to the documentation of this file.
1#include <algorithm>
2#include <array>
3#include <string_view>
4
5#include <QtCore/QByteArrayView>
6#include <QtCore/QString>
7
8#include "GPSFamilyProtocol.h"
9#include "GPSProtocolMath.h"
10#include "GPSReceiverFamilies.h"
11#include "GPSStreamDemux.h"
12#include "GPSTime.h"
13#include "MonotonicClock.h"
14#include "NMEASentence.h"
16#include "UBX/UBXProtocol.h"
17
18namespace Msg = UBX::Msg;
19
20namespace {
21
23constexpr std::chrono::milliseconds PACKET_TIMEOUT{8};
25constexpr std::chrono::milliseconds EPOCH_READ_SLICE{200};
26
27constexpr uint8_t PVT_VALID_DATE = 0x01;
28constexpr uint8_t PVT_VALID_TIME = 0x02;
29constexpr uint8_t PVT_FULLY_RESOLVED = 0x04;
30constexpr uint8_t FIX_OK = 0x01;
31constexpr uint8_t DIFFERENTIAL_SOLUTION = 0x02;
32
33GPSPositionReport::FixType navigationFix(uint8_t wireFix, uint8_t flags)
34{
36 if (!(flags & FIX_OK)) {
37 return Fix::NoFix;
38 }
39 switch (wireFix) {
40 case 0:
41 return Fix::NoFix;
42 case 5:
43 // Time only: a receiver in time mode, such as a base station, holds its surveyed or fixed position.
44 return Fix::Fix3D;
45 case 1:
46 return Fix::Extrapolated;
47 case 2:
48 // Correction flags cannot establish a three-dimensional navigation solution.
49 return Fix::Fix2D;
50 case 3:
51 case 4:
52 break;
53 default:
54 return Fix::Unknown;
55 }
56 const uint8_t carrier = flags >> 6;
57 if (carrier == 1) {
58 return Fix::RTKFloat;
59 }
60 if (carrier == 2) {
61 return Fix::RTKFixed;
62 }
63 return flags & DIFFERENTIAL_SOLUTION ? Fix::Differential : Fix::Fix3D;
64}
65
66bool velocityValid(GPSPositionReport::FixType fix)
67{
68 return fix != GPSPositionReport::FixType::Unknown && fix != GPSPositionReport::FixType::NoFix;
69}
70
73template <size_t N>
74QByteArrayView cString(const uint8_t (&field)[N])
75{
76 const QByteArrayView bytes(field, N - 1);
77 const auto end = bytes.indexOf('\0');
78 return end < 0 ? bytes : bytes.first(end);
79}
80
82GPSIntegrityReport::AntennaState antennaState(uint8_t status)
83{
84 switch (status) {
85 case 2:
87 case 3:
89 case 4:
91 default:
93 }
94}
95
97template <typename Header, typename Block, typename Count, typename Assign>
98void decodeSatelliteBlocks(std::span<const uint8_t> payload, GPSDecodedSatellites& report, Count count, Assign assign)
99{
100 const auto satelliteCount =
101 std::min<size_t>(count(Wire::decode<Header>(payload)), GPSDecodedSatellites::SAT_INFO_MAX_SATELLITES);
102 if (satelliteCount == 0) {
104 }
105 for (size_t index = 0; index < satelliteCount; ++index) {
106 assign(report, Wire::decode<Block>(payload, UBX::WIRE_SIZE<Header> + index * UBX::WIRE_SIZE<Block>));
107 }
108}
109
110} // namespace
111
112namespace UBX {
113
114GPSReceiveUpdates Protocol::onFrame(const GPSFrame& frame, GPSDecodeContext& context)
115{
116 if (frame.kind == GPSFrameKind::RTCM3) {
117 context.publishRTCM(frame.bytes);
118 return {};
119 }
120 return _decode(static_cast<uint16_t>(frame.messageId), frame.payload, context);
121}
122
124{
125 if (_mode.navigation) {
126 _epochs.expire(context);
127 }
128}
129
131{
132 _base = config.base;
133 _setMode({.navigation = true, .corrections = true}, context.stream());
134 _ready = true;
135 return true;
136}
137
139{
140 _setMode({.navigation = true, .corrections = decode.corrections}, context.stream());
141 return true;
142}
143
144void Protocol::_setMode(const Mode& mode, GPSStreamDemux& stream)
145{
146 _mode = mode;
147 _epochs = {};
148 stream.setEnabled(GPSFrameKind::RTCM3, mode.corrections);
149}
150
151bool Protocol::completeReceiveCycle(GPSReceiveUpdates handled)
152{
153 const bool ready = (_timeModeReadbackPending && _timeModeReadback.has_value()) ||
154 (_controller.readbackPending() && _controller.readbackReady()) ||
155 (_ready ? handled.testFlag(GPSReceiveUpdate::Position) : handled != GPSReceiveUpdates{});
156 if (ready) {
157 _epochStarted = false;
158 }
159 return ready;
160}
161
162std::chrono::milliseconds Protocol::nextReadSlice(std::chrono::milliseconds timeout) const
163{
164 if (_epochStarted) {
165 return PACKET_TIMEOUT;
166 }
167 return _mode.navigation ? (std::min) (timeout, EPOCH_READ_SLICE) : timeout;
168}
169
170GPSReceiveUpdates Protocol::_decode(uint16_t message, std::span<const uint8_t> payload, GPSDecodeContext& context)
171{
172 const auto* schema = UBX::messageSchema(message);
173 if (!UBX::validPayload(message, payload, schema)) {
174 return {};
175 }
176 if (message == Msg::NAV_EOE.value() && _mode.navigation) {
177 const uint32_t tow = LittleEndian::read<uint32_t>(payload, 0).value_or(GPSTime::WEEK_MS);
178 if (tow < GPSTime::WEEK_MS) {
179 _epochs.end(tow, context);
180 }
182 }
183 if (!_accept(message, payload)) {
184 return {};
185 }
186 // Only navigation decoding accepts the messages with a time of week.
187 if (schema && schema->towOffset >= 0) {
188 const auto tow = LittleEndian::read<uint32_t>(payload, static_cast<size_t>(schema->towOffset)).value_or(0);
189 if (auto* epoch = _epochs.find(tow, context)) {
190 _decodeEpoch(message, payload, *epoch);
191 }
193 }
194 const auto updates = _decodeHandled(message, payload, context);
195 if (updates.testFlag(GPSReceiveUpdate::Satellites)) {
196 context.publishSatellites(_satellites);
197 }
198 return updates;
199}
200
201bool Protocol::_accept(uint16_t message, std::span<const uint8_t> payload)
202{
203 const bool navigation = _mode.navigation;
204 const auto disable = [this, message] {
205 _requests.disableMessage = message;
206 return false;
207 };
208 if (std::ranges::find(NAVIGATION_MESSAGES, message, &MessageId::value) != NAVIGATION_MESSAGES.end()) {
209 return navigation;
210 }
211 switch (message) {
212 case Msg::CFG_TMODE3.value():
213 return _timeModeReadbackPending;
214 case Msg::CFG_VALGET.value():
215 return _controller.readbackPending() || _controller.readbackReplyOutstanding();
216 case Msg::CFG_MSG.value():
217 return _controller.ratePollPending();
218 case Msg::NAV_SVIN.value():
219 case Msg::MON_VER.value():
220 return true;
221 case Msg::INF_DEBUG.value():
222 case Msg::INF_ERROR.value():
223 case Msg::INF_NOTICE.value():
224 case Msg::INF_WARNING.value():
225 return payload.size() < UBX::MAX_CONTROL_PAYLOAD_SIZE;
226 case Msg::ACK_ACK.value():
227 return _controller.awaitingAcknowledgement() || _controller.ratePollPending();
228 case Msg::ACK_NAK.value():
229 return _controller.awaitingAcknowledgement() || _controller.readbackPending() ||
230 _controller.readbackReplyOutstanding() || _controller.ratePollPending() || _timeModeReadbackPending;
231 default:
232 return disable();
233 }
234}
235
236void Protocol::_decodeEpoch(uint16_t message, std::span<const uint8_t> payload, EpochAssembler::Epoch& epoch)
237{
238 auto& navigation = epoch.position.navigation;
239 switch (message) {
240 case Msg::NAV_PVT.value(): {
241 const auto pvt = Wire::decode<UBX::NavPvt>(payload);
242 navigation.fixType = navigationFix(pvt.fixType, pvt.flags);
243 epoch.position.velocityValid = velocityValid(navigation.fixType);
244 navigation.satellitesUsed = gpsSatellitesUsed(pvt.numSV);
245 // NAV-HPPOSLLH of the same epoch supersedes the position.
246 if (!epoch.highPrecision) {
247 navigation.latitudeDegrees = UBX::latitudeDegrees(pvt.lat);
248 navigation.longitudeDegrees = UBX::longitudeDegrees(pvt.lon);
249 navigation.altitudeMslMeters = pvt.hMSL * 1e-3;
250 navigation.altitudeEllipsoidMeters = pvt.height * 1e-3;
251 navigation.horizontalAccuracyMeters = static_cast<float>(pvt.hAcc) * 1e-3f;
252 navigation.verticalAccuracyMeters = static_cast<float>(pvt.vAcc) * 1e-3f;
253 }
254 navigation.speedMetersPerSecond = static_cast<float>(pvt.gSpeed) * 1e-3f;
255 navigation.courseRadians = static_cast<float>(pvt.headMot * 1e-5 * GPSProtocolMath::DEG_TO_RAD);
256 // Receiver time that is not valid publishes 0, the defined "unavailable" value, so a receiver that lost
257 // time never keeps reporting the last known time.
258 const bool timeValid =
259 (pvt.valid & PVT_VALID_DATE) && (pvt.valid & PVT_VALID_TIME) && (pvt.valid & PVT_FULLY_RESOLVED);
260 navigation.utcTimeUs = timeValid ? GPSProtocolMath::utcMicroseconds(pvt.year, pvt.month, pvt.day, pvt.hour,
261 pvt.min, pvt.sec, pvt.nano)
262 : 0;
263 epoch.hasPvt = true;
264 _epochStarted = true;
265 break;
266 }
267 case Msg::NAV_HPPOSLLH.value(): {
268 const auto hp = Wire::decode<UBX::NavHpposllh>(payload);
269 if (hp.flags != 0) {
270 break;
271 }
272 navigation.latitudeDegrees = UBX::latitudeDegrees(hp.lat) + hp.latHp * 1e-9;
273 navigation.longitudeDegrees = UBX::longitudeDegrees(hp.lon) + hp.lonHp * 1e-9;
274 navigation.altitudeMslMeters = hp.hMSL * 1e-3 + hp.hMSLHp * 1e-4;
275 navigation.altitudeEllipsoidMeters = hp.height * 1e-3 + hp.heightHp * 1e-4;
276 navigation.horizontalAccuracyMeters = static_cast<float>(hp.hAcc) * 1e-4f;
277 navigation.verticalAccuracyMeters = static_cast<float>(hp.vAcc) * 1e-4f;
278 epoch.highPrecision = true;
279 _epochStarted = true;
280 break;
281 }
282 case Msg::NAV_DOP.value(): {
283 const auto dop = Wire::decode<UBX::NavDop>(payload);
284 navigation.horizontalDop = dop.hDOP * UBX::DOP_PER_UNIT;
285 navigation.verticalDop = dop.vDOP * UBX::DOP_PER_UNIT;
286 break;
287 }
288 default:
289 break;
290 }
291}
292
293GPSReceiveUpdates Protocol::_decodeHandled(uint16_t message, std::span<const uint8_t> payload,
294 GPSDecodeContext& context)
295{
296 const uint64_t now = context.nowUs();
297 switch (message) {
298 case Msg::NAV_SAT.value():
299 case Msg::NAV_SVINFO.value():
300 _decodeSatellites(message, payload);
301 for (uint8_t index = 0; index < _satellites.count; ++index) {
302 auto& system = _satellites.constellations[index];
303 system.inViewTimestampUs = now;
304 if (system.inUse) {
305 system.inUseTimestampUs = now;
306 }
307 }
309 case Msg::NAV_SVIN.value():
310 return _decodeSurveyIn(payload, context);
311 case Msg::NAV_STATUS.value(): {
312 const auto status = Wire::decode<UBX::NavStatus>(payload);
313 _integrity.spoofing.state = GPSIntegrityReport::spoofingStateFromValue((status.flags2 >> 3) & 0x03);
314 _integrity.spoofing.timestampUs = now;
315 break;
316 }
317 case Msg::MON_HW.value(): {
318 const auto hardware = Wire::decode<UBX::MonHw>(payload);
319 _integrity.antenna = {.timestampUs = now, .state = antennaState(hardware.aStatus)};
320 if (!_secSigSeen) {
321 _integrity.jamming = {.timestampUs = now,
322 .state = GPSIntegrityReport::jammingStateFromValue((hardware.flags >> 2) & 0x03)};
323 }
324 break;
325 }
326 case Msg::MON_RF.value(): {
327 // Each RF block (F9P reports two, X20 three) has its own jamming monitor and antenna supervisor; the worst
328 // state any block reports stands.
331 for (size_t offset = UBX::WIRE_SIZE<UBX::MonRf> - UBX::WIRE_SIZE<UBX::MonRfBlock>; offset < payload.size();
332 offset += UBX::WIRE_SIZE<UBX::MonRfBlock>) {
333 const auto block = Wire::decode<UBX::MonRfBlock>(payload, offset);
334 jamming = (std::max) (jamming, GPSIntegrityReport::jammingStateFromValue(block.flags & 0x03));
335 antenna = (std::max) (antenna, antennaState(block.antStatus));
336 }
337 if (!_secSigSeen) {
338 _integrity.jamming = {.timestampUs = now, .state = jamming};
339 }
340 _integrity.antenna = {.timestampUs = now, .state = antenna};
341 break;
342 }
343 case Msg::SEC_SIG.value(): {
344 const auto signal = Wire::decode<UBX::SecSig>(payload);
345 // v1 keeps jamFlags at offset 4. The per-band groups of v2 and v3 are not read; spoofing still comes
346 // from NAV-STATUS.
347 const uint8_t flags = signal.version == 1 ? signal.jamFlags : signal.flags;
348 // jamDetEnabled in bit 0, then the jamming state as MON-RF reports it.
349 _integrity.jamming.state = (flags & 0x01) ? GPSIntegrityReport::jammingStateFromValue((flags >> 1) & 0x03)
350 : GPSIntegrityReport::JammingState::Unknown;
351 _integrity.jamming.timestampUs = now;
352 _secSigSeen = true;
353 break;
354 }
355 case Msg::MON_VER.value():
356 _decodeMonVer(payload);
357 // Polled only while configuring, which waits for it as an acknowledgement.
358 _controller.accept(UBX::Acknowledgement{message, true});
360 case Msg::INF_ERROR.value():
361 case Msg::INF_WARNING.value(): {
362 const QByteArrayView text(payload);
363 // Before logging: a message handler may decode more traffic, which reuses the frame's storage.
364 const bool outputOverflow = _mode.navigation && text.startsWith("txbuf");
365 const auto end = text.indexOf('\0');
366 const QString warning =
367 QStringLiteral("ubx msg: %1").arg(QString::fromUtf8(end < 0 ? text : text.first(end)));
368 // Receivers repeat a warning, such as an output overflow every epoch; only its first report warns.
369 if (warning == _lastWarning && MonotonicClock::fresh(_lastWarningUs, now, REPEATED_WARNING_INTERVAL)) {
370 qCDebug(UBXProtocolLog).noquote() << warning;
371 } else {
372 _lastWarning = warning;
373 _lastWarningUs = now;
374 qCWarning(UBXProtocolLog).noquote() << warning;
375 }
376 if (outputOverflow) {
377 _integrity.outputOverflowUs = now;
378 context.publishIntegrity(_integrity);
379 }
380 return {};
381 }
382 case Msg::CFG_TMODE3.value():
383 if (payload[0] == 0 && payload[1] == 0) {
384 _timeModeReadback = payload[2];
385 }
386 return {};
387 case Msg::CFG_VALGET.value():
388 _controller.accept(UBX::decodeConfigurationValues(payload));
389 return {};
390 case Msg::CFG_MSG.value(): {
391 const auto rates = Wire::decode<UBX::CfgMsgRates>(payload);
392 _controller.accept(UBX::MessageRates{rates.msg, std::to_array(rates.rates)});
393 return {};
394 }
395 case Msg::ACK_ACK.value():
396 case Msg::ACK_NAK.value():
397 _controller.accept(
398 UBX::Acknowledgement{Wire::decode<UBX::Ack>(payload).msg, message == Msg::ACK_ACK.value()});
400 default:
401 return {};
402 }
403 // The integrity messages above update the integrity report.
404 context.publishIntegrity(_integrity);
406}
407
408GPSReceiveUpdates Protocol::_decodeSurveyIn(std::span<const uint8_t> payload, GPSDecodeContext& context)
409{
410 const auto svin = Wire::decode<UBX::NavSvin>(payload);
411 _surveyStopped = svin.active == 0 && svin.valid == 0;
412 if (!_mode.navigation) {
414 }
415 GPSSurveyReport status{};
417 .x = (static_cast<double>(svin.meanX) + static_cast<double>(svin.meanXHP) * 0.01) * 0.01,
418 .y = (static_cast<double>(svin.meanY) + static_cast<double>(svin.meanYHP) * 0.01) * 0.01,
419 .z = (static_cast<double>(svin.meanZ) + static_cast<double>(svin.meanZHP) * 0.01) * 0.01,
420 });
421 status.duration = std::chrono::seconds(svin.dur);
422 status.meanAccuracyMeters = svin.meanAcc * 1e-4;
423 status.valid = (svin.valid & 1) != 0;
424 status.active = (svin.active & 1) != 0;
425 context.publishSurvey(status);
426 if (svin.valid == 1 && svin.active == 0) {
427 _requests.rtcmActivation = true;
428 }
430}
431
432void Protocol::_decodeMonVer(std::span<const uint8_t> payload)
433{
434 auto& identity = _identity;
435 identity = {};
436 const auto version = Wire::decode<UBX::MonVer>(payload);
437 identity.firmware = cString(version.swVersion).toByteArray();
438 // https://forum.u-blox.com/index.php/9432/need-help-decoding-ubx-mon-ver-hardware-string
439 const QByteArrayView hardware = cString(version.hwVersion);
440 identity.hardware = hardware.toByteArray();
441 identity.board = UBX::boardFromHardwareVersion(hardware);
442 if (identity.board == UBX::Board::unknown) {
443 qCWarning(UBXProtocolLog).noquote() << QStringLiteral("unknown board hw: %1").arg(QString::fromUtf8(hardware));
444 }
446 for (size_t offset = UBX::WIRE_SIZE<UBX::MonVer>; offset < payload.size();
447 offset += UBX::WIRE_SIZE<UBX::MonVerExtension>) {
448 const auto block = Wire::decode<UBX::MonVerExtension>(payload, offset);
449 const QByteArrayView extension = cString(block.extension);
450 // FWVER: product category and firmware version.
451 if (const auto at = extension.indexOf("FWVER="); at >= 0) {
452 const QByteArrayView field = extension.sliced(at);
453 if (field.startsWith("FWVER=SPG ") || field.startsWith("FWVER=HPS ")) {
454 identity.timeModeUnsupported = true;
455 }
456 identity.firmware = field.sliced(6).toByteArray();
457 qCDebug(UBXProtocolLog) << "u-blox firmware version:" << identity.firmware;
458 if (identity.board == UBX::Board::u_blox9 && field.contains("HPGL1L5")) {
460 }
461 }
462 // PROTVER: supported protocol version.
463 if (const auto at = extension.indexOf("PROTVER="); at >= 0) {
464 const QByteArrayView text = extension.sliced(at + 8);
465 identity.protocolVersion = text.toByteArray();
466 qCDebug(UBXProtocolLog) << "u-blox protocol version:" << text;
467 const std::string_view protocol(text.data(), size_t(text.size()));
468 const auto major = NMEA::number<unsigned>(protocol.substr(0, protocol.find('.')));
469 if (!major || *major == 0) {
471 return;
472 }
473 identity.protocol27 = *major >= 27;
474 if (*major < 20) {
475 identity.timeModeUnsupported = true;
476 }
477 }
478 // MOD: module identification, set in production.
479 if (const auto at = extension.indexOf("MOD="); at >= 0) {
480 const QByteArrayView field = extension.sliced(at);
481 if (field.startsWith("MOD=NEO-M8P-0") || field == "MOD=NEO-M8N" || field == "MOD=NEO-M9N") {
482 identity.timeModeUnsupported = true;
483 }
484 identity.model = field.sliced(4).toByteArray();
485 identity.isM8p = field.contains("M8P");
486 if (identity.board == UBX::Board::u_blox9 && field.contains("F9P")) {
488 } else if (identity.board == UBX::Board::u_blox10 && field.contains("DAN-F10N")) {
490 }
491 qCDebug(UBXProtocolLog) << "u-blox module:" << identity.model;
492 }
493 }
494}
495
496void Protocol::_decodeSatellites(uint16_t message, std::span<const uint8_t> payload)
497{
498 _satellites = {};
499 if (message == Msg::NAV_SAT.value()) {
500 constexpr GPSConstellation SYSTEMS[] = {
503 decodeSatelliteBlocks<UBX::NavSat, UBX::NavSatSatellite>(
504 payload, _satellites, [](const auto& header) { return header.numSvs; },
505 [&SYSTEMS](auto& report, const auto& satellite) {
506 report.addSatellite(
507 satellite.gnssId < std::size(SYSTEMS) ? SYSTEMS[satellite.gnssId] : GPSConstellation::Unknown,
508 (satellite.flags & 8) != 0);
509 });
510 return;
511 }
512 decodeSatelliteBlocks<UBX::NavSvinfo, UBX::NavSvinfoChannel>(
513 payload, _satellites, [](const auto& header) { return header.numCh; },
514 [](auto& report, const auto& channel) {
515 report.addSatellite(GPSConstellation::Unknown, (channel.flags & 1) != 0);
516 });
517}
518
519} // namespace UBX
std::optional< uint8_t > gpsSatellitesUsed(std::optional< unsigned > count)
A reported used-satellite count; receivers report UINT8_MAX and above when the count is unknown.
GPSConstellation
@ RTCM3
0xD3 frames with a valid CRC-24Q.
Config config
void publishIntegrity(GPSIntegrityReport &report)
Stamps the family's working report with the receipt time and publishes a copy.
void publishRTCM(std::span< const uint8_t > frame)
uint64_t nowUs() const
GPSStreamDemux & stream()
void publishSatellites(const GPSDecodedSatellites &report)
void publishSurvey(GPSSurveyReport &status)
Stamps status with the receipt time and publishes a copy. Survey progress is not an update flag.
void setEnabled(GPSFrameKind kind, bool enabled)
Enables or disables an available framer; either way it restarts from an empty state.
Epoch * find(uint32_t tow, GPSDecodeContext &context)
The epoch at tow, started now when new; nullptr for a time of week already published or too old.
void end(uint32_t tow, GPSDecodeContext &context)
Publishes the epoch at tow and the ones before it.
void expire(GPSDecodeContext &context)
Publishes epochs older than MAX_AGE.
void flush(GPSDecodeContext &context) override
Publishes epochs past their deadline.
bool completeReceiveCycle(GPSReceiveUpdates handled) override
Whether a receive cycle has what it waits for: a pending readback, or else a navigation update.
QString identity() const override
Model and firmware reported during configuration; empty when unknown.
Definition UBXProtocol.h:74
GPSReceiveUpdates onFrame(const GPSFrame &frame, GPSDecodeContext &context) override
bool armNavigationDecode(GPSNavigationDecode decode, GPSDecodeContext &context) override
static constexpr std::chrono::seconds REPEATED_WARNING_INTERVAL
A receiver warning or error repeated within this interval is logged at debug level.
Definition UBXProtocol.h:61
std::chrono::milliseconds nextReadSlice(std::chrono::milliseconds timeout) const override
Reads briefly once part of an epoch has arrived, as the rest follows back to back.
static constexpr std::array NAVIGATION_MESSAGES
Definition UBXProtocol.h:56
bool armDecodeOnly(const GPSConfig &config, GPSDecodeContext &context) override
The decoder state a successful configure() leaves: navigation, survey and corrections decoding.
bool readbackReplyOutstanding() const
A reply to a readback settled without it can still arrive.
void accept(Acknowledgement response)
constexpr double DEG_TO_RAD
GPSEllipsoidPosition fromEcef(const Ecef &position)
uint64_t utcMicroseconds(int year, int month, int day, int hour, int minute, int second, int32_t nsec)
QString outputOverflow(RTKSettings::ReceiverRole role, bool compactObservations)
The warning that the receiver produces more output than its connection carries.
constexpr uint32_t WEEK_MS
Milliseconds in a GPS week; every time of week is below it.
Definition GPSTime.h:9
constexpr bool fresh(uint64_t timestampUs, uint64_t nowUs, std::chrono::microseconds lifetime)
Whether a receipt is younger than lifetime.
constexpr MessageId NAV_SVIN
Definition UBXFrame.h:51
constexpr MessageId ACK_NAK
Definition UBXFrame.h:59
constexpr MessageId MON_HW
Definition UBXFrame.h:69
constexpr MessageId INF_DEBUG
Definition UBXFrame.h:58
constexpr MessageId MON_RF
Definition UBXFrame.h:70
constexpr MessageId SEC_SIG
Definition UBXFrame.h:71
constexpr MessageId NAV_EOE
Definition UBXFrame.h:52
constexpr MessageId ACK_ACK
Definition UBXFrame.h:60
constexpr MessageId NAV_STATUS
Definition UBXFrame.h:44
constexpr MessageId NAV_DOP
Definition UBXFrame.h:45
constexpr MessageId CFG_VALGET
Definition UBXFrame.h:67
constexpr MessageId NAV_SAT
Definition UBXFrame.h:50
constexpr MessageId MON_VER
Definition UBXFrame.h:68
constexpr MessageId NAV_SVINFO
Definition UBXFrame.h:49
constexpr MessageId INF_WARNING
Definition UBXFrame.h:56
constexpr MessageId CFG_MSG
Definition UBXFrame.h:62
constexpr MessageId INF_ERROR
Definition UBXFrame.h:55
constexpr MessageId CFG_TMODE3
Definition UBXFrame.h:65
constexpr MessageId INF_NOTICE
Definition UBXFrame.h:57
constexpr MessageId NAV_HPPOSLLH
Definition UBXFrame.h:47
constexpr MessageId NAV_PVT
Definition UBXFrame.h:46
u-blox receivers over UBX: CFG-VALSET configuration from protocol 27, CFG-MSG and CFG-TMODE3 before i...
constexpr ReceiverProfile receiverProfile(Board board)
Definition UBXPlan.h:82
std::optional< ConfigurationValues > decodeConfigurationValues(std::span< const uint8_t > payload)
Decodes a CFG-VALGET response (version 1, RAM layer) of at most ConfigurationValues::MAX_KEYS values.
constexpr const MessageSchema * messageSchema(uint16_t message)
bool validPayload(uint16_t message, std::span< const uint8_t > payload, const MessageSchema *schema)
double longitudeDegrees(int32_t value)
double latitudeDegrees(int32_t value)
constexpr Board boardFromHardwareVersion(std::string_view hardwareVersion)
The generation whose MON-VER hwVersion is hardwareVersion; unknown when none matches.
Definition UBXPlan.h:93
constexpr size_t MAX_CONTROL_PAYLOAD_SIZE
constexpr float DOP_PER_UNIT
The receiver configuration requested for one configure() attempt.
static constexpr uint16_t SAT_INFO_MAX_SATELLITES
void addSatellite(GPSConstellation constellation, bool used)
std::array< Constellation, MAX_CONSTELLATIONS > constellations
Constellation * ensureConstellation(GPSConstellation constellation)
AntennaState
Ordered by severity, so the worst of several reports is the greatest.
static constexpr SpoofingState spoofingStateFromValue(int value)
static constexpr JammingState jammingStateFromValue(int value)
What a decode-only runtime decodes from a receiver configured elsewhere (GPSFamilyProtocol::armNaviga...
GPSNavigationValues::FixType FixType
GPSEllipsoidPosition position
constexpr uint16_t value() const
Definition UBXFrame.h:27
The CFG-MSG output rates of one message on each I/O port, as a poll returns them.
bool protocol27
Configures through CFG-VALSET unless MON-VER reports an older protocol version.
Definition UBXPlan.h:48