TinyModem
Header-only software audio modem library
Loading...
Searching...
No Matches
Morse.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
20
31struct MorseConfig : public audio_tools::AudioInfo {
33 sample_rate = 48000;
34 channels = 1;
35 bits_per_sample = 16;
36 }
37
41 float wpm = 20.0f;
43 float frequency = 700.0f;
45 float volume = 1.0f;
47 float ramp_ms = 5.0f;
49 bool adaptive_speed = true;
51 float min_amplitude = 0.01f;
56 bool rx_one = false;
58 uint8_t channel = 0;
60 float rx_noise = 0.0f;
61
64 bool setMode(const char* mode) {
66 float speed;
67 if (!parseMode(mode, a, speed)) return false;
68 alphabet = a;
69 if (speed > 0) wpm = speed;
70 return true;
71 }
72
74 static bool isMode(const char* mode) {
76 float speed;
77 return parseMode(mode, a, speed);
78 }
79
81 float unitTime() const { return 1.2f / wpm; }
82
84 bool isValid() const {
85 if (wpm < 1 || wpm > 100) {
86 LOGE("Morse: the speed must be 1 - 100 wpm");
87 return false;
88 }
89 if (frequency <= 0 || frequency >= 0.45f * sample_rate) {
90 LOGE("Morse: invalid frequency %.1f Hz", (double)frequency);
91 return false;
92 }
93 if (ramp_ms < 0 || rx_timeout_units < 8) {
94 LOGE("Morse: invalid ramp_ms or rx_timeout_units");
95 return false;
96 }
97 return true;
98 }
99
100 protected:
101 static bool parseMode(const char* mode, MorseAlphabet& alphabet,
102 float& speed) {
103 if (mode == nullptr) return false;
105 speed = 0;
106 const char* rest;
107 if (startsWith(mode, "morse")) {
108 rest = mode + 5;
109 } else if (startsWith(mode, "cw")) {
110 rest = mode + 2;
111 } else {
112 return false;
113 }
114 if (*rest == '-') rest++;
115 if (startsWith(rest, "american")) {
117 rest += 8;
118 if (*rest == '-') rest++;
119 } else if (startsWith(rest, "international")) {
120 rest += 13;
121 if (*rest == '-') rest++;
122 }
123 if (*rest == 0) return true;
124 char* end;
125 speed = strtol(rest, &end, 10);
126 return end != rest && *end == 0 && speed >= 1 && speed <= 100;
127 }
128
129 static bool startsWith(const char* str, const char* prefix) {
130 while (*prefix) {
131 if (tolower(*str++) != *prefix++) return false;
132 }
133 return true;
134 }
135};
136
144 public:
150
153 static const Timing& timing(MorseAlphabet alphabet) {
154 static const Timing international = {1, 3, 3, 3, 1, 1, 3, 7};
155 static const Timing american = {1, 2, 4, 5, 1, 2, 3, 6};
156 return alphabet == MorseAlphabet::AMERICAN ? american : international;
157 }
158
161 static const char* encode(MorseAlphabet alphabet, char ch) {
162 ch = toupper((unsigned char)ch);
163 for (const Entry* e = table(alphabet); e->ch; e++)
164 if (e->ch == ch) return e->code;
165 return nullptr;
166 }
167
169 static char decode(MorseAlphabet alphabet, const char* code) {
170 for (const Entry* e = table(alphabet); e->ch; e++)
171 if (strcmp(e->code, code) == 0) return e->ch;
172 return 0;
173 }
174
175 protected:
176 struct Entry {
177 char ch;
178 const char* code;
179 };
180
181 static const Entry* table(MorseAlphabet alphabet) {
182 // ITU-R M.1677 (and the common '!'); a line break is sent as AA
183 static const Entry international[] = {
184 {'A', ".-"}, {'B', "-..."}, {'C', "-.-."}, {'D', "-.."},
185 {'E', "."}, {'F', "..-."}, {'G', "--."}, {'H', "...."},
186 {'I', ".."}, {'J', ".---"}, {'K', "-.-"}, {'L', ".-.."},
187 {'M', "--"}, {'N', "-."}, {'O', "---"}, {'P', ".--."},
188 {'Q', "--.-"}, {'R', ".-."}, {'S', "..."}, {'T', "-"},
189 {'U', "..-"}, {'V', "...-"}, {'W', ".--"}, {'X', "-..-"},
190 {'Y', "-.--"}, {'Z', "--.."}, {'0', "-----"}, {'1', ".----"},
191 {'2', "..---"}, {'3', "...--"}, {'4', "....-"}, {'5', "....."},
192 {'6', "-...."}, {'7', "--..."}, {'8', "---.."}, {'9', "----."},
193 {'.', ".-.-.-"}, {',', "--..--"}, {'?', "..--.."}, {'\'', ".----."},
194 {'!', "-.-.--"}, {'/', "-..-."}, {'(', "-.--."}, {')', "-.--.-"},
195 {'&', ".-..."}, {':', "---..."}, {';', "-.-.-."}, {'=', "-...-"},
196 {'+', ".-.-."}, {'-', "-....-"}, {'_', "..--.-"}, {'"', ".-..-."},
197 {'$', "...-..-"}, {'@', ".--.-."}, {'\n', ".-.-"}, {0, nullptr}};
198 // American (railroad) Morse code
199 static const Entry american[] = {
200 {'A', ".-"}, {'B', "-..."}, {'C', ".. ."}, {'D', "-.."},
201 {'E', "."}, {'F', ".-."}, {'G', "--."}, {'H', "...."},
202 {'I', ".."}, {'J', "-.-."}, {'K', "-.-"}, {'L', "_"},
203 {'M', "--"}, {'N', "-."}, {'O', ". ."}, {'P', "....."},
204 {'Q', "..-."}, {'R', ". .."}, {'S', "..."}, {'T', "-"},
205 {'U', "..-"}, {'V', "...-"}, {'W', ".--"}, {'X', ".-.."},
206 {'Y', ".. .."}, {'Z', "... ."}, {'0', "="}, {'1', ".--."},
207 {'2', "..-.."}, {'3', "...-."}, {'4', "....-"}, {'5', "---"},
208 {'6', "......"}, {'7', "--.."}, {'8', "-...."}, {'9', "-..-"},
209 {'.', "..--.."}, {',', ".-.-"}, {'?', "-..-."}, {'!', "---."},
210 {'&', ". ..."}, {0, nullptr}};
211 return alphabet == MorseAlphabet::AMERICAN ? american : international;
212 }
213};
214
215} // namespace tinymodem
The Morse code tables and timing. A code is a string of '.' (dot), '-' (dash), '_' (long dash of the ...
Definition Morse.h:143
static const Entry * table(MorseAlphabet alphabet)
Definition Morse.h:181
static const Timing & timing(MorseAlphabet alphabet)
Definition Morse.h:153
static char decode(MorseAlphabet alphabet, const char *code)
Character of a code: returns 0 if the code is unknown.
Definition Morse.h:169
static const char * encode(MorseAlphabet alphabet, char ch)
Definition Morse.h:161
TinyModem: software audio modems and packets with error correction.
Definition AX25.h:11
MorseAlphabet
Morse code alphabet.
Definition Morse.h:13
@ AMERICAN
Definition Morse.h:18
@ INTERNATIONAL
International Morse code (ITU-R M.1677): dots and dashes of 3 units.
Definition Morse.h:15
Definition Morse.h:176
const char * code
Definition Morse.h:178
char ch
Definition Morse.h:177
Lengths in units.
Definition Morse.h:146
float extra_long_dash
Definition Morse.h:147
float dot
Definition Morse.h:147
float letter_gap
Definition Morse.h:148
float element_gap
Definition Morse.h:148
float dash
Definition Morse.h:147
float inner_gap
Definition Morse.h:148
float long_dash
Definition Morse.h:147
float word_gap
Definition Morse.h:148
bool setMode(const char *mode)
Definition Morse.h:64
float wpm
speed in words per minute
Definition Morse.h:41
float frequency
frequency of the tone in Hz
Definition Morse.h:43
bool isValid() const
Checks the configuration: returns false (and logs the reason) if invalid.
Definition Morse.h:84
uint8_t channel
receiver: input channel which is used for the decoding
Definition Morse.h:58
static bool startsWith(const char *str, const char *prefix)
Definition Morse.h:129
static bool parseMode(const char *mode, MorseAlphabet &alphabet, float &speed)
Definition Morse.h:101
float ramp_ms
transmitter: rise and fall time of the tone in ms (avoids clicks)
Definition Morse.h:47
MorseConfig()
Definition Morse.h:32
static bool isMode(const char *mode)
true if the mode is a Morse code mode
Definition Morse.h:74
int rx_timeout_units
Definition Morse.h:54
float unitTime() const
length of a unit (dot) in seconds
Definition Morse.h:81
MorseAlphabet alphabet
International (default) or American Morse code.
Definition Morse.h:39
float rx_noise
receiver: (testing only) amplitude of added uniform noise
Definition Morse.h:60
bool rx_one
receiver: stop after the first transmission
Definition Morse.h:56
bool adaptive_speed
receiver: adapt to the speed of the sender
Definition Morse.h:49
float min_amplitude
receiver: minimum amplitude of the tone (1.0 = full scale)
Definition Morse.h:51
float volume
transmit amplitude (0.0 - 1.0)
Definition Morse.h:45