morse_codec/
decoder.rs

1//! Live decoder for morse code that converts morse code to characters. Supports real-time decoding of incoming signals and decoding
2//! prepared morse signals. This module supports Farnsworth timing mode and can be used for morse
3//! code practice.
4//!
5//! Receives morse signals and decodes them character by character
6//! to create a char array (charray) message with constant max length.
7//! Empty characters will be filled with the const FILLER and
8//! decoding errors will be filled with DECODING_ERROR_CHAR.
9//! Trade-offs to support no_std include:
10//! * No vectors or any other type of dynamic heap memory used, all data is plain old stack arrays.
11//! * We decode the signals character by character instead of creating a large buffer for all
12//!   signals and decoding it at the end. As a result, if an initial reference short duration is not
13//!   provided, there's a problem with words starting with 'T' decoding as different characters. This is a problem because
14//!   we can't determine the length of spaces (low signals) after the high signal being long with only one signal as reference.
15//!   Creating a large buffer would fix this, because we could audit the entire signal buffer to iron out wrong decodings,
16//!   but the large size of the buffer would not fit into small RAM capacities of certain 8 bit
17//!   MCUs like AVR ATmega328P with SRAM size of 2KB and even smaller sizes for simpler chips. So we
18//!   clear the buffer every time we add a character.
19//!
20//!   One way to fix the wrong decoding problems of 'T' character is to provide an initial reference short signal
21//!   length to the decoder. A good intermediate value is 100 milliseconds.
22//!
23//! ```rust
24//! use morse_codec::decoder::Decoder;
25//!
26//! const MSG_MAX: usize = 64;
27//! let mut decoder = Decoder::<MSG_MAX>::new()
28//!     .with_reference_short_ms(90)
29//!     .build();
30//!
31//! // We receive high signal from button. 100 ms is a short dit signal because reference_short_ms is 90
32//! // ms, default tolerance range factor is 0.5. 90 ms falls into 100 x 0.5 = 50 ms to 100 + 50 = 150 ms.
33//! // So it's a short or dit signal.
34//! decoder.signal_event(100, true);
35//! // We receive a low signal from the button. 80 ms low signal is a signal space dit.
36//! // It falls between 50 and 150.
37//! decoder.signal_event(80, false);
38//! // 328 ms high long signal is a dah. 328 x 0.5 = 164, 328 + 164 = 492.
39//! // Reference short signal 90 x 3 (long signal multiplier) = 270. 270 falls into the range.
40//! decoder.signal_event(328, true);
41//! // 412 ms low long signal will end the character.
42//! decoder.signal_event(412, false);
43//! // At this point the character will be decoded and added to the message.
44//!
45//! // Resulting character will be 'A' or '.-' in morse code.
46//! let message = decoder.message.as_str();
47//! assert_eq!(message, "A");
48//! ```
49//!
50
51use core::ops::RangeInclusive;
52
53use crate::{
54    message::Message,
55    Character,
56    CharacterSet,
57    MorseCodeArray,
58    MorseCodeSet,
59    MorseSignal::{self, Long as L, Short as S},
60    DECODING_ERROR_CHAR,
61    DEFAULT_CHARACTER_SET,
62    DEFAULT_MORSE_CODE_SET,
63    LONG_SIGNAL_MULTIPLIER,
64    MORSE_ARRAY_LENGTH,
65    MORSE_DEFAULT_CHAR,
66    WORD_SPACE_MULTIPLIER,
67};
68
69/// Decoding precision is either Lazy, Accurate or Farnsworth(speed_reduction_factor: f32).
70///
71/// If Lazy is selected, short and long signals will be considered to saturate their
72/// fields on the extreme ends. For example a short signal can be 1 ms to short range end
73/// and a long signal is from this point to the start of a very long (word separator) signal.
74/// If Accurate is selected, short and long signals will only be decoded correctly if they fall into a range
75/// of lower tolerance value and higher tolerance value. Default value for tolerance factor is 0.5.
76/// So if a short signal is expected to be 100 ms, correct decoding signal can be anywhere between
77/// 50 ms to 150 ms, but not 10 ms.
78///
79/// Default precision is Lazy, as it's the most human friendly precision.
80///
81/// Farnsworth precision means extra delays will be added to spaces between characters and
82/// words but character decoding speed is not affected.
83/// Difference between current decoding speed and a reduced decoding speed will determine
84/// the length of the delays. The reduced decoding speed is determined by the factor value
85/// passed to the enum variant Farnsworth. This value will be multiplied by the current speed
86/// to find a reduction in overall speed. Factor value is clamped between 0.01 and 0.99.
87#[derive(Debug, PartialEq)]
88pub enum Precision {
89    Lazy,
90    Accurate,
91    Farnsworth(f32),
92}
93
94use Precision::{Lazy, Accurate, Farnsworth};
95
96type MilliSeconds = u16;
97
98#[derive(PartialEq, Copy, Clone, Debug)]
99enum SignalDuration {
100    Empty,
101    Short(MilliSeconds),
102    Long(MilliSeconds),
103    Other(MilliSeconds),
104}
105use SignalDuration::{Empty as SDEmpty, Short as SDShort, Long as SDLong, Other as SDOther};
106
107// Signal buffer length is morse array length + 1, because we need to be able to
108// resolve a character ending long signal (either 3x or word space 7x) at the end
109// of each character.
110const SIGNAL_BUFFER_LENGTH: usize = MORSE_ARRAY_LENGTH + 1;
111type SignalBuffer = [SignalDuration; SIGNAL_BUFFER_LENGTH];
112
113/// This is the builder, or public interface of the decoder using builder pattern.
114/// It builds a MorseDecoder which is the concrete implementation and returns it with `build()`.
115/// For details on how to use the decoder, refer to [MorseDecoder] documentation.
116pub struct Decoder<const MSG_MAX: usize> {
117    // User defined
118    precision: Precision,
119    character_set: CharacterSet,
120    morse_code_set: MorseCodeSet,
121    signal_tolerance: f32,
122    reference_short_ms: MilliSeconds,
123    message: Message<MSG_MAX>,
124    // Internal stuff
125    current_character: MorseCodeArray,
126    signal_pos: usize,
127    signal_buffer: SignalBuffer,
128}
129
130impl<const MSG_MAX: usize> Default for Decoder<MSG_MAX> {
131    fn default() -> Self {
132        Self::new()
133    }
134}
135
136impl<const MSG_MAX: usize> Decoder<MSG_MAX> {
137    pub fn new() -> Self {
138        Self {
139            // User defined
140            precision: Lazy,
141            character_set: DEFAULT_CHARACTER_SET,
142            morse_code_set: DEFAULT_MORSE_CODE_SET,
143            signal_tolerance: 0.50,
144            reference_short_ms: 0,
145            message: Message::default(),
146            // Internal stuff
147            current_character: MORSE_DEFAULT_CHAR,
148            signal_pos: 0,
149            signal_buffer: [SDEmpty; SIGNAL_BUFFER_LENGTH],
150        }
151    }
152
153    /// Build decoder with a starting message.
154    ///
155    /// `edit_pos_end` means we'll continue decoding from the end of this string.
156    /// If you pass false to it, we'll start from the beginning.
157    pub fn with_message(mut self, message_str: &str, edit_pos_end: bool) -> Self {
158        self.message = Message::new(message_str, edit_pos_end, self.message.is_edit_clamped());
159
160        self
161    }
162
163    /// Build decoder with an arbitrary editing start position.
164    ///
165    /// Maybe client code saved the previous editing position to an EEPROM, harddisk, local
166    /// storage in web and wants to continue from that.
167    pub fn with_edit_position(mut self, pos: usize) -> Self {
168        self.message.set_edit_pos(pos);
169
170        self
171    }
172
173    /// Set decoder precision.
174    ///
175    /// * Precision::Lazy is more human friendly,
176    /// * Precision::Accurate is for learning or a challenge - contest.
177    /// * Precision::Farnsworth means extra delays will be added to spaces between characters and
178    ///     words but intracharacter speed is not affected.
179    ///     Difference between current decoding speed and a reduced decoding speed will determine
180    ///     the length of the delays. The reduced decoding speed is determined by the factor value
181    ///     passed to the enum variant Farnsworth. This value will be multiplied by the current speed
182    ///     to find a reduction in overall speed. Factor value will be clamped between 0.01 and 0.99.
183    ///
184    /// As an example for Farnsworth precision, let's say
185    /// client code wants a reduction to half the current speed:
186    /// ```ignore
187    /// let decoder = Decoder::new().with_precision(Precision::Farnsworth(0.5)).build();
188    /// // At this point if our words per minute speed is 20,
189    /// // overall transmission speed will be reduced to 10 WPM
190    /// // preserving the character speed at 20 WPM but distributing
191    /// // the difference in time among spaces between chars and words.
192    /// ```
193    pub fn with_precision(mut self, precision: Precision) -> Self {
194        if let Farnsworth(factor) = precision {
195            self.precision = Farnsworth(factor.clamp(0.01, 0.99));
196        } else {
197            self.precision = precision;
198        }
199
200        self
201    }
202
203    /// Use a different character set than default english alphabet.
204    ///
205    /// This can be helpful to create a message with trivial encryption.
206    /// Letters can be shuffled for example. With utf-8 feature flag, a somewhat
207    /// stronger encryption can be used. These kind of encryptions can
208    /// easily be broken with powerful algorithms and AI.
209    /// **DON'T** use it for secure communication.
210    pub fn with_character_set(mut self, character_set: CharacterSet) -> Self {
211        self.character_set = character_set;
212
213        self
214    }
215
216    /// Use a different morse code set than the default.
217    ///
218    /// It's mainly useful for a custom morse code set with utf8
219    /// character sets. Different alphabets have different corresponding morse code sets.
220    pub fn with_morse_code_set(mut self, morse_code_set: MorseCodeSet) -> Self {
221        self.morse_code_set = morse_code_set;
222
223        self
224    }
225
226    /// Use a different signal tolerance range factor than the default 0.5.
227    ///
228    /// Tolerance factors higher than 0.5 tend to overlap and result in wrong decoding.
229    /// You can lower this value though for stricter morse signalling.
230    /// In any case the value will be clamped between 0.0 and 1.0 so values
231    /// higher than 1.0 will be 1.0.
232    pub fn with_signal_tolerance(mut self, signal_tolerance: f32) -> Self {
233        self.signal_tolerance = signal_tolerance.clamp(0.0, 1.0);
234
235        self
236    }
237
238    /// Change initial reference short signal duration from 0 to some other value.
239    ///
240    /// This value will determine the reference durations of signal types (short, long or very long).
241    /// The value will be multiplied by LONG_SIGNAL_MULTIPLIER (x3) and WORD_SPACE_MULTIPLIER (x7) to
242    /// determine long signals and very long word separator signals.
243    /// Default value of 0 means MorseDecoder will try to calculate the reference short duration
244    /// from incoming signals. This might not work well if the message starts with a 'T'.
245    pub fn with_reference_short_ms(mut self, reference_short_ms: MilliSeconds) -> Self {
246        self.reference_short_ms = reference_short_ms;
247
248        self
249    }
250
251    /// Change the wrapping behaviour of message position to clamping.
252    ///
253    /// This will prevent the position cycling back to 0 when overflows or
254    /// jumping forward to max when falls below 0. Effectively limiting the position
255    /// to move within the message length from 0 to message length maximum without jumps.
256    ///
257    /// If at one point you want to change it back to wrapping:
258    ///
259    /// ```ignore
260    /// decoder.message.set_edit_position_clamp(false);
261    /// ```
262    pub fn with_message_pos_clamping(mut self) -> Self {
263        self.message.set_edit_position_clamp(true);
264
265        self
266    }
267
268    /// Build and get yourself a shiny new [MorseDecoder].
269    ///
270    /// The ring is yours now...
271    pub fn build(self) -> MorseDecoder<MSG_MAX> {
272        let Decoder {
273            precision,
274            character_set,
275            morse_code_set,
276            signal_tolerance,
277            reference_short_ms,
278            message,
279            current_character,
280            signal_pos,
281            signal_buffer,
282        } = self;
283
284        MorseDecoder::<MSG_MAX> {
285            precision,
286            character_set,
287            morse_code_set,
288            signal_tolerance,
289            reference_short_ms,
290            message,
291            current_character,
292            signal_pos,
293            signal_buffer,
294        }
295    }
296}
297
298/// This is the concrete implementation of the decoder.
299///
300/// It doesn't have a new function, or public data members,
301/// so to get an instance of it, use public builder interface [Decoder].
302pub struct MorseDecoder<const MSG_MAX: usize> {
303    // User defined
304    precision: Precision,
305    character_set: CharacterSet,
306    morse_code_set: MorseCodeSet,
307    signal_tolerance: f32,
308    reference_short_ms: MilliSeconds,
309    pub message: Message<MSG_MAX>,
310    // Internal stuff
311    current_character: MorseCodeArray,
312    signal_pos: usize,
313    signal_buffer: SignalBuffer,
314}
315
316// Private stuff.. Don' look at it
317impl<const MSG_MAX: usize> MorseDecoder<MSG_MAX> {
318    fn get_char_from_morse_char(&self, morse_char: &MorseCodeArray) -> Character {
319        let index = self.morse_code_set
320            .iter()
321            .position(|mchar| mchar == morse_char);
322
323        if let Some(i) = index {
324            self.character_set[i]
325        } else {
326            DECODING_ERROR_CHAR
327        }
328    }
329
330    fn add_to_signal_buffer(&mut self, signal_duration: SignalDuration) {
331        if self.signal_pos < SIGNAL_BUFFER_LENGTH {
332            self.signal_buffer[self.signal_pos] = signal_duration;
333            self.signal_pos += 1;
334        }
335    }
336
337    fn decode_signal_buffer(&mut self) -> MorseCodeArray {
338        let mut morse_array: MorseCodeArray = MORSE_DEFAULT_CHAR;
339
340        //DBG
341        //println!("Signal buffer decoding: {:?}", self.signal_buffer);
342
343        self.signal_buffer
344            .iter()
345            .take(6)
346            .enumerate()
347            .for_each(|(i, signal)| match signal {
348                SDShort(_) => {
349                    morse_array[i] = Some(S);
350                }
351                SDLong(_) => morse_array[i] = Some(L),
352                _ => {}
353            });
354
355        morse_array
356    }
357
358    fn resolve_signal_duration(
359        &mut self,
360        duration_ms: MilliSeconds,
361        tolerance_range: &RangeInclusive<MilliSeconds>,
362        is_high: bool,
363    ) -> SignalDuration {
364        let resolve_accurate_or_farnsworth = |long_ms: MilliSeconds| -> SignalDuration {
365            if tolerance_range.contains(&self.reference_short_ms) {
366                SDShort(duration_ms)
367            } else if tolerance_range.contains(&long_ms) {
368                SDLong(duration_ms)
369            } else {
370                SDOther(duration_ms)
371            }
372        };
373
374        match self.precision {
375            Lazy => {
376                let short_tolerance_range = self.signal_tolerance_range(self.reference_short_ms);
377                let short_range_end = short_tolerance_range.end() + 50; // 50 ms padding gives better results with humans
378
379                if (0u16..short_range_end).contains(&duration_ms) {
380                    SDShort(duration_ms)
381                } else if (short_range_end..self.word_space_ms()).contains(&duration_ms) {
382                    SDLong(duration_ms)
383                } else {
384                    SDOther(duration_ms)
385                }
386            }
387            Accurate => {
388                resolve_accurate_or_farnsworth(self.long_signal_ms())
389            }
390            Farnsworth(factor) => {
391                if is_high {
392                    resolve_accurate_or_farnsworth(self.long_signal_ms())
393                } else {
394                    let farnsworth_long = self.calculate_farnsworth_short(factor) * LONG_SIGNAL_MULTIPLIER;
395
396                    resolve_accurate_or_farnsworth(farnsworth_long)
397                }
398            }
399        }
400    }
401
402    fn signal_tolerance_range(&self, duration_ms: MilliSeconds) -> RangeInclusive<MilliSeconds> {
403        let diff = (duration_ms as f32 * self.signal_tolerance) as MilliSeconds;
404
405        duration_ms - diff..=duration_ms.saturating_add(diff)
406    }
407
408    fn reset_character(&mut self) {
409        self.signal_buffer = [SDEmpty; SIGNAL_BUFFER_LENGTH];
410        self.signal_pos = 0;
411        self.current_character = MORSE_DEFAULT_CHAR;
412    }
413
414    fn update_reference_short_ms(&mut self, duration_ms: MilliSeconds) {
415        self.reference_short_ms = duration_ms;
416    }
417
418    fn long_signal_ms(&self) -> MilliSeconds {
419        self.reference_short_ms * LONG_SIGNAL_MULTIPLIER
420    }
421
422    fn word_space_ms(&self) -> MilliSeconds {
423        let multiplier = match self.precision {
424            // Adding some padding to the end of word space to aid the lazy sleazy operator
425            Lazy => WORD_SPACE_MULTIPLIER + 1,
426            Accurate => WORD_SPACE_MULTIPLIER,
427            // Early return if we have a Farnsworth precision.
428            // We calculate the word space from a slower
429            // farnsworth short duration and return it.
430            Farnsworth(factor) => {
431                return self.calculate_farnsworth_short(factor) * WORD_SPACE_MULTIPLIER
432            }
433        };
434
435        self.reference_short_ms * multiplier
436    }
437
438    fn calculate_farnsworth_short(&self, speed_reduction_factor: f32) -> MilliSeconds {
439        // WPM stands for Words per Minute
440        let current_wpm = self.get_wpm() as f32;
441        //println!("FARNSWORTH: current WPM: {}", current_wpm);
442
443        let reduced_wpm = current_wpm * speed_reduction_factor;
444        //println!("FARNSWORTH: reduced WPM: {}", reduced_wpm);
445
446        let delay_time_ms = (((60.0 * current_wpm) - (37.2 * reduced_wpm)) / (current_wpm * reduced_wpm)) * 1000.0;
447        //println!("FARNSWORTH: current reference short: {}", self.reference_short_ms);
448        //println!("FARNSWORTH: delay time total: {} and short: {}", delay_time_ms, (delay_time_ms / 19.0) as MilliSeconds);
449
450        (delay_time_ms / 19.0) as MilliSeconds
451    }
452}
453
454// Public API for the masses
455impl<const MSG_MAX: usize> MorseDecoder<MSG_MAX> {
456    /// Returns currently resolved reference short signal duration.
457    ///
458    /// Reference short signal is resolved continuously by the decoder as signal events pour in.
459    /// As longer signal durations are calculated by multiplying this value,
460    /// it might be useful for the client code.
461    pub fn get_reference_short(&self) -> MilliSeconds {
462        self.reference_short_ms
463    }
464
465    /// Returns the current signal entry speed in
466    /// Words Per Minute format.
467    pub fn get_wpm(&self) -> u16 {
468        (1.2 / (self.reference_short_ms as f32 / 1000.0)) as u16
469    }
470
471    /// Returns last decoded character for easy access.
472    pub fn get_last_decoded_char(&self) -> Character {
473        self.message.get_last_changed_char()
474    }
475
476    /// Directly add a prepared signal to the character.
477    ///
478    /// Signal duration resolving is done by the client code, or you're using a prepared signal.
479    pub fn add_signal_to_character(&mut self, signal: Option<MorseSignal>) {
480        if self.signal_pos < MORSE_ARRAY_LENGTH {
481            self.current_character[self.signal_pos] = signal;
482            self.signal_pos += 1;
483        }
484    }
485
486    /// Add current decoded character to the message.
487    ///
488    /// This happens automatically when using `signal_event` calls.
489    /// Use this with `add_signal_to_character` directly with
490    /// prepared [MorseSignal] enums.
491    pub fn add_current_char_to_message(&mut self) {
492        if self.message.get_edit_pos() < MSG_MAX {
493            let ch = self.get_char_from_morse_char(&self.current_character);
494            self.message.add_char(ch);
495
496            // If message position is clamping then this should not do anything.
497            // at the end of message position.
498            // If wrapping then it should reset the position to 0, so above condition
499            // should pass next time.
500            self.message.shift_edit_right();
501
502            self.reset_character();
503        }
504    }
505
506    /// Manually end a sequence of signals.
507    ///
508    /// This decodes the current character and moves to the next one.
509    /// With end_word flag it will optionally add a space after it.
510    /// Especially useful when client code can't determine if signal
511    /// input by the operator ended, because no other high signal is
512    /// following the low signal at the end. At that point a separate button
513    /// or whatever can be used to trigger this function.
514    pub fn signal_event_end(&mut self, end_word: bool) {
515        self.current_character = self.decode_signal_buffer();
516        self.add_current_char_to_message();
517
518        if end_word {
519            self.current_character = MORSE_DEFAULT_CHAR;
520            self.add_current_char_to_message();
521        }
522    }
523
524    /// Send signal events to the decoder, filling signal buffer one event at a time.
525    ///
526    /// When a character ending long space signal or a word ending long space is sent,
527    /// signal buffer will be decoded automatically and character will be added to message.
528    /// Note that if signal input itself has ended, oftentimes there's no way to send that signal.
529    /// Use `signal_event_end` at that point to manually end the character.
530    pub fn signal_event(&mut self, duration_ms: MilliSeconds, is_high: bool) {
531        let tolerance_range = self.signal_tolerance_range(duration_ms);
532
533        match self.signal_pos {
534            // Signal is the first in the series.
535            // Since this is the first signal we encounter, we'll treat it as if it's a short, when
536            // reference_short_ms == 0, otherwise try to resolve signal duration based on
537            // reference short learned from previous letters or based on
538            // initial_reference_short_ms provided to the constructor.
539            // If we have set it short preemptively, we later on check if this first short turns out to be long instead
540            // (see one of the match arms). We'll update the first buffer item with the correct value then don't worry.
541            0 => {
542                if is_high {
543                    //DBG
544                    //println!("START CHARACTER -----------------------");
545
546                    if self.reference_short_ms == 0 {
547                        self.add_to_signal_buffer(SDShort(duration_ms));
548                        self.update_reference_short_ms(duration_ms);
549
550                        //DBG
551                        //println!("Initial ref short is set to {}", duration_ms);
552                    } else {
553                        let resolved_duration = self.resolve_signal_duration(duration_ms, &tolerance_range, is_high);
554
555                        //DBG
556                        //println!("\tINTIAL HIGH: tolerance range: {:?}, position is: {}, resolved duration: {:?}, ref short is: {}", tolerance_range, pos, resolved_duration, self.reference_short_ms);
557
558                        self.add_to_signal_buffer(resolved_duration);
559                    }
560                } else {
561                    // Do nothing if we receive a low signal at the start of a series.
562                    // This happens when event engine of the client code sends low signals
563                    // inadvertently perhaps while idling or outright sends a wrong low signal at the start of a letter
564                    // which is rude.
565                }
566            }
567
568            // Signal is not high. It can be one of three things at this point:
569            // 1. It's a short duration space signal (space between two signals)
570            // 2. It's a long duration space. At this point we decode the entire signal buffer and
571            // add resulting character to the message
572            // 3. It's a very long signal (x7 or more) to divide two words in the message. So
573            // we check the signal buffer and add the character, as well as a space after it.
574            _pos if !is_high => {
575                if duration_ms < self.reference_short_ms && !tolerance_range.contains(&self.reference_short_ms) {
576                    //println!("Updating reference short to {}", duration_ms);
577                    self.update_reference_short_ms(duration_ms);
578                }
579
580                let resolved_duration = self.resolve_signal_duration(duration_ms, &tolerance_range, is_high);
581
582                //DBG
583                //println!("LOW SIGNAL: tolerance range: {:?}, position is: {}, resolved duration: {:?}, ref short is: {}", tolerance_range, _pos, resolved_duration, self.reference_short_ms);
584
585                match resolved_duration {
586                    SDLong(_) => {
587                        //DBG
588                        //println!("END CHARACTER --------------");
589
590                        self.signal_event_end(false);
591                    }
592                    SDOther(ms) if ms >= self.word_space_ms() => {
593                        //DBG
594                        //println!("END WORD --------------");
595
596                        self.signal_event_end(true);
597                    }
598                    _ => (),
599                }
600            }
601
602            // Signal is not the first in a series and there are signals to be fed to the buffer.
603            // At this point signal is high and we try to resolve signal duration and save the signal to character.
604            // Also we check if the first duration in the array was wrongly saved as short but
605            // should be long instead. We fix it to long duration if it's wrong.
606            // The reason why we check at this position starting from index 2+ is that
607            // we get a better calibrated short signal from the low signal before it (index 1)
608            pos if pos < SIGNAL_BUFFER_LENGTH && is_high => {
609                let resolved_duration = self.resolve_signal_duration(duration_ms, &tolerance_range, is_high);
610
611                //DBG
612                //println!("\tHIGH SIGNAL: tolerance range: {:?}, position is: {}, resolved duration: {:?}, ref short is: {}", tolerance_range, pos, resolved_duration, self.reference_short_ms);
613
614                self.add_to_signal_buffer(resolved_duration);
615
616                if let SDShort(first_duration) = self.signal_buffer[0] {
617                    match resolved_duration {
618                        SDLong(_) => {
619                            // If current signal is long and it's tolerance range contains the
620                            // first short signal, the first short signal should be a long
621                            if tolerance_range.contains(&first_duration) {
622                                self.signal_buffer[0] = SDLong(duration_ms);
623                            }
624                        }
625                        SDShort(_) => {
626                            // This is an edge case we need to handle where the character being
627                            // decoded, has a long high signal as the first signal in it and
628                            // has only short signals after it (including this one). If tolerance range
629                            // of the short signal we just got happens to be in the range of first
630                            // short signal divided by long signal multiplier (by default 3),
631                            // first short signal was indeed a long one, but we missed it.
632                            if tolerance_range.contains(&(first_duration / LONG_SIGNAL_MULTIPLIER)) {
633                                self.signal_buffer[0] = SDLong(duration_ms);
634                            }
635                        }
636                        _ => (),
637                    }
638                }
639            }
640
641            // This means we got the maximum amount of signals to the buffer, but still couldn't
642            // decode the character. Either because we never received a character ender low
643            // signal (3x short space) or a word ending long signal (7x short space)
644            // or outright couldn't decode them, but hey.
645            // We put a decoding error character at this point. And move on.
646            _ => {
647                //DBG
648                //println!("We reached the end of buffer and couldn't decode the character. signal_buffer so far is: {:?}", self.signal_buffer);
649                self.message.add_char(DECODING_ERROR_CHAR);
650                self.message.shift_edit_right();
651                self.reset_character();
652            }
653        }
654    }
655}