Skip to main content

statime_wire/common/
clock_quality.rs

1use super::clock_accuracy::ClockAccuracy;
2use crate::Error;
3
4/// A description of the accuracy and type of a clock.
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
6#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
7pub struct ClockQuality {
8    /// The PTP clock class.
9    ///
10    /// Per the standard, 248 is the default, and a good option for most use
11    /// cases. For grandmaster clocks, this should be below 128 to ensure the
12    /// clock never takes time from another source. A value of 6 is a good
13    /// option for a node with an external time source.
14    ///
15    /// For other potential values, see *IEEE1588-2019 section 7.6.2.5*.
16    pub clock_class: u8,
17
18    /// The accuracy of the clock
19    pub clock_accuracy: ClockAccuracy,
20
21    /// 2-log of the variance (in seconds^2) of the clock when not synchronized.
22    ///
23    /// See *IEEE1588-2019 section 7.6.3.5* for more details.
24    pub offset_scaled_log_variance: u16,
25}
26
27impl Default for ClockQuality {
28    fn default() -> Self {
29        Self {
30            clock_class: 248,
31            clock_accuracy: ClockAccuracy::default(),
32            // See 7.6.3.3 for the description of the calculation procedure.
33            // We estimate clock variance of desktop to be no worse than
34            // 2^-23 seconds^2, based on experience from ntpd-rs
35            offset_scaled_log_variance: 0x8000 - (23 * 256),
36        }
37    }
38}
39
40impl ClockQuality {
41    /// Serialize a clock quality to a buffer of 4 bytes.
42    ///
43    /// # Errors
44    /// Fails when the provided buffer is too short.
45    pub fn serialize(self, buffer: &mut [u8]) -> Result<(), Error> {
46        *buffer.get_mut(0).ok_or(Error::BufferTooShort)? = self.clock_class;
47        *buffer.get_mut(1).ok_or(Error::BufferTooShort)? = self.clock_accuracy.to_primitive();
48        buffer
49            .get_mut(2..4)
50            .ok_or(Error::BufferTooShort)?
51            .copy_from_slice(&self.offset_scaled_log_variance.to_be_bytes());
52        Ok(())
53    }
54
55    /// Deserialize a clock quality from a buffer of 4 bytes.
56    ///
57    /// # Errors
58    /// Fails when the provided buffer is too short.
59    #[expect(
60        clippy::get_first,
61        reason = "Prefer uniform way of accessing the elements of the buffer in this deserializer"
62    )]
63    #[allow(
64        clippy::missing_panics_doc,
65        reason = "Clippy can't tell the try_into will never fail"
66    )]
67    pub fn deserialize(buffer: &[u8]) -> Result<Self, Error> {
68        Ok(Self {
69            clock_class: *buffer.get(0).ok_or(Error::BufferTooShort)?,
70            clock_accuracy: ClockAccuracy::from_primitive(
71                *buffer.get(1).ok_or(Error::BufferTooShort)?,
72            ),
73            offset_scaled_log_variance: u16::from_be_bytes(
74                buffer
75                    .get(2..4)
76                    .ok_or(Error::BufferTooShort)?
77                    .try_into()
78                    .unwrap(),
79            ),
80        })
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn timestamp_wireformat() {
90        let representations = [(
91            [0x7a, 0x2a, 0x12, 0x34u8],
92            ClockQuality {
93                clock_class: 122,
94                clock_accuracy: ClockAccuracy::MS2_5,
95                offset_scaled_log_variance: 0x1234,
96            },
97        )];
98
99        for (byte_representation, object_representation) in representations {
100            // Test the serialization output
101            let mut serialization_buffer = [0; 4];
102            object_representation
103                .serialize(&mut serialization_buffer)
104                .unwrap();
105            assert_eq!(serialization_buffer, byte_representation);
106
107            // Test the deserialization output
108            let deserialized_data = ClockQuality::deserialize(&byte_representation).unwrap();
109            assert_eq!(deserialized_data, object_representation);
110        }
111    }
112}