TinyModem
Header-only software audio modem library
Loading...
Searching...
No Matches
MFSKEncoder.h
Go to the documentation of this file.
1#pragma once
2
3#include <math.h>
4
5#include "AudioTools.h"
6#include "MFSKConfig.h"
7#include "ModemBase.h"
8
9namespace tinymodem {
10
12inline unsigned grayEncode(unsigned value) { return value ^ (value >> 1); }
13
15inline unsigned grayDecode(unsigned gray) {
16 unsigned value = gray;
17 for (unsigned shift = gray >> 1; shift != 0; shift >>= 1) value ^= shift;
18 return value;
19}
20
33class MFSKEncoder : public ModemEncoder {
34 public:
35 MFSKEncoder() = default;
36 MFSKEncoder(Print& out) { setOutput(out); }
37
40 MFSKConfig result;
41 result.setMode("mfsk16");
42 return result;
43 }
44
46 void setAudioInfo(audio_tools::AudioInfo info) override {
47 cfg.sample_rate = info.sample_rate;
48 cfg.channels = info.channels;
49 cfg.bits_per_sample = info.bits_per_sample;
50 if (is_active) begin();
51 }
52
53 audio_tools::AudioInfo audioInfo() override { return cfg; }
54
55 using audio_tools::AudioEncoder::begin;
56
59 cfg = config;
60 return begin();
61 }
62
63 bool begin() override {
64 is_active = false;
65 if (!cfg.isValid()) return false;
66 if (cfg.bits_per_sample != 16) {
67 LOGE("MFSKEncoder: bits_per_sample must be 16, not %d",
68 (int)cfg.bits_per_sample);
69 return false;
70 }
71 if (cfg.channels < 1) {
72 LOGE("MFSKEncoder: invalid number of channels: %d", (int)cfg.channels);
73 return false;
74 }
75 if (!tones.begin(cfg, cfg.volume)) return false;
76 bits_per_symbol = cfg.bitsPerSymbol();
77 samples_per_symbol = cfg.samplesPerSymbol();
78 transmitting = false;
79 is_active = true;
80 return true;
81 }
82
83 void end() override {
85 is_active = false;
86 }
87
89 size_t write(const uint8_t* data, size_t len) override {
90 if (!is_active) return 0;
91 for (size_t j = 0; j < len; j++) {
93 bit_buffer = (bit_buffer << 8) | data[j];
94 bit_count += 8;
96 }
97 return len;
98 }
99
102 bool endTransmission(size_t silence_samples = 0) override {
103 if (!is_active || !transmitting) return false;
104 // pad the last symbol with 0 bits
105 if (bit_count > 0) {
108 writeBits();
109 }
110 for (int j = 0; j < TRAILER_SYMBOLS; j++) writeSymbol(-1);
111 if (silence_samples) tones.tone(0, silence_samples);
112 transmitting = false;
113 return true;
114 }
115
117 bool isTransmitting() override { return transmitting; }
118
120 float symbolRate() override { return cfg.symbol_rate; }
121
123 MFSKConfig& config() { return cfg; }
124
125 operator bool() override { return is_active; }
126
127 protected:
128 static constexpr int TRAILER_SYMBOLS = 2;
130 bool is_active = false;
131 bool transmitting = false;
134 uint32_t symbol_index = 0;
135 uint32_t bit_buffer = 0;
136 int bit_count = 0;
137
139 transmitting = true;
140 symbol_index = 0;
141 bit_buffer = 0;
142 bit_count = 0;
143 // preamble: alternating lowest and highest tone
144 for (int j = 0; j < cfg.preamble_symbols; j++)
145 writeSymbol(j % 2 == 0 ? 0 : cfg.tones - 1);
146 // sync symbols
147 for (int j = 0; j < cfg.syncLength(); j++) writeSymbol(cfg.syncSymbol(j));
148 }
149
151 void writeBits() {
152 unsigned value = (bit_buffer >> (bit_count - bits_per_symbol)) &
153 ((1u << bits_per_symbol) - 1);
155 bit_buffer &= (1u << bit_count) - 1;
156 writeSymbol(grayEncode(value));
157 }
158
161 void writeSymbol(int tone) {
162 size_t start = llround((double)symbol_index * samples_per_symbol);
163 size_t stop = llround((double)(symbol_index + 1) * samples_per_symbol);
164 symbol_index++;
165 tones.tone(tone < 0 ? 0.0f : cfg.toneFrequency(tone), stop - start);
166 }
167};
168
169} // namespace tinymodem
float symbolRate() override
Symbols per second.
Definition MFSKEncoder.h:120
int bit_count
Definition MFSKEncoder.h:136
MFSKConfig cfg
Definition MFSKEncoder.h:129
static constexpr int TRAILER_SYMBOLS
Definition MFSKEncoder.h:128
size_t write(const uint8_t *data, size_t len) override
Modulates the bytes.
Definition MFSKEncoder.h:89
audio_tools::AudioInfo audioInfo() override
Definition MFSKEncoder.h:53
uint32_t bit_buffer
Definition MFSKEncoder.h:135
MFSKEncoder(Print &out)
Definition MFSKEncoder.h:36
uint32_t symbol_index
Definition MFSKEncoder.h:134
bool transmitting
Definition MFSKEncoder.h:131
void startTransmission()
Definition MFSKEncoder.h:138
bool isTransmitting() override
true between the first byte and endTransmission()
Definition MFSKEncoder.h:117
bool is_active
Definition MFSKEncoder.h:130
void writeSymbol(int tone)
Definition MFSKEncoder.h:161
MFSKConfig & config()
The configuration which was provided in begin().
Definition MFSKEncoder.h:123
bool begin() override
Definition MFSKEncoder.h:63
double samples_per_symbol
Definition MFSKEncoder.h:133
MFSKConfig defaultConfig()
Provides a default configuration: mfsk16.
Definition MFSKEncoder.h:39
void end() override
Definition MFSKEncoder.h:83
int bits_per_symbol
Definition MFSKEncoder.h:132
void writeBits()
Writes the next bits_per_symbol bits from the bit buffer.
Definition MFSKEncoder.h:151
bool begin(MFSKConfig config)
Starts the processing with the indicated configuration.
Definition MFSKEncoder.h:58
void setAudioInfo(audio_tools::AudioInfo info) override
Defines the sample rate and number of channels of the generated audio.
Definition MFSKEncoder.h:46
bool endTransmission(size_t silence_samples=0) override
Definition MFSKEncoder.h:102
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
TinyModem: software audio modems and packets with error correction.
Definition AX25.h:11
unsigned grayEncode(unsigned value)
Gray code of a value: neighbouring tones differ in a single bit.
Definition MFSKEncoder.h:12
unsigned grayDecode(unsigned gray)
Inverse of grayEncode().
Definition MFSKEncoder.h:15
Configuration of the multi-tone FSK modem (MFSKEncoder, MFSKDecoder). Each symbol is one of tones ton...
Definition MFSKConfig.h:23
bool setMode(const char *mode)
Definition MFSKConfig.h:53