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