TinyModem
Header-only software audio modem library
Loading...
Searching...
No Matches
GGWaveEncoder.h
Go to the documentation of this file.
1#pragma once
2
3#include <math.h>
4#include <string.h>
5
6#include "AudioTools.h"
7#include "GGWave.h"
8#include "ModemBase.h"
9#include "OFDM.h"
10
11namespace tinymodem {
12
26 public:
27 GGWaveEncoder() = default;
28 GGWaveEncoder(Print& out) { setOutput(out); }
29
32 GGWaveConfig result;
33 result.setMode("ggwave-fast");
34 return result;
35 }
36
37 void setOutput(Print& out) override {
38 samples.setOutput(out);
39 ModemEncoder::setOutput(out); // writeSilence(), writeTone()
40 }
41
43 void setAudioInfo(audio_tools::AudioInfo info) override {
44 cfg.sample_rate = info.sample_rate;
45 cfg.channels = info.channels;
46 cfg.bits_per_sample = info.bits_per_sample;
47 if (is_active) begin();
48 }
49
50 audio_tools::AudioInfo audioInfo() override { return cfg; }
51
52 using audio_tools::AudioEncoder::begin;
53
56 cfg = config;
57 return begin();
58 }
59
60 bool begin() override {
61 is_active = false;
62 if (!cfg.isValid()) return false;
63 if (cfg.bits_per_sample != 16) {
64 LOGE("GGWaveEncoder: bits_per_sample must be 16, not %d",
65 (int)cfg.bits_per_sample);
66 return false;
67 }
68 if (cfg.channels < 1) {
69 LOGE("GGWaveEncoder: invalid number of channels: %d", (int)cfg.channels);
70 return false;
71 }
72 samples.begin(cfg.channels, cfg.volume);
73 if (!tones.begin(cfg, cfg.volume)) return false;
74 protocol = cfg.protocolInfo();
75 frame_size = cfg.frameSize();
76 table.begin(frame_size);
77 message_len = 0;
78 is_active = true;
79 return true;
80 }
81
82 void end() override {
84 is_active = false;
85 }
86
88 size_t write(const uint8_t* data, size_t len) override {
89 if (!is_active) return 0;
90 for (size_t j = 0; j < len; j++) {
91 message[message_len++] = data[j];
93 }
94 return len;
95 }
96
99 bool endTransmission(size_t silence_samples = 0) override {
100 if (!is_active || message_len == 0) return false;
101 sendMessage();
102 for (size_t j = 0; j < silence_samples; j++) samples.write(0.0f);
103 samples.flush();
104 return true;
105 }
106
108 bool isTransmitting() override { return message_len > 0; }
109
111 float symbolRate() override { return cfg.symbolRate(); }
112
114 GGWaveConfig& config() { return cfg; }
115
116 operator bool() override { return is_active; }
117
118 protected:
120 struct Tone {
121 int bin;
122 int phase;
123 };
124
129 bool is_active = false;
130 int frame_size = 1024;
132 int message_len = 0;
134 2 * (GGWaveConfig::MAX_PAYLOAD / 5) + 3];
135
136 void sendMessage() {
137 int len = message_len;
138 int ecc = GGWaveConfig::eccBytes(len);
139 int bpt = protocol.bytes_per_tx;
140 uint8_t plain[1 + GGWaveConfig::MAX_PAYLOAD];
141 plain[0] = len;
142 for (int i = 0; i < len; i++)
143 plain[i + 1] = message[i] ^ (cfg.dss ? GGWaveConfig::dssMagic(i) : 0);
144 memset(encoded, 0, sizeof(encoded));
146 GGWaveRS::encode(plain + 1, len, ecc,
148
150 // start marker: the even tones use bin 2i, the odd tones bin 2i + 1
151 for (int i = 0; i < GGWaveConfig::MARKER_TONES; i++)
152 tones[i] = {2 * i + i % 2, i};
153 for (int f = 0; f < GGWaveConfig::MARKER_FRAMES; f++)
156
157 // data: each nibble selects one of 16 bins
158 int nsymbols = cfg.dataSymbols(len);
159 for (int s = 0; s < nsymbols; s++) {
160 int n = 0;
161 for (int j = 0; j < bpt; j++) {
162 uint8_t value = encoded[s * bpt + j];
163 int k0 = (2 * j) * 16 + (value & 15);
164 int k1 = (2 * j + 1) * 16 + (value >> 4);
165 tones[n++] = {k0, k0 / 2};
166 tones[n++] = {k1, k1 / 2};
167 }
168 for (int f = 0; f < protocol.frames_per_tx; f++)
169 sendFrame(tones, n, f, protocol.frames_per_tx);
170 }
171
172 // end marker: inverse of the start marker
173 for (int i = 0; i < GGWaveConfig::MARKER_TONES; i++)
174 tones[i] = {2 * i + 1 - i % 2, i};
175 for (int f = 0; f < GGWaveConfig::MARKER_FRAMES; f++)
178 samples.flush();
179 message_len = 0;
180 }
181
185 void sendFrame(const Tone* tones, int ntones, int cycle, int frames) {
186 const int n = frame_size;
187 const int total = frames * n;
188 const float ds = 0.15f * total;
189 const float ids = 1.0f / ds;
190 const int begin = (int)(0.15f * total);
191 const int end = (int)((1.0f - 0.15f) * total);
192 const float scale = 1.0f / ntones;
193 float sin_phase[GGWaveConfig::MARKER_TONES];
194 float cos_phase[GGWaveConfig::MARKER_TONES];
197 for (int t = 0; t < ntones; t++) {
198 // ggwave: phase offset = pi * index / data bits per symbol
199 double phase = M_PI * tones[t].phase / (8 * protocol.bytes_per_tx);
200 sin_phase[t] = sin(phase);
201 cos_phase[t] = cos(phase);
202 idx[t] = 0;
203 step[t] = protocol.freq_start + tones[t].bin;
204 }
205 for (int i = 0; i < n; i++) {
206 float value = 0;
207 for (int t = 0; t < ntones; t++) {
208 // sin(angle + phase)
209 value += table.sine(idx[t]) * cos_phase[t] +
210 table.cosine(idx[t]) * sin_phase[t];
211 idx[t] += step[t];
212 if (idx[t] >= n) idx[t] -= n;
213 }
214 int k = cycle * n + i;
215 float envelope = 1.0f;
216 if (k < begin) {
217 envelope = k * ids;
218 } else if (k > end) {
219 envelope = (total - k) * ids;
220 }
221 samples.write(value * envelope * scale);
222 }
223 }
224};
225
226} // namespace tinymodem
GGWaveProtocol protocol
Definition GGWaveEncoder.h:126
bool isTransmitting() override
true if there is data which was not sent yet
Definition GGWaveEncoder.h:108
SampleOutput samples
Definition GGWaveEncoder.h:127
int message_len
Definition GGWaveEncoder.h:132
GGWaveConfig defaultConfig()
Provides a default configuration: ggwave-fast.
Definition GGWaveEncoder.h:31
GGWaveConfig cfg
Definition GGWaveEncoder.h:125
GGWaveConfig & config()
The configuration which was provided in begin().
Definition GGWaveEncoder.h:114
int frame_size
Definition GGWaveEncoder.h:130
void sendFrame(const Tone *tones, int ntones, int cycle, int frames)
Definition GGWaveEncoder.h:185
void setAudioInfo(audio_tools::AudioInfo info) override
Defines the sample rate and number of channels of the generated audio.
Definition GGWaveEncoder.h:43
uint8_t message[GGWaveConfig::MAX_PAYLOAD]
Definition GGWaveEncoder.h:131
GGWaveEncoder(Print &out)
Definition GGWaveEncoder.h:28
bool is_active
Definition GGWaveEncoder.h:129
bool endTransmission(size_t silence_samples=0) override
Definition GGWaveEncoder.h:99
void end() override
Definition GGWaveEncoder.h:82
OFDMTable table
Definition GGWaveEncoder.h:128
audio_tools::AudioInfo audioInfo() override
Definition GGWaveEncoder.h:50
bool begin() override
Definition GGWaveEncoder.h:60
uint8_t encoded[GGWaveConfig::LENGTH_BYTES+GGWaveConfig::MAX_PAYLOAD+2 *(GGWaveConfig::MAX_PAYLOAD/5)+3]
Definition GGWaveEncoder.h:134
float symbolRate() override
Data symbols per second.
Definition GGWaveEncoder.h:111
void setOutput(Print &out) override
Definition GGWaveEncoder.h:37
void sendMessage()
Definition GGWaveEncoder.h:136
bool begin(GGWaveConfig config)
Starts the processing with the indicated configuration.
Definition GGWaveEncoder.h:55
size_t write(const uint8_t *data, size_t len) override
Collects the bytes and sends full messages.
Definition GGWaveEncoder.h:88
Common API of the modulators (FSKEncoder, MFSKEncoder): the written data is converted to 16 bit PCM a...
Definition ModemBase.h:286
void setOutput(Print &out) override
Definition ModemBase.h:288
ToneOutput tones
Definition ModemBase.h:334
Sine and cosine of 2 * PI * i / size.
Definition OFDM.h:140
static void encode(const uint8_t *msg, int len, int ecc, uint8_t *out, int fcr=0)
Definition ReedSolomon.h:28
Writes float samples (-1.0 to 1.0) as 16 bit PCM with the requested number of channels and volume to ...
Definition ModemBase.h:118
TinyModem: software audio modems and packets with error correction.
Definition AX25.h:11
Configuration of the ggwave compatible modem (GGWaveEncoder, GGWaveDecoder). Each message (up to 140 ...
Definition GGWave.h:74
static uint8_t dssMagic(int i)
Value of the ggwave DSS sequence for the indicated payload byte.
Definition GGWave.h:220
static constexpr int MARKER_TONES
number of tones of a marker
Definition GGWave.h:89
static constexpr int MARKER_FRAMES
number of frames of the start and end marker
Definition GGWave.h:87
static constexpr int LENGTH_BYTES
bytes of the Reed-Solomon coded length at the start of a message
Definition GGWave.h:91
static int eccBytes(int len)
Number of Reed-Solomon parity bytes for a payload of len bytes.
Definition GGWave.h:164
bool setMode(const char *mode)
Selects the protocol by mode name: returns false if invalid.
Definition GGWave.h:112
static constexpr int MAX_PAYLOAD
max payload of a message (variable length format)
Definition GGWave.h:85
a tone: FFT bin relative to freq_start and index of the phase offset
Definition GGWaveEncoder.h:120
int phase
Definition GGWaveEncoder.h:122
int bin
Definition GGWaveEncoder.h:121
Transmission protocol of ggwave (https://github.com/ggerganov/ggwave): the ids and parameters are ide...
Definition GGWave.h:36