quadrature_decoder/
lib.rs

1//! An efficient `no_std`-compatible implementation of a quadrature decoder,
2//! based on a finite-state-transducer with support for different step-modes.
3#![warn(missing_docs)]
4#![cfg_attr(not(test), no_std)]
5
6mod decoder;
7mod index_decoder;
8mod state_transducer;
9mod validator;
10
11pub use self::decoder::{IncrementalDecoder, IndexedIncrementalDecoder};
12
13use self::state_transducer::StateTransducer;
14
15mod sealed {
16    pub trait Sealed {}
17}
18
19/// An error indicating an invalid quadrature signal sequence.
20#[repr(u8)]
21#[derive(Clone, Copy, PartialEq, Eq, Debug)]
22#[allow(non_camel_case_types)]
23pub enum Error {
24    /// Invalid gray-code sequence [00, 11].
25    E00_11 = 0b_00_11,
26    /// Invalid gray-code sequence [11, 00].
27    E11_00 = 0b_11_00,
28    /// Invalid gray-code sequence [01, 10].
29    E01_10 = 0b_01_10,
30    /// Invalid gray-code sequence [10, 01].
31    E10_01 = 0b_10_01,
32}
33
34/// The change detected by a quadrature decoder.
35#[repr(i8)]
36#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
37pub enum Change {
38    /// Channel A leads channel B, commonly describing a forwards change.
39    Positive = 1,
40    /// Channel B leads channel A, commonly describing a backwards change.
41    Negative = -1,
42}
43
44/// A quadrature-based decoder's step mode.
45pub trait StepMode: sealed::Sealed {
46    /// The step-mode's number of pulses per (quadrature) cycle (PPC).
47    const PULSES_PER_CYCLE: usize;
48}
49
50/// A step mode that is able to detect a "change" (e.g. movement)
51/// for every stable full cycle (i.e. 1 change per quadrature cycle).
52///
53/// Full-step mode provides:
54/// - high noise-resistance (factor 4× relative to naïve decoding)
55/// - low resolution (factor 1× relative to native resolution)
56pub struct FullStep;
57
58impl sealed::Sealed for FullStep {}
59impl StepMode for FullStep {
60    /// The number of pulses per (quadrature) cycle (PPC).
61    ///
62    /// As an example, consider the effective pulses per revolution (PPR)
63    /// of a rotary encoder with 100 cycles per revolution (CPR): 100 PPR.
64    const PULSES_PER_CYCLE: usize = 1;
65}
66
67/// A step mode that is able to detect a "change" (e.g. movement) (e.g. movement)
68/// for every stable half cycle (i.e. 2 changes per quadrature cycle),
69/// resulting in an effective 2× resolution multiplication.
70///
71/// Half-step mode effectively doubles the resolution of the decoder.
72///
73/// Half-step mode provides:
74/// - medium noise-resistance (factor 2× relative to naïve decoding)
75/// - medium resolution (factor 1× relative to native resolution)
76pub struct HalfStep;
77
78impl sealed::Sealed for HalfStep {}
79impl StepMode for HalfStep {
80    /// The number of pulses per (quadrature) cycle (PPC).
81    ///
82    /// As an example, consider the effective pulses per revolution (PPR)
83    /// of a rotary encoder with 100 cycles per revolution (CPR): 200 PPR.
84    const PULSES_PER_CYCLE: usize = 2;
85}
86
87/// A step mode that is able to detect a "change" (e.g. movement)
88/// for every stable quarter cycle (i.e. 4 change per quadrature cycle),
89/// resulting in an effective 4× resolution multiplication.
90///
91/// Quad-step mode effectively quadruples the resolution of the decoder.
92///
93/// Quad-step mode provides:
94/// - low noise-resistance (factor 1× relative to naïve decoding)
95/// - high resolution (factor 1× relative to native resolution)
96pub struct QuadStep;
97
98impl sealed::Sealed for QuadStep {}
99impl StepMode for QuadStep {
100    /// The number of pulses per (quadrature) cycle (PPC).
101    ///
102    /// As an example, consider the effective pulses per revolution (PPR)
103    /// of a rotary encoder with 100 cycles per revolution (CPR): 400 PPR.
104    const PULSES_PER_CYCLE: usize = 4;
105}