use core::ops::RangeInclusive;
use crate::{
message::Message,
Character,
CharacterSet,
MorseCodeArray,
MorseCodeSet,
MorseSignal::{self, Long as L, Short as S},
DECODING_ERROR_CHAR,
DEFAULT_CHARACTER_SET,
DEFAULT_MORSE_CODE_SET,
LONG_SIGNAL_MULTIPLIER,
MORSE_ARRAY_LENGTH,
MORSE_DEFAULT_CHAR,
WORD_SPACE_MULTIPLIER,
};
#[derive(Debug, PartialEq)]
pub enum Precision {
Lazy,
Accurate,
Farnsworth(f32),
}
use Precision::{Lazy, Accurate, Farnsworth};
type MilliSeconds = u16;
#[derive(PartialEq, Copy, Clone, Debug)]
enum SignalDuration {
Empty,
Short(MilliSeconds),
Long(MilliSeconds),
Other(MilliSeconds),
}
use SignalDuration::{Empty as SDEmpty, Short as SDShort, Long as SDLong, Other as SDOther};
const SIGNAL_BUFFER_LENGTH: usize = MORSE_ARRAY_LENGTH + 1;
type SignalBuffer = [SignalDuration; SIGNAL_BUFFER_LENGTH];
pub struct Decoder<const MSG_MAX: usize> {
precision: Precision,
character_set: CharacterSet,
morse_code_set: MorseCodeSet,
signal_tolerance: f32,
reference_short_ms: MilliSeconds,
message: Message<MSG_MAX>,
current_character: MorseCodeArray,
signal_pos: usize,
signal_buffer: SignalBuffer,
}
impl<const MSG_MAX: usize> Default for Decoder<MSG_MAX> {
fn default() -> Self {
Self::new()
}
}
impl<const MSG_MAX: usize> Decoder<MSG_MAX> {
pub fn new() -> Self {
Self {
precision: Lazy,
character_set: DEFAULT_CHARACTER_SET,
morse_code_set: DEFAULT_MORSE_CODE_SET,
signal_tolerance: 0.50,
reference_short_ms: 0,
message: Message::default(),
current_character: MORSE_DEFAULT_CHAR,
signal_pos: 0,
signal_buffer: [SDEmpty; SIGNAL_BUFFER_LENGTH],
}
}
pub fn with_message(mut self, message_str: &str, edit_pos_end: bool) -> Self {
self.message = Message::new(message_str, edit_pos_end, self.message.is_edit_clamped());
self
}
pub fn with_edit_position(mut self, pos: usize) -> Self {
self.message.set_edit_pos(pos);
self
}
pub fn with_precision(mut self, precision: Precision) -> Self {
if let Farnsworth(factor) = precision {
self.precision = Farnsworth(factor.clamp(0.01, 0.99));
} else {
self.precision = precision;
}
self
}
pub fn with_character_set(mut self, character_set: CharacterSet) -> Self {
self.character_set = character_set;
self
}
pub fn with_morse_code_set(mut self, morse_code_set: MorseCodeSet) -> Self {
self.morse_code_set = morse_code_set;
self
}
pub fn with_signal_tolerance(mut self, signal_tolerance: f32) -> Self {
self.signal_tolerance = signal_tolerance.clamp(0.0, 1.0);
self
}
pub fn with_reference_short_ms(mut self, reference_short_ms: MilliSeconds) -> Self {
self.reference_short_ms = reference_short_ms;
self
}
pub fn with_message_pos_clamping(mut self) -> Self {
self.message.set_edit_position_clamp(true);
self
}
pub fn build(self) -> MorseDecoder<MSG_MAX> {
let Decoder {
precision,
character_set,
morse_code_set,
signal_tolerance,
reference_short_ms,
message,
current_character,
signal_pos,
signal_buffer,
} = self;
MorseDecoder::<MSG_MAX> {
precision,
character_set,
morse_code_set,
signal_tolerance,
reference_short_ms,
message,
current_character,
signal_pos,
signal_buffer,
}
}
}
pub struct MorseDecoder<const MSG_MAX: usize> {
precision: Precision,
character_set: CharacterSet,
morse_code_set: MorseCodeSet,
signal_tolerance: f32,
reference_short_ms: MilliSeconds,
pub message: Message<MSG_MAX>,
current_character: MorseCodeArray,
signal_pos: usize,
signal_buffer: SignalBuffer,
}
impl<const MSG_MAX: usize> MorseDecoder<MSG_MAX> {
fn get_char_from_morse_char(&self, morse_char: &MorseCodeArray) -> Character {
let index = self.morse_code_set
.iter()
.position(|mchar| mchar == morse_char);
if let Some(i) = index {
self.character_set[i]
} else {
DECODING_ERROR_CHAR
}
}
fn add_to_signal_buffer(&mut self, signal_duration: SignalDuration) {
if self.signal_pos < SIGNAL_BUFFER_LENGTH {
self.signal_buffer[self.signal_pos] = signal_duration;
self.signal_pos += 1;
}
}
fn decode_signal_buffer(&mut self) -> MorseCodeArray {
let mut morse_array: MorseCodeArray = MORSE_DEFAULT_CHAR;
self.signal_buffer
.iter()
.take(6)
.enumerate()
.for_each(|(i, signal)| match signal {
SDShort(_) => {
morse_array[i] = Some(S);
}
SDLong(_) => morse_array[i] = Some(L),
_ => {}
});
morse_array
}
fn resolve_signal_duration(
&mut self,
duration_ms: MilliSeconds,
tolerance_range: &RangeInclusive<MilliSeconds>,
is_high: bool,
) -> SignalDuration {
let resolve_accurate_or_farnsworth = |long_ms: MilliSeconds| -> SignalDuration {
if tolerance_range.contains(&self.reference_short_ms) {
SDShort(duration_ms)
} else if tolerance_range.contains(&long_ms) {
SDLong(duration_ms)
} else {
SDOther(duration_ms)
}
};
match self.precision {
Lazy => {
let short_tolerance_range = self.signal_tolerance_range(self.reference_short_ms);
let short_range_end = short_tolerance_range.end() + 50;
if (0u16..short_range_end).contains(&duration_ms) {
SDShort(duration_ms)
} else if (short_range_end..self.word_space_ms()).contains(&duration_ms) {
SDLong(duration_ms)
} else {
SDOther(duration_ms)
}
}
Accurate => {
resolve_accurate_or_farnsworth(self.long_signal_ms())
}
Farnsworth(factor) => {
if is_high {
resolve_accurate_or_farnsworth(self.long_signal_ms())
} else {
let farnsworth_long = self.calculate_farnsworth_short(factor) * LONG_SIGNAL_MULTIPLIER;
resolve_accurate_or_farnsworth(farnsworth_long)
}
}
}
}
fn signal_tolerance_range(&self, duration_ms: MilliSeconds) -> RangeInclusive<MilliSeconds> {
let diff = (duration_ms as f32 * self.signal_tolerance) as MilliSeconds;
duration_ms - diff..=duration_ms.saturating_add(diff)
}
fn reset_character(&mut self) {
self.signal_buffer = [SDEmpty; SIGNAL_BUFFER_LENGTH];
self.signal_pos = 0;
self.current_character = MORSE_DEFAULT_CHAR;
}
fn update_reference_short_ms(&mut self, duration_ms: MilliSeconds) {
self.reference_short_ms = duration_ms;
}
fn long_signal_ms(&self) -> MilliSeconds {
self.reference_short_ms * LONG_SIGNAL_MULTIPLIER
}
fn word_space_ms(&self) -> MilliSeconds {
let multiplier = match self.precision {
Lazy => WORD_SPACE_MULTIPLIER + 1,
Accurate => WORD_SPACE_MULTIPLIER,
Farnsworth(factor) => {
return self.calculate_farnsworth_short(factor) * WORD_SPACE_MULTIPLIER
}
};
self.reference_short_ms * multiplier
}
fn calculate_farnsworth_short(&self, speed_reduction_factor: f32) -> MilliSeconds {
let current_wpm = self.get_wpm() as f32;
let reduced_wpm = current_wpm * speed_reduction_factor;
let delay_time_ms = (((60.0 * current_wpm) - (37.2 * reduced_wpm)) / (current_wpm * reduced_wpm)) * 1000.0;
(delay_time_ms / 19.0) as MilliSeconds
}
}
impl<const MSG_MAX: usize> MorseDecoder<MSG_MAX> {
pub fn get_reference_short(&self) -> MilliSeconds {
self.reference_short_ms
}
pub fn get_wpm(&self) -> u16 {
(1.2 / (self.reference_short_ms as f32 / 1000.0)) as u16
}
pub fn get_last_decoded_char(&self) -> Character {
self.message.get_last_changed_char()
}
pub fn add_signal_to_character(&mut self, signal: Option<MorseSignal>) {
if self.signal_pos < MORSE_ARRAY_LENGTH {
self.current_character[self.signal_pos] = signal;
self.signal_pos += 1;
}
}
pub fn add_current_char_to_message(&mut self) {
if self.message.get_edit_pos() < MSG_MAX {
let ch = self.get_char_from_morse_char(&self.current_character);
self.message.add_char(ch);
self.message.shift_edit_right();
self.reset_character();
}
}
pub fn signal_event_end(&mut self, end_word: bool) {
self.current_character = self.decode_signal_buffer();
self.add_current_char_to_message();
if end_word {
self.current_character = MORSE_DEFAULT_CHAR;
self.add_current_char_to_message();
}
}
pub fn signal_event(&mut self, duration_ms: MilliSeconds, is_high: bool) {
let tolerance_range = self.signal_tolerance_range(duration_ms);
match self.signal_pos {
0 => {
if is_high {
if self.reference_short_ms == 0 {
self.add_to_signal_buffer(SDShort(duration_ms));
self.update_reference_short_ms(duration_ms);
} else {
let resolved_duration = self.resolve_signal_duration(duration_ms, &tolerance_range, is_high);
self.add_to_signal_buffer(resolved_duration);
}
} else {
}
}
_pos if !is_high => {
if duration_ms < self.reference_short_ms && !tolerance_range.contains(&self.reference_short_ms) {
self.update_reference_short_ms(duration_ms);
}
let resolved_duration = self.resolve_signal_duration(duration_ms, &tolerance_range, is_high);
match resolved_duration {
SDLong(_) => {
self.signal_event_end(false);
}
SDOther(ms) if ms >= self.word_space_ms() => {
self.signal_event_end(true);
}
_ => (),
}
}
pos if pos < SIGNAL_BUFFER_LENGTH && is_high => {
let resolved_duration = self.resolve_signal_duration(duration_ms, &tolerance_range, is_high);
self.add_to_signal_buffer(resolved_duration);
if let SDShort(first_duration) = self.signal_buffer[0] {
match resolved_duration {
SDLong(_) => {
if tolerance_range.contains(&first_duration) {
self.signal_buffer[0] = SDLong(duration_ms);
}
}
SDShort(_) => {
if tolerance_range.contains(&(first_duration / LONG_SIGNAL_MULTIPLIER)) {
self.signal_buffer[0] = SDLong(duration_ms);
}
}
_ => (),
}
}
}
_ => {
self.message.add_char(DECODING_ERROR_CHAR);
self.message.shift_edit_right();
self.reset_character();
}
}
}
}