uf_crsf/packets/
remote.rs1use crate::packets::{CrsfPacket, PacketType};
2use crate::CrsfParsingError;
3use core::mem::size_of;
4
5const TIMING_CORRECTION_SUB_TYPE: u8 = 0x10;
7const TIMING_CORRECTION_PAYLOAD_SIZE: usize = size_of::<u32>() + size_of::<i32>();
8
9#[derive(Clone, Debug, PartialEq)]
14#[cfg_attr(feature = "defmt", derive(defmt::Format))]
15pub struct Remote {
16 pub dst_addr: u8,
17 pub src_addr: u8,
18 pub payload: RemotePayload,
19}
20
21impl Remote {
22 pub fn new(
23 dst_addr: u8,
24 src_addr: u8,
25 payload: RemotePayload,
26 ) -> Result<Self, CrsfParsingError> {
27 Ok(Self {
28 dst_addr,
29 src_addr,
30 payload,
31 })
32 }
33}
34
35#[derive(Clone, Debug, PartialEq)]
37#[cfg_attr(feature = "defmt", derive(defmt::Format))]
38pub enum RemotePayload {
39 TimingCorrection(TimingCorrection),
40 }
42
43#[derive(Clone, Debug, PartialEq)]
47#[cfg_attr(feature = "defmt", derive(defmt::Format))]
48pub struct TimingCorrection {
49 pub update_interval: u32,
51 pub offset: i32,
54}
55
56impl CrsfPacket for Remote {
57 const PACKET_TYPE: PacketType = PacketType::RadioId;
58 const MIN_PAYLOAD_SIZE: usize = 2 + 1 + TIMING_CORRECTION_PAYLOAD_SIZE;
61
62 fn from_bytes(data: &[u8]) -> Result<Self, CrsfParsingError> {
63 if data.len() < 3 {
64 return Err(CrsfParsingError::InvalidPayloadLength);
65 }
66
67 let dst_addr = data[0];
68 let src_addr = data[1];
69 let sub_type = data[2];
70 let sub_payload = &data[3..];
71
72 let payload = match sub_type {
73 TIMING_CORRECTION_SUB_TYPE => {
74 if sub_payload.len() < TIMING_CORRECTION_PAYLOAD_SIZE {
75 return Err(CrsfParsingError::InvalidPayloadLength);
76 }
77 let timing_correction = TimingCorrection {
78 update_interval: u32::from_be_bytes(
79 sub_payload[0..size_of::<u32>()]
80 .try_into()
81 .expect("infallible due to length check"),
82 ),
83 offset: i32::from_be_bytes(
84 sub_payload[size_of::<u32>()..TIMING_CORRECTION_PAYLOAD_SIZE]
85 .try_into()
86 .expect("infallible due to length check"),
87 ),
88 };
89 RemotePayload::TimingCorrection(timing_correction)
90 }
91 _ => return Err(CrsfParsingError::InvalidPayload), };
93
94 Ok(Self {
95 dst_addr,
96 src_addr,
97 payload,
98 })
99 }
100
101 fn to_bytes(&self, buffer: &mut [u8]) -> Result<usize, CrsfParsingError> {
102 match &self.payload {
103 RemotePayload::TimingCorrection(p) => {
104 const LEN: usize = 2 + 1 + TIMING_CORRECTION_PAYLOAD_SIZE;
105 if buffer.len() < LEN {
106 return Err(CrsfParsingError::BufferOverflow);
107 }
108 buffer[0] = self.dst_addr;
109 buffer[1] = self.src_addr;
110 buffer[2] = TIMING_CORRECTION_SUB_TYPE;
111 buffer[3..7].copy_from_slice(&p.update_interval.to_be_bytes());
112 buffer[7..11].copy_from_slice(&p.offset.to_be_bytes());
113 Ok(LEN)
114 }
115 }
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn test_timing_correction_from_bytes() {
125 let data: [u8; 11] = [
127 0xEA, 0xEE, TIMING_CORRECTION_SUB_TYPE,
130 0x00,
131 0x00,
132 0xC3,
133 0x50, 0xFF,
135 0xFF,
136 0xFF,
137 0xF9, ];
139 let packet = Remote::from_bytes(&data).unwrap();
140 assert_eq!(packet.dst_addr, 0xEA);
141 assert_eq!(packet.src_addr, 0xEE);
142 match packet.payload {
143 RemotePayload::TimingCorrection(tc) => {
144 assert_eq!(tc.update_interval, 50000);
145 assert_eq!(tc.offset, -7);
146 }
147 }
148 }
149
150 #[test]
151 fn test_timing_correction_to_bytes() {
152 let packet = Remote {
153 dst_addr: 0xEA,
154 src_addr: 0xEE,
155 payload: RemotePayload::TimingCorrection(TimingCorrection {
156 update_interval: 50000,
157 offset: -7,
158 }),
159 };
160 let mut buffer = [0u8; 11];
161 let len = packet.to_bytes(&mut buffer).unwrap();
162 assert_eq!(len, 11);
163 let expected: [u8; 11] = [
164 0xEA,
165 0xEE,
166 TIMING_CORRECTION_SUB_TYPE,
167 0x00,
168 0x00,
169 0xC3,
170 0x50,
171 0xFF,
172 0xFF,
173 0xFF,
174 0xF9,
175 ];
176 assert_eq!(buffer, expected);
177 }
178
179 #[test]
180 fn test_remote_round_trip() {
181 let packet = Remote {
182 dst_addr: 0xC8,
183 src_addr: 0xEC,
184 payload: RemotePayload::TimingCorrection(TimingCorrection {
185 update_interval: 12345,
186 offset: -6789,
187 }),
188 };
189 let mut buffer = [0u8; 11];
190 packet.to_bytes(&mut buffer).unwrap();
191 let round_trip = Remote::from_bytes(&buffer).unwrap();
192 assert_eq!(packet, round_trip);
193 }
194
195 #[test]
196 fn test_from_bytes_invalid_len() {
197 let data: [u8; 2] = [0; 2];
198 let result = Remote::from_bytes(&data);
199 assert!(matches!(
200 result,
201 Err(CrsfParsingError::InvalidPayloadLength)
202 ));
203 }
204
205 #[test]
206 fn test_from_bytes_unknown_subtype() {
207 let data: [u8; 11] = [0xEA, 0xEE, 0x11, 0, 0, 0, 0, 0, 0, 0, 0];
208 let result = Remote::from_bytes(&data);
209 assert!(matches!(result, Err(CrsfParsingError::InvalidPayload)));
210 }
211}