QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
GPSNMEAStream.cc
Go to the documentation of this file.
1#include "GPSNMEAStream.h"
2
3#include <algorithm>
4#include <cmath>
5#include <limits>
6
7#include "GPSCommandChannel.h"
8#include "GPSDeadline.h"
9#include "GPSFamilyProtocol.h"
10#include "GPSProtocolMath.h"
11#include "NMEASentence.h"
12
13namespace {
14
15void applyNavigationEpoch(GPSDecodedPosition& report, const NMEA::NavigationEpoch& epoch)
16{
17 report.navigation.latitudeDegrees = epoch.latitude;
19 report.navigation.altitudeMslMeters = epoch.altitudeMslMeters.value_or(std::numeric_limits<double>::quiet_NaN());
21 epoch.altitudeEllipsoidMeters().value_or(std::numeric_limits<double>::quiet_NaN());
22 report.navigation.horizontalDop = static_cast<float>(epoch.horizontalDop.value_or(NAN));
23 report.navigation.verticalDop = static_cast<float>(epoch.verticalDop.value_or(NAN));
24 report.navigation.horizontalAccuracyMeters = static_cast<float>(epoch.horizontalAccuracyMeters.value_or(NAN));
25 report.navigation.verticalAccuracyMeters = static_cast<float>(epoch.verticalAccuracyMeters.value_or(NAN));
27 report.navigation.fixType = epoch.fixQuality;
29 report.navigation.speedMetersPerSecond = static_cast<float>(epoch.speedMetersPerSecond.value_or(NAN));
31 epoch.courseDegrees ? static_cast<float>(*epoch.courseDegrees * GPSProtocolMath::DEG_TO_RAD) : NAN;
32 report.velocityValid = epoch.speedMetersPerSecond || epoch.courseDegrees;
33}
34
35GPSDecodedSatellites satelliteReport(const NMEA::SatelliteSystem& system)
36{
38 report.fullSnapshot = false;
39 auto* constellation = report.ensureConstellation(system.constellation);
40 if (!constellation) {
41 return report;
42 }
43 constellation->inViewTimestampUs = system.inViewTimestampUs;
44 constellation->inView = system.inView;
45 if (system.inUse) {
46 constellation->inUseTimestampUs = system.inUseTimestampUs;
47 constellation->inUse = *system.inUse;
48 }
49 return report;
50}
51
52} // namespace
53
55 : _navigation(navigation)
56{}
57
59{
60 stream.reset();
61 _satelliteAssembler.clear();
62 _pendingSatellites.clear();
63 _navigationAssembler.reset();
64 _position = {};
65}
66
67GPSReceiveUpdates GPSNMEAStream::_decodeRTCM(const GPSFrame& frame, GPSDecodeContext& context)
68{
69 if (_rtcmEnabled) {
70 context.publishRTCM(frame.bytes);
71 }
72 return {};
73}
74
75GPSReceiveUpdates GPSNMEAStream::_decodeStandard(std::string_view line, GPSDecodeContext& context)
76{
77 const auto sentence = NMEA::sentence(line);
78 if (!sentence) {
79 return {};
80 }
81 const uint64_t now = context.nowUs();
82 GPSReceiveUpdates updates = GPSReceiveUpdate::Activity;
83 auto satelliteUpdate = _satelliteAssembler.ingest(*sentence, now);
84 _queueSatellites(satelliteUpdate.completed);
85 if (_navigation == Navigation::ReceiverSpecific) {
86 return updates;
87 }
88 const auto navigation = NMEA::navigationStatus(*sentence);
89 const auto update = sentence->type() == "GSA" && navigation && navigation->valid && !satelliteUpdate.accepted
90 ? std::optional<NMEA::NavigationUpdate>()
91 : _navigationAssembler.ingest(*sentence, now);
92 if (update && update->type == NMEA::NavigationUpdate::Type::FixLoss) {
93 _position = {};
94 _position.navigation.timestampUs = now;
95 _position.navigation.fixType = GPSPositionReport::FixType::NoFix;
96 // Without a fix no satellite is used, unless the sentence reports a count of its own.
97 const uint8_t used = gpsSatellitesUsed(update->epoch.satellitesUsed).value_or(0);
98 _position.navigation.satellitesUsed = used;
99 context.publishSatelliteUsage(used);
101 } else if (update && update->type == NMEA::NavigationUpdate::Type::Epoch &&
102 update->trigger == NMEA::NavigationUpdate::Trigger::Position && sentence->type() == "GGA") {
103 applyNavigationEpoch(_position, update->epoch);
104 context.publishSatelliteUsage(gpsSatellitesUsed(update->epoch.satellitesUsed));
106 } else if (update && update->type == NMEA::NavigationUpdate::Type::Epoch &&
108 applyNavigationEpoch(_position, update->epoch);
110 }
111 return updates;
112}
113
114GPSReceiveUpdates GPSNMEAStream::_finishLine(GPSReceiveUpdates updates, GPSDecodeContext& context)
115{
116 _publishSatellites(context);
117 if (updates & GPSReceiveUpdate::Position) {
118 context.publishPosition(_position);
119 }
120 return updates;
121}
122
124{
125 const uint64_t now = context.nowUs();
126 if (const auto expired = _navigationAssembler.expireUntimedMetadata(now)) {
127 applyNavigationEpoch(_position, *expired);
128 }
129 _queueSatellites(_satelliteAssembler.flushDue(now));
130 _publishSatellites(context);
131}
132
133std::chrono::milliseconds GPSNMEAStream::limitReceiveTimeout(std::chrono::milliseconds timeout, uint64_t nowUs) const
134{
135 if (const auto deadline = _satelliteAssembler.deadlineUs()) {
136 timeout = std::min(timeout, GPSDeadline{*deadline}.remaining(nowUs));
137 }
138 return timeout;
139}
140
141GPSReceiveUpdates GPSNMEAStream::receive(GPSCommandChannel& channel, std::chrono::milliseconds timeout) const
142{
143 const GPSReceiveUpdates updates = channel.receiveCycle(limitReceiveTimeout(timeout, channel.nowUs()));
144 channel.serviceControls();
145 return updates;
146}
147
148void GPSNMEAStream::_queueSatellites(const NMEA::SatelliteEpoch& epoch)
149{
150 for (const auto& system : epoch) {
151 if (system.inViewTimestampUs != 0 || system.inUseTimestampUs != 0) {
152 _pendingSatellites.push_back(system);
153 }
154 }
155}
156
157void GPSNMEAStream::_publishSatellites(GPSDecodeContext& context)
158{
159 for (const auto& system : _pendingSatellites) {
160 context.publishSatellites(satelliteReport(system));
161 }
162 _pendingSatellites.clear();
163}
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.
void serviceControls()
Runs the family's streaming services once under SERVICE_TIMEOUT, then flushes. Nested calls do nothin...
uint64_t nowUs() const
GPSReceiveUpdates receiveCycle(std::chrono::milliseconds timeout)
void publishSatelliteUsage(std::optional< int > count)
void publishPosition(const GPSDecodedPosition &report)
void publishRTCM(std::span< const uint8_t > frame)
uint64_t nowUs() const
void publishSatellites(const GPSDecodedSatellites &report)
GPSReceiveUpdates receive(GPSCommandChannel &channel, std::chrono::milliseconds timeout) const
GPSNMEAStream(Navigation navigation=Navigation::StandardNMEA)
void flush(GPSDecodeContext &context)
Expires untimed metadata and publishes satellite epochs that became due.
void reset(GPSStreamDemux &stream)
Starts a new receiver session: clears every framer of stream, the epochs and the working reports.
Navigation
Who turns sentences into positions: this stream from standard NMEA, or the family's own sentences.
std::chrono::milliseconds limitReceiveTimeout(std::chrono::milliseconds timeout, uint64_t nowUs) const
Shortens a receive timeout so a pending satellite epoch is published when it becomes due.
std::optional< NavigationEpoch > expireUntimedMetadata(uint64_t nowUs)
std::optional< uint64_t > deadlineUs() const
Update ingest(const Sentence &input, uint64_t receivedAtUs)
SatelliteEpoch flushDue(uint64_t nowUs)
constexpr double DEG_TO_RAD
std::optional< Sentence > sentence(std::string_view text)
std::vector< SatelliteSystem > SatelliteEpoch
std::optional< NavigationStatus > navigationStatus(const Sentence &input)
std::chrono::milliseconds remaining(uint64_t nowUs) const
Rounded up to whole milliseconds and capped at INT32_MAX milliseconds.
Definition GPSDeadline.h:20
Decoded navigation plus whether the producer accepted its velocity solution.
GPSNavigationValues navigation
Constellation * ensureConstellation(GPSConstellation constellation)
std::optional< uint8_t > satellitesUsed
std::optional< double > horizontalDop
std::optional< double > horizontalAccuracyMeters
std::optional< double > verticalDop
std::optional< double > altitudeEllipsoidMeters() const
std::optional< double > altitudeMslMeters
std::optional< double > courseDegrees
std::optional< double > verticalAccuracyMeters
std::optional< unsigned > satellitesUsed
std::optional< double > speedMetersPerSecond
std::optional< int > inUse
GPSConstellation constellation