quadrature_decoder/decoder/indexed.rs
1//! Quadrature-based decoder.
2
3use num_traits::{One, SaturatingAdd, Zero};
4
5use crate::{index_decoder::IndexDecoder, Change, Error, IncrementalDecoder, StepMode};
6
7/// A robust indexed quadrature decoder with support for multiple step-modes,
8/// based on which channel (A vs. B) is leading the other.
9///
10/// ```plain
11/// ┌ ─ ┐ ┌───┐ ┌───┐ ┌───┐ ┌ ─ ─ high
12/// A │ │ │ │ │
13/// ─ ┘ └───┘ └───┘ └───┘ └ ─ ┘ low
14/// AB:
15/// ┌ ─ ┐ ┌───┐ ┌───┐ ┌───┐ ┌ ─ high
16/// B │ │ │ │ │
17/// ─ ─ ┘ └───┘ └───┘ └───┘ └ ─ ┘ low
18///
19/// ┌─┐ high
20/// Z │ │
21/// ─ ─ ─ ──────┘ └────────────────── ─ ─ ─ low
22/// Time: ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─▶
23/// ┌ ─ ┐ ┌───┐ ┌───┐ ┌───┐ ┌ ─ high
24/// A │ │ │ │ │
25/// ─ ─ ┘ └───┘ └───┘ └───┘ └ ─ ┘ low
26/// BA:
27/// ┌ ─ ┐ ┌───┐ ┌───┐ ┌───┐ ┌ ─ ─ high
28/// B │ │ │ │ │
29/// ─ ┘ └───┘ └───┘ └───┘ └ ─ ┘ low
30///
31/// ┌─┐ high
32/// Z │ │
33/// ─ ─ ─ ──────┘ └────────────────── ─ ─ ─ low
34/// ```
35#[derive(Debug)]
36pub struct IndexedIncrementalDecoder<Mode, T = i32> {
37 decoder: IncrementalDecoder<Mode, T>,
38 indexer: IndexDecoder,
39}
40
41impl<Mode, T> Default for IndexedIncrementalDecoder<Mode, T>
42where
43 Mode: StepMode,
44 IncrementalDecoder<Mode, T>: Default,
45{
46 fn default() -> Self {
47 Self::new(IncrementalDecoder::default())
48 }
49}
50
51impl<Mode, T> IndexedIncrementalDecoder<Mode, T>
52where
53 Mode: StepMode,
54{
55 pub(crate) fn new(decoder: IncrementalDecoder<Mode, T>) -> Self {
56 Self {
57 decoder,
58 indexer: Default::default(),
59 }
60 }
61}
62
63impl<Mode, T> IndexedIncrementalDecoder<Mode, T>
64where
65 Mode: StepMode,
66 T: Copy + Zero + One + SaturatingAdd + From<i8>,
67{
68 /// Updates the decoder's state based on the given `a` and `b` pulse train (aka channel) readings,
69 /// returning the direction if a change was detected, `None` if no change was detected,
70 /// or `Err(_)` if an invalid input (i.e. a counteral "jump") was detected.
71 ///
72 /// Upon detection of a raising edge on the `z` pulse train the counter gets reset back to `0`.
73 ///
74 /// Depending on whether it matters why the decoder did not detect a change
75 /// (e.g. due to actual lack of change or an erroneous read)
76 /// you would either call `decoder.update(a, b)` directly, or via `decoder.update(a, b).unwrap_or_default()`
77 /// to fall back to `None` in case of `Err(_)`.
78 pub fn update(&mut self, a: bool, b: bool, z: bool) -> Result<Option<Change>, Error> {
79 let result = self.decoder.update(a, b);
80
81 if self.indexer.update(z) {
82 self.decoder.set_counter(Zero::zero());
83 }
84
85 result
86 }
87
88 /// Resets the decoder to its initial state and its counter counter back to `0`.
89 pub fn reset(&mut self) {
90 self.decoder.reset();
91 self.indexer.reset();
92 }
93
94 /// Returns the decoder's counter counter relative to its initial counter in number of cycles.
95 ///
96 /// A change of `Change::Positive` increments the counter counter,
97 /// while a change of `Change::Negative` decrements it.
98 pub fn counter(&self) -> T {
99 self.decoder.counter()
100 }
101
102 /// Sets the decoder's counter.
103 pub fn set_counter(&mut self, counter: T) {
104 self.decoder.set_counter(counter);
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use crate::HalfStep;
111
112 use super::*;
113
114 #[test]
115 fn index() {
116 let a: Vec<bool> = vec![false, false, true, true, false, false, true, true];
117 let b: Vec<bool> = vec![true, false, false, true, true, false, false, true];
118 let z: Vec<bool> = vec![false, false, false, false, true, false, false, false];
119
120 let pulse_trains = a.into_iter().zip(b).zip(z);
121
122 let changes: Vec<Option<Change>> = vec![
123 None,
124 Some(Change::Positive),
125 None,
126 Some(Change::Positive),
127 None,
128 Some(Change::Positive),
129 None,
130 Some(Change::Positive),
131 ];
132 let counters: Vec<i32> = vec![0, 1, 1, 2, 0, 1, 1, 2];
133
134 let expected = changes.into_iter().zip(counters);
135
136 let mut decoder: IndexedIncrementalDecoder<HalfStep> = Default::default();
137
138 for (input, expected) in pulse_trains.zip(expected) {
139 let ((a, b), z) = input;
140 let (expected_change, expected_counter) = expected;
141
142 let change = decoder.update(a, b, z).unwrap();
143
144 assert_eq!(change, expected_change);
145 assert_eq!(decoder.counter(), expected_counter);
146 }
147 }
148}