morse_codec/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
//! Library for live decoding and encoding of morse code messages. no_std to support multiple
//! devices including embedded. UTF-8 is not supported at the moment, but can be implemented behind
//! a feature flag in the future.
//!
//! You can create messages by sending individual high and low signals in milliseconds to decoder,
//! from the keyboard, mouse clicks, or a button connected to some embedded device.
//! You can also bypass signal input and add prepared short or long morse signals to characters
//! directly.
//!
//! Use the encoder to turn your messages or characters into morse code strings or create a
//! sequence of signals from the encoder to drive an external component such as an LED, step motor or speaker.
//!
//! # Features
//! * Decoder
//! * Encoder
//!
//! The lib is no_std outside testing to make sure it will work on embedded devices
//! as well as operating systems.

// There're debug println!() statements in various parts of
// the code marked by a "// DBG" sign on top. In order to use them on a development environment
// with a proper OS and std, comment out the below attribute and uncomment the debug lines you want.

#![cfg_attr(not(test), no_std)]

// This is the array length for a sequence of morse signals or character representation of those
// signals while encoding
const MORSE_ARRAY_LENGTH: usize = 6;

/// Maximum number of characters in a mapping set of morse code to letters.
pub const CHARACTER_SET_LENGTH: usize = 53;

const LONG_SIGNAL_MULTIPLIER: u16 = 3;
const WORD_SPACE_MULTIPLIER: u16 = 7;

/// Char version of the [FILLER_BYTE] coz why not? It's mainly used while generating bytes from
/// &str slices. A [char] which is utf-8 by default in Rust, can be more than one byte, turning
/// chars into bytes if they're ascii makes the code stable.
pub const FILLER_CHAR: char = '#';

/// We use this character to fill message arrays so when we encounter this char
/// it actually means there's no character there.
///
/// The character # is not a part of international morse code, so it's a good candidate.
pub const FILLER_BYTE: u8 = FILLER_CHAR as u8;

/// If a decoding error happens, we put this character as a placeholder.
pub const DECODING_ERROR_BYTE: u8 = b'?';

/// Building block of morse characters.
///
/// This enum can be used with the decoder to directly add signals to characters.
#[derive(Clone, Debug, PartialEq)]
pub enum MorseSignal {
    Short,
    Long,
}
use MorseSignal::{Long as L, Short as S};

type MorseCodeArray = [Option<MorseSignal>; MORSE_ARRAY_LENGTH];

/// This corresponds to empty character ' ' which is the default character
pub const MORSE_DEFAULT_CHAR: MorseCodeArray = [None, None, None, None, None, None];

