1use std::fmt;
4
5use crate::error::ValidationError;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
64pub enum Mode {
65 Fm = 0,
67 Dv = 1,
69 Am = 2,
72 Lsb = 3,
75 Usb = 4,
78 Cw = 5,
81 Nfm = 6,
84 Dr = 7,
87 Wfm = 8,
90 CwReverse = 9,
94}
95
96impl Mode {
97 pub const COUNT: u8 = 10;
99}
100
101impl fmt::Display for Mode {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 match self {
104 Self::Fm => f.write_str("FM"),
105 Self::Dv => f.write_str("DV"),
106 Self::Am => f.write_str("AM"),
107 Self::Lsb => f.write_str("LSB"),
108 Self::Usb => f.write_str("USB"),
109 Self::Cw => f.write_str("CW"),
110 Self::Nfm => f.write_str("NFM"),
111 Self::Dr => f.write_str("DR"),
112 Self::Wfm => f.write_str("WFM"),
113 Self::CwReverse => f.write_str("CW-R"),
114 }
115 }
116}
117
118impl TryFrom<u8> for Mode {
119 type Error = ValidationError;
120
121 fn try_from(value: u8) -> Result<Self, Self::Error> {
122 match value {
123 0 => Ok(Self::Fm),
124 1 => Ok(Self::Dv),
125 2 => Ok(Self::Am),
126 3 => Ok(Self::Lsb),
127 4 => Ok(Self::Usb),
128 5 => Ok(Self::Cw),
129 6 => Ok(Self::Nfm),
130 7 => Ok(Self::Dr),
131 8 => Ok(Self::Wfm),
132 9 => Ok(Self::CwReverse),
133 _ => Err(ValidationError::ModeOutOfRange(value)),
134 }
135 }
136}
137
138impl From<Mode> for u8 {
139 fn from(mode: Mode) -> Self {
140 mode as Self
141 }
142}
143
144#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
163pub enum PowerLevel {
164 High = 0,
166 Medium = 1,
168 Low = 2,
170 ExtraLow = 3,
172}
173
174impl PowerLevel {
175 pub const COUNT: u8 = 4;
177}
178
179impl fmt::Display for PowerLevel {
180 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181 match self {
182 Self::High => f.write_str("High"),
183 Self::Medium => f.write_str("Medium"),
184 Self::Low => f.write_str("Low"),
185 Self::ExtraLow => f.write_str("EL"),
186 }
187 }
188}
189
190impl TryFrom<u8> for PowerLevel {
191 type Error = ValidationError;
192
193 fn try_from(value: u8) -> Result<Self, Self::Error> {
194 match value {
195 0 => Ok(Self::High),
196 1 => Ok(Self::Medium),
197 2 => Ok(Self::Low),
198 3 => Ok(Self::ExtraLow),
199 _ => Err(ValidationError::PowerLevelOutOfRange(value)),
200 }
201 }
202}
203
204impl From<PowerLevel> for u8 {
205 fn from(level: PowerLevel) -> Self {
206 level as Self
207 }
208}
209
210#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
220pub struct ShiftDirection(u8);
221
222impl ShiftDirection {
223 pub const SIMPLEX: Self = Self(0);
225 pub const UP: Self = Self(1);
227 pub const DOWN: Self = Self(2);
229 pub const SPLIT: Self = Self(3);
231
232 pub const fn new(value: u8) -> Result<Self, ValidationError> {
238 if value <= 15 {
239 Ok(Self(value))
240 } else {
241 Err(ValidationError::ShiftOutOfRange(value))
242 }
243 }
244
245 #[must_use]
247 pub const fn as_u8(self) -> u8 {
248 self.0
249 }
250
251 #[must_use]
253 pub const fn is_known(self) -> bool {
254 self.0 <= 3
255 }
256}
257
258impl TryFrom<u8> for ShiftDirection {
259 type Error = ValidationError;
260
261 fn try_from(value: u8) -> Result<Self, Self::Error> {
262 Self::new(value)
263 }
264}
265
266impl From<ShiftDirection> for u8 {
267 fn from(dir: ShiftDirection) -> Self {
268 dir.0
269 }
270}
271
272#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
312pub enum StepSize {
313 Hz5000 = 0,
315 Hz6250 = 1,
317 Hz8330 = 2,
319 Hz9000 = 3,
321 Hz10000 = 4,
323 Hz12500 = 5,
325 Hz15000 = 6,
327 Hz20000 = 7,
329 Hz25000 = 8,
331 Hz30000 = 9,
333 Hz50000 = 10,
335 Hz100000 = 11,
337}
338
339impl StepSize {
340 pub const COUNT: u8 = 12;
342
343 #[must_use]
345 pub const fn as_hz(self) -> u32 {
346 match self {
347 Self::Hz5000 => 5_000,
348 Self::Hz6250 => 6_250,
349 Self::Hz8330 => 8_330,
350 Self::Hz9000 => 9_000,
351 Self::Hz10000 => 10_000,
352 Self::Hz12500 => 12_500,
353 Self::Hz15000 => 15_000,
354 Self::Hz20000 => 20_000,
355 Self::Hz25000 => 25_000,
356 Self::Hz30000 => 30_000,
357 Self::Hz50000 => 50_000,
358 Self::Hz100000 => 100_000,
359 }
360 }
361
362 #[must_use]
364 pub const fn as_khz_str(self) -> &'static str {
365 match self {
366 Self::Hz5000 => "5.0",
367 Self::Hz6250 => "6.25",
368 Self::Hz8330 => "8.33",
369 Self::Hz9000 => "9.0",
370 Self::Hz10000 => "10.0",
371 Self::Hz12500 => "12.5",
372 Self::Hz15000 => "15.0",
373 Self::Hz20000 => "20.0",
374 Self::Hz25000 => "25.0",
375 Self::Hz30000 => "30.0",
376 Self::Hz50000 => "50.0",
377 Self::Hz100000 => "100.0",
378 }
379 }
380}
381
382impl fmt::Display for StepSize {
383 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
384 write!(f, "{} kHz", self.as_khz_str())
385 }
386}
387
388impl TryFrom<u8> for StepSize {
389 type Error = ValidationError;
390
391 fn try_from(value: u8) -> Result<Self, Self::Error> {
392 match value {
393 0 => Ok(Self::Hz5000),
394 1 => Ok(Self::Hz6250),
395 2 => Ok(Self::Hz8330),
396 3 => Ok(Self::Hz9000),
397 4 => Ok(Self::Hz10000),
398 5 => Ok(Self::Hz12500),
399 6 => Ok(Self::Hz15000),
400 7 => Ok(Self::Hz20000),
401 8 => Ok(Self::Hz25000),
402 9 => Ok(Self::Hz30000),
403 10 => Ok(Self::Hz50000),
404 11 => Ok(Self::Hz100000),
405 _ => Err(ValidationError::StepSizeOutOfRange(value)),
406 }
407 }
408}
409
410impl From<StepSize> for u8 {
411 fn from(step: StepSize) -> Self {
412 step as Self
413 }
414}
415
416#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
426pub enum CoarseStepMultiplier {
427 X1 = 0,
429 X2 = 1,
431 X5 = 2,
433 X10 = 3,
435 X50 = 4,
437 X100 = 5,
439}
440
441impl CoarseStepMultiplier {
442 pub const COUNT: u8 = 6;
444
445 #[must_use]
447 pub const fn multiplier(self) -> u16 {
448 match self {
449 Self::X1 => 1,
450 Self::X2 => 2,
451 Self::X5 => 5,
452 Self::X10 => 10,
453 Self::X50 => 50,
454 Self::X100 => 100,
455 }
456 }
457}
458
459impl fmt::Display for CoarseStepMultiplier {
460 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
461 write!(f, "x{}", self.multiplier())
462 }
463}
464
465impl TryFrom<u8> for CoarseStepMultiplier {
466 type Error = ValidationError;
467
468 fn try_from(value: u8) -> Result<Self, Self::Error> {
469 match value {
470 0 => Ok(Self::X1),
471 1 => Ok(Self::X2),
472 2 => Ok(Self::X5),
473 3 => Ok(Self::X10),
474 4 => Ok(Self::X50),
475 5 => Ok(Self::X100),
476 _ => Err(ValidationError::SettingOutOfRange {
477 name: "coarse step multiplier",
478 value,
479 detail: "must be 0-5",
480 }),
481 }
482 }
483}
484
485impl From<CoarseStepMultiplier> for u8 {
486 fn from(mult: CoarseStepMultiplier) -> Self {
487 mult as Self
488 }
489}
490
491#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
516pub enum MemoryMode {
517 Fm = 0,
519 Dv = 1,
521 Am = 2,
523 Lsb = 3,
525 Usb = 4,
527 Cw = 5,
529 Nfm = 6,
531 Dr = 7,
533}
534
535impl MemoryMode {
536 pub const COUNT: u8 = 8;
538}
539
540impl fmt::Display for MemoryMode {
541 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
542 match self {
543 Self::Fm => f.write_str("FM"),
544 Self::Dv => f.write_str("DV"),
545 Self::Am => f.write_str("AM"),
546 Self::Lsb => f.write_str("LSB"),
547 Self::Usb => f.write_str("USB"),
548 Self::Cw => f.write_str("CW"),
549 Self::Nfm => f.write_str("NFM"),
550 Self::Dr => f.write_str("DR"),
551 }
552 }
553}
554
555impl TryFrom<u8> for MemoryMode {
556 type Error = ValidationError;
557
558 fn try_from(value: u8) -> Result<Self, Self::Error> {
559 match value {
560 0 => Ok(Self::Fm),
561 1 => Ok(Self::Dv),
562 2 => Ok(Self::Am),
563 3 => Ok(Self::Lsb),
564 4 => Ok(Self::Usb),
565 5 => Ok(Self::Cw),
566 6 => Ok(Self::Nfm),
567 7 => Ok(Self::Dr),
568 _ => Err(ValidationError::MemoryModeOutOfRange(value)),
569 }
570 }
571}
572
573impl From<MemoryMode> for u8 {
574 fn from(mode: MemoryMode) -> Self {
575 mode as Self
576 }
577}
578
579#[cfg(test)]
580mod tests {
581 use super::*;
582 use crate::error::ValidationError;
583
584 #[test]
587 fn mode_valid_range() {
588 for i in 0u8..Mode::COUNT {
589 let val = Mode::try_from(i).unwrap();
590 assert_eq!(u8::from(val), i, "Mode round-trip failed at {i}");
591 }
592 }
593
594 #[test]
595 fn mode_invalid() {
596 assert!(Mode::try_from(Mode::COUNT).is_err());
597 assert!(Mode::try_from(255).is_err());
598 }
599
600 #[test]
601 fn mode_round_trip() {
602 for i in 0u8..Mode::COUNT {
603 let val = Mode::try_from(i).unwrap();
604 assert_eq!(u8::from(val), i);
605 }
606 }
607
608 #[test]
609 fn mode_error_variant() {
610 let err = Mode::try_from(Mode::COUNT).unwrap_err();
611 assert!(matches!(err, ValidationError::ModeOutOfRange(10)));
612 }
613
614 #[test]
615 fn mode_display() {
616 assert_eq!(Mode::Fm.to_string(), "FM");
617 assert_eq!(Mode::Dv.to_string(), "DV");
618 assert_eq!(Mode::Am.to_string(), "AM");
619 assert_eq!(Mode::Lsb.to_string(), "LSB");
620 assert_eq!(Mode::Usb.to_string(), "USB");
621 assert_eq!(Mode::Cw.to_string(), "CW");
622 assert_eq!(Mode::Nfm.to_string(), "NFM");
623 assert_eq!(Mode::Dr.to_string(), "DR");
624 assert_eq!(Mode::Wfm.to_string(), "WFM");
625 assert_eq!(Mode::CwReverse.to_string(), "CW-R");
626 }
627
628 #[test]
631 fn power_level_valid_range() {
632 for i in 0u8..PowerLevel::COUNT {
633 let val = PowerLevel::try_from(i).unwrap();
634 assert_eq!(u8::from(val), i, "PowerLevel round-trip failed at {i}");
635 }
636 }
637
638 #[test]
639 fn power_level_invalid() {
640 assert!(PowerLevel::try_from(PowerLevel::COUNT).is_err());
641 assert!(PowerLevel::try_from(255).is_err());
642 }
643
644 #[test]
645 fn power_level_round_trip() {
646 for i in 0u8..PowerLevel::COUNT {
647 let val = PowerLevel::try_from(i).unwrap();
648 assert_eq!(u8::from(val), i);
649 }
650 }
651
652 #[test]
653 fn power_level_error_variant() {
654 let err = PowerLevel::try_from(PowerLevel::COUNT).unwrap_err();
655 assert!(matches!(err, ValidationError::PowerLevelOutOfRange(4)));
656 }
657
658 #[test]
659 fn power_level_display() {
660 assert_eq!(PowerLevel::High.to_string(), "High");
661 assert_eq!(PowerLevel::Medium.to_string(), "Medium");
662 assert_eq!(PowerLevel::Low.to_string(), "Low");
663 assert_eq!(PowerLevel::ExtraLow.to_string(), "EL");
664 }
665
666 #[test]
669 fn shift_direction_valid_range() {
670 for i in 0u8..=15 {
672 let val = ShiftDirection::try_from(i).unwrap();
673 assert_eq!(u8::from(val), i, "ShiftDirection round-trip failed at {i}");
674 }
675 }
676
677 #[test]
678 fn shift_direction_invalid() {
679 assert!(ShiftDirection::try_from(16).is_err());
680 assert!(ShiftDirection::try_from(255).is_err());
681 }
682
683 #[test]
684 fn shift_direction_round_trip() {
685 for i in 0u8..=15 {
686 let val = ShiftDirection::try_from(i).unwrap();
687 assert_eq!(u8::from(val), i);
688 }
689 }
690
691 #[test]
692 fn shift_direction_known_constants() {
693 assert_eq!(ShiftDirection::SIMPLEX.as_u8(), 0);
694 assert_eq!(ShiftDirection::UP.as_u8(), 1);
695 assert_eq!(ShiftDirection::DOWN.as_u8(), 2);
696 assert_eq!(ShiftDirection::SPLIT.as_u8(), 3);
697 assert!(ShiftDirection::SIMPLEX.is_known());
698 assert!(ShiftDirection::SPLIT.is_known());
699 }
700
701 #[test]
702 fn shift_direction_extended_vfo_values() {
703 let ext = ShiftDirection::new(8).unwrap();
705 assert_eq!(ext.as_u8(), 8);
706 assert!(!ext.is_known());
707 }
708
709 #[test]
710 fn shift_direction_error_variant() {
711 let err = ShiftDirection::try_from(16).unwrap_err();
712 assert!(matches!(err, ValidationError::ShiftOutOfRange(16)));
713 }
714
715 #[test]
718 fn step_size_valid_range() {
719 for i in 0u8..StepSize::COUNT {
720 let val = StepSize::try_from(i).unwrap();
721 assert_eq!(u8::from(val), i, "StepSize round-trip failed at {i}");
722 }
723 }
724
725 #[test]
726 fn step_size_invalid() {
727 assert!(StepSize::try_from(StepSize::COUNT).is_err());
728 assert!(StepSize::try_from(255).is_err());
729 }
730
731 #[test]
732 fn step_size_round_trip() {
733 for i in 0u8..StepSize::COUNT {
734 let val = StepSize::try_from(i).unwrap();
735 assert_eq!(u8::from(val), i);
736 }
737 }
738
739 #[test]
740 fn step_size_error_variant() {
741 let err = StepSize::try_from(StepSize::COUNT).unwrap_err();
742 assert!(matches!(err, ValidationError::StepSizeOutOfRange(12)));
743 }
744
745 #[test]
746 fn step_size_as_hz() {
747 assert_eq!(StepSize::Hz5000.as_hz(), 5_000);
748 assert_eq!(StepSize::Hz6250.as_hz(), 6_250);
749 assert_eq!(StepSize::Hz8330.as_hz(), 8_330);
750 assert_eq!(StepSize::Hz9000.as_hz(), 9_000);
751 assert_eq!(StepSize::Hz10000.as_hz(), 10_000);
752 assert_eq!(StepSize::Hz12500.as_hz(), 12_500);
753 assert_eq!(StepSize::Hz15000.as_hz(), 15_000);
754 assert_eq!(StepSize::Hz20000.as_hz(), 20_000);
755 assert_eq!(StepSize::Hz25000.as_hz(), 25_000);
756 assert_eq!(StepSize::Hz30000.as_hz(), 30_000);
757 assert_eq!(StepSize::Hz50000.as_hz(), 50_000);
758 assert_eq!(StepSize::Hz100000.as_hz(), 100_000);
759 }
760
761 #[test]
762 fn step_size_as_khz_str() {
763 assert_eq!(StepSize::Hz5000.as_khz_str(), "5.0");
764 assert_eq!(StepSize::Hz6250.as_khz_str(), "6.25");
765 assert_eq!(StepSize::Hz8330.as_khz_str(), "8.33");
766 assert_eq!(StepSize::Hz9000.as_khz_str(), "9.0");
767 assert_eq!(StepSize::Hz10000.as_khz_str(), "10.0");
768 assert_eq!(StepSize::Hz12500.as_khz_str(), "12.5");
769 assert_eq!(StepSize::Hz15000.as_khz_str(), "15.0");
770 assert_eq!(StepSize::Hz20000.as_khz_str(), "20.0");
771 assert_eq!(StepSize::Hz25000.as_khz_str(), "25.0");
772 assert_eq!(StepSize::Hz30000.as_khz_str(), "30.0");
773 assert_eq!(StepSize::Hz50000.as_khz_str(), "50.0");
774 assert_eq!(StepSize::Hz100000.as_khz_str(), "100.0");
775 }
776
777 #[test]
778 fn step_size_display() {
779 assert_eq!(StepSize::Hz5000.to_string(), "5.0 kHz");
780 assert_eq!(StepSize::Hz25000.to_string(), "25.0 kHz");
781 assert_eq!(StepSize::Hz8330.to_string(), "8.33 kHz");
782 }
783
784 #[test]
787 fn memory_mode_valid_range() {
788 for i in 0u8..MemoryMode::COUNT {
789 let val = MemoryMode::try_from(i).unwrap();
790 assert_eq!(u8::from(val), i, "MemoryMode round-trip failed at {i}");
791 }
792 }
793
794 #[test]
795 fn memory_mode_invalid() {
796 assert!(MemoryMode::try_from(MemoryMode::COUNT).is_err());
797 assert!(MemoryMode::try_from(255).is_err());
798 }
799
800 #[test]
801 fn memory_mode_round_trip() {
802 for i in 0u8..MemoryMode::COUNT {
803 let val = MemoryMode::try_from(i).unwrap();
804 assert_eq!(u8::from(val), i);
805 }
806 }
807
808 #[test]
809 fn memory_mode_error_variant() {
810 let err = MemoryMode::try_from(MemoryMode::COUNT).unwrap_err();
811 assert!(matches!(err, ValidationError::MemoryModeOutOfRange(8)));
812 }
813
814 #[test]
815 fn memory_mode_display() {
816 assert_eq!(MemoryMode::Fm.to_string(), "FM");
817 assert_eq!(MemoryMode::Dv.to_string(), "DV");
818 assert_eq!(MemoryMode::Am.to_string(), "AM");
819 assert_eq!(MemoryMode::Lsb.to_string(), "LSB");
820 assert_eq!(MemoryMode::Usb.to_string(), "USB");
821 assert_eq!(MemoryMode::Cw.to_string(), "CW");
822 assert_eq!(MemoryMode::Nfm.to_string(), "NFM");
823 assert_eq!(MemoryMode::Dr.to_string(), "DR");
824 }
825
826 #[test]
827 fn cat_mode_matches_flash_encoding() {
828 assert_eq!(u8::from(Mode::Fm), u8::from(MemoryMode::Fm));
830 assert_eq!(u8::from(Mode::Dv), u8::from(MemoryMode::Dv));
831 assert_eq!(u8::from(Mode::Am), u8::from(MemoryMode::Am));
832 assert_eq!(u8::from(Mode::Lsb), u8::from(MemoryMode::Lsb));
833 assert_eq!(u8::from(Mode::Usb), u8::from(MemoryMode::Usb));
834 assert_eq!(u8::from(Mode::Cw), u8::from(MemoryMode::Cw));
835 assert_eq!(u8::from(Mode::Nfm), u8::from(MemoryMode::Nfm));
836 assert_eq!(u8::from(Mode::Dr), u8::from(MemoryMode::Dr));
837 }
838
839 #[test]
842 fn coarse_step_multiplier_valid_range() {
843 for i in 0u8..CoarseStepMultiplier::COUNT {
844 let val = CoarseStepMultiplier::try_from(i).unwrap();
845 assert_eq!(
846 u8::from(val),
847 i,
848 "CoarseStepMultiplier round-trip failed at {i}"
849 );
850 }
851 }
852
853 #[test]
854 fn coarse_step_multiplier_invalid() {
855 assert!(CoarseStepMultiplier::try_from(CoarseStepMultiplier::COUNT).is_err());
856 assert!(CoarseStepMultiplier::try_from(255).is_err());
857 }
858
859 #[test]
860 fn coarse_step_multiplier_round_trip() {
861 for i in 0u8..CoarseStepMultiplier::COUNT {
862 let val = CoarseStepMultiplier::try_from(i).unwrap();
863 assert_eq!(u8::from(val), i);
864 }
865 }
866
867 #[test]
868 fn coarse_step_multiplier_values() {
869 assert_eq!(CoarseStepMultiplier::X1.multiplier(), 1);
870 assert_eq!(CoarseStepMultiplier::X2.multiplier(), 2);
871 assert_eq!(CoarseStepMultiplier::X5.multiplier(), 5);
872 assert_eq!(CoarseStepMultiplier::X10.multiplier(), 10);
873 assert_eq!(CoarseStepMultiplier::X50.multiplier(), 50);
874 assert_eq!(CoarseStepMultiplier::X100.multiplier(), 100);
875 }
876
877 #[test]
878 fn coarse_step_multiplier_display() {
879 assert_eq!(CoarseStepMultiplier::X1.to_string(), "x1");
880 assert_eq!(CoarseStepMultiplier::X2.to_string(), "x2");
881 assert_eq!(CoarseStepMultiplier::X5.to_string(), "x5");
882 assert_eq!(CoarseStepMultiplier::X10.to_string(), "x10");
883 assert_eq!(CoarseStepMultiplier::X50.to_string(), "x50");
884 assert_eq!(CoarseStepMultiplier::X100.to_string(), "x100");
885 }
886}