TinyModem
Header-only software audio modem library
Loading...
Searching...
No Matches
PSK.h
Go to the documentation of this file.
1#pragma once
2
3#include <ctype.h>
4#include <stdint.h>
5#include <stdlib.h>
6#include <string.h>
7
8#include "AudioTools.h"
9
10namespace tinymodem {
11
13enum class PSKModulation {
21};
22
34struct PSKConfig : public audio_tools::AudioInfo {
36 sample_rate = 48000;
37 channels = 1;
38 bits_per_sample = 16;
39 }
40
42 float symbol_rate = 31.25f;
46 float carrier_freq = 1000.0f;
48 float volume = 1.0f;
54 bool afc = true;
56 float min_amplitude = 0.005f;
58 bool rx_one = false;
60 uint8_t channel = 0;
62 float rx_noise = 0.0f;
63
66 bool setMode(const char* mode) {
67 PSKModulation mod;
68 float rate = modeRate(mode, mod);
69 if (rate <= 0) return false;
70 symbol_rate = rate;
71 modulation = mod;
73 return true;
74 }
75
77 int bitsPerSymbol() const {
80 : 3;
81 }
82
84 static bool isMode(const char* mode) {
85 PSKModulation mod;
86 return modeRate(mode, mod) > 0;
87 }
88
90 float samplesPerSymbol() const { return sample_rate / symbol_rate; }
91
93 bool isValid() const {
94 if (symbol_rate <= 0 || carrier_freq <= 0) {
95 LOGE("PSK: invalid symbol_rate or carrier_freq");
96 return false;
97 }
98 if (carrier_freq + symbol_rate >= sample_rate / 2.0f) {
99 LOGE("PSK: the carrier must be below %.1f Hz",
100 (double)(sample_rate / 2.0f - symbol_rate));
101 return false;
102 }
103 if (carrier_freq < 2 * symbol_rate) {
104 LOGE("PSK: the carrier must be at least twice the symbol rate");
105 return false;
106 }
107 if (samplesPerSymbol() < 8) {
108 LOGE("PSK: at least 8 samples per symbol are needed");
109 return false;
110 }
111 return true;
112 }
113
114 protected:
115 static float modeRate(const char* mode, PSKModulation& mod) {
116 if (mode == nullptr) return 0;
118 if (tolower(mode[0]) == 'q') {
120 mode++;
121 } else if (mode[0] == '8') {
123 mode++;
124 }
125 const char* prefix = "psk";
126 for (int j = 0; j < 3; j++)
127 if (tolower(mode[j]) != prefix[j]) return 0;
128 const char* number = mode + 3;
129 for (const char* c = number; *c; c++)
130 if (!isdigit((unsigned char)*c)) return 0;
131 int n = atoi(number);
132 if (mod == PSKModulation::PSK8)
133 return (n == 125 || n == 250 || n == 500 || n == 1000) ? n : 0;
134 switch (n) {
135 case 31:
136 return 31.25f;
137 case 63:
138 return 62.5f;
139 case 125:
140 case 250:
141 case 500:
142 return n;
143 default:
144 return 0;
145 }
146 }
147};
148
155class Varicode {
156 public:
158 struct Code {
159 uint16_t bits; // most significant bit is sent first
160 uint8_t length; // number of bits
161 };
162
164 static Code encode(uint8_t ch) { return table()[ch]; }
165
167 static int decode(uint16_t bits) {
168 for (int j = 0; j < 256; j++)
169 if (table()[j].bits == bits) return j;
170 return -1;
171 }
172
173 protected:
174 static const Code* table() {
175 // generated from fldigi's varicodetab1 (src/psk/pskvaricode.cxx)
176 static const Code codes[256] = {
177 {0x2AB, 10}, {0x2DB, 10}, {0x2ED, 10}, {0x377, 10}, {0x2EB, 10}, {0x35F, 10},
178 {0x2EF, 10}, {0x2FD, 10}, {0x2FF, 10}, {0x0EF, 8}, {0x01D, 5}, {0x36F, 10},
179 {0x2DD, 10}, {0x01F, 5}, {0x375, 10}, {0x3AB, 10}, {0x2F7, 10}, {0x2F5, 10},
180 {0x3AD, 10}, {0x3AF, 10}, {0x35B, 10}, {0x36B, 10}, {0x36D, 10}, {0x357, 10},
181 {0x37B, 10}, {0x37D, 10}, {0x3B7, 10}, {0x355, 10}, {0x35D, 10}, {0x3BB, 10},
182 {0x2FB, 10}, {0x37F, 10}, {0x001, 1}, {0x1FF, 9}, {0x15F, 9}, {0x1F5, 9},
183 {0x1DB, 9}, {0x2D5, 10}, {0x2BB, 10}, {0x17F, 9}, {0x0FB, 8}, {0x0F7, 8},
184 {0x16F, 9}, {0x1DF, 9}, {0x075, 7}, {0x035, 6}, {0x057, 7}, {0x1AF, 9},
185 {0x0B7, 8}, {0x0BD, 8}, {0x0ED, 8}, {0x0FF, 8}, {0x177, 9}, {0x15B, 9},
186 {0x16B, 9}, {0x1AD, 9}, {0x1AB, 9}, {0x1B7, 9}, {0x0F5, 8}, {0x1BD, 9},
187 {0x1ED, 9}, {0x055, 7}, {0x1D7, 9}, {0x2AF, 10}, {0x2BD, 10}, {0x07D, 7},
188 {0x0EB, 8}, {0x0AD, 8}, {0x0B5, 8}, {0x077, 7}, {0x0DB, 8}, {0x0FD, 8},
189 {0x155, 9}, {0x07F, 7}, {0x1FD, 9}, {0x17D, 9}, {0x0D7, 8}, {0x0BB, 8},
190 {0x0DD, 8}, {0x0AB, 8}, {0x0D5, 8}, {0x1DD, 9}, {0x0AF, 8}, {0x06F, 7},
191 {0x06D, 7}, {0x157, 9}, {0x1B5, 9}, {0x15D, 9}, {0x175, 9}, {0x17B, 9},
192 {0x2AD, 10}, {0x1F7, 9}, {0x1EF, 9}, {0x1FB, 9}, {0x2BF, 10}, {0x16D, 9},
193 {0x2DF, 10}, {0x00B, 4}, {0x05F, 7}, {0x02F, 6}, {0x02D, 6}, {0x003, 2},
194 {0x03D, 6}, {0x05B, 7}, {0x02B, 6}, {0x00D, 4}, {0x1EB, 9}, {0x0BF, 8},
195 {0x01B, 5}, {0x03B, 6}, {0x00F, 4}, {0x007, 3}, {0x03F, 6}, {0x1BF, 9},
196 {0x015, 5}, {0x017, 5}, {0x005, 3}, {0x037, 6}, {0x07B, 7}, {0x06B, 7},
197 {0x0DF, 8}, {0x05D, 7}, {0x1D5, 9}, {0x2B7, 10}, {0x1BB, 9}, {0x2B5, 10},
198 {0x2D7, 10}, {0x3B5, 10}, {0x3BD, 10}, {0x3BF, 10}, {0x3D5, 10}, {0x3D7, 10},
199 {0x3DB, 10}, {0x3DD, 10}, {0x3DF, 10}, {0x3EB, 10}, {0x3ED, 10}, {0x3EF, 10},
200 {0x3F5, 10}, {0x3F7, 10}, {0x3FB, 10}, {0x3FD, 10}, {0x3FF, 10}, {0x555, 11},
201 {0x557, 11}, {0x55B, 11}, {0x55D, 11}, {0x55F, 11}, {0x56B, 11}, {0x56D, 11},
202 {0x56F, 11}, {0x575, 11}, {0x577, 11}, {0x57B, 11}, {0x57D, 11}, {0x57F, 11},
203 {0x5AB, 11}, {0x5AD, 11}, {0x5AF, 11}, {0x5B5, 11}, {0x5B7, 11}, {0x5BB, 11},
204 {0x5BD, 11}, {0x5BF, 11}, {0x5D5, 11}, {0x5D7, 11}, {0x5DB, 11}, {0x5DD, 11},
205 {0x5DF, 11}, {0x5EB, 11}, {0x5ED, 11}, {0x5EF, 11}, {0x5F5, 11}, {0x5F7, 11},
206 {0x5FB, 11}, {0x5FD, 11}, {0x5FF, 11}, {0x6AB, 11}, {0x6AD, 11}, {0x6AF, 11},
207 {0x6B5, 11}, {0x6B7, 11}, {0x6BB, 11}, {0x6BD, 11}, {0x6BF, 11}, {0x6D5, 11},
208 {0x6D7, 11}, {0x6DB, 11}, {0x6DD, 11}, {0x6DF, 11}, {0x6EB, 11}, {0x6ED, 11},
209 {0x6EF, 11}, {0x6F5, 11}, {0x6F7, 11}, {0x6FB, 11}, {0x6FD, 11}, {0x6FF, 11},
210 {0x755, 11}, {0x757, 11}, {0x75B, 11}, {0x75D, 11}, {0x75F, 11}, {0x76B, 11},
211 {0x76D, 11}, {0x76F, 11}, {0x775, 11}, {0x777, 11}, {0x77B, 11}, {0x77D, 11},
212 {0x77F, 11}, {0x7AB, 11}, {0x7AD, 11}, {0x7AF, 11}, {0x7B5, 11}, {0x7B7, 11},
213 {0x7BB, 11}, {0x7BD, 11}, {0x7BF, 11}, {0x7D5, 11}, {0x7D7, 11}, {0x7DB, 11},
214 {0x7DD, 11}, {0x7DF, 11}, {0x7EB, 11}, {0x7ED, 11}, {0x7EF, 11}, {0x7F5, 11},
215 {0x7F7, 11}, {0x7FB, 11}, {0x7FD, 11}, {0x7FF, 11}, {0xAAB, 12}, {0xAAD, 12},
216 {0xAAF, 12}, {0xAB5, 12}, {0xAB7, 12}, {0xABB, 12}, {0xABD, 12}, {0xABF, 12},
217 {0xAD5, 12}, {0xAD7, 12}, {0xADB, 12}, {0xADD, 12}, {0xADF, 12}, {0xAEB, 12},
218 {0xAED, 12}, {0xAEF, 12}, {0xAF5, 12}, {0xAF7, 12}, {0xAFB, 12}, {0xAFD, 12},
219 {0xAFF, 12}, {0xB55, 12}, {0xB57, 12}, {0xB5B, 12}
220 };
221 return codes;
222 }
223};
224
232 public:
234
236 static Code encode(uint8_t ch) { return table()[ch]; }
237
239 static int decode(uint16_t bits) {
240 for (int j = 0; j < 256; j++)
241 if (table()[j].bits == bits) return j;
242 return -1;
243 }
244
245 protected:
246 static const Code* table() {
247 // generated from fldigi's varicode table (src/mfsk/mfskvaricode.cxx)
248 static const Code codes[256] = {
249 {0x75C, 11}, {0x760, 11}, {0x768, 11}, {0x76C, 11}, {0x770, 11}, {0x774, 11},
250 {0x778, 11}, {0x77C, 11}, {0x0A8, 8}, {0x780, 11}, {0x7A0, 11}, {0x7A8, 11},
251 {0x7AC, 11}, {0x0AC, 8}, {0x7B0, 11}, {0x7B4, 11}, {0x7B8, 11}, {0x7BC, 11},
252 {0x7C0, 11}, {0x7D0, 11}, {0x7D4, 11}, {0x7D8, 11}, {0x7DC, 11}, {0x7E0, 11},
253 {0x7E8, 11}, {0x7EC, 11}, {0x7F0, 11}, {0x7F4, 11}, {0x7F8, 11}, {0x7FC, 11},
254 {0x800, 12}, {0xA00, 12}, {0x004, 3}, {0x1C0, 9}, {0x1FC, 9}, {0x2D8, 10},
255 {0x2A8, 10}, {0x2A0, 10}, {0x200, 10}, {0x1BC, 9}, {0x1F4, 9}, {0x1F0, 9},
256 {0x2B4, 10}, {0x1E0, 9}, {0x0A0, 8}, {0x1D8, 9}, {0x1D4, 9}, {0x1E8, 9},
257 {0x0E0, 8}, {0x0F0, 8}, {0x140, 9}, {0x154, 9}, {0x174, 9}, {0x160, 9},
258 {0x16C, 9}, {0x1A0, 9}, {0x180, 9}, {0x1AC, 9}, {0x1EC, 9}, {0x1F8, 9},
259 {0x2C0, 10}, {0x1DC, 9}, {0x2BC, 10}, {0x1D0, 9}, {0x280, 10}, {0x0BC, 8},
260 {0x100, 9}, {0x0D4, 8}, {0x0DC, 8}, {0x0B8, 8}, {0x0F8, 8}, {0x150, 9},
261 {0x158, 9}, {0x0C0, 8}, {0x1B4, 9}, {0x17C, 9}, {0x0F4, 8}, {0x0E8, 8},
262 {0x0FC, 8}, {0x0D0, 8}, {0x0EC, 8}, {0x1B0, 9}, {0x0D8, 8}, {0x0B4, 8},
263 {0x0B0, 8}, {0x15C, 9}, {0x1A8, 9}, {0x168, 9}, {0x170, 9}, {0x178, 9},
264 {0x1B8, 9}, {0x2E8, 10}, {0x2D0, 10}, {0x2EC, 10}, {0x2D4, 10}, {0x2B0, 10},
265 {0x2AC, 10}, {0x014, 5}, {0x060, 7}, {0x038, 6}, {0x034, 6}, {0x008, 4},
266 {0x050, 7}, {0x058, 7}, {0x030, 6}, {0x018, 5}, {0x080, 8}, {0x070, 7},
267 {0x02C, 6}, {0x040, 7}, {0x01C, 5}, {0x010, 5}, {0x054, 7}, {0x078, 7},
268 {0x020, 6}, {0x028, 6}, {0x00C, 4}, {0x03C, 6}, {0x06C, 7}, {0x068, 7},
269 {0x074, 7}, {0x05C, 7}, {0x07C, 7}, {0x2DC, 10}, {0x2B8, 10}, {0x2E0, 10},
270 {0x2F0, 10}, {0xA80, 12}, {0xAA0, 12}, {0xAA8, 12}, {0xAAC, 12}, {0xAB0, 12},
271 {0xAB4, 12}, {0xAB8, 12}, {0xABC, 12}, {0xAC0, 12}, {0xAD0, 12}, {0xAD4, 12},
272 {0xAD8, 12}, {0xADC, 12}, {0xAE0, 12}, {0xAE8, 12}, {0xAEC, 12}, {0xAF0, 12},
273 {0xAF4, 12}, {0xAF8, 12}, {0xAFC, 12}, {0xB00, 12}, {0xB40, 12}, {0xB50, 12},
274 {0xB54, 12}, {0xB58, 12}, {0xB5C, 12}, {0xB60, 12}, {0xB68, 12}, {0xB6C, 12},
275 {0xB70, 12}, {0xB74, 12}, {0xB78, 12}, {0xB7C, 12}, {0x2F4, 10}, {0x2F8, 10},
276 {0x2FC, 10}, {0x300, 10}, {0x340, 10}, {0x350, 10}, {0x354, 10}, {0x358, 10},
277 {0x35C, 10}, {0x360, 10}, {0x368, 10}, {0x36C, 10}, {0x370, 10}, {0x374, 10},
278 {0x378, 10}, {0x37C, 10}, {0x380, 10}, {0x3A0, 10}, {0x3A8, 10}, {0x3AC, 10},
279 {0x3B0, 10}, {0x3B4, 10}, {0x3B8, 10}, {0x3BC, 10}, {0x3C0, 10}, {0x3D0, 10},
280 {0x3D4, 10}, {0x3D8, 10}, {0x3DC, 10}, {0x3E0, 10}, {0x3E8, 10}, {0x3EC, 10},
281 {0x3F0, 10}, {0x3F4, 10}, {0x3F8, 10}, {0x3FC, 10}, {0x400, 11}, {0x500, 11},
282 {0x540, 11}, {0x550, 11}, {0x554, 11}, {0x558, 11}, {0x55C, 11}, {0x560, 11},
283 {0x568, 11}, {0x56C, 11}, {0x570, 11}, {0x574, 11}, {0x578, 11}, {0x57C, 11},
284 {0x580, 11}, {0x5A0, 11}, {0x5A8, 11}, {0x5AC, 11}, {0x5B0, 11}, {0x5B4, 11},
285 {0x5B8, 11}, {0x5BC, 11}, {0x5C0, 11}, {0x5D0, 11}, {0x5D4, 11}, {0x5D8, 11},
286 {0x5DC, 11}, {0x5E0, 11}, {0x5E8, 11}, {0x5EC, 11}, {0x5F0, 11}, {0x5F4, 11},
287 {0x5F8, 11}, {0x5FC, 11}, {0x600, 11}, {0x680, 11}, {0x6A0, 11}, {0x6A8, 11},
288 {0x6AC, 11}, {0x6B0, 11}, {0x6B4, 11}, {0x6B8, 11}, {0x6BC, 11}, {0x6C0, 11},
289 {0x6D0, 11}, {0x6D4, 11}, {0x6D8, 11}, {0x6DC, 11}, {0x6E0, 11}, {0x6E8, 11},
290 {0x6EC, 11}, {0x6F0, 11}, {0x6F4, 11}, {0x6F8, 11}, {0x6FC, 11}, {0x700, 11},
291 {0x740, 11}, {0x750, 11}, {0x754, 11}, {0x758, 11}
292 };
293 return codes;
294 }
295};
296
302class QPSKCode {
303 public:
304 static constexpr int K = 5;
305 static constexpr int STATES = 1 << (K - 1);
307 static constexpr int DEPTH = 32;
308
310 static int output(int reg) {
311 return parity(reg & 0x17) | (parity(reg & 0x19) << 1);
312 }
313
315 int encode(int bit) {
316 shreg = ((shreg << 1) | (bit & 1)) & ((1 << K) - 1);
317 return output(shreg);
318 }
319
321 void reset() {
322 shreg = 0;
323 for (int s = 0; s < STATES; s++) metrics[s] = 0;
324 count = 0;
325 pos = 0;
326 }
327
330 int decode(const float metric[4]) {
331 float next[STATES];
332 uint16_t decisions = 0;
333 for (int s = 0; s < STATES; s++) {
334 // predecessors: the oldest bit (msb of the predecessor) was 0 or 1
335 int bit = s & 1;
336 int p0 = s >> 1, p1 = (s >> 1) | (STATES >> 1);
337 float m0 = metrics[p0] + metric[output((p0 << 1) | bit)];
338 float m1 = metrics[p1] + metric[output((p1 << 1) | bit)];
339 if (m1 > m0) {
340 next[s] = m1;
341 decisions |= 1 << s;
342 } else {
343 next[s] = m0;
344 }
345 }
346 float best = next[0];
347 for (int s = 1; s < STATES; s++)
348 if (next[s] > best) best = next[s];
349 for (int s = 0; s < STATES; s++) metrics[s] = next[s] - best;
350 history[pos] = decisions;
351 pos = (pos + 1) % DEPTH;
352 if (count < DEPTH) count++;
353 return count < DEPTH ? -1 : traceBack(DEPTH);
354 }
355
357 template <class F>
358 void flush(F on_bit) {
359 // the bit of DEPTH steps ago was already provided
360 int n = count == DEPTH ? DEPTH - 1 : count;
361 for (int j = n; j > 0; j--) on_bit(traceBack(j));
362 count = 0;
363 }
364
365 protected:
366 int shreg = 0;
367 float metrics[STATES] = {0};
368 uint16_t history[DEPTH];
369 int pos = 0;
370 int count = 0;
371
372 static int parity(int v) {
373 v ^= v >> 4;
374 v ^= v >> 2;
375 v ^= v >> 1;
376 return v & 1;
377 }
378
380 int traceBack(int depth) {
381 int state = 0;
382 for (int s = 1; s < STATES; s++)
383 if (metrics[s] > metrics[state]) state = s;
384 int idx = pos;
385 for (int j = 0; j < depth - 1; j++) {
386 idx = (idx + DEPTH - 1) % DEPTH;
387 int msb = (history[idx] >> state) & 1;
388 state = (state >> 1) | (msb << (K - 2));
389 }
390 return state & 1;
391 }
392};
393
394} // namespace tinymodem
The IZ8BLY MFSK Varicode (as used by fldigi for 8PSK): a code starts with 1 and does not contain "001...
Definition PSK.h:231
Varicode::Code Code
Definition PSK.h:233
static const Code * table()
Definition PSK.h:246
static int decode(uint16_t bits)
Byte of the indicated code: returns -1 if the code is unknown.
Definition PSK.h:239
static Code encode(uint8_t ch)
Code of the indicated byte.
Definition PSK.h:236
Rate 1/2 convolutional code with K=5 (polynomials 0x17 and 0x19) of QPSK31 (fldigi) and a soft decisi...
Definition PSK.h:302
static constexpr int K
Definition PSK.h:304
static constexpr int DEPTH
decisions which are kept for the trace back (decoding delay in bits)
Definition PSK.h:307
uint16_t history[DEPTH]
Definition PSK.h:368
int encode(int bit)
Encodes one bit: returns the 2 bit symbol.
Definition PSK.h:315
int count
Definition PSK.h:370
static int parity(int v)
Definition PSK.h:372
float metrics[STATES]
Definition PSK.h:367
int shreg
Definition PSK.h:366
int traceBack(int depth)
Input bit of depth steps ago on the best path.
Definition PSK.h:380
void flush(F on_bit)
Provides the bits which are still in the decoder (oldest first).
Definition PSK.h:358
static int output(int reg)
encoder output (2 bits) for the register of the last K input bits
Definition PSK.h:310
static constexpr int STATES
Definition PSK.h:305
int decode(const float metric[4])
Definition PSK.h:330
void reset()
Resets the encoder and decoder state.
Definition PSK.h:321
int pos
Definition PSK.h:369
The PSK31 Varicode (as extended by fldigi to all 256 byte values): frequent characters have short cod...
Definition PSK.h:155
static Code encode(uint8_t ch)
Code of the indicated byte.
Definition PSK.h:164
static const Code * table()
Definition PSK.h:174
static int decode(uint16_t bits)
Byte of the indicated code: returns -1 if the code is unknown.
Definition PSK.h:167
TinyModem: software audio modems and packets with error correction.
Definition AX25.h:11
PSKModulation
Modulation of the PSK modem.
Definition PSK.h:13
@ QPSK
Definition PSK.h:18
@ PSK8
3 bits per symbol without FEC, MFSK Varicode
Definition PSK.h:20
@ BPSK
1 bit per symbol, PSK31 Varicode
Definition PSK.h:15
PSKConfig()
Definition PSK.h:35
float samplesPerSymbol() const
number of samples per symbol
Definition PSK.h:90
int bitsPerSymbol() const
bits per symbol: 1 (BPSK), 2 (QPSK) or 3 (8PSK)
Definition PSK.h:77
PSKModulation modulation
modulation (bits per symbol)
Definition PSK.h:44
bool afc
receiver: frequency tracking (automatic frequency control)
Definition PSK.h:54
static bool isMode(const char *mode)
true if the mode is a PSK mode
Definition PSK.h:84
float symbol_rate
symbols per second
Definition PSK.h:42
bool rx_one
receiver: stop after the first transmission
Definition PSK.h:58
float carrier_freq
carrier frequency in Hz
Definition PSK.h:46
bool isValid() const
Checks the configuration: returns false (and logs the reason) if invalid.
Definition PSK.h:93
bool setMode(const char *mode)
Definition PSK.h:66
float min_amplitude
receiver: minimum signal level (1.0 = full scale)
Definition PSK.h:56
static float modeRate(const char *mode, PSKModulation &mod)
Definition PSK.h:115
uint8_t channel
receiver: input channel which is used for the decoding
Definition PSK.h:60
float rx_noise
receiver: (testing only) amplitude of added uniform noise
Definition PSK.h:62
float volume
transmit amplitude (0.0 - 1.0)
Definition PSK.h:48
int postamble_symbols
transmitter: number of symbols with a steady carrier after the data
Definition PSK.h:52
int preamble_symbols
transmitter: number of phase reversals before the data
Definition PSK.h:50
Varicode bits of a character.
Definition PSK.h:158
uint8_t length
Definition PSK.h:160
uint16_t bits
Definition PSK.h:159