QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
FemtoProtocol.cc
Go to the documentation of this file.
1#include <algorithm>
2#include <chrono>
3#include <optional>
4#include <string_view>
5#include <variant>
6
7#include <QtCore/QString>
8
9#include "Femto/FemtoPlan.h"
10#include "GPSASCIILog.h"
11#include "GPSCommandChannel.h"
13#include "GPSReceiverFamilies.h"
14#include "GPSStreamDemux.h"
15#include "GPSSurveyClock.h"
16#include "NMEASentence.h"
17#include "QGCLoggingCategory.h"
18
19QGC_LOGGING_CATEGORY(FemtoProtocolLog, "GPS.Protocols.Femto")
20
21namespace {
22
28class Protocol final : public GPSNMEAFamilyProtocol
29{
30public:
31 bool configure(GPSCommandChannel& channel, GPSConfig config, unsigned& baud) override;
32
33 bool probe(GPSCommandChannel& channel) override;
34
36 void serviceStreaming(GPSCommandChannel& channel) override;
37
38private:
40 GPSReceiveUpdates _decodeLine(std::string_view line, GPSDecodeContext& context) override;
41 void _setUpBase(GPSCommandChannel& channel);
42 void _activateRTCMOutput(GPSCommandChannel& channel);
43
45 bool _rtcmOutputActive = false;
47 bool _rtcmActivationPending = false;
48 GPSSurveyClock _surveyClock;
49};
50
51bool Protocol::configure(GPSCommandChannel& channel, GPSConfig config, unsigned& baud)
52{
53 _ready = false;
54 _rtcmOutputActive = false;
55 _rtcmActivationPending = false;
56 _surveyClock = {};
57 // Corrections are only framed from a receiver that answered.
58 GPSStreamDemux& stream = channel.stream();
59 stream.setEnabled(GPSFrameKind::RTCM3, false);
60 _nmea.setRTCMEnabled(false);
61 _nmea.reset(stream);
62 _base = config.base;
63 // No command depends on the rate, so the link keeps the one selected or detected; otherwise it runs at Plan::BAUD.
64 baud = config.linkBaud(baud, Femto::Plan::BAUD);
65 (void) channel.setBaudrate(baud);
67 for (unsigned round = 1; round < Femto::Plan::IDENTIFY_ROUNDS && !identity.succeeded(); ++round) {
69 }
70 if (!identity.succeeded()) {
71 if (identity.outcome == GPSCommandOutcome::Rejected) {
72 channel.failSequence(identity);
73 } else {
74 channel.failNoAnswer({&baud, 1});
75 }
76 return false;
77 }
78
80 stream.setEnabled(GPSFrameKind::RTCM3, true);
81 _nmea.setRTCMEnabled(true);
82 _setUpBase(channel);
83 _ready = !channel.failed();
84 return _ready;
85}
86
87void Protocol::serviceStreaming(GPSCommandChannel& channel)
88{
89 if (_rtcmActivationPending) {
90 _rtcmActivationPending = false;
91 _activateRTCMOutput(channel);
92 }
93}
94
95void Protocol::_setUpBase(GPSCommandChannel& channel)
96{
97 GPSDecodeContext& context = channel.context();
98 const auto* fixed = std::get_if<GPSBaseStationConfig::Fixed>(&_base.mode);
99 if (!fixed) {
101 return;
102 }
103 _surveyClock.start(context);
104 return;
105 }
106 if (!channel.runRequired(Femto::Plan::fixedBase(fixed->position))) {
107 return;
108 }
109 _activateRTCMOutput(channel);
110 if (_rtcmOutputActive) {
111 context.publishSurvey(false, true, {}, fixed->position);
112 }
113}
114
115void Protocol::_activateRTCMOutput(GPSCommandChannel& channel)
116{
118 _rtcmOutputActive = true;
119 }
120}
121
122GPSReceiveUpdates Protocol::_decodeLine(std::string_view line, GPSDecodeContext& context)
123{
124 if (line.size() >= 6 && line.substr(3, 3) == "GGA") {
125 const auto sentence = NMEA::sentence(line);
126 const auto fix = sentence ? NMEA::gga(*sentence) : std::nullopt;
127 if (!fix) {
128 return {};
129 }
130 // Only a survey started by this configuration may complete it; earlier GGA output is stale.
131 if (!_rtcmOutputActive && _surveyClock.running() && fix->quality == Femto::Plan::AVERAGED_POSITION_QUALITY) {
132 _surveyClock.finish(context, true,
133 {.latitudeDegrees = fix->latitude,
134 .longitudeDegrees = fix->longitude,
135 .altitudeMeters = fix->altitude + fix->geoidSeparation});
136 _rtcmActivationPending = true;
137 }
138 }
139 _surveyClock.publishProgress(context);
140 return {};
141}
142
143bool portName(std::string_view field)
144{
145 const size_t digits = field.find_first_of("0123456789");
146 if (digits == 0 || digits == std::string_view::npos) {
147 return false;
148 }
149 return std::ranges::all_of(field.substr(0, digits), NMEA::isAsciiUpper) &&
150 std::ranges::all_of(field.substr(digits), NMEA::isAsciiDigit);
151}
152
155QLatin1StringView signature(const GPSFrame& frame)
156{
157 const std::string_view text = frame.text();
158 if (frame.kind != GPSFrameKind::ASCIILine) {
159 return {};
160 }
161 if (text.starts_with('<') && (text.ends_with(" OK") || text.starts_with(Femto::Plan::REJECTED))) {
162 return QLatin1StringView("abbreviated ASCII replies");
163 }
164 const auto log = gpsASCIILog(text);
165 return log && portName(log->source) ? QLatin1StringView("NovAtel-format ASCII logs") : QLatin1StringView();
166}
167
169bool Protocol::probe(GPSCommandChannel& channel)
170{
171 bool identified = false;
172 for (unsigned round = 0; round < Femto::Plan::IDENTIFY_ROUNDS && !identified && !channel.failed(); ++round) {
174 }
175 return identified;
176}
177
178} // namespace
179
180namespace Femto {
181
183 .type = GPSType::femto,
184 .logCategory = &FemtoProtocolLog,
185 // RTCM3 is framed once the receiver answered (see Protocol::configure()).
187 .baudCandidates = Plan::BAUD_RATES,
188 .create = &gpsCreateProtocol<Protocol>,
189 .signature = &signature,
190};
191
192} // namespace Femto
std::optional< GPSASCIILog > gpsASCIILog(std::string_view line)
Definition GPSASCIILog.h:21
@ Protocol
The receiver violated or rejected the control protocol.
@ ASCIILine
Printable lines up to CR/LF, without the terminator; the decoder checks checksums.
@ RTCM3
0xD3 frames with a valid CRC-24Q.
@ NMEASentence
'$' to a valid "*hh" checksum, without a line ending.
Config config
#define QGC_LOGGING_CATEGORY(name, categoryStr)
GPSDecodeContext & context()
bool setBaudrate(unsigned baudrate)
void failSequence(const GPSCommandSequence::Result &result)
GPSStreamDemux & stream()
bool runRequired(const GPSCommandSequence &sequence)
Runs sequence and fails control over a failed step as failSequence() does.
GPSCommandSequence::Result runSequence(const GPSCommandSequence &sequence)
Runs sequence in order until a required step fails or I/O fails; failed attempts retry up to their co...
void failNoAnswer(std::span< const unsigned > rates)
void publishSurvey(GPSSurveyReport &status)
Stamps status with the receipt time and publishes a copy. Survey progress is not an update flag.
virtual void serviceStreaming(GPSCommandChannel &channel)
virtual bool probe(GPSCommandChannel &channel)
virtual bool configure(GPSCommandChannel &channel, GPSConfig config, unsigned &baud)=0
virtual GPSReceiveUpdates _decodeLine(std::string_view line, GPSDecodeContext &context)=0
Decodes one line after standard NMEA did.
void setEnabled(GPSFrameKind kind, bool enabled)
Enables or disables an available framer; either way it restarts from an empty state.
Host-timed survey-in duration for receivers that report completion but not elapsed time.
constexpr std::array BAUD_RATES
Rates the port query probes, in order. Trimble receivers default to 115200.
Definition AshtechPlan.h:31
QByteArray line(std::string_view command)
command as one CR/LF-terminated line.
const GPSReceiverFamily FAMILY
constexpr std::string_view REJECTED
Definition FemtoPlan.h:26
constexpr unsigned AVERAGED_POSITION_QUALITY
GGA fix quality of a position the receiver holds fixed: position averaging finished.
Definition FemtoPlan.h:45
GPSCommandSequence sequence(std::span< const Command > commands)
Definition FemtoPlan.cc:24
constexpr std::array IDENTIFY
Stops every log on this port and identifies the receiver, in up to IDENTIFY_ROUNDS rounds.
Definition FemtoPlan.h:36
GPSCommandSequence fixedBase(const GPSEllipsoidPosition &position)
ELLIPSOID_HEIGHTS, FIX_POSITION for position, then GGA_OUTPUT.
Definition FemtoPlan.cc:33
constexpr std::array RTCM_OUTPUT
RTCM3 output once the base position is known.
Definition FemtoPlan.h:62
constexpr std::array SURVEY_IN
Survey-in base: receiver position averaging. A GGA fix of quality 7 completes it, and RTCM_OUTPUT fol...
Definition FemtoPlan.h:48
constexpr unsigned IDENTIFY_ROUNDS
Definition FemtoPlan.h:40
constexpr Command VERSION
Identifies the receiver without changing it, so receiver detection probes with it alone.
Definition FemtoPlan.h:33
constexpr unsigned BAUD
The link rate unless one is selected or detected, and the only rate receiver detection probes.
Definition FemtoPlan.h:29
Femtomes receivers: ASCII commands, with GGA sentences and RTCM3 output, set up as RTK base stations.
Definition FemtoPlan.cc:8
std::optional< Sentence > sentence(std::string_view text)
constexpr bool isAsciiUpper(char ch)
std::optional< GGA > gga(const Sentence &input)
Quality zero is a valid fix-loss observation with empty coordinates and optional hemisphere fields.
constexpr bool isAsciiDigit(char ch)
Locale-independent ASCII character classes for receiver text.
std::optional< Frame > frame(std::string_view text)
Configuration used only by the RTK base-station role.
The receiver configuration requested for one configure() attempt.