TinyModem
Header-only software audio modem library
Loading...
Searching...
No Matches
DataCodecs.h
Go to the documentation of this file.
1#pragma once
2
3#include <ctype.h>
4#include <stdarg.h>
5#include <stdint.h>
6#include <stdio.h>
7
8#include "AudioTools.h"
9
10namespace tinymodem {
11
13inline uint64_t bitReverse(uint64_t value, unsigned bits) {
14 uint64_t out = 0;
15 while (bits--) {
16 out = (out << 1) | (value & 1);
17 value >>= 1;
18 }
19 return out;
20}
21
23inline uint64_t bitWindow(uint64_t value, unsigned offset, unsigned bits) {
24 uint64_t mask = (bits >= 64) ? ~0ULL : ((1ULL << bits) - 1);
25 return (value >> offset) & mask;
26}
27
32class DataCodec {
33 public:
34 virtual ~DataCodec() = default;
36 virtual void reset() {}
38 virtual int encode(uint32_t words[2], uint8_t ch) {
39 words[0] = ch;
40 return 1;
41 }
42
43 virtual size_t decode(Print& out, uint64_t bits, unsigned n_bits) = 0;
44};
45
47class AsciiCodec : public DataCodec {
48 public:
49 size_t decode(Print& out, uint64_t bits, unsigned /*n_bits*/) override {
50 return out.write((uint8_t)(bits & 0xFF));
51 }
52};
53
55class BinaryCodec : public DataCodec {
56 public:
57 size_t decode(Print& out, uint64_t bits, unsigned n_bits) override {
58 for (unsigned j = 0; j < n_bits; j++) out.write((bits >> j & 1) + '0');
59 out.write('\n');
60 return n_bits + 1;
61 }
62};
63
65class BaudotCodec : public DataCodec {
66 public:
68 void setUSOS(bool usos) { this->usos = usos; }
69
70 void reset() override { charset = 1; }
71
74 void resetShift() { charset = 0; }
75
76 int encode(uint32_t words[2], uint8_t ch) override {
77 ch = toupper(ch);
78 if (ch >= 0x60) return 0; // non-encodable
79 int n = 0;
80 uint8_t charset_mask = encodeTable(ch)[1];
81 if ((charset & charset_mask) == 0) {
82 if (charset_mask == 0) return 0; // non-encodable
83 if (charset == 0) charset = 1;
84 if (charset_mask != 3) charset = charset_mask;
85 words[n++] = charset == 1 ? LTRS : FIGS;
86 }
87 words[n++] = encodeTable(ch)[0];
88 // TX un-shift on space
89 if (ch == ' ' && usos) charset = 1;
90 return n;
91 }
92
93 size_t decode(Print& out, uint64_t bits, unsigned /*n_bits*/) override {
94 uint8_t databits = bits & 0x1F;
95 if (databits == FIGS) {
96 charset = 2;
97 return 0;
98 }
99 if (databits == LTRS) {
100 charset = 1;
101 return 0;
102 }
103 // RX un-shift on space
104 if (databits == SPACE && usos) charset = 1;
105 // letters or U.S. figures
106 return out.write(decodeTable(databits)[charset == 1 ? 0 : 1]);
107 }
108
109 protected:
110 static constexpr uint8_t LTRS = 0x1F;
111 static constexpr uint8_t FIGS = 0x1B;
112 static constexpr uint8_t SPACE = 0x04;
114 unsigned charset = 0;
115 bool usos = true;
116
118 static const char* decodeTable(uint8_t idx) {
119 static const char table[32][3] = {
120 {'_', '^', '^'}, {'E', '3', '3'}, {0xA, 0xA, 0xA},
121 {'A', '-', '-'}, {' ', ' ', ' '}, {'S', 0x7, '\''},
122 {'I', '8', '8'}, {'U', '7', '7'}, {0xD, 0xD, 0xD},
123 {'D', '$', '^'}, {'R', '4', '4'}, {'J', '\'', 0x7},
124 {'N', ',', ','}, {'F', '!', '!'}, {'C', ':', ':'},
125 {'K', '(', '('}, {'T', '5', '5'}, {'Z', '"', '+'},
126 {'L', ')', ')'}, {'W', '2', '2'}, {'H', '#', '%'},
127 {'Y', '6', '6'}, {'P', '0', '0'}, {'Q', '1', '1'},
128 {'O', '9', '9'}, {'B', '?', '?'}, {'G', '&', '&'},
129 {'%', '%', '%'}, {'M', '.', '.'}, {'X', '/', '/'},
130 {'V', ';', '='}, {'%', '%', '%'},
131 };
132 return table[idx];
133 }
134
136 static const uint8_t* encodeTable(uint8_t idx) {
137 static const uint8_t table[0x60][2] = {
138 // 0x00
139 {0x00, 3}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
140 {0x05, 2}, {0, 0}, {0, 0}, {0x02, 3}, {0, 0}, {0, 0}, {0x08, 3},
141 {0, 0}, {0, 0},
142 // 0x10
143 {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
144 {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
145 // 0x20: ! " # $ % & ' ( ) * + , - . /
146 {0x04, 3}, {0x0d, 2}, {0x11, 2}, {0x14, 2}, {0x09, 2}, {0, 0},
147 {0x1a, 2}, {0x0b, 2}, {0x0f, 2}, {0x12, 2}, {0, 0}, {0x12, 2},
148 {0x0c, 2}, {0x03, 2}, {0x1c, 2}, {0x1d, 2},
149 // 0x30: 0-9 : ; < = > ?
150 {0x16, 2}, {0x17, 2}, {0x13, 2}, {0x01, 2}, {0x0a, 2}, {0x10, 2},
151 {0x15, 2}, {0x07, 2}, {0x06, 2}, {0x18, 2}, {0x0e, 2}, {0x1e, 2},
152 {0, 0}, {0, 0}, {0, 0}, {0x19, 2},
153 // 0x40: @ A-O
154 {0, 0}, {0x03, 1}, {0x19, 1}, {0x0e, 1}, {0x09, 1}, {0x01, 1},
155 {0x0d, 1}, {0x1a, 1}, {0x14, 1}, {0x06, 1}, {0x0b, 1}, {0x0f, 1},
156 {0x12, 1}, {0x1c, 1}, {0x0c, 1}, {0x18, 1},
157 // 0x50: P-Z [ \ ] ^ _
158 {0x16, 1}, {0x17, 1}, {0x0a, 1}, {0x05, 1}, {0x10, 1}, {0x07, 1},
159 {0x1e, 1}, {0x13, 1}, {0x1d, 1}, {0x15, 1}, {0x11, 1}, {0, 0},
160 {0, 0}, {0, 0}, {0, 0}, {0, 0},
161 };
162 return table[idx];
163 }
164};
165
171class CallerIDCodec : public DataCodec {
172 public:
173 void reset() override {
174 msg_type = 0;
175 ndata = 0;
176 }
177
178 size_t decode(Print& out, uint64_t bits, unsigned /*n_bits*/) override {
179 if (msg_type == 0) {
180 if (bits == MSG_MDMF || bits == MSG_SDMF)
181 msg_type = bits;
182 else
183 return 0;
184 buf[ndata++] = bits;
185 return 0;
186 }
187 if (ndata >= sizeof(buf)) {
188 reset();
189 return 0;
190 }
191 buf[ndata++] = bits;
192 // collect bytes until we have the message length + type + checksum
193 unsigned msglen = buf[1];
194 if (ndata < msglen + 2) return 0;
195
196 size_t n = out.print("CALLER-ID\n");
197 n += msg_type == MSG_MDMF ? decodeMDMF(out) : decodeSDMF(out);
198 reset();
199 return n;
200 }
201
202 protected:
203 static constexpr uint8_t MSG_MDMF = 0x80;
204 static constexpr uint8_t MSG_SDMF = 0x04;
205 static constexpr uint8_t DATA_DATETIME = 0x01;
206 static constexpr uint8_t DATA_PHONE = 0x02;
207 static constexpr uint8_t DATA_PHONE_NA = 0x04;
208 static constexpr uint8_t DATA_NAME = 0x07;
209 static constexpr uint8_t DATA_NAME_NA = 0x08;
210 unsigned msg_type = 0;
211 unsigned ndata = 0;
212 uint8_t buf[256];
213
214 static const char* typeName(unsigned type) {
215 static const char* names[] = {"unknown0:", "Time:", "Phone:",
216 "unknown3:", "Phone:", "unknown5:",
217 "unknown6:", "Name:", "Name:"};
218 return names[type];
219 }
220
221 static size_t printFmt(Print& out, const char* fmt, ...) {
222 char tmp[80];
223 va_list args;
224 va_start(args, fmt);
225 vsnprintf(tmp, sizeof(tmp), fmt, args);
226 va_end(args);
227 return out.print(tmp);
228 }
229
230 static size_t printPhone(Print& out, const uint8_t* m) {
231 return printFmt(out, "%.3s-%.3s-%.4s\n", m, m + 3, m + 6);
232 }
233
234 static size_t printDateTime(Print& out, const uint8_t* m) {
235 return printFmt(out, "%.2s/%.2s %.2s:%.2s\n", m, m + 2, m + 4, m + 6);
236 }
237
238 size_t decodeMDMF(Print& out) {
239 size_t n = 0;
240 unsigned i = 0;
241 unsigned msglen = buf[1];
242 const uint8_t* m = buf + 2;
243 while (i < msglen) {
244 unsigned type = *m++;
245 if (type > DATA_NAME_NA) return n; // bad datastream
246 unsigned len = *m++;
247 if (m + 2 + len >= buf + sizeof(buf)) return n; // bad datastream
248 n += printFmt(out, "%-6s ", typeName(type));
249 const char* prdata = nullptr;
250 unsigned prlen = 0;
251 switch (type) {
252 case DATA_DATETIME:
253 if (len >= 8) n += printDateTime(out, m);
254 break;
255 case DATA_PHONE:
256 if (len == 10) {
257 n += printPhone(out, m);
258 break;
259 }
260 // fallthrough
261 case DATA_NAME:
262 prdata = (const char*)m;
263 prlen = len;
264 break;
265 case DATA_PHONE_NA:
266 case DATA_NAME_NA:
267 if (len == 1 && *m == 'O') {
268 prdata = "[N/A]";
269 prlen = 5;
270 } else if (len == 1 && *m == 'P') {
271 prdata = "[blocked]";
272 prlen = 9;
273 }
274 break;
275 default:
276 break;
277 }
278 if (prdata) {
279 n += out.write((const uint8_t*)prdata, prlen);
280 n += out.write('\n');
281 }
282 m += len;
283 i += len + 2;
284 }
285 return n;
286 }
287
288 size_t decodeSDMF(Print& out) {
289 size_t n = 0;
290 unsigned msglen = buf[1];
291 const uint8_t* m = buf + 2;
292 n += printFmt(out, "%-6s ", typeName(DATA_DATETIME));
293 n += printDateTime(out, m);
294 if (msglen < 8) return n;
295 m += 8;
296 n += printFmt(out, "%-6s ", typeName(DATA_PHONE));
297 unsigned len = msglen - 8;
298 if (len == 10) {
299 n += printPhone(out, m);
300 } else {
301 n += out.write(m, len);
302 n += out.write('\n');
303 }
304 return n;
305 }
306};
307
309class UICCodec : public DataCodec {
310 public:
311 UICCodec(bool trainToGround = false) { train = trainToGround; }
312 void setTrainToGround(bool flag) { train = flag; }
313
314 size_t decode(Print& out, uint64_t input, unsigned /*n_bits*/) override {
315 unsigned code = bitReverse(bitWindow(input, 24, 8), 8);
316 char tmp[100];
317 snprintf(tmp, sizeof(tmp),
318 "Train ID: %X%X%X%X%X%X - Message: %02X (%s)\n",
319 (unsigned)bitWindow(input, 0, 4), (unsigned)bitWindow(input, 4, 4),
320 (unsigned)bitWindow(input, 8, 4), (unsigned)bitWindow(input, 12, 4),
321 (unsigned)bitWindow(input, 16, 4),
322 (unsigned)bitWindow(input, 20, 4), code, meaning(code));
323 return out.print(tmp);
324 }
325
326 protected:
327 bool train = false;
328
330 struct Message {
331 int code;
332 const char* meaning;
333 };
334
335 const char* meaning(unsigned code) {
336 static const Message ground_to_train[] = {
337 {0x00, "Test"}, {0x02, "Run slower"},
338 {0x03, "Extension of telegram"},
339 {0x04, "Run faster"}, {0x06, "Written order"},
340 {0x08, "Speech"}, {0x09, "Emergency stop"},
341 {0x0C, "Announcem. by loudspeaker"},
342 {0x55, "Idle"}, {-1, nullptr}};
343 static const Message train_to_ground[] = {
344 {0x08, "Communic. desired"},
345 {0x0A, "Acknowl. of order"},
346 {0x06, "Advice"},
347 {0x00, "Test"},
348 {0x09, "Train staff wish to comm."},
349 {0x0C, "Telephone link desired"},
350 {0x03, "Extension of telegram"},
351 {-1, nullptr}};
352 for (const Message* m = train ? train_to_ground : ground_to_train;
353 m->code != -1; m++) {
354 if (m->code == (int)code) return m->meaning;
355 }
356 return "Unknown";
357 }
358};
359
361class PrintFilter : public Print {
362 public:
363 void setOutput(Print* out) { p_out = out; }
364 size_t write(uint8_t ch) override {
365 if (p_out == nullptr) return 0;
366 return p_out->write(isprint(ch) || isspace(ch) ? ch : '.');
367 }
368 size_t write(const uint8_t* data, size_t len) override {
369 for (size_t j = 0; j < len; j++) write(data[j]);
370 return len;
371 }
372
373 protected:
374 Print* p_out = nullptr;
375};
376
377} // namespace tinymodem
ASCII 8-bit data (passthrough).
Definition DataCodecs.h:47
size_t decode(Print &out, uint64_t bits, unsigned) override
Decodes the data bits of a frame: returns the number of written bytes.
Definition DataCodecs.h:49
Baudot (ITA2) 5-bit data.
Definition DataCodecs.h:65
static constexpr uint8_t FIGS
Definition DataCodecs.h:111
static const char * decodeTable(uint8_t idx)
letter, U.S. figs, CCITT No.2 figs (Europe)
Definition DataCodecs.h:118
void resetShift()
Definition DataCodecs.h:74
bool usos
Definition DataCodecs.h:115
int encode(uint32_t words[2], uint8_t ch) override
Converts a character into 0, 1 or 2 data words: returns the count.
Definition DataCodecs.h:76
static const uint8_t * encodeTable(uint8_t idx)
index: ascii char; values: bits, ltrs(1) or figs(2) or both(3)
Definition DataCodecs.h:136
void setUSOS(bool usos)
Unshift on space.
Definition DataCodecs.h:68
void reset() override
resets the internal state (called when the carrier is acquired)
Definition DataCodecs.h:70
static constexpr uint8_t SPACE
Definition DataCodecs.h:112
static constexpr uint8_t LTRS
Definition DataCodecs.h:110
unsigned charset
0 unknown, 1 LTRS, 2 FIGS
Definition DataCodecs.h:114
size_t decode(Print &out, uint64_t bits, unsigned) override
Decodes the data bits of a frame: returns the number of written bytes.
Definition DataCodecs.h:93
Raw N-bit binary data: prints the bits as '0' and '1' followed by newline.
Definition DataCodecs.h:55
size_t decode(Print &out, uint64_t bits, unsigned n_bits) override
Decodes the data bits of a frame: returns the number of written bytes.
Definition DataCodecs.h:57
Caller-ID (USA SDMF/MDMF) decoder. See http://melabs.com/resources/callerid.htm.
Definition DataCodecs.h:171
static constexpr uint8_t DATA_DATETIME
Definition DataCodecs.h:205
static size_t printPhone(Print &out, const uint8_t *m)
Definition DataCodecs.h:230
static constexpr uint8_t DATA_NAME_NA
Definition DataCodecs.h:209
unsigned ndata
Definition DataCodecs.h:211
static size_t printFmt(Print &out, const char *fmt,...)
Definition DataCodecs.h:221
size_t decodeSDMF(Print &out)
Definition DataCodecs.h:288
static constexpr uint8_t MSG_MDMF
Definition DataCodecs.h:203
static constexpr uint8_t DATA_PHONE
Definition DataCodecs.h:206
static size_t printDateTime(Print &out, const uint8_t *m)
Definition DataCodecs.h:234
unsigned msg_type
Definition DataCodecs.h:210
size_t decodeMDMF(Print &out)
Definition DataCodecs.h:238
static constexpr uint8_t DATA_PHONE_NA
Definition DataCodecs.h:207
size_t decode(Print &out, uint64_t bits, unsigned) override
Decodes the data bits of a frame: returns the number of written bytes.
Definition DataCodecs.h:178
static constexpr uint8_t DATA_NAME
Definition DataCodecs.h:208
static const char * typeName(unsigned type)
Definition DataCodecs.h:214
static constexpr uint8_t MSG_SDMF
Definition DataCodecs.h:204
uint8_t buf[256]
Definition DataCodecs.h:212
void reset() override
resets the internal state (called when the carrier is acquired)
Definition DataCodecs.h:173
Converts between characters and the data bits of a frame.
Definition DataCodecs.h:32
virtual size_t decode(Print &out, uint64_t bits, unsigned n_bits)=0
Decodes the data bits of a frame: returns the number of written bytes.
virtual int encode(uint32_t words[2], uint8_t ch)
Converts a character into 0, 1 or 2 data words: returns the count.
Definition DataCodecs.h:38
virtual ~DataCodec()=default
virtual void reset()
resets the internal state (called when the carrier is acquired)
Definition DataCodecs.h:36
Print which replaces non printable characters with '.'.
Definition DataCodecs.h:361
void setOutput(Print *out)
Definition DataCodecs.h:363
size_t write(const uint8_t *data, size_t len) override
Definition DataCodecs.h:368
size_t write(uint8_t ch) override
Definition DataCodecs.h:364
Print * p_out
Definition DataCodecs.h:374
const char * meaning(unsigned code)
Definition DataCodecs.h:335
void setTrainToGround(bool flag)
Definition DataCodecs.h:312
size_t decode(Print &out, uint64_t input, unsigned) override
Decodes the data bits of a frame: returns the number of written bytes.
Definition DataCodecs.h:314
UICCodec(bool trainToGround=false)
Definition DataCodecs.h:311
bool train
Definition DataCodecs.h:327
TinyModem: software audio modems and packets with error correction.
Definition AX25.h:11
uint64_t bitReverse(uint64_t value, unsigned bits)
Reverses the ordering of the bits on an integer.
Definition DataCodecs.h:13
uint64_t bitWindow(uint64_t value, unsigned offset, unsigned bits)
Gets "bits" bits from "value" starting "offset" bits from the start.
Definition DataCodecs.h:23
UIC message code and its meaning.
Definition DataCodecs.h:330
int code
Definition DataCodecs.h:331
const char * meaning
Definition DataCodecs.h:332