QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
NMEAFramer.h
Go to the documentation of this file.
1#pragma once
2
3#include <cstddef>
4#include <cstdint>
5#include <optional>
6#include <span>
7#include <string_view>
8
9#include <QtCore/QtGlobal>
10
11namespace NMEA {
12
15class Framer
16{
17public:
18 explicit Framer(std::span<uint8_t> buffer)
19 : _buffer(buffer)
20 {}
21
22 Q_DISABLE_COPY_MOVE(Framer)
23
24 size_t addByte(uint8_t byte)
25 {
26 if (_buffer.size() < 6) {
27 return 0;
28 }
29 switch (_state) {
30 case State::Waiting:
31 if (byte == '$') {
32 _size = 1;
33 _buffer[0] = byte;
34 _checksum = 0;
35 _state = State::Body;
36 }
37 break;
38 case State::Body:
39 if (byte == '$') {
40 _size = 0;
41 _checksum = 0;
42 } else if (byte == '*') {
43 _state = State::ChecksumHigh;
44 } else {
45 _checksum ^= byte;
46 }
47 // Preserve the receiver framers' original payload limit and checksum headroom.
48 if (_size >= _buffer.size() - 5) {
49 reset();
50 } else {
51 _buffer[_size++] = byte;
52 }
53 break;
54 case State::ChecksumHigh:
55 _buffer[_size++] = byte;
56 _state = State::ChecksumLow;
57 break;
58 case State::ChecksumLow: {
59 _buffer[_size++] = byte;
60 const size_t length =
61 _buffer[_size - 2] == _hexDigit(_checksum >> 4) && _buffer[_size - 1] == _hexDigit(_checksum)
62 ? _size
63 : 0;
64 reset();
65 return length;
66 }
67 }
68 return 0;
69 }
70
71 void reset()
72 {
73 _state = State::Waiting;
74 _size = 0;
75 _checksum = 0;
76 }
77
78private:
79 static constexpr char _hexDigit(unsigned value) { return "0123456789ABCDEF"[value & 0xf]; }
80
81 enum class State
82 {
83 Waiting,
84 Body,
85 ChecksumHigh,
86 ChecksumLow
87 };
88 std::span<uint8_t> _buffer;
89 size_t _size = 0;
90 uint8_t _checksum = 0;
91 State _state = State::Waiting;
92};
93
96{
97public:
98 explicit LineFramer(std::span<char> buffer)
99 : _buffer(buffer)
100 {}
101
103 std::optional<std::string_view> addByte(uint8_t byte)
104 {
105 const char ch = static_cast<char>(byte);
106 if (ch == '\r') {
107 if (_active && !_discard) {
108 _lineEnded = true;
109 }
110 return std::nullopt;
111 }
112
113 if (ch == '\n') {
114 std::optional<std::string_view> line;
115 if (_active && !_discard && _size > 0) {
116 line = std::string_view(_buffer.data(), _size);
117 }
118 reset();
119 return line;
120 }
121
122 if (_startsLine(ch)) {
123 _active = true;
124 _discard = false;
125 _lineEnded = false;
126 _size = 0;
127 _append(ch);
128 return std::nullopt;
129 }
130
131 _active = true;
132
133 if (_lineEnded || ch < ' ' || ch > '~' || _size == _buffer.size()) {
134 _size = 0;
135 _discard = true;
136 _lineEnded = false;
137 return std::nullopt;
138 }
139
140 _append(ch);
141 return std::nullopt;
142 }
143
144 void reset()
145 {
146 _active = false;
147 _discard = false;
148 _lineEnded = false;
149 _size = 0;
150 }
151
152private:
153 static bool _startsLine(char ch) { return ch == '$' || ch == '#'; }
154
155 void _append(char ch)
156 {
157 if (_discard || _size == _buffer.size()) {
158 _discard = true;
159 _size = 0;
160 return;
161 }
162 _buffer[_size++] = ch;
163 }
164
165 std::span<char> _buffer;
166 size_t _size = 0;
167 bool _active = false;
168 bool _discard = false;
169 bool _lineEnded = false;
170};
171
172} // namespace NMEA
size_t addByte(uint8_t byte)
Definition NMEAFramer.h:24
void reset()
Definition NMEAFramer.h:71
Framer(std::span< uint8_t > buffer)
Definition NMEAFramer.h:18
Frames printable ASCII lines. A '$' or '#' restarts the line, so a sentence or log is not lost after ...
Definition NMEAFramer.h:96
std::optional< std::string_view > addByte(uint8_t byte)
Returns a completed line without its terminator; the view is valid until the next byte.
Definition NMEAFramer.h:103
LineFramer(std::span< char > buffer)
Definition NMEAFramer.h:98