quadrature_decoder/decoder/
incremental.rs

1//! Quadrature-based decoder.
2
3use core::marker::PhantomData;
4
5use num_traits::{One, SaturatingAdd, Zero};
6
7use crate::{
8    state_transducer::{Input, Output},
9    validator::InputValidator,
10    Change, Error, FullStep, HalfStep, QuadStep, StateTransducer, StepMode,
11};
12
13/// A robust quadrature decoder with support for multiple step-modes,
14/// based on which channel (A vs. B) is leading the other.
15///
16/// ```plain
17///                ┌ ─ ┐   ┌───┐   ┌───┐   ┌───┐   ┌ ─ ─ high
18///            A           │   │   │   │   │                  
19///              ─ ┘   └───┘   └───┘   └───┘   └ ─ ┘     low  
20/// AB:                                                  
21///                  ┌ ─ ┐   ┌───┐   ┌───┐   ┌───┐   ┌ ─ high
22///            B             │   │   │   │   │                
23///              ─ ─ ┘   └───┘   └───┘   └───┘   └ ─ ┘   low  
24/// Time: ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─▶
25///                  ┌ ─ ┐   ┌───┐   ┌───┐   ┌───┐   ┌ ─ high
26///            A             │   │   │   │   │                
27///              ─ ─ ┘   └───┘   └───┘   └───┘   └ ─ ┘   low  
28/// BA:                                                  
29///                ┌ ─ ┐   ┌───┐   ┌───┐   ┌───┐   ┌ ─ ─ high
30///            B           │   │   │   │   │                  
31///              ─ ┘   └───┘   └───┘   └───┘   └ ─ ┘     low  
32/// ```
33#[derive(Debug)]
34pub struct IncrementalDecoder<Mode, T = i32> {
35    transducer: StateTransducer<'static, 8, 4>,
36    validator: InputValidator,
37    counter: T,
38    _phantom: PhantomData<Mode>,
39}
40
41impl<T> Default for IncrementalDecoder<FullStep, T>
42where
43    T: Zero,
44{
45    fn default() -> Self {
46        Self::new(StateTransducer::new(
47            &crate::state_transducer::full_step::TRANSITIONS,
48        ))
49    }
50}
51
52impl<T> Default for IncrementalDecoder<HalfStep, T>
53where
54    T: Zero,
55{
56    fn default() -> Self {
57        Self::new(StateTransducer::new(
58            &crate::state_transducer::half_step::TRANSITIONS,
59        ))
60    }
61}
62
63impl<T> Default for IncrementalDecoder<QuadStep, T>
64where
65    T: Zero,
66{
67    fn default() -> Self {
68        Self::new(StateTransducer::new(
69            &crate::state_transducer::quad_step::TRANSITIONS,
70        ))
71    }
72}
73
74impl<Mode, T> IncrementalDecoder<Mode, T>
75where
76    Mode: StepMode,
77    T: Zero,
78{
79    pub(crate) fn new(transducer: StateTransducer<'static, 8, 4>) -> Self {
80        Self {
81            transducer,
82            validator: Default::default(),
83            counter: Zero::zero(),
84            _phantom: PhantomData,
85        }
86    }
87}
88
89impl<Mode, T> IncrementalDecoder<Mode, T>
90where
91    Mode: StepMode,
92    T: Copy + Zero + One + SaturatingAdd + From<i8>,
93{
94    /// Updates the decoder's state based on the given `a` and `b` pulse train (aka channel) readings,
95    /// returning the direction if a change was detected, `None` if no change was detected,
96    /// or `Err(_)` if an invalid input (i.e. a counteral "jump") was detected.
97    ///
98    /// Depending on whether it matters why the decoder did not detect a change
99    /// (e.g. due to actual lack of change or an erroneous read)
100    /// you would either call `decoder.update(a, b)` directly, or via `decoder.update(a, b).unwrap_or_default()`
101    /// to fall back to `None` in case of `Err(_)`.
102    pub fn update(&mut self, a: bool, b: bool) -> Result<Option<Change>, Error> {
103        let input = Input::new(a, b);
104
105        let validation_result = self.validator.validate(input);
106        let transducer_output = self.transducer.step(input);
107
108        match (validation_result, transducer_output) {
109            (Err(error), output) => {
110                debug_assert_eq!(output, Output::N, "Expected `None` output from transducer.");
111                Err(error)
112            }
113            (Ok(_), Output::N) => Ok(None),
114            (Ok(_), Output::AB) => {
115                let change = Change::Positive;
116                let delta: T = (change as i8).into();
117                self.counter = self.counter.saturating_add(&delta);
118                Ok(Some(change))
119            }
120            (Ok(_), Output::BA) => {
121                let change = Change::Negative;
122                let delta: T = (change as i8).into();
123                self.counter = self.counter.saturating_add(&delta);
124                Ok(Some(change))
125            }
126            (_, Output::E) => {
127                // Transducers are expected to not return error outputs since their states tend to
128                // be insufficient for reliable detection without false positives/negatives.
129                panic!("Unexpected error output from transducer.")
130            }
131        }
132    }
133
134    /// Resets the decoder to its initial state and its counter counter back to `0`.
135    pub fn reset(&mut self) {
136        self.transducer.reset();
137        self.validator.reset();
138        self.counter = Zero::zero();
139    }
140
141    /// Returns the decoder's counter counter relative to its initial counter in number of cycles.
142    ///
143    /// A change of `Change::Positive` increments the counter counter,
144    /// while a change of `Change::Negative` decrements it.
145    pub fn counter(&self) -> T {
146        self.counter
147    }
148
149    /// Sets the decoder's counter.
150    pub fn set_counter(&mut self, counter: T) {
151        self.counter = counter;
152    }
153}