QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
GPSStreamDemux.cc
Go to the documentation of this file.
1#include "GPSStreamDemux.h"
2
3#include <vector>
4
5#include "NMEAFramer.h"
6#include "RTCMFramer.h"
7#include "SBF/SBFBlocks.h"
8#include "UBX/UBXFrame.h"
9
11{
12public:
13 enum class Claim : uint8_t
14 {
15 None,
16 Default,
17 Watch,
18 Owns,
19 Locked,
20 };
21
23 : _kind(kind)
24 {}
25
26 virtual ~Framer() = default;
27
28 Q_DISABLE_COPY(Framer)
29
30 GPSFrameKind kind() const { return _kind; }
31
32 virtual Claim claim(uint8_t byte) const = 0;
33 virtual void feed(uint8_t byte, Handler& handler) = 0;
34 virtual void reset() = 0;
35
36 bool enabled = false;
37
38private:
39 GPSFrameKind _kind;
40};
41
42namespace {
43
45
46class RTCM3Framer final : public GPSStreamDemux::Framer
47{
48public:
49 RTCM3Framer()
51 {}
52
53 Claim claim(uint8_t byte) const override
54 {
55 if (_framer.hasPartialFrame()) {
56 return Claim::Locked;
57 }
59 }
60
61 void feed(uint8_t byte, GPSStreamDemux::Handler& handler) override
62 {
63 // A completed candidate can leave further frames in its recovery suffix.
64 for (bool complete = _framer.addByte(byte); complete; complete = _framer.nextFrame()) {
65 if (!_framer.valid()) {
66 continue;
67 }
68 const auto frame = _framer.frame();
69 handler.frame({.kind = GPSFrameKind::RTCM3,
70 .bytes = frame,
71 .payload = frame.subspan(RTCMFramer::HEADER_SIZE,
73 .messageId = RTCMFramer::frameMessageId(frame)});
74 }
75 }
76
77 void reset() override { _framer.reset(); }
78
79private:
80 RTCMFramer _framer;
81};
82
83class UBXFramer final : public GPSStreamDemux::Framer
84{
85public:
86 UBXFramer()
87 : Framer(GPSFrameKind::UBX)
88 {}
89
90 Claim claim(uint8_t byte) const override
91 {
92 if (!_decoder.idle()) {
93 return Claim::Locked;
94 }
95 return byte == UBX::SYNC1 ? Claim::Owns : Claim::None;
96 }
97
98 void feed(uint8_t byte, GPSStreamDemux::Handler& handler) override
99 {
100 if (const auto frame = _decoder.consume(byte)) {
101 handler.frame({.kind = GPSFrameKind::UBX,
102 .bytes = frame->bytes,
103 .payload = frame->payload,
104 .messageId = frame->message});
105 }
106 }
107
108 void reset() override { _decoder.reset(); }
109
110private:
111 UBX::FrameDecoder _decoder;
112};
113
117class SBFFramer final : public GPSStreamDemux::Framer
118{
119public:
120 explicit SBFFramer(bool shareSync)
121 : Framer(GPSFrameKind::SBF)
122 , _shareSync(shareSync)
123 {}
124
125 Claim claim(uint8_t byte) const override
126 {
127 switch (_state) {
128 case State::Sync1:
129 return byte == SBF::SYNC1 ? (_shareSync ? Claim::Watch : Claim::Owns) : Claim::None;
130 case State::Sync2:
131 return _shareSync ? Claim::Watch : Claim::Locked;
132 case State::Payload:
133 case State::Skip:
134 return Claim::Locked;
135 }
136 return Claim::None;
137 }
138
139 void feed(uint8_t byte, GPSStreamDemux::Handler& handler) override
140 {
141 switch (_state) {
142 case State::Sync1:
143 if (byte == SBF::SYNC1) {
144 (void) _add(byte);
145 _state = State::Sync2;
146 }
147 break;
148 case State::Sync2:
149 if (byte == SBF::SYNC2) {
150 (void) _add(byte);
151 _state = State::Payload;
152 } else {
153 reset();
154 }
155 break;
156 case State::Payload:
157 if (_add(byte)) {
158 // A complete candidate holds at least the header.
159 const std::span<const uint8_t> bytes(_wire.data(), _size);
160 const auto header = _header();
161 const uint16_t remaining =
162 header.length > _size && header.length % 4 == 0 && header.length <= MAX_SKIPPED_LENGTH
163 ? header.length - _size
164 : 0;
165 handler.frame(
166 {.kind = GPSFrameKind::SBF, .bytes = bytes, .payload = bytes, .messageId = header.number()});
167 reset();
168 if (remaining > 0) {
169 _state = State::Skip;
170 _remaining = remaining;
171 }
172 }
173 break;
174 case State::Skip:
175 if (--_remaining == 0) {
176 reset();
177 }
178 break;
179 }
180 }
181
182 void reset() override
183 {
184 _state = State::Sync1;
185 _size = 0;
186 _remaining = 0;
187 }
188
189private:
190 enum class State : uint8_t
191 {
192 Sync1,
193 Sync2,
194 Payload,
196 Skip,
197 };
198
200 static constexpr uint16_t MAX_SKIPPED_LENGTH = 4096;
201
203 SBF::BlockHeader _header() const { return Wire::decode<SBF::BlockHeader>(_wire); }
204
206 bool _add(uint8_t byte)
207 {
208 _wire[_size++] = byte;
209 return (_size >= Wire::SIZE<SBF::BlockHeader> && _size >= _header().length) || _size >= _wire.size();
210 }
211
212 std::array<uint8_t, 110> _wire{};
213 uint16_t _size = 0;
214 uint16_t _remaining = 0;
215 State _state = State::Sync1;
216 bool _shareSync = false;
217};
218
219class NMEASentenceFramer final : public GPSStreamDemux::Framer
220{
221public:
222 NMEASentenceFramer()
223 : Framer(GPSFrameKind::NMEASentence)
224 , _buffer(BUFFER_SIZE)
225 , _framer(_buffer)
226 {}
227
228 Claim claim(uint8_t) const override { return Claim::Default; }
229
230 void feed(uint8_t byte, GPSStreamDemux::Handler& handler) override
231 {
232 if (const size_t length = _framer.addByte(byte); length > 0) {
233 const std::span<const uint8_t> bytes(_buffer.data(), length);
234 handler.frame({.kind = GPSFrameKind::NMEASentence, .bytes = bytes, .payload = bytes});
235 }
236 }
237
238 void reset() override { _framer.reset(); }
239
240private:
241 static constexpr size_t BUFFER_SIZE = 600;
242
243 std::vector<uint8_t> _buffer;
244 NMEA::Framer _framer;
245};
246
247class ASCIILineFramer final : public GPSStreamDemux::Framer
248{
249public:
250 ASCIILineFramer()
251 : Framer(GPSFrameKind::ASCIILine)
252 , _buffer(BUFFER_SIZE)
253 , _framer(_buffer)
254 {}
255
256 Claim claim(uint8_t) const override { return Claim::Default; }
257
258 void feed(uint8_t byte, GPSStreamDemux::Handler& handler) override
259 {
260 if (const auto line = _framer.addByte(byte)) {
261 const std::span<const uint8_t> bytes(reinterpret_cast<const uint8_t*>(line->data()), line->size());
262 handler.frame({.kind = GPSFrameKind::ASCIILine, .bytes = bytes, .payload = bytes});
263 }
264 }
265
266 void reset() override { _framer.reset(); }
267
268private:
269 static constexpr size_t BUFFER_SIZE = 4096;
270
271 std::vector<char> _buffer;
272 NMEA::LineFramer _framer;
273};
274
275} // namespace
276
278{
279 if (config.framers & GPSFrameKind::RTCM3) {
280 _framers[0] = std::make_unique<RTCM3Framer>();
281 }
282 if (config.framers & GPSFrameKind::UBX) {
283 _framers[1] = std::make_unique<UBXFramer>();
284 }
285 if (config.framers & GPSFrameKind::SBF) {
286 _framers[2] = std::make_unique<SBFFramer>(config.shareSBFSync);
287 }
288 if (config.framers & GPSFrameKind::NMEASentence) {
289 _framers[3] = std::make_unique<NMEASentenceFramer>();
290 }
291 if (config.framers & GPSFrameKind::ASCIILine) {
292 _framers[4] = std::make_unique<ASCIILineFramer>();
293 }
294 for (const auto& framer : _framers) {
295 if (framer) {
296 framer->enabled = config.enabled.testFlag(framer->kind());
297 }
298 }
299}
300
302
303void GPSStreamDemux::push(uint8_t byte, Handler& handler)
304{
305 std::array<Claim, FRAMER_COUNT> claims{};
306 Framer* exclusive = nullptr;
307 for (size_t index = 0; index < _framers.size(); ++index) {
308 const auto& framer = _framers[index];
309 if (!framer || !framer->enabled) {
310 continue;
311 }
312 claims[index] = framer->claim(byte);
313 if (claims[index] == Claim::Locked) {
314 exclusive = framer.get();
315 break;
316 }
317 if (claims[index] == Claim::Owns && !exclusive) {
318 exclusive = framer.get();
319 }
320 }
321 if (exclusive) {
322 for (const auto& framer : _framers) {
323 if (framer && framer->enabled && framer.get() != exclusive) {
324 framer->reset();
325 }
326 }
327 exclusive->feed(byte, handler);
328 return;
329 }
330 bool defaultFed = false;
331 for (size_t index = 0; index < _framers.size(); ++index) {
332 if (claims[index] == Claim::Watch || (claims[index] == Claim::Default && !defaultFed)) {
333 defaultFed |= claims[index] == Claim::Default;
334 _framers[index]->feed(byte, handler);
335 }
336 }
337}
338
340{
341 for (const auto& framer : _framers) {
342 if (framer) {
343 framer->reset();
344 }
345 }
346}
347
349{
350 for (const auto& framer : _framers) {
351 if (framer && framer->kind() == kind) {
352 framer->reset();
353 }
354 }
355}
356
358{
359 for (const auto& framer : _framers) {
360 if (framer && framer->kind() == kind) {
361 framer->reset();
362 framer->enabled = enabled;
363 }
364 }
365}
GPSFrameKind
Wire framings the stream demultiplexer recognises; also the set of framers a receiver family enables.
@ SBF
Septentrio "$@" block candidates, up to the first 110 bytes; the decoder checks the CRC.
@ UBX
u-blox 0xB5 0x62 frames with a valid Fletcher checksum.
@ 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
virtual ~Framer()=default
virtual Claim claim(uint8_t byte) const =0
Framer(GPSFrameKind kind)
GPSFrameKind kind() const
virtual void reset()=0
virtual void feed(uint8_t byte, Handler &handler)=0
virtual void frame(const GPSFrame &frame)=0
Receives each completed frame; the handler may reset or enable framers.
GPSStreamDemux(const GPSStreamConfig &config)
void push(uint8_t byte, Handler &handler)
void setEnabled(GPSFrameKind kind, bool enabled)
Enables or disables an available framer; either way it restarts from an empty state.
Frames printable ASCII lines. A '$' or '#' restarts the line, so a sentence or log is not lost after ...
Definition NMEAFramer.h:96
static constexpr uint16_t CRC_SIZE
Definition RTCMFramer.h:23
static constexpr uint16_t HEADER_SIZE
Definition RTCMFramer.h:22
static constexpr uint8_t PREAMBLE
Definition RTCMFramer.h:21
static uint16_t frameMessageId(std::span< const uint8_t > bytes)
Message number from a frame header; zero when the header is incomplete.
Definition RTCMFramer.h:56
QByteArray line(std::string_view command)
command as one CR/LF-terminated line.
constexpr std::chrono::microseconds remaining(uint64_t timestampUs, uint64_t nowUs, std::chrono::microseconds lifetime)
Time left before a receipt reaches lifetime; zero for a missing, future or expired one.
std::optional< Frame > frame(std::string_view text)
Septentrio receivers configured as RTK bases with SBF commands; they stream PVTGeodetic blocks and RT...
constexpr uint8_t SYNC1
Definition SBFBlocks.h:17
constexpr uint8_t SYNC2
Definition SBFBlocks.h:18
u-blox receivers over UBX: CFG-VALSET configuration from protocol 27, CFG-MSG and CFG-TMODE3 before i...
constexpr uint8_t SYNC1
Definition UBXFrame.h:118
The framers of one receiver stream and how they share it.
Starts every block. The CRC covers id through the end of the block; length counts the whole block.
Definition SBFBlocks.h:22