TinyModem
Header-only software audio modem library
Loading...
Searching...
No Matches
PacketStream.h
Go to the documentation of this file.
1#pragma once
2
3#include <string.h>
4
5#include "AudioTools.h"
6#include "AudioTools/Communication/FEC/ReedSolomon/rs.hpp"
7#include "ModemBase.h"
8
9namespace tinymodem {
10
12inline uint16_t crc16(const uint8_t* data, size_t len, uint16_t crc = 0xFFFF) {
13 for (size_t i = 0; i < len; i++) {
14 crc ^= (uint16_t)data[i] << 8;
15 for (int b = 0; b < 8; b++)
16 crc = (crc & 0x8000) ? (crc << 1) ^ 0x1021 : (crc << 1);
17 }
18 return crc;
19}
20
24 unsigned received = 0;
26 unsigned corrected = 0;
28 unsigned corrected_bytes = 0;
30 unsigned dropped = 0;
31};
32
35 static constexpr uint8_t FLAG = 0x7E;
36 static constexpr uint8_t ESC = 0x7D;
37 static constexpr uint8_t ESC_XOR = 0x20;
38 static constexpr int CRC_BYTES = 2;
39};
40
55template <uint8_t MAX_PAYLOAD = 64, uint8_t ECC_BYTES = 16>
56class PacketEncoder : public Print {
57 static_assert(MAX_PAYLOAD >= 1, "MAX_PAYLOAD must be at least 1");
58 static_assert(ECC_BYTES >= 2, "ECC_BYTES must be at least 2");
59 static_assert(MAX_PAYLOAD + PacketFormat::CRC_BYTES + ECC_BYTES <= 255,
60 "a packet is limited to 255 bytes");
61
62 public:
63 PacketEncoder() = default;
64 PacketEncoder(Print& out) { setOutput(out); }
65
66 void setOutput(Print& out) { p_out = &out; }
67
68 size_t write(uint8_t ch) override { return write(&ch, 1); }
69
70 size_t write(const uint8_t* data, size_t len) override {
71 if (p_out == nullptr) return 0;
72 for (size_t j = 0; j < len; j++) {
73 payload[payload_len++] = data[j];
74 if (payload_len == MAX_PAYLOAD) writePacket();
75 }
76 return len;
77 }
78
80 void flush() override {
81 if (payload_len > 0) writePacket();
82 }
83
85 int pending() const { return payload_len; }
86
87 protected:
88 static constexpr int MSG_LEN = MAX_PAYLOAD + PacketFormat::CRC_BYTES;
89 Print* p_out = nullptr;
90 uint8_t payload[MAX_PAYLOAD];
91 int payload_len = 0;
92 RS::ReedSolomon<MSG_LEN, ECC_BYTES> rs;
93
94 void writePacket() {
95 // shortened code: the unused leading message bytes are 0 and not sent
96 uint8_t msg[MSG_LEN] = {0};
97 uint8_t codeword[MSG_LEN + ECC_BYTES];
98 int skip = MAX_PAYLOAD - payload_len;
99 memcpy(msg + skip, payload, payload_len);
100 uint16_t crc = crc16(payload, payload_len);
101 msg[MSG_LEN - 2] = crc >> 8;
102 msg[MSG_LEN - 1] = crc & 0xFF;
103 rs.Encode(msg, codeword);
104
106 for (int j = skip; j < MSG_LEN + ECC_BYTES; j++) writeEscaped(codeword[j]);
108 payload_len = 0;
109 }
110
111 void writeEscaped(uint8_t b) {
112 if (b == PacketFormat::FLAG || b == PacketFormat::ESC) {
113 p_out->write(PacketFormat::ESC);
115 }
116 p_out->write(b);
117 }
118};
119
128template <uint8_t MAX_PAYLOAD = 64, uint8_t ECC_BYTES = 16>
129class PacketDecoder : public Print {
130 public:
131 PacketDecoder() = default;
132 PacketDecoder(Print& out) { setOutput(out); }
133
134 void setOutput(Print& out) { p_out = &out; }
135
136 size_t write(uint8_t ch) override {
137 if (ch == PacketFormat::FLAG) {
138 if (frame_len > 0 || overflow) processFrame();
139 frame_len = 0;
140 overflow = false;
141 escaped = false;
142 return 1;
143 }
144 if (ch == PacketFormat::ESC) {
145 escaped = true;
146 return 1;
147 }
148 if (escaped) {
150 escaped = false;
151 }
152 if (frame_len < MAX_FRAME)
153 frame[frame_len++] = ch;
154 else
155 overflow = true;
156 return 1;
157 }
158
159 size_t write(const uint8_t* data, size_t len) override {
160 for (size_t j = 0; j < len; j++) write(data[j]);
161 return len;
162 }
163
165 const PacketStatistics& statistics() const { return stats; }
166
168 void reset() {
170 frame_len = 0;
171 overflow = false;
172 escaped = false;
173 }
174
175 protected:
176 static constexpr int MSG_LEN = MAX_PAYLOAD + PacketFormat::CRC_BYTES;
177 static constexpr int MAX_FRAME = MSG_LEN + ECC_BYTES;
178 Print* p_out = nullptr;
179 uint8_t frame[MAX_FRAME];
180 int frame_len = 0;
181 bool overflow = false;
182 bool escaped = false;
184 RS::ReedSolomon<MSG_LEN, ECC_BYTES> rs;
185
187 int n = frame_len;
188 if (overflow || n < PacketFormat::CRC_BYTES + ECC_BYTES + 1) {
189 stats.dropped++;
190 return;
191 }
192 // restore the full codeword of the shortened code
193 uint8_t codeword[MAX_FRAME] = {0};
194 int skip = MAX_FRAME - n;
195 memcpy(codeword + skip, frame, n);
196 uint8_t msg[MSG_LEN];
197 if (rs.Decode(codeword, msg) != 0) {
198 stats.dropped++;
199 return;
200 }
201 // the padding must still be 0
202 for (int j = 0; j < skip; j++) {
203 if (msg[j] != 0) {
204 stats.dropped++;
205 return;
206 }
207 }
208 int payload_len = n - ECC_BYTES - PacketFormat::CRC_BYTES;
209 const uint8_t* payload = msg + skip;
210 uint16_t crc = crc16(payload, payload_len);
211 if (msg[MSG_LEN - 2] != (crc >> 8) || msg[MSG_LEN - 1] != (crc & 0xFF)) {
212 stats.dropped++;
213 return;
214 }
215 // count the corrected bytes
216 uint8_t corrected[MAX_FRAME];
217 rs.Encode(msg, corrected);
218 int errors = 0;
219 for (int j = skip; j < MAX_FRAME; j++)
220 if (corrected[j] != codeword[j]) errors++;
221 stats.received++;
222 if (errors > 0) {
223 stats.corrected++;
224 stats.corrected_bytes += errors;
225 }
226 if (p_out != nullptr) p_out->write(payload, payload_len);
227 }
228};
229
245template <uint8_t MAX_PAYLOAD = 64, uint8_t ECC_BYTES = 16>
246class PacketStream : public Stream {
247 public:
248 PacketStream() { decoder.setOutput(rx_queue); }
250 PacketStream(Stream& io) {
251 decoder.setOutput(rx_queue);
252 setStream(io);
253 }
254
255 PacketStream(Print& out) {
256 decoder.setOutput(rx_queue);
257 setOutput(out);
258 }
259
261 void setStream(Stream& io) {
262 p_in = &io;
263 setOutput(io);
264 }
265
267 void setOutput(Print& out) {
268 p_out = &out;
269 encoder.setOutput(out);
270 }
271
273 bool begin() {
274 decoder.setOutput(rx_queue);
275 decoder.reset();
276 rx_queue.clear();
277 return true;
278 }
279
280 size_t write(uint8_t ch) override { return encoder.write(&ch, 1); }
281
282 size_t write(const uint8_t* data, size_t len) override {
283 return encoder.write(data, len);
284 }
285
287 void flush() override {
288 encoder.flush();
289 if (p_out != nullptr) p_out->flush();
290 }
291
292 int available() override {
293 update();
294 return rx_queue.available();
295 }
296
297 int read() override {
298 update();
299 return rx_queue.read();
300 }
301
302 int peek() override {
303 update();
304 return rx_queue.peek();
305 }
306
308 size_t readBytes(uint8_t* data, size_t len) {
309 size_t n = 0;
310 while (n < len) {
311 int ch = read();
312 if (ch < 0) break;
313 data[n++] = ch;
314 }
315 return n;
316 }
317
318 size_t readBytes(char* data, size_t len) {
319 return readBytes((uint8_t*)data, len);
320 }
321
323 void update() {
324 if (p_in == nullptr) return;
325 uint8_t buffer[64];
326 int len;
327 while ((len = p_in->available()) > 0) {
328 if (len > (int)sizeof(buffer)) len = sizeof(buffer);
329 len = p_in->readBytes(buffer, len);
330 if (len <= 0) break;
331 decoder.write(buffer, len);
332 }
333 }
334
336 const PacketStatistics& statistics() const { return decoder.statistics(); }
337
338 protected:
339 Print* p_out = nullptr;
340 Stream* p_in = nullptr;
344};
345
346} // namespace tinymodem
FIFO for received data which grows when needed: e.g. one decoding step can produce more data than exp...
Definition ModemBase.h:249
Receives the packets of a PacketEncoder: the written bytes (e.g. the output of a modem decoder) are d...
Definition PacketStream.h:129
static constexpr int MAX_FRAME
Definition PacketStream.h:177
bool overflow
Definition PacketStream.h:181
uint8_t frame[MAX_FRAME]
Definition PacketStream.h:179
int frame_len
Definition PacketStream.h:180
const PacketStatistics & statistics() const
Statistics of the received packets.
Definition PacketStream.h:165
void reset()
Resets the statistics and drops an incomplete frame.
Definition PacketStream.h:168
void setOutput(Print &out)
Definition PacketStream.h:134
Print * p_out
Definition PacketStream.h:178
void processFrame()
Definition PacketStream.h:186
PacketDecoder(Print &out)
Definition PacketStream.h:132
PacketStatistics stats
Definition PacketStream.h:183
RS::ReedSolomon< MSG_LEN, ECC_BYTES > rs
Definition PacketStream.h:184
size_t write(uint8_t ch) override
Definition PacketStream.h:136
bool escaped
Definition PacketStream.h:182
static constexpr int MSG_LEN
Definition PacketStream.h:176
size_t write(const uint8_t *data, size_t len) override
Definition PacketStream.h:159
Splits the written data into packets with a CRC-16 and Reed-Solomon forward error correction,...
Definition PacketStream.h:56
uint8_t payload[MAX_PAYLOAD]
Definition PacketStream.h:90
int pending() const
Number of buffered bytes which were not sent yet.
Definition PacketStream.h:85
int payload_len
Definition PacketStream.h:91
Print * p_out
Definition PacketStream.h:89
void writePacket()
Definition PacketStream.h:94
size_t write(const uint8_t *data, size_t len) override
Definition PacketStream.h:70
void setOutput(Print &out)
Definition PacketStream.h:66
void writeEscaped(uint8_t b)
Definition PacketStream.h:111
PacketEncoder(Print &out)
Definition PacketStream.h:64
size_t write(uint8_t ch) override
Definition PacketStream.h:68
static constexpr int MSG_LEN
Definition PacketStream.h:88
RS::ReedSolomon< MSG_LEN, ECC_BYTES > rs
Definition PacketStream.h:92
void flush() override
Sends the buffered data as packet.
Definition PacketStream.h:80
bool begin()
Resets the receive state and the statistics.
Definition PacketStream.h:273
PacketStream()
Definition PacketStream.h:248
void flush() override
Sends the buffered data as packet and flushes the underlying stream.
Definition PacketStream.h:287
Print * p_out
Definition PacketStream.h:339
size_t readBytes(char *data, size_t len)
Definition PacketStream.h:318
int available() override
Definition PacketStream.h:292
int read() override
Definition PacketStream.h:297
PacketEncoder< MAX_PAYLOAD, ECC_BYTES > encoder
Definition PacketStream.h:341
size_t readBytes(uint8_t *data, size_t len)
Reads the available data (does not wait for more data).
Definition PacketStream.h:308
Stream * p_in
Definition PacketStream.h:340
const PacketStatistics & statistics() const
Statistics of the received packets.
Definition PacketStream.h:336
PacketDecoder< MAX_PAYLOAD, ECC_BYTES > decoder
Definition PacketStream.h:342
void update()
Reads the available data from the underlying stream.
Definition PacketStream.h:323
void setOutput(Print &out)
Defines the output (send only).
Definition PacketStream.h:267
ByteQueue rx_queue
Definition PacketStream.h:343
PacketStream(Print &out)
send only
Definition PacketStream.h:255
void setStream(Stream &io)
Defines the stream for sending and receiving.
Definition PacketStream.h:261
int peek() override
Definition PacketStream.h:302
size_t write(uint8_t ch) override
Definition PacketStream.h:280
size_t write(const uint8_t *data, size_t len) override
Definition PacketStream.h:282
PacketStream(Stream &io)
send and receive
Definition PacketStream.h:250
TinyModem: software audio modems and packets with error correction.
Definition AX25.h:11
uint16_t crc16(const uint8_t *data, size_t len, uint16_t crc=0xFFFF)
CRC-16-CCITT (polynomial 0x1021, initial value 0xFFFF).
Definition PacketStream.h:12
Constants of the packet format.
Definition PacketStream.h:34
static constexpr int CRC_BYTES
Definition PacketStream.h:38
static constexpr uint8_t ESC_XOR
Definition PacketStream.h:37
static constexpr uint8_t ESC
Definition PacketStream.h:36
static constexpr uint8_t FLAG
Definition PacketStream.h:35
Statistics of the received packets.
Definition PacketStream.h:22
unsigned dropped
frames which could not be corrected or failed the CRC check
Definition PacketStream.h:30
unsigned corrected_bytes
number of corrected bytes
Definition PacketStream.h:28
unsigned corrected
delivered packets which needed an error correction
Definition PacketStream.h:26
unsigned received
packets which were delivered
Definition PacketStream.h:24