1use crate::packets::CrsfPacket;
2use crate::packets::PacketType;
3use crate::CrsfParsingError;
4
5#[derive(Clone, Debug, PartialEq)]
10#[cfg_attr(feature = "defmt", derive(defmt::Format))]
11pub struct GpsTime {
12 pub year: i16,
13 pub month: u8,
14 pub day: u8,
15 pub hour: u8,
16 pub minute: u8,
17 pub second: u8,
18 pub millisecond: u16,
19}
20
21impl GpsTime {
22 pub fn new(
23 year: i16,
24 month: u8,
25 day: u8,
26 hour: u8,
27 minute: u8,
28 second: u8,
29 millisecond: u16,
30 ) -> Result<Self, CrsfParsingError> {
31 Ok(Self {
32 year,
33 month,
34 day,
35 hour,
36 minute,
37 second,
38 millisecond,
39 })
40 }
41}
42
43impl CrsfPacket for GpsTime {
44 const PACKET_TYPE: PacketType = PacketType::GpsTime;
45 const MIN_PAYLOAD_SIZE: usize = size_of::<i16>() + 5 * size_of::<u8>() + size_of::<u16>();
46
47 fn to_bytes(&self, buffer: &mut [u8]) -> Result<usize, CrsfParsingError> {
48 self.validate_buffer_size(buffer)?;
49 buffer[0..2].copy_from_slice(&self.year.to_be_bytes());
50 buffer[2] = self.month;
51 buffer[3] = self.day;
52 buffer[4] = self.hour;
53 buffer[5] = self.minute;
54 buffer[6] = self.second;
55 buffer[7..9].copy_from_slice(&self.millisecond.to_be_bytes());
56 Ok(Self::MIN_PAYLOAD_SIZE)
57 }
58
59 fn from_bytes(data: &[u8]) -> Result<Self, CrsfParsingError> {
60 if data.len() != Self::MIN_PAYLOAD_SIZE {
61 return Err(CrsfParsingError::InvalidPayloadLength);
62 }
63
64 Ok(Self {
65 year: i16::from_be_bytes(data[0..2].try_into().unwrap()),
66 month: data[2],
67 day: data[3],
68 hour: data[4],
69 minute: data[5],
70 second: data[6],
71 millisecond: u16::from_be_bytes(data[7..9].try_into().unwrap()),
72 })
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn test_gps_time_to_bytes() {
82 assert_eq!(GpsTime::MIN_PAYLOAD_SIZE, 9);
83 let gps_time = GpsTime {
84 year: 2024,
85 month: 10,
86 day: 27,
87 hour: 12,
88 minute: 34,
89 second: 56,
90 millisecond: 789,
91 };
92
93 let mut buffer = [0u8; GpsTime::MIN_PAYLOAD_SIZE];
94 gps_time.to_bytes(&mut buffer).unwrap();
95
96 let expected_bytes: [u8; GpsTime::MIN_PAYLOAD_SIZE] =
97 [0x07, 0xe8, 0x0a, 0x1b, 0x0c, 0x22, 0x38, 0x03, 0x15];
98
99 assert_eq!(buffer, expected_bytes);
100 }
101
102 #[test]
103 fn test_gps_time_from_bytes() {
104 let data: [u8; GpsTime::MIN_PAYLOAD_SIZE] =
105 [0x07, 0xe8, 0x0a, 0x1b, 0x0c, 0x22, 0x38, 0x03, 0x15];
106
107 let gps_time = GpsTime::from_bytes(&data).unwrap();
108
109 assert_eq!(
110 gps_time,
111 GpsTime {
112 year: 2024,
113 month: 10,
114 day: 27,
115 hour: 12,
116 minute: 34,
117 second: 56,
118 millisecond: 789,
119 }
120 );
121 }
122
123 #[test]
124 fn test_gps_time_round_trip() {
125 let gps_time = GpsTime {
126 year: 2024,
127 month: 10,
128 day: 27,
129 hour: 12,
130 minute: 34,
131 second: 56,
132 millisecond: 789,
133 };
134
135 let mut buffer = [0u8; GpsTime::MIN_PAYLOAD_SIZE];
136 gps_time.to_bytes(&mut buffer).unwrap();
137
138 let round_trip_gps_time = GpsTime::from_bytes(&buffer).unwrap();
139
140 assert_eq!(gps_time, round_trip_gps_time);
141 }
142
143 #[test]
144 fn test_edge_cases() {
145 let gps_time = GpsTime {
146 year: -1,
147 month: 1,
148 day: 1,
149 hour: 0,
150 minute: 0,
151 second: 0,
152 millisecond: 65535,
153 };
154
155 let mut buffer = [0u8; GpsTime::MIN_PAYLOAD_SIZE];
156 gps_time.to_bytes(&mut buffer).unwrap();
157 let round_trip_gps_time = GpsTime::from_bytes(&buffer).unwrap();
158 assert_eq!(gps_time, round_trip_gps_time);
159 }
160
161 #[test]
162 fn test_to_bytes_buffer_too_small() {
163 let gps_time = GpsTime {
164 year: -1,
165 month: 1,
166 day: 1,
167 hour: 0,
168 minute: 0,
169 second: 0,
170 millisecond: 65535,
171 };
172 let mut buffer: [u8; 8] = [0; 8];
173 let result = gps_time.to_bytes(&mut buffer);
174 assert!(matches!(result, Err(CrsfParsingError::BufferOverflow)));
175 }
176
177 #[test]
178 fn test_from_bytes_too_small() {
179 let data: [u8; 8] = [0; 8];
180 let result = GpsTime::from_bytes(&data);
181 assert_eq!(result, Err(CrsfParsingError::InvalidPayloadLength));
182 }
183}