1use std::fmt;
7
8use crate::error::ValidationError;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
23pub struct SquelchLevel(u8);
24
25impl SquelchLevel {
26 pub const OPEN: Self = Self(0);
28 pub const MAX: Self = Self(6);
30 pub const COUNT: u8 = 7;
32
33 pub const fn new(value: u8) -> Result<Self, ValidationError> {
39 if value > 6 {
40 Err(ValidationError::SettingOutOfRange {
41 name: "squelch level",
42 value,
43 detail: "must be 0-6",
44 })
45 } else {
46 Ok(Self(value))
47 }
48 }
49
50 #[must_use]
52 pub const fn as_u8(self) -> u8 {
53 self.0
54 }
55}
56
57impl TryFrom<u8> for SquelchLevel {
58 type Error = ValidationError;
59
60 fn try_from(value: u8) -> Result<Self, Self::Error> {
61 Self::new(value)
62 }
63}
64
65impl From<SquelchLevel> for u8 {
66 fn from(level: SquelchLevel) -> Self {
67 level.0
68 }
69}
70
71impl fmt::Display for SquelchLevel {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 write!(f, "SQ{}", self.0)
74 }
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
91pub struct AfGainLevel(u8);
92
93impl AfGainLevel {
94 #[must_use]
99 pub const fn new(value: u8) -> Self {
100 Self(value)
101 }
102
103 #[must_use]
105 pub const fn as_u8(self) -> u8 {
106 self.0
107 }
108}
109
110impl From<u8> for AfGainLevel {
111 fn from(value: u8) -> Self {
112 Self::new(value)
113 }
114}
115
116impl From<AfGainLevel> for u8 {
117 fn from(level: AfGainLevel) -> Self {
118 level.0
119 }
120}
121
122impl fmt::Display for AfGainLevel {
123 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124 write!(f, "{}", self.0)
125 }
126}
127
128#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
137pub struct SMeterReading(u8);
138
139impl SMeterReading {
140 pub const COUNT: u8 = 6;
142
143 pub const fn new(value: u8) -> Result<Self, ValidationError> {
149 if value > 5 {
150 Err(ValidationError::SettingOutOfRange {
151 name: "S-meter reading",
152 value,
153 detail: "must be 0-5",
154 })
155 } else {
156 Ok(Self(value))
157 }
158 }
159
160 #[must_use]
162 pub const fn as_u8(self) -> u8 {
163 self.0
164 }
165
166 #[must_use]
168 pub const fn s_unit(&self) -> &'static str {
169 match self.0 {
170 0 => "S0",
171 1 => "S1",
172 2 => "S3",
173 3 => "S5",
174 4 => "S7",
175 5 => "S9",
176 _ => "S?",
177 }
178 }
179}
180
181impl TryFrom<u8> for SMeterReading {
182 type Error = ValidationError;
183
184 fn try_from(value: u8) -> Result<Self, Self::Error> {
185 Self::new(value)
186 }
187}
188
189impl From<SMeterReading> for u8 {
190 fn from(reading: SMeterReading) -> Self {
191 reading.0
192 }
193}
194
195impl fmt::Display for SMeterReading {
196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197 f.write_str(self.s_unit())
198 }
199}
200
201#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
224pub enum VfoMemoryMode {
225 Vfo = 0,
227 Memory = 1,
229 Call = 2,
231 Weather = 3,
233}
234
235impl VfoMemoryMode {
236 pub const COUNT: u8 = 4;
238}
239
240impl fmt::Display for VfoMemoryMode {
241 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242 match self {
243 Self::Vfo => f.write_str("VFO"),
244 Self::Memory => f.write_str("Memory"),
245 Self::Call => f.write_str("Call"),
246 Self::Weather => f.write_str("Weather"),
247 }
248 }
249}
250
251impl TryFrom<u8> for VfoMemoryMode {
252 type Error = ValidationError;
253
254 fn try_from(value: u8) -> Result<Self, Self::Error> {
255 match value {
256 0 => Ok(Self::Vfo),
257 1 => Ok(Self::Memory),
258 2 => Ok(Self::Call),
259 3 => Ok(Self::Weather),
260 _ => Err(ValidationError::SettingOutOfRange {
261 name: "VFO/memory mode",
262 value,
263 detail: "must be 0-3",
264 }),
265 }
266 }
267}
268
269impl From<VfoMemoryMode> for u8 {
270 fn from(mode: VfoMemoryMode) -> Self {
271 mode as Self
272 }
273}
274
275#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
284pub enum FilterMode {
285 Ssb = 0,
287 Cw = 1,
289 Am = 2,
291}
292
293impl FilterMode {
294 pub const COUNT: u8 = 3;
296}
297
298impl fmt::Display for FilterMode {
299 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300 match self {
301 Self::Ssb => f.write_str("SSB"),
302 Self::Cw => f.write_str("CW"),
303 Self::Am => f.write_str("AM"),
304 }
305 }
306}
307
308impl TryFrom<u8> for FilterMode {
309 type Error = ValidationError;
310
311 fn try_from(value: u8) -> Result<Self, Self::Error> {
312 match value {
313 0 => Ok(Self::Ssb),
314 1 => Ok(Self::Cw),
315 2 => Ok(Self::Am),
316 _ => Err(ValidationError::SettingOutOfRange {
317 name: "filter mode",
318 value,
319 detail: "must be 0-2",
320 }),
321 }
322 }
323}
324
325impl From<FilterMode> for u8 {
326 fn from(mode: FilterMode) -> Self {
327 mode as Self
328 }
329}
330
331#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
353pub enum BatteryLevel {
354 Empty = 0,
356 OneThird = 1,
358 TwoThirds = 2,
360 Full = 3,
362 Charging = 4,
364}
365
366impl BatteryLevel {
367 pub const COUNT: u8 = 5;
369}
370
371impl fmt::Display for BatteryLevel {
372 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
373 match self {
374 Self::Empty => f.write_str("Empty"),
375 Self::OneThird => f.write_str("1/3"),
376 Self::TwoThirds => f.write_str("2/3"),
377 Self::Full => f.write_str("Full"),
378 Self::Charging => f.write_str("Charging"),
379 }
380 }
381}
382
383impl TryFrom<u8> for BatteryLevel {
384 type Error = ValidationError;
385
386 fn try_from(value: u8) -> Result<Self, Self::Error> {
387 match value {
388 0 => Ok(Self::Empty),
389 1 => Ok(Self::OneThird),
390 2 => Ok(Self::TwoThirds),
391 3 => Ok(Self::Full),
392 4 => Ok(Self::Charging),
393 _ => Err(ValidationError::SettingOutOfRange {
394 name: "battery level",
395 value,
396 detail: "must be 0-4",
397 }),
398 }
399 }
400}
401
402impl From<BatteryLevel> for u8 {
403 fn from(level: BatteryLevel) -> Self {
404 level as Self
405 }
406}
407
408#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
423pub struct VoxGain(u8);
424
425impl VoxGain {
426 pub const MAX: u8 = 9;
428
429 pub const fn new(value: u8) -> Result<Self, ValidationError> {
435 if value > 9 {
436 Err(ValidationError::SettingOutOfRange {
437 name: "VOX gain",
438 value,
439 detail: "must be 0-9",
440 })
441 } else {
442 Ok(Self(value))
443 }
444 }
445
446 #[must_use]
448 pub const fn as_u8(self) -> u8 {
449 self.0
450 }
451}
452
453impl TryFrom<u8> for VoxGain {
454 type Error = ValidationError;
455
456 fn try_from(value: u8) -> Result<Self, Self::Error> {
457 Self::new(value)
458 }
459}
460
461impl From<VoxGain> for u8 {
462 fn from(gain: VoxGain) -> Self {
463 gain.0
464 }
465}
466
467impl fmt::Display for VoxGain {
468 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
469 write!(f, "{}", self.0)
470 }
471}
472
473#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
488pub struct VoxDelay(u8);
489
490impl VoxDelay {
491 pub const MAX: u8 = 30;
493
494 pub const fn new(value: u8) -> Result<Self, ValidationError> {
500 if value > 30 {
501 Err(ValidationError::SettingOutOfRange {
502 name: "VOX delay",
503 value,
504 detail: "must be 0-30",
505 })
506 } else {
507 Ok(Self(value))
508 }
509 }
510
511 #[must_use]
513 pub const fn as_u8(self) -> u8 {
514 self.0
515 }
516
517 #[must_use]
519 pub const fn as_millis(self) -> u16 {
520 self.0 as u16 * 100
521 }
522}
523
524impl TryFrom<u8> for VoxDelay {
525 type Error = ValidationError;
526
527 fn try_from(value: u8) -> Result<Self, Self::Error> {
528 Self::new(value)
529 }
530}
531
532impl From<VoxDelay> for u8 {
533 fn from(delay: VoxDelay) -> Self {
534 delay.0
535 }
536}
537
538impl fmt::Display for VoxDelay {
539 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
540 write!(f, "{}ms", self.as_millis())
541 }
542}
543
544#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
552pub enum TncBaud {
553 Bps1200 = 0,
555 Bps9600 = 1,
557}
558
559impl TncBaud {
560 pub const COUNT: u8 = 2;
562}
563
564impl fmt::Display for TncBaud {
565 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
566 match self {
567 Self::Bps1200 => f.write_str("1200 bps"),
568 Self::Bps9600 => f.write_str("9600 bps"),
569 }
570 }
571}
572
573impl TryFrom<u8> for TncBaud {
574 type Error = ValidationError;
575
576 fn try_from(value: u8) -> Result<Self, Self::Error> {
577 match value {
578 0 => Ok(Self::Bps1200),
579 1 => Ok(Self::Bps9600),
580 _ => Err(ValidationError::SettingOutOfRange {
581 name: "TNC baud rate",
582 value,
583 detail: "must be 0-1",
584 }),
585 }
586 }
587}
588
589impl From<TncBaud> for u8 {
590 fn from(baud: TncBaud) -> Self {
591 baud as Self
592 }
593}
594
595#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
604pub enum BeaconMode {
605 Off = 0,
607 Manual = 1,
609 Ptt = 2,
611 Auto = 3,
613 SmartBeaconing = 4,
615}
616
617impl BeaconMode {
618 pub const COUNT: u8 = 5;
620}
621
622impl fmt::Display for BeaconMode {
623 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
624 match self {
625 Self::Off => f.write_str("Off"),
626 Self::Manual => f.write_str("Manual"),
627 Self::Ptt => f.write_str("PTT"),
628 Self::Auto => f.write_str("Auto"),
629 Self::SmartBeaconing => f.write_str("SmartBeaconing"),
630 }
631 }
632}
633
634impl TryFrom<u8> for BeaconMode {
635 type Error = ValidationError;
636
637 fn try_from(value: u8) -> Result<Self, Self::Error> {
638 match value {
639 0 => Ok(Self::Off),
640 1 => Ok(Self::Manual),
641 2 => Ok(Self::Ptt),
642 3 => Ok(Self::Auto),
643 4 => Ok(Self::SmartBeaconing),
644 _ => Err(ValidationError::SettingOutOfRange {
645 name: "beacon mode",
646 value,
647 detail: "must be 0-4",
648 }),
649 }
650 }
651}
652
653impl From<BeaconMode> for u8 {
654 fn from(mode: BeaconMode) -> Self {
655 mode as Self
656 }
657}
658
659#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
668pub struct DstarSlot(u8);
669
670impl DstarSlot {
671 pub const MIN: u8 = 1;
673 pub const MAX: u8 = 6;
675
676 pub const fn new(value: u8) -> Result<Self, ValidationError> {
682 if value == 0 || value > 6 {
683 Err(ValidationError::SettingOutOfRange {
684 name: "D-STAR slot",
685 value,
686 detail: "must be 1-6",
687 })
688 } else {
689 Ok(Self(value))
690 }
691 }
692
693 #[must_use]
695 pub const fn as_u8(self) -> u8 {
696 self.0
697 }
698}
699
700impl TryFrom<u8> for DstarSlot {
701 type Error = ValidationError;
702
703 fn try_from(value: u8) -> Result<Self, Self::Error> {
704 Self::new(value)
705 }
706}
707
708impl From<DstarSlot> for u8 {
709 fn from(slot: DstarSlot) -> Self {
710 slot.0
711 }
712}
713
714impl fmt::Display for DstarSlot {
715 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
716 write!(f, "Slot {}", self.0)
717 }
718}
719
720#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
729pub struct CallsignSlot(u8);
730
731impl CallsignSlot {
732 pub const MAX: u8 = 10;
734
735 pub const fn new(value: u8) -> Result<Self, ValidationError> {
741 if value > 10 {
742 Err(ValidationError::SettingOutOfRange {
743 name: "callsign slot",
744 value,
745 detail: "must be 0-10",
746 })
747 } else {
748 Ok(Self(value))
749 }
750 }
751
752 #[must_use]
754 pub const fn as_u8(self) -> u8 {
755 self.0
756 }
757}
758
759impl TryFrom<u8> for CallsignSlot {
760 type Error = ValidationError;
761
762 fn try_from(value: u8) -> Result<Self, Self::Error> {
763 Self::new(value)
764 }
765}
766
767impl From<CallsignSlot> for u8 {
768 fn from(slot: CallsignSlot) -> Self {
769 slot.0
770 }
771}
772
773impl fmt::Display for CallsignSlot {
774 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
775 write!(f, "Slot {}", self.0)
776 }
777}
778
779#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
800pub enum DetectOutputMode {
801 Af = 0,
803 If = 1,
805 Detect = 2,
807}
808
809impl DetectOutputMode {
810 pub const COUNT: u8 = 3;
812}
813
814impl fmt::Display for DetectOutputMode {
815 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
816 match self {
817 Self::Af => f.write_str("AF"),
818 Self::If => f.write_str("IF"),
819 Self::Detect => f.write_str("Detect"),
820 }
821 }
822}
823
824impl TryFrom<u8> for DetectOutputMode {
825 type Error = ValidationError;
826
827 fn try_from(value: u8) -> Result<Self, Self::Error> {
828 match value {
829 0 => Ok(Self::Af),
830 1 => Ok(Self::If),
831 2 => Ok(Self::Detect),
832 _ => Err(ValidationError::SettingOutOfRange {
833 name: "detect output mode",
834 value,
835 detail: "must be 0-2",
836 }),
837 }
838 }
839}
840
841impl From<DetectOutputMode> for u8 {
842 fn from(mode: DetectOutputMode) -> Self {
843 mode as Self
844 }
845}
846
847#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
859pub enum DvGatewayMode {
860 Off = 0,
862 ReflectorTerminal = 1,
864 AccessPoint = 2,
869}
870
871impl DvGatewayMode {
872 pub const COUNT: u8 = 3;
874}
875
876impl fmt::Display for DvGatewayMode {
877 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
878 match self {
879 Self::Off => f.write_str("Off"),
880 Self::ReflectorTerminal => f.write_str("Reflector TERM"),
881 Self::AccessPoint => f.write_str("Access Point"),
882 }
883 }
884}
885
886impl TryFrom<u8> for DvGatewayMode {
887 type Error = ValidationError;
888
889 fn try_from(value: u8) -> Result<Self, Self::Error> {
890 match value {
891 0 => Ok(Self::Off),
892 1 => Ok(Self::ReflectorTerminal),
893 2 => Ok(Self::AccessPoint),
894 _ => Err(ValidationError::SettingOutOfRange {
895 name: "DV gateway mode",
896 value,
897 detail: "must be 0-2",
898 }),
899 }
900 }
901}
902
903impl From<DvGatewayMode> for u8 {
904 fn from(mode: DvGatewayMode) -> Self {
905 mode as Self
906 }
907}
908
909#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
921pub enum TncMode {
922 Aprs = 0,
924 Navitra = 1,
926 Kiss = 2,
932 Mmdvm = 3,
936}
937
938impl TncMode {
939 pub const COUNT: u8 = 4;
941}
942
943impl fmt::Display for TncMode {
944 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
945 match self {
946 Self::Aprs => f.write_str("APRS"),
947 Self::Navitra => f.write_str("NAVITRA"),
948 Self::Kiss => f.write_str("KISS"),
949 Self::Mmdvm => f.write_str("MMDVM"),
950 }
951 }
952}
953
954impl TryFrom<u8> for TncMode {
955 type Error = ValidationError;
956
957 fn try_from(value: u8) -> Result<Self, Self::Error> {
958 match value {
959 0 => Ok(Self::Aprs),
960 1 => Ok(Self::Navitra),
961 2 => Ok(Self::Kiss),
962 3 => Ok(Self::Mmdvm),
963 _ => Err(ValidationError::SettingOutOfRange {
964 name: "TNC mode",
965 value,
966 detail: "must be 0-3",
967 }),
968 }
969 }
970}
971
972impl From<TncMode> for u8 {
973 fn from(mode: TncMode) -> Self {
974 mode as Self
975 }
976}
977
978#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
999pub struct FilterWidthIndex(u8);
1000
1001impl FilterWidthIndex {
1002 const MAX_SSB_CW: u8 = 4;
1004 const MAX_AM: u8 = 3;
1006
1007 pub const fn new(value: u8, mode: FilterMode) -> Result<Self, ValidationError> {
1014 let max = match mode {
1015 FilterMode::Ssb | FilterMode::Cw => Self::MAX_SSB_CW,
1016 FilterMode::Am => Self::MAX_AM,
1017 };
1018 if value > max {
1019 Err(ValidationError::SettingOutOfRange {
1020 name: "filter width index",
1021 value,
1022 detail: match mode {
1023 FilterMode::Ssb | FilterMode::Cw => "must be 0-4 for SSB/CW",
1024 FilterMode::Am => "must be 0-3 for AM",
1025 },
1026 })
1027 } else {
1028 Ok(Self(value))
1029 }
1030 }
1031
1032 pub const fn from_raw(value: u8) -> Result<Self, ValidationError> {
1042 if value > Self::MAX_SSB_CW {
1043 Err(ValidationError::SettingOutOfRange {
1044 name: "filter width index",
1045 value,
1046 detail: "must be 0-4",
1047 })
1048 } else {
1049 Ok(Self(value))
1050 }
1051 }
1052
1053 #[must_use]
1055 pub const fn as_u8(self) -> u8 {
1056 self.0
1057 }
1058}
1059
1060impl TryFrom<u8> for FilterWidthIndex {
1061 type Error = ValidationError;
1062
1063 fn try_from(value: u8) -> Result<Self, Self::Error> {
1064 Self::from_raw(value)
1065 }
1066}
1067
1068impl From<FilterWidthIndex> for u8 {
1069 fn from(idx: FilterWidthIndex) -> Self {
1070 idx.0
1071 }
1072}
1073
1074impl fmt::Display for FilterWidthIndex {
1075 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1076 write!(f, "{}", self.0)
1077 }
1078}
1079
1080#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1100pub enum GpsRadioMode {
1101 Normal = 0,
1103 GpsReceiver = 1,
1105}
1106
1107impl GpsRadioMode {
1108 pub const COUNT: u8 = 2;
1110}
1111
1112impl TryFrom<u8> for GpsRadioMode {
1113 type Error = ValidationError;
1114
1115 fn try_from(value: u8) -> Result<Self, Self::Error> {
1116 match value {
1117 0 => Ok(Self::Normal),
1118 1 => Ok(Self::GpsReceiver),
1119 _ => Err(ValidationError::SettingOutOfRange {
1120 name: "GPS radio mode",
1121 value,
1122 detail: "must be 0-1",
1123 }),
1124 }
1125 }
1126}
1127
1128impl From<GpsRadioMode> for u8 {
1129 fn from(mode: GpsRadioMode) -> Self {
1130 mode as Self
1131 }
1132}
1133
1134impl fmt::Display for GpsRadioMode {
1135 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1136 match self {
1137 Self::Normal => write!(f, "Normal"),
1138 Self::GpsReceiver => write!(f, "GPS Receiver"),
1139 }
1140 }
1141}
1142
1143#[cfg(test)]
1148mod tests {
1149 use super::*;
1150
1151 #[test]
1152 fn squelch_level_valid() {
1153 for v in 0..SquelchLevel::COUNT {
1154 let val = SquelchLevel::new(v).unwrap();
1155 assert_eq!(val.as_u8(), v, "SquelchLevel round-trip failed at {v}");
1156 }
1157 assert!(SquelchLevel::new(SquelchLevel::COUNT).is_err());
1158 }
1159
1160 #[test]
1161 fn squelch_level_round_trip() {
1162 let sq = SquelchLevel::new(4).unwrap();
1163 assert_eq!(u8::from(sq), 4);
1164 assert_eq!(sq.as_u8(), 4);
1165 }
1166
1167 #[test]
1168 fn af_gain_valid() {
1169 assert_eq!(AfGainLevel::new(0).as_u8(), 0);
1171 assert_eq!(AfGainLevel::new(99).as_u8(), 99);
1172 assert_eq!(AfGainLevel::new(113).as_u8(), 113);
1173 assert_eq!(AfGainLevel::new(255).as_u8(), 255);
1174 }
1175
1176 #[test]
1177 fn smeter_s_units() {
1178 assert_eq!(SMeterReading::new(0).unwrap().s_unit(), "S0");
1179 assert_eq!(
1180 SMeterReading::new(SMeterReading::COUNT - 1)
1181 .unwrap()
1182 .s_unit(),
1183 "S9"
1184 );
1185 assert!(SMeterReading::new(SMeterReading::COUNT).is_err());
1186 }
1187
1188 #[test]
1189 fn vfo_memory_mode_round_trip() {
1190 for v in 0..VfoMemoryMode::COUNT {
1191 let mode = VfoMemoryMode::try_from(v).unwrap();
1192 assert_eq!(u8::from(mode), v);
1193 }
1194 assert!(VfoMemoryMode::try_from(VfoMemoryMode::COUNT).is_err());
1195 }
1196
1197 #[test]
1198 fn filter_mode_round_trip() {
1199 for v in 0..FilterMode::COUNT {
1200 let mode = FilterMode::try_from(v).unwrap();
1201 assert_eq!(u8::from(mode), v);
1202 }
1203 assert!(FilterMode::try_from(FilterMode::COUNT).is_err());
1204 }
1205
1206 #[test]
1207 fn battery_level_round_trip() {
1208 for v in 0..BatteryLevel::COUNT {
1209 let bl = BatteryLevel::try_from(v).unwrap();
1210 assert_eq!(u8::from(bl), v);
1211 }
1212 assert!(BatteryLevel::try_from(BatteryLevel::COUNT).is_err());
1213 }
1214
1215 #[test]
1216 fn battery_level_charging() {
1217 assert_eq!(BatteryLevel::try_from(4).unwrap(), BatteryLevel::Charging);
1218 }
1219
1220 #[test]
1221 fn vox_gain_valid() {
1222 assert!(VoxGain::new(0).is_ok());
1223 assert!(VoxGain::new(VoxGain::MAX).is_ok());
1224 assert!(VoxGain::new(VoxGain::MAX + 1).is_err());
1225 }
1226
1227 #[test]
1228 fn vox_delay_millis() {
1229 let d = VoxDelay::new(15).unwrap();
1230 assert_eq!(d.as_millis(), 1500);
1231 assert!(VoxDelay::new(VoxDelay::MAX + 1).is_err());
1232 }
1233
1234 #[test]
1235 fn tnc_baud_round_trip() {
1236 for v in 0..TncBaud::COUNT {
1237 let val = TncBaud::try_from(v).unwrap();
1238 assert_eq!(u8::from(val), v, "TncBaud round-trip failed at {v}");
1239 }
1240 assert!(TncBaud::try_from(TncBaud::COUNT).is_err());
1241 }
1242
1243 #[test]
1244 fn beacon_mode_round_trip() {
1245 for v in 0..BeaconMode::COUNT {
1246 let mode = BeaconMode::try_from(v).unwrap();
1247 assert_eq!(u8::from(mode), v);
1248 }
1249 assert!(BeaconMode::try_from(BeaconMode::COUNT).is_err());
1250 }
1251
1252 #[test]
1253 fn dstar_slot_valid() {
1254 assert!(DstarSlot::new(DstarSlot::MIN - 1).is_err());
1255 assert!(DstarSlot::new(DstarSlot::MIN).is_ok());
1256 assert!(DstarSlot::new(DstarSlot::MAX).is_ok());
1257 assert!(DstarSlot::new(DstarSlot::MAX + 1).is_err());
1258 }
1259
1260 #[test]
1261 fn tnc_mode_round_trip() {
1262 for v in 0..TncMode::COUNT {
1263 let mode = TncMode::try_from(v).unwrap();
1264 assert_eq!(u8::from(mode), v);
1265 }
1266 assert!(TncMode::try_from(TncMode::COUNT).is_err());
1267 }
1268
1269 #[test]
1270 fn tnc_mode_kiss() {
1271 assert_eq!(TncMode::try_from(2).unwrap(), TncMode::Kiss);
1272 }
1273
1274 #[test]
1275 fn callsign_slot_valid() {
1276 assert!(CallsignSlot::new(0).is_ok());
1277 assert!(CallsignSlot::new(CallsignSlot::MAX).is_ok());
1278 assert!(CallsignSlot::new(CallsignSlot::MAX + 1).is_err());
1279 }
1280
1281 #[test]
1282 fn filter_width_ssb_cw_range() {
1283 for v in 0..=4 {
1284 assert!(FilterWidthIndex::new(v, FilterMode::Ssb).is_ok());
1285 assert!(FilterWidthIndex::new(v, FilterMode::Cw).is_ok());
1286 }
1287 assert!(FilterWidthIndex::new(5, FilterMode::Ssb).is_err());
1288 assert!(FilterWidthIndex::new(5, FilterMode::Cw).is_err());
1289 }
1290
1291 #[test]
1292 fn filter_width_am_range() {
1293 for v in 0..=3 {
1294 assert!(FilterWidthIndex::new(v, FilterMode::Am).is_ok());
1295 }
1296 assert!(FilterWidthIndex::new(4, FilterMode::Am).is_err());
1297 }
1298
1299 #[test]
1300 fn filter_width_from_raw() {
1301 assert!(FilterWidthIndex::from_raw(4).is_ok());
1302 assert!(FilterWidthIndex::from_raw(5).is_err());
1303 }
1304
1305 #[test]
1306 fn detect_output_mode_round_trip() {
1307 for v in 0..DetectOutputMode::COUNT {
1308 let val = DetectOutputMode::try_from(v).unwrap();
1309 assert_eq!(
1310 u8::from(val),
1311 v,
1312 "DetectOutputMode round-trip failed at {v}"
1313 );
1314 }
1315 assert!(DetectOutputMode::try_from(DetectOutputMode::COUNT).is_err());
1316 }
1317
1318 #[test]
1319 fn dv_gateway_mode_round_trip() {
1320 for v in 0..DvGatewayMode::COUNT {
1321 let val = DvGatewayMode::try_from(v).unwrap();
1322 assert_eq!(u8::from(val), v, "DvGatewayMode round-trip failed at {v}");
1323 }
1324 assert!(DvGatewayMode::try_from(DvGatewayMode::COUNT).is_err());
1325 }
1326
1327 #[test]
1328 fn gps_radio_mode_round_trip() {
1329 for v in 0..GpsRadioMode::COUNT {
1330 let val = GpsRadioMode::try_from(v).unwrap();
1331 assert_eq!(u8::from(val), v, "GpsRadioMode round-trip failed at {v}");
1332 }
1333 assert!(GpsRadioMode::try_from(GpsRadioMode::COUNT).is_err());
1334 }
1335}