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;