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}