/// Internal representation of morse characters. It's an array of length [CHARACTER_SET_LENGTH].
///
/// Human language letters can be converted to these morse code arrays or vice-versa.
pub const MORSE_CHARACTERS: [MorseCodeArray; CHARACTER_SET_LENGTH] = [
    //
    // Default char is empty character
    MORSE_DEFAULT_CHAR, // Empty character ' '
    //
    // Letters
    [Some(S), Some(L), None, None, None, None],       // A
    [Some(L), Some(S), Some(S), Some(S), None, None], // B
    [Some(L), Some(S), Some(L), Some(S), None, None], // C
    [Some(L), Some(S), Some(S), None, None, None],    // D
    [Some(S), None, None, None, None, None],          // E
    [Some(S), Some(S), Some(L), Some(S), None, None], // F
    [Some(L), Some(L), Some(S), None, None, None],    // G
    [Some(S), Some(S), Some(S), Some(S), None, None], // H
    [Some(S), Some(S), None, None, None, None],       // I
    [Some(S), Some(L), Some(L), Some(L), None, None], // J
    [Some(L), Some(S), Some(L), None, None, None],    // K
    [Some(S), Some(L), Some(S), Some(S), None, None], // L
    [Some(L), Some(L), None, None, None, None],       // M
    [Some(L), Some(S), None, None, None, None],       // N
    [Some(L), Some(L), Some(L), None, None, None],    // O
    [Some(S), Some(L), Some(L), Some(S), None, None], // P
    [Some(L), Some(L), Some(S), Some(L), None, None], // Q
    [Some(S), Some(L), Some(S), None, None, None],    // R
    [Some(S), Some(S), Some(S), None, None, None],    // S
    [Some(L), None, None, None, None, None],          // T
    [Some(S), Some(S), Some(L), None, None, None],    // U
    [Some(S), Some(S), Some(S), Some(L), None, None], // V
    [Some(S), Some(L), Some(L), None, None, None],    // W
    [Some(L), Some(S), Some(S), Some(L), None, None], // X
    [Some(L), Some(S), Some(L), Some(L), None, None], // Y
    [Some(L), Some(L), Some(S), Some(S), None, None], // Z
    //
    // Numbers
    [Some(S), Some(L), Some(L), Some(L), Some(L), None], // 1
    [Some(S), Some(S), Some(L), Some(L), Some(L), None], // 2
    [Some(S), Some(S), Some(S), Some(L), Some(L), None], // 3
    [Some(S), Some(S), Some(S), Some(S), Some(L), None], // 4
    [Some(S), Some(S), Some(S), Some(S), Some(S), None], // 5
    [Some(L), Some(S), Some(S), Some(S), Some(S), None], // 6
    [Some(L), Some(L), Some(S), Some(S), Some(S), None], // 7
    [Some(L), Some(L), Some(L), Some(S), Some(S), None], // 8
    [Some(L), Some(L), Some(L), Some(L), Some(S), None], // 9
    [Some(L), Some(L), Some(L), Some(L), Some(L), None], // 0
    //
    // Punctuation marks
    [Some(L), Some(L), Some(S), Some(S), Some(L), Some(L)], // Comma                ,
    [Some(S), Some(S), Some(L), Some(L), Some(S), Some(S)], // Question mark        ?
    [Some(L), Some(L), Some(L), Some(S), Some(S), Some(S)], // Colon                :
    [Some(L), Some(S), Some(S), Some(S), Some(S), Some(L)], // Dash                 -
    [Some(S), Some(L), Some(S), Some(S), Some(L), Some(S)], // Double quote         "
    [Some(L), Some(S), Some(L), Some(L), Some(S), None],    // Left bracket         (
    [Some(L), Some(S), Some(S), Some(S), Some(L), None],    // Equals               =
    [Some(L), Some(S), Some(S), Some(L), None, None],       // Multiplication       X
    [Some(S), Some(L), Some(S), Some(L), Some(S), Some(L)], // Full stop (period)   .
    [Some(L), Some(S), Some(L), Some(S), Some(L), Some(S)], // Semicolon            ;
    [Some(L), Some(S), Some(S), Some(L), Some(S), None],    // Slash                /
    [Some(S), Some(L), Some(L), Some(L), Some(L), Some(S)], // Apostrophe           '
    [Some(S), Some(S), Some(L), Some(L), Some(S), Some(L)], // Underscore           _
    [Some(L), Some(S), Some(L), Some(L), Some(S), Some(L)], // Right bracket        )
    [Some(S), Some(L), Some(S), Some(L), Some(S), None],    // Addition             +
    [Some(S), Some(L), Some(L), Some(S), Some(L), Some(S)], // At sign              @
];

/// Client code can use this type to construct a different character mapping to morse code
/// and construct the decoder or encoder with this custom character set.
pub type CharacterSet = [u8; CHARACTER_SET_LENGTH];

/// Default international morse code characters. It includes English language letters, numbers and
/// punctuation marks.
///
/// Empty character b' ' should be added at the beginning.
/// It does not include special characters longer than 6 signals to keep arrays small. So no $ sign for ya.
/// In order to change it and use a different mapping, client code can use [CharacterSet] type
/// to construct an array of u8 with [CHARACTER_SET_LENGTH].
///
/// ```
/// let my_set: CharacterSet = [...PUT SOME BYTES HERE...];
/// let decoder = Decoder::<128>::new().with_character_set(my_set).build();
/// ```
///
pub const DEFAULT_CHARACTERS: CharacterSet = [
    b' ', b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O',
    b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', b'1', b'2', b'3', b'4', b'5',
    b'6', b'7', b'8', b'9', b'0', b',', b'?', b':', b'-', b'"', b'(', b'=', b'X', b'.', b';', b'/',
    b'\'', b'_', b')', b'+', b'@',
];

#[cfg(feature = "decoder")]
pub mod decoder;

#[cfg(feature = "encoder")]
pub mod encoder;

pub mod message;