TinyModem
Header-only software audio modem library
Loading...
Searching...
No Matches
OliviaEncoder.h
Go to the documentation of this file.
1#pragma once
2
3#include <math.h>
4
5#include "AudioTools.h"
6#include "ModemBase.h"
7#include "Olivia.h"
8
9namespace tinymodem {
10
26 public:
27 OliviaEncoder() = default;
28 OliviaEncoder(Print& out) { setOutput(out); }
29
32
33 void setOutput(Print& out) override {
34 samples.setOutput(out);
35 ModemEncoder::setOutput(out); // writeSilence(), writeTone()
36 }
37
39 void setAudioInfo(audio_tools::AudioInfo info) override {
40 cfg.sample_rate = info.sample_rate;
41 cfg.channels = info.channels;
42 cfg.bits_per_sample = info.bits_per_sample;
43 if (is_active) begin();
44 }
45
46 audio_tools::AudioInfo audioInfo() override { return cfg; }
47
48 using audio_tools::AudioEncoder::begin;
49
52 cfg = config;
53 return begin();
54 }
55
56 bool begin() override {
57 is_active = false;
58 if (!cfg.isValid()) return false;
59 if (cfg.bits_per_sample != 16) {
60 LOGE("OliviaEncoder: bits_per_sample must be 16, not %d",
61 (int)cfg.bits_per_sample);
62 return false;
63 }
64 if (cfg.channels < 1) {
65 LOGE("OliviaEncoder: invalid number of channels: %d", (int)cfg.channels);
66 return false;
67 }
68 samples.begin(cfg.channels, cfg.volume);
69 if (!tones.begin(cfg, cfg.volume)) return false;
70 bits = cfg.bitsPerSymbol();
71 codec.begin(bits, cfg.contestia);
72 symbol_time = cfg.symbolSepar() / OliviaConfig::BASE_RATE;
73 transmitting = false;
74 block_len = 0;
75 is_active = true;
76 return true;
77 }
78
79 void end() override {
81 is_active = false;
82 }
83
85 size_t write(const uint8_t* data, size_t len) override {
86 if (!is_active) return 0;
87 for (size_t j = 0; j < len; j++) {
88 uint8_t ch = data[j];
89 // 127 is the escape character
90 if (codec.isSkipped(ch) || (!cfg.contestia && ch == 127)) continue;
91 if (!cfg.contestia && ch > 127) {
92 addChar(127);
93 ch &= 127;
94 }
95 addChar(ch);
96 }
97 samples.flush();
98 return len;
99 }
100
103 bool endTransmission(size_t silence_samples = 0) override {
104 if (!is_active || !transmitting) return false;
105 if (block_len > 0) {
106 while (block_len < bits) block[block_len++] = 0;
107 sendBlock();
108 }
109 sendSymbol(-1); // the end of the last symbol
110 if (cfg.start_tones) sendStartTones();
111 for (size_t j = 0; j < silence_samples; j++) samples.write(0.0f);
112 samples.flush();
113 transmitting = false;
114 return true;
115 }
116
118 bool writeIdle(size_t nsamples) override {
119 if (!is_active) return false;
120 size_t block_samples =
121 symbol_time * codec.blockLen() * cfg.sample_rate;
122 size_t n = nsamples / block_samples + 1;
123 for (size_t j = 0; j < n * bits; j++) addChar(0);
124 samples.flush();
125 return true;
126 }
127
129 bool isTransmitting() override { return transmitting; }
130
132 float symbolRate() override { return cfg.symbolRate(); }
133
135 OliviaConfig& config() { return cfg; }
136
137 operator bool() override { return is_active; }
138
139 protected:
143 bool is_active = false;
144 bool transmitting = false;
145 int bits = 5;
146 uint8_t block[8];
147 int block_len = 0;
148 double symbol_time = 0; // in seconds
149 // modulator
150 uint64_t symbol_index = 0;
151 uint64_t sample_index = 0;
152 float prev_freq = 0, prev_phase = 0; // previous symbol
153 float phase = 0; // phase of the next symbol
154 uint32_t random_seed = 1;
155
157 transmitting = true;
158 if (cfg.start_tones) sendStartTones();
159 symbol_index = 0;
160 sample_index = 0;
161 prev_freq = 0;
162 phase = 0;
163 }
164
165 void addChar(uint8_t ch) {
167 block[block_len++] = ch;
168 if (block_len == bits) sendBlock();
169 }
170
171 void sendBlock() {
172 uint8_t symbols[64];
173 codec.encodeBlock(block, symbols);
174 for (int t = 0; t < codec.blockLen(); t++)
176 block_len = 0;
177 }
178
181 void sendSymbol(int tone) {
182 float freq = tone < 0 ? 0 : cfg.toneFreq(tone);
183 uint64_t stop = llround((symbol_index + 1) * symbol_time * cfg.sample_rate);
184 double start_time = symbol_index * symbol_time;
185 for (; sample_index < stop; sample_index++) {
186 float tau = sample_index / (double)cfg.sample_rate - start_time;
187 // raised cosine over two symbol periods: 0 - 1 in the first half
188 float w = 0.5f - 0.5f * cosf((float)M_PI * tau / symbol_time);
189 float value = 0;
190 if (freq > 0) value += w * cosf(phase + 2.0f * (float)M_PI * freq * tau);
191 if (prev_freq > 0)
192 value += (1.0f - w) *
193 cosf(prev_phase +
194 2.0f * (float)M_PI * prev_freq * (tau + symbol_time));
195 samples.write(value);
196 }
197 symbol_index++;
198 // continuous phase in the middle of the symbol and a random +/- 90 degree
199 // step like fldigi
200 prev_freq = freq;
202 random_seed = random_seed * 1103515245u + 12345u;
203 float step = ((random_seed >> 16) & 1) ? (float)M_PI / 2 : -(float)M_PI / 2;
204 phase = fmodf(phase + 2.0f * (float)M_PI * freq * symbol_time + step,
205 2.0f * (float)M_PI);
206 }
207
211 size_t len = 2048 * cfg.sample_rate / 8000;
212 size_t ramp = len / 8;
213 float freqs[2] = {cfg.center_freq - cfg.bandwidth / 2.0f,
214 cfg.center_freq + cfg.bandwidth / 2.0f};
215 for (int j = 0; j < 4; j++) {
216 float w = 2.0f * (float)M_PI * freqs[j % 2] / cfg.sample_rate;
217 for (size_t i = 0; i < len; i++) {
218 float gain = 1.0f;
219 if (i < ramp) gain = 0.5f - 0.5f * cosf((float)M_PI * i / ramp);
220 if (len - 1 - i < ramp)
221 gain = 0.5f - 0.5f * cosf((float)M_PI * (len - 1 - i) / ramp);
222 samples.write(gain * cosf(w * i));
223 }
224 }
225 }
226};
227
228} // namespace tinymodem
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
The forward error correction of Olivia and Contestia (as in fldigi): each character of a block of bit...
Definition Olivia.h:213
static uint8_t grayCode(uint8_t binary)
Definition Olivia.h:287
void startTransmission()
Definition OliviaEncoder.h:156
bool writeIdle(size_t nsamples) override
Sends idle blocks (characters 0) for the indicated number of samples.
Definition OliviaEncoder.h:118
void sendSymbol(int tone)
Definition OliviaEncoder.h:181
bool transmitting
Definition OliviaEncoder.h:144
float prev_freq
Definition OliviaEncoder.h:152
uint8_t block[8]
Definition OliviaEncoder.h:146
void addChar(uint8_t ch)
Definition OliviaEncoder.h:165
OliviaCodec codec
Definition OliviaEncoder.h:142
float phase
Definition OliviaEncoder.h:153
bool begin(OliviaConfig config)
Starts the processing with the indicated configuration.
Definition OliviaEncoder.h:51
void end() override
Definition OliviaEncoder.h:79
bool begin() override
Definition OliviaEncoder.h:56
uint32_t random_seed
Definition OliviaEncoder.h:154
uint64_t symbol_index
Definition OliviaEncoder.h:150
void setOutput(Print &out) override
Definition OliviaEncoder.h:33
OliviaConfig & config()
The configuration which was provided in begin().
Definition OliviaEncoder.h:135
OliviaEncoder(Print &out)
Definition OliviaEncoder.h:28
bool isTransmitting() override
true between the first character and endTransmission()
Definition OliviaEncoder.h:129
uint64_t sample_index
Definition OliviaEncoder.h:151
bool is_active
Definition OliviaEncoder.h:143
double symbol_time
Definition OliviaEncoder.h:148
void sendBlock()
Definition OliviaEncoder.h:171
float symbolRate() override
Symbols per second.
Definition OliviaEncoder.h:132
float prev_phase
Definition OliviaEncoder.h:152
int bits
Definition OliviaEncoder.h:145
int block_len
Definition OliviaEncoder.h:147
SampleOutput samples
Definition OliviaEncoder.h:141
bool endTransmission(size_t silence_samples=0) override
Definition OliviaEncoder.h:103
OliviaConfig cfg
Definition OliviaEncoder.h:140
size_t write(const uint8_t *data, size_t len) override
Sends the characters.
Definition OliviaEncoder.h:85
void sendStartTones()
Definition OliviaEncoder.h:210
void setAudioInfo(audio_tools::AudioInfo info) override
Defines the sample rate and number of channels of the generated audio.
Definition OliviaEncoder.h:39
OliviaConfig defaultConfig()
Provides a default configuration: olivia-32-1000.
Definition OliviaEncoder.h:31
audio_tools::AudioInfo audioInfo() override
Definition OliviaEncoder.h:46
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 Olivia and Contestia modems (OliviaEncoder, OliviaDecoder): MFSK with 2 to 256 t...
Definition Olivia.h:31
static constexpr float BASE_RATE
internal sample rate which defines the signal
Definition Olivia.h:65