TinyModem
Header-only software audio modem library
Loading...
Searching...
No Matches
CSS.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#include "PacketStream.h"
10#include "ReedSolomon.h"
11
12namespace tinymodem {
13
31struct CSSConfig : public audio_tools::AudioInfo {
33 sample_rate = 48000;
34 channels = 1;
35 bits_per_sample = 16;
36 }
37
42 float bandwidth = 1000.0f;
44 float center_freq = 1500.0f;
46 float volume = 1.0f;
50 int max_frame = 64;
53 int ecc_bytes = 8;
56 float detect_threshold = 8.0f;
58 bool rx_one = false;
60 uint8_t channel = 0;
62 float rx_noise = 0.0f;
63
66 bool setMode(const char* mode) {
67 int sf;
68 float bw;
69 if (!parseMode(mode, sf, bw)) return false;
71 bandwidth = bw;
72 return true;
73 }
74
76 static bool isMode(const char* mode) {
77 int sf;
78 float bw;
79 return parseMode(mode, sf, bw);
80 }
81
83 int chips() const { return 1 << spreading_factor; }
84
86 float symbolTime() const { return chips() / bandwidth; }
87
89 float symbolRate() const { return bandwidth / chips(); }
90
92 float lowFreq() const { return center_freq - bandwidth / 2; }
93
95 bool isValid() const {
97 LOGE("CSS: the spreading factor must be 5 - 10");
98 return false;
99 }
100 if (bandwidth <= 0 || lowFreq() <= 0) {
101 LOGE("CSS: invalid bandwidth or center frequency");
102 return false;
103 }
104 if (center_freq + bandwidth / 2 >= 0.45f * sample_rate) {
105 LOGE("CSS: the highest frequency must be below %.1f Hz",
106 (double)(0.45f * sample_rate));
107 return false;
108 }
111 LOGE("CSS: invalid preamble_symbols, max_frame or ecc_bytes");
112 return false;
113 }
114 return true;
115 }
116
118 static constexpr int MAX_FRAME = 200;
120 static constexpr int HEADER_ECC = 4;
121
122 protected:
123 static bool parseMode(const char* mode, int& sf, float& bw) {
124 if (mode == nullptr) return false;
125 const char* prefix = "css";
126 for (int j = 0; j < 3; j++)
127 if (tolower(mode[j]) != prefix[j]) return false;
128 sf = 7;
129 bw = 1000.0f;
130 const char* rest = mode + 3;
131 if (*rest == 0) return true;
132 char* end;
133 sf = strtol(rest, &end, 10);
134 if (end == rest || sf < 5 || sf > 10) return false;
135 if (*end == '-') {
136 const char* number = end + 1;
137 bw = strtol(number, &end, 10);
138 if (end == number || bw < 100) return false;
139 }
140 return *end == 0;
141 }
142};
143
152class CSSFrame {
153 public:
154 static constexpr int HEADER_BYTES = 1 + CSSConfig::HEADER_ECC;
155
157 static int symbols(int bytes, int sf) { return (bytes * 8 + sf - 1) / sf; }
158
160 static int payloadBytes(int len, int ecc) { return len + 2 + ecc; }
161
163 static void header(int len, uint8_t* out) {
164 uint8_t msg = len;
166 }
167
169 static int decodeHeader(uint8_t* data) {
170 if (RSCodec::correct(data, 1, CSSConfig::HEADER_ECC) < 0) return -1;
171 int len = data[0];
172 return len >= 1 && len <= CSSConfig::MAX_FRAME ? len : -1;
173 }
174
176 static void payload(const uint8_t* data, int len, int ecc, uint8_t* out) {
177 uint8_t msg[CSSConfig::MAX_FRAME + 2];
178 memcpy(msg, data, len);
179 uint16_t crc = crc16(data, len);
180 msg[len] = crc >> 8;
181 msg[len + 1] = crc & 0xFF;
182 RSCodec::encode(msg, len + 2, ecc, out);
183 }
184
187 static int decodePayload(uint8_t* data, int len, int ecc) {
188 int corrected = RSCodec::correct(data, len + 2, ecc);
189 if (corrected < 0) return -1;
190 uint16_t crc = crc16(data, len);
191 if (data[len] != (crc >> 8) || data[len + 1] != (crc & 0xFF)) return -1;
192 return corrected;
193 }
194
196 static int toSymbols(const uint8_t* data, int len, int sf, uint16_t* out) {
197 int n = symbols(len, sf);
198 for (int s = 0; s < n; s++) {
199 int value = 0;
200 for (int b = 0; b < sf; b++) {
201 int bit = s * sf + b;
202 int v = bit < len * 8 ? (data[bit / 8] >> (7 - bit % 8)) & 1 : 0;
203 value = (value << 1) | v;
204 }
205 out[s] = inverseGray(value);
206 }
207 return n;
208 }
209
211 static void fromSymbols(const uint16_t* symbols, int n, int sf,
212 uint8_t* data, int len) {
213 memset(data, 0, len);
214 for (int s = 0; s < n; s++) {
215 int value = symbols[s] ^ (symbols[s] >> 1); // Gray code
216 for (int b = 0; b < sf; b++) {
217 int bit = s * sf + b;
218 if (bit >= len * 8) return;
219 if ((value >> (sf - 1 - b)) & 1) data[bit / 8] |= 0x80 >> (bit % 8);
220 }
221 }
222 }
223
224 static int inverseGray(int g) {
225 g ^= g >> 1;
226 g ^= g >> 2;
227 g ^= g >> 4;
228 g ^= g >> 8;
229 return g;
230 }
231};
232
233} // namespace tinymodem
Frame format of the chirp spread spectrum modem: the bytes of the header code word (length and parity...
Definition CSS.h:152
static int inverseGray(int g)
Definition CSS.h:224
static int symbols(int bytes, int sf)
number of symbols for the indicated number of bytes
Definition CSS.h:157
static constexpr int HEADER_BYTES
Definition CSS.h:154
static int toSymbols(const uint8_t *data, int len, int sf, uint16_t *out)
Converts bytes into symbols.
Definition CSS.h:196
static void payload(const uint8_t *data, int len, int ecc, uint8_t *out)
Payload code word: data, CRC-16 and parity.
Definition CSS.h:176
static int payloadBytes(int len, int ecc)
bytes of the payload code word
Definition CSS.h:160
static void fromSymbols(const uint16_t *symbols, int n, int sf, uint8_t *data, int len)
Converts symbols into bytes.
Definition CSS.h:211
static void header(int len, uint8_t *out)
Header code word: length and parity.
Definition CSS.h:163
static int decodeHeader(uint8_t *data)
Corrects the header in place: returns the length or -1 if invalid.
Definition CSS.h:169
static int decodePayload(uint8_t *data, int len, int ecc)
Definition CSS.h:187
static void encode(const uint8_t *msg, int len, int ecc, uint8_t *out, int fcr=0)
Definition ReedSolomon.h:28
static constexpr int MAX_ECC
Definition ReedSolomon.h:24
static int correct(uint8_t *data, int len, int ecc, int fcr=0)
Definition ReedSolomon.h:58
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
int preamble_symbols
transmitter: number of up chirps before the sync symbols
Definition CSS.h:48
CSSConfig()
Definition CSS.h:32
float symbolRate() const
symbols per second
Definition CSS.h:89
static constexpr int HEADER_ECC
header: length byte and Reed-Solomon parity
Definition CSS.h:120
static bool isMode(const char *mode)
true if the mode is a chirp spread spectrum mode
Definition CSS.h:76
bool rx_one
receiver: stop after the first frame
Definition CSS.h:58
int chips() const
chips (FFT bins) per symbol
Definition CSS.h:83
float lowFreq() const
lowest frequency of the chirps in Hz
Definition CSS.h:92
float volume
transmit amplitude (0.0 - 1.0)
Definition CSS.h:46
float symbolTime() const
duration of a symbol in seconds
Definition CSS.h:86
int ecc_bytes
Definition CSS.h:53
static bool parseMode(const char *mode, int &sf, float &bw)
Definition CSS.h:123
uint8_t channel
receiver: input channel which is used for the decoding
Definition CSS.h:60
static constexpr int MAX_FRAME
max payload of a frame
Definition CSS.h:118
float center_freq
center frequency in Hz
Definition CSS.h:44
bool isValid() const
Checks the configuration: returns false (and logs the reason) if invalid.
Definition CSS.h:95
bool setMode(const char *mode)
Definition CSS.h:66
float detect_threshold
Definition CSS.h:56
float bandwidth
bandwidth of the chirps in Hz
Definition CSS.h:42
int spreading_factor
Definition CSS.h:40
float rx_noise
receiver: (testing only) amplitude of added uniform noise
Definition CSS.h:62
int max_frame
transmitter: max number of bytes per frame (1 - 200)
Definition CSS.h:50