Skip to main content

moteus_protocol/
resolution.rs

1// Copyright 2026 mjbots Robotic Systems, LLC.  info@mjbots.com
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Resolution types for register values.
16
17/// The resolution (data type) used when encoding or decoding a register value.
18///
19/// Each resolution type determines:
20/// - The number of bytes used on the wire
21/// - The scaling applied to convert between wire format and physical units
22/// - The range of representable values
23#[non_exhaustive]
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
25#[repr(i8)]
26pub enum Resolution {
27    /// 8-bit signed integer (1 byte)
28    Int8 = 0,
29    /// 16-bit signed integer (2 bytes)
30    Int16 = 1,
31    /// 32-bit signed integer (4 bytes)
32    Int32 = 2,
33    /// 32-bit IEEE 754 float (4 bytes)
34    Float = 3,
35    /// This register should be ignored/not transmitted
36    #[default]
37    Ignore = 4,
38}
39
40impl core::fmt::Display for Resolution {
41    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
42        match self {
43            Resolution::Int8 => write!(f, "int8"),
44            Resolution::Int16 => write!(f, "int16"),
45            Resolution::Int32 => write!(f, "int32"),
46            Resolution::Float => write!(f, "float"),
47            Resolution::Ignore => write!(f, "ignore"),
48        }
49    }
50}
51
52impl Resolution {
53    /// Returns the size in bytes for this resolution type.
54    ///
55    /// Returns 0 for `Ignore`.
56    #[inline]
57    pub const fn size(self) -> usize {
58        match self {
59            Resolution::Int8 => 1,
60            Resolution::Int16 => 2,
61            Resolution::Int32 => 4,
62            Resolution::Float => 4,
63            Resolution::Ignore => 0,
64        }
65    }
66
67    /// Returns the multiplex protocol type code for this resolution.
68    ///
69    /// This is used in the wire protocol to identify the data type.
70    #[inline]
71    pub const fn type_code(self) -> u8 {
72        match self {
73            Resolution::Int8 => 0x00,
74            Resolution::Int16 => 0x04,
75            Resolution::Int32 => 0x08,
76            Resolution::Float => 0x0c,
77            Resolution::Ignore => 0x00, // Should not be used
78        }
79    }
80
81    /// Creates a Resolution from a type code.
82    ///
83    /// Returns `None` if the type code is invalid.
84    #[inline]
85    pub const fn from_type_code(code: u8) -> Option<Resolution> {
86        match code & 0x0c {
87            0x00 => Some(Resolution::Int8),
88            0x04 => Some(Resolution::Int16),
89            0x08 => Some(Resolution::Int32),
90            0x0c => Some(Resolution::Float),
91            _ => None,
92        }
93    }
94
95    /// Returns the minimum representable value for integer types.
96    ///
97    /// This value is used to represent NaN in the protocol.
98    #[inline]
99    pub const fn nan_value(self) -> i32 {
100        match self {
101            Resolution::Int8 => -128,
102            Resolution::Int16 => -32768,
103            Resolution::Int32 => i32::MIN,
104            Resolution::Float => 0, // NaN represented differently
105            Resolution::Ignore => 0,
106        }
107    }
108
109    /// Returns the maximum representable value for integer types.
110    #[inline]
111    pub const fn max_value(self) -> i32 {
112        match self {
113            Resolution::Int8 => 127,
114            Resolution::Int16 => 32767,
115            Resolution::Int32 => i32::MAX,
116            Resolution::Float => 0, // Not applicable
117            Resolution::Ignore => 0,
118        }
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    #[test]
127    fn test_resolution_size() {
128        assert_eq!(Resolution::Int8.size(), 1);
129        assert_eq!(Resolution::Int16.size(), 2);
130        assert_eq!(Resolution::Int32.size(), 4);
131        assert_eq!(Resolution::Float.size(), 4);
132        assert_eq!(Resolution::Ignore.size(), 0);
133    }
134
135    #[test]
136    fn test_type_code_roundtrip() {
137        for res in [
138            Resolution::Int8,
139            Resolution::Int16,
140            Resolution::Int32,
141            Resolution::Float,
142        ] {
143            let code = res.type_code();
144            assert_eq!(Resolution::from_type_code(code), Some(res));
145        }
146    }
147}