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}