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 LinkStatistics {
12 pub uplink_rssi_1: u8,
14 pub uplink_rssi_2: u8,
16 pub uplink_link_quality: u8,
18 pub uplink_snr: i8,
20 pub active_antenna: u8,
22 pub rf_mode: u8,
24 pub uplink_tx_power: u8,
26 pub downlink_rssi: u8,
28 pub downlink_link_quality: u8,
30 pub downlink_snr: i8,
32}
33
34impl LinkStatistics {
35 #[allow(clippy::too_many_arguments)]
36 pub fn new(
37 uplink_rssi_1: u8,
38 uplink_rssi_2: u8,
39 uplink_link_quality: u8,
40 uplink_snr: i8,
41 active_antenna: u8,
42 rf_mode: u8,
43 uplink_tx_power: u8,
44 downlink_rssi: u8,
45 downlink_link_quality: u8,
46 downlink_snr: i8,
47 ) -> Result<Self, CrsfParsingError> {
48 Ok(Self {
49 uplink_rssi_1,
50 uplink_rssi_2,
51 uplink_link_quality,
52 uplink_snr,
53 active_antenna,
54 rf_mode,
55 uplink_tx_power,
56 downlink_rssi,
57 downlink_link_quality,
58 downlink_snr,
59 })
60 }
61}
62
63impl CrsfPacket for LinkStatistics {
64 const PACKET_TYPE: PacketType = PacketType::LinkStatistics;
65 const MIN_PAYLOAD_SIZE: usize = (3 + 5) * size_of::<u8>() + 2 * size_of::<i8>();
68
69 fn to_bytes(&self, buffer: &mut [u8]) -> Result<usize, CrsfParsingError> {
70 self.validate_buffer_size(buffer)?;
71 buffer[0] = self.uplink_rssi_1;
72 buffer[1] = self.uplink_rssi_2;
73 buffer[2] = self.uplink_link_quality;
74 buffer[3] = self.uplink_snr as u8;
75 buffer[4] = self.active_antenna;
76 buffer[5] = self.rf_mode;
77 buffer[6] = self.uplink_tx_power;
78 buffer[7] = self.downlink_rssi;
79 buffer[8] = self.downlink_link_quality;
80 buffer[9] = self.downlink_snr as u8;
81 Ok(Self::MIN_PAYLOAD_SIZE)
82 }
83
84 fn from_bytes(data: &[u8]) -> Result<Self, CrsfParsingError> {
85 if data.len() == Self::MIN_PAYLOAD_SIZE {
86 Ok(Self {
87 uplink_rssi_1: data[0],
88 uplink_rssi_2: data[1],
89 uplink_link_quality: data[2],
90 uplink_snr: data[3] as i8,
91 active_antenna: data[4],
92 rf_mode: data[5],
93 uplink_tx_power: data[6],
94 downlink_rssi: data[7],
95 downlink_link_quality: data[8],
96 downlink_snr: data[9] as i8,
97 })
98 } else {
99 Err(CrsfParsingError::InvalidPayloadLength)
100 }
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn test_link_statistics_to_bytes() {
110 assert_eq!(LinkStatistics::MIN_PAYLOAD_SIZE, 10);
111 let link_statistics = LinkStatistics {
112 uplink_rssi_1: 100,
113 uplink_rssi_2: 75,
114 uplink_link_quality: 90,
115 uplink_snr: -10,
116 active_antenna: 1,
117 rf_mode: 2,
118 uplink_tx_power: 20,
119 downlink_rssi: 110,
120 downlink_link_quality: 80,
121 downlink_snr: -5,
122 };
123
124 let mut buffer = [0u8; LinkStatistics::MIN_PAYLOAD_SIZE];
125 let _ = link_statistics.to_bytes(&mut buffer);
126
127 let expected_bytes: [u8; LinkStatistics::MIN_PAYLOAD_SIZE] =
128 [100, 75, 90, 246, 1, 2, 20, 110, 80, 251];
129
130 assert_eq!(buffer, expected_bytes);
131 }
132
133 #[test]
134 fn test_link_statistics_from_bytes() {
135 let data: [u8; LinkStatistics::MIN_PAYLOAD_SIZE] =
136 [100, 75, 90, 246, 1, 2, 20, 110, 80, 251];
137
138 let link_statistics = LinkStatistics::from_bytes(&data).unwrap();
139
140 assert_eq!(
141 link_statistics,
142 LinkStatistics {
143 uplink_rssi_1: 100,
144 uplink_rssi_2: 75,
145 uplink_link_quality: 90,
146 uplink_snr: -10,
147 active_antenna: 1,
148 rf_mode: 2,
149 uplink_tx_power: 20,
150 downlink_rssi: 110,
151 downlink_link_quality: 80,
152 downlink_snr: -5,
153 }
154 );
155 }
156
157 #[test]
158 fn test_link_statistics_round_trip() {
159 let link_statistics = LinkStatistics {
160 uplink_rssi_1: 100,
161 uplink_rssi_2: 75,
162 uplink_link_quality: 90,
163 uplink_snr: -10,
164 active_antenna: 1,
165 rf_mode: 2,
166 uplink_tx_power: 20,
167 downlink_rssi: 110,
168 downlink_link_quality: 80,
169 downlink_snr: -5,
170 };
171
172 let mut buffer = [0u8; LinkStatistics::MIN_PAYLOAD_SIZE];
173 link_statistics.to_bytes(&mut buffer).unwrap();
174
175 let round_trip_link_statistics = LinkStatistics::from_bytes(&buffer).unwrap();
176
177 assert_eq!(link_statistics, round_trip_link_statistics);
178 }
179
180 #[test]
181 fn test_edge_cases() {
182 let link_statistics = LinkStatistics {
183 uplink_rssi_1: 255,
184 uplink_rssi_2: 100,
185 uplink_link_quality: 100,
186 uplink_snr: -128,
187 active_antenna: 3,
188 rf_mode: 4,
189 uplink_tx_power: 50,
190 downlink_rssi: 200,
191 downlink_link_quality: 90,
192 downlink_snr: 127,
193 };
194
195 let mut buffer = [0u8; LinkStatistics::MIN_PAYLOAD_SIZE];
196 link_statistics.to_bytes(&mut buffer).unwrap();
197 let round_trip_link_statistics = LinkStatistics::from_bytes(&buffer).unwrap();
198 assert_eq!(link_statistics, round_trip_link_statistics);
199 }
200}