1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2#[allow(unused_qualifications)]
6pub struct FeatureSet {
7 firmware: crate::headers::InternalFirmware,
8 raw: ::bitvec::array::BitArray<u32, ::bitvec::order::Lsb0>,
9}
10#[allow(unused_qualifications, clippy::cast_possible_truncation)]
11impl FeatureSet {
12 pub(crate) fn new(raw: u32, firmware: crate::headers::InternalFirmware) -> Self {
13 Self {
14 firmware,
15 raw: ::bitvec::array::BitArray::new(raw),
16 }
17 }
18
19 fn iter(&self) -> impl Iterator<Item = <Self as crate::units::FlagSet>::Flag> + '_ {
20 self.raw
21 .iter_ones()
22 .filter_map(|bit| <Feature>::from_bit(bit as u32, self.firmware))
23 }
24}
25#[allow(unused_qualifications, clippy::cast_possible_truncation)]
26impl crate::units::FlagSet for FeatureSet {
27 type Flag = Feature;
28
29 fn is_set(&self, flag: Self::Flag) -> bool {
30 flag.to_bit(self.firmware)
31 .map_or(false, |bit| self.raw[bit as usize])
32 }
33
34 fn as_names(&self) -> ::alloc::vec::Vec<&'static str> {
35 self.iter()
36 .map(|flag| <Feature as crate::units::Flag>::as_name(&flag))
37 .collect()
38 }
39}
40#[allow(unused_qualifications)]
41impl ::core::fmt::Display for FeatureSet {
42 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
43 let names = <Self as crate::units::FlagSet>::as_names(self);
44 f.write_str(&names.join("|"))
45 }
46}
47#[cfg(feature = "_serde")]
48#[allow(clippy::cast_possible_truncation)]
49impl ::serde::Serialize for FeatureSet {
50 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
51 where
52 S: serde::Serializer,
53 {
54 use serde::ser::SerializeSeq;
55 let mut seq = serializer.serialize_seq(None)?;
56 for flag in self.iter() {
57 seq.serialize_element(&flag)?;
58 }
59 seq.end()
60 }
61}
62#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
63#[cfg_attr(feature = "_serde", derive(serde::Serialize))]
64#[non_exhaustive]
68pub enum Feature {
69 AirMode,
71 AntiGravity,
73 BatProfileAutoswitch,
75 Blackbox,
77 ChannelForwarding,
79 CurrentMeter,
81 Dashboard,
83 DynamicFilter,
85 EscSensor,
87 FwAutotrim,
89 FwLaunch,
91 Geozone,
93 Gps,
95 InflightAccCal,
97 LedStrip,
99 MotorStop,
101 Osd,
103 PwmOutputEnable,
105 RangeFinder,
107 ReversibleMotors,
109 RssiAdc,
111 RxMsp,
113 RxParallelPwm,
115 RxPpm,
117 RxSerial,
119 RxSpi,
121 ServoTilt,
123 SoftSerial,
125 SuperexpoRates,
127 Telemetry,
129 ThreeD,
131 ThrottleVbatCompensation,
133 Transponder,
135 TxProfileSelection,
137 Vbat,
139 Vtx,
141}
142#[allow(unused_qualifications)]
143impl crate::units::Flag for Feature {
144 fn as_name(&self) -> &'static str {
145 match self {
146 Self::AirMode => "AIRMODE",
147 Self::AntiGravity => "ANTI_GRAVITY",
148 Self::BatProfileAutoswitch => "BAT_PROFILE_AUTOSWITCH",
149 Self::Blackbox => "BLACKBOX",
150 Self::ChannelForwarding => "CHANNEL_FORWARDING",
151 Self::CurrentMeter => "CURRENT_METER",
152 Self::Dashboard => "DASHBOARD",
153 Self::DynamicFilter => "DYNAMIC_FILTER",
154 Self::EscSensor => "ESC_SENSOR",
155 Self::FwAutotrim => "FW_AUTOTRIM",
156 Self::FwLaunch => "FW_LAUNCH",
157 Self::Geozone => "GEOZONE",
158 Self::Gps => "GPS",
159 Self::InflightAccCal => "INFLIGHT_ACC_CAL",
160 Self::LedStrip => "LED_STRIP",
161 Self::MotorStop => "MOTOR_STOP",
162 Self::Osd => "OSD",
163 Self::PwmOutputEnable => "PWM_OUTPUT_ENABLE",
164 Self::RangeFinder => "RANGEFINDER",
165 Self::ReversibleMotors => "REVERSIBLE_MOTORS",
166 Self::RssiAdc => "RSSI_ADC",
167 Self::RxMsp => "RX_MSP",
168 Self::RxParallelPwm => "RX_PARALLEL_PWM",
169 Self::RxPpm => "RX_PPM",
170 Self::RxSerial => "RX_SERIAL",
171 Self::RxSpi => "RX_SPI",
172 Self::ServoTilt => "SERVO_TILT",
173 Self::SoftSerial => "SOFTSERIAL",
174 Self::SuperexpoRates => "SUPEREXPO_RATES",
175 Self::Telemetry => "TELEMETRY",
176 Self::ThreeD => "3D",
177 Self::ThrottleVbatCompensation => "THR_VBAT_COMP",
178 Self::Transponder => "TRANSPONDER",
179 Self::TxProfileSelection => "TX_PROF_SEL",
180 Self::Vbat => "VBAT",
181 Self::Vtx => "VTX",
182 }
183 }
184}
185#[allow(unused_qualifications)]
186impl ::core::fmt::Display for Feature {
187 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
188 let s = <Self as crate::units::Flag>::as_name(self);
189 f.write_str(s)
190 }
191}
192#[allow(
193 unused_qualifications,
194 clippy::enum_glob_use,
195 clippy::match_same_arms,
196 clippy::unseparated_literal_suffix
197)]
198impl Feature {
199 const fn from_bit(bit: u32, fw: crate::headers::InternalFirmware) -> Option<Self> {
200 use crate::headers::InternalFirmware::*;
201 match (bit, fw) {
202 (0u32, Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5) => {
203 Some(Self::RxPpm)
204 }
205 (0u32, Inav5 | Inav6 | Inav7 | Inav8) => Some(Self::ThrottleVbatCompensation),
206 (1u32, Inav5 | Inav6 | Inav7 | Inav8) => Some(Self::Vbat),
207 (2u32, Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5) => {
208 Some(Self::InflightAccCal)
209 }
210 (2u32, Inav5 | Inav6 | Inav7 | Inav8) => Some(Self::TxProfileSelection),
211 (3u32, Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5) => {
212 Some(Self::RxSerial)
213 }
214 (3u32, Inav5 | Inav6 | Inav7 | Inav8) => Some(Self::BatProfileAutoswitch),
215 (
216 4u32,
217 Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5 | Inav5 | Inav6,
218 ) => Some(Self::MotorStop),
219 (4u32, Inav8) => Some(Self::Geozone),
220 (5u32, Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5) => {
221 Some(Self::ServoTilt)
222 }
223 (
224 6u32,
225 Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5 | Inav5 | Inav6
226 | Inav7 | Inav8,
227 ) => Some(Self::SoftSerial),
228 (
229 7u32,
230 Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5 | Inav5 | Inav6
231 | Inav7 | Inav8,
232 ) => Some(Self::Gps),
233 (9u32, Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5) => {
234 Some(Self::RangeFinder)
235 }
236 (
237 10u32,
238 Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5 | Inav5 | Inav6
239 | Inav7 | Inav8,
240 ) => Some(Self::Telemetry),
241 (11u32, Inav5 | Inav6 | Inav7 | Inav8) => Some(Self::CurrentMeter),
242 (12u32, Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5) => {
243 Some(Self::ThreeD)
244 }
245 (12u32, Inav5 | Inav6 | Inav7 | Inav8) => Some(Self::ReversibleMotors),
246 (13u32, Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5) => {
247 Some(Self::RxParallelPwm)
248 }
249 (14u32, Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5) => {
250 Some(Self::RxMsp)
251 }
252 (
253 15u32,
254 Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5 | Inav5 | Inav6
255 | Inav7 | Inav8,
256 ) => Some(Self::RssiAdc),
257 (
258 16u32,
259 Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5 | Inav5 | Inav6
260 | Inav7 | Inav8,
261 ) => Some(Self::LedStrip),
262 (
263 17u32,
264 Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5 | Inav5 | Inav6
265 | Inav7 | Inav8,
266 ) => Some(Self::Dashboard),
267 (18u32, Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5) => {
268 Some(Self::Osd)
269 }
270 (19u32, Inav5 | Inav6 | Inav7 | Inav8) => Some(Self::Blackbox),
271 (20u32, Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5) => {
272 Some(Self::ChannelForwarding)
273 }
274 (
275 21u32,
276 Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5 | Inav5 | Inav6
277 | Inav7 | Inav8,
278 ) => Some(Self::Transponder),
279 (
280 22u32,
281 Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5 | Inav5 | Inav6
282 | Inav7 | Inav8,
283 ) => Some(Self::AirMode),
284 (23u32, Inav5 | Inav6 | Inav7 | Inav8) => Some(Self::SuperexpoRates),
285 (24u32, Inav5 | Inav6 | Inav7 | Inav8) => Some(Self::Vtx),
286 (25u32, Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5) => {
287 Some(Self::RxSpi)
288 }
289 (27u32, Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5) => {
290 Some(Self::EscSensor)
291 }
292 (28u32, Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5) => {
293 Some(Self::AntiGravity)
294 }
295 (28u32, Inav5 | Inav6 | Inav7 | Inav8) => Some(Self::PwmOutputEnable),
296 (29u32, Betaflight4_2) => Some(Self::DynamicFilter),
297 (29u32, Inav5 | Inav6 | Inav7 | Inav8) => Some(Self::Osd),
298 (30u32, Inav5 | Inav6 | Inav7 | Inav8) => Some(Self::FwLaunch),
299 (31u32, Inav5 | Inav6 | Inav7 | Inav8) => Some(Self::FwAutotrim),
300 _ => None,
301 }
302 }
303
304 const fn to_bit(self, fw: crate::headers::InternalFirmware) -> Option<u32> {
305 use crate::headers::InternalFirmware::*;
306 match (self, fw) {
307 (Self::RxPpm, Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5) => {
308 Some(0u32)
309 }
310 (Self::ThrottleVbatCompensation, Inav5 | Inav6 | Inav7 | Inav8) => Some(0u32),
311 (Self::Vbat, Inav5 | Inav6 | Inav7 | Inav8) => Some(1u32),
312 (
313 Self::InflightAccCal,
314 Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5,
315 ) => Some(2u32),
316 (Self::TxProfileSelection, Inav5 | Inav6 | Inav7 | Inav8) => Some(2u32),
317 (Self::RxSerial, Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5) => {
318 Some(3u32)
319 }
320 (Self::BatProfileAutoswitch, Inav5 | Inav6 | Inav7 | Inav8) => Some(3u32),
321 (
322 Self::MotorStop,
323 Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5 | Inav5 | Inav6,
324 ) => Some(4u32),
325 (Self::Geozone, Inav8) => Some(4u32),
326 (Self::ServoTilt, Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5) => {
327 Some(5u32)
328 }
329 (
330 Self::SoftSerial,
331 Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5 | Inav5 | Inav6
332 | Inav7 | Inav8,
333 ) => Some(6u32),
334 (
335 Self::Gps,
336 Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5 | Inav5 | Inav6
337 | Inav7 | Inav8,
338 ) => Some(7u32),
339 (Self::RangeFinder, Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5) => {
340 Some(9u32)
341 }
342 (
343 Self::Telemetry,
344 Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5 | Inav5 | Inav6
345 | Inav7 | Inav8,
346 ) => Some(10u32),
347 (Self::CurrentMeter, Inav5 | Inav6 | Inav7 | Inav8) => Some(11u32),
348 (Self::ThreeD, Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5) => {
349 Some(12u32)
350 }
351 (Self::ReversibleMotors, Inav5 | Inav6 | Inav7 | Inav8) => Some(12u32),
352 (
353 Self::RxParallelPwm,
354 Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5,
355 ) => Some(13u32),
356 (Self::RxMsp, Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5) => {
357 Some(14u32)
358 }
359 (
360 Self::RssiAdc,
361 Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5 | Inav5 | Inav6
362 | Inav7 | Inav8,
363 ) => Some(15u32),
364 (
365 Self::LedStrip,
366 Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5 | Inav5 | Inav6
367 | Inav7 | Inav8,
368 ) => Some(16u32),
369 (
370 Self::Dashboard,
371 Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5 | Inav5 | Inav6
372 | Inav7 | Inav8,
373 ) => Some(17u32),
374 (Self::Osd, Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5) => {
375 Some(18u32)
376 }
377 (Self::Blackbox, Inav5 | Inav6 | Inav7 | Inav8) => Some(19u32),
378 (
379 Self::ChannelForwarding,
380 Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5,
381 ) => Some(20u32),
382 (
383 Self::Transponder,
384 Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5 | Inav5 | Inav6
385 | Inav7 | Inav8,
386 ) => Some(21u32),
387 (
388 Self::AirMode,
389 Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5 | Inav5 | Inav6
390 | Inav7 | Inav8,
391 ) => Some(22u32),
392 (Self::SuperexpoRates, Inav5 | Inav6 | Inav7 | Inav8) => Some(23u32),
393 (Self::Vtx, Inav5 | Inav6 | Inav7 | Inav8) => Some(24u32),
394 (Self::RxSpi, Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5) => {
395 Some(25u32)
396 }
397 (Self::EscSensor, Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5) => {
398 Some(27u32)
399 }
400 (Self::AntiGravity, Betaflight4_2 | Betaflight4_3 | Betaflight4_4 | Betaflight4_5) => {
401 Some(28u32)
402 }
403 (Self::PwmOutputEnable, Inav5 | Inav6 | Inav7 | Inav8) => Some(28u32),
404 (Self::DynamicFilter, Betaflight4_2) => Some(29u32),
405 (Self::Osd, Inav5 | Inav6 | Inav7 | Inav8) => Some(29u32),
406 (Self::FwLaunch, Inav5 | Inav6 | Inav7 | Inav8) => Some(30u32),
407 (Self::FwAutotrim, Inav5 | Inav6 | Inav7 | Inav8) => Some(31u32),
408 _ => None,
409 }
410 }
411}