1use crate::protocol::programming;
33use crate::types::settings::{
34 AltitudeRainUnit, AutoPowerOff, DisplayUnits, KeyLockType, Language, SpeedDistanceUnit,
35 TemperatureUnit,
36};
37use crate::types::{Frequency, MemoryMode, PowerLevel};
38
39const SETTINGS_OFFSET: usize = 0x0000;
45
46const SETTINGS_SIZE: usize = (programming::SETTINGS_END as usize + 1
48 - programming::SETTINGS_START as usize)
49 * programming::PAGE_SIZE;
50
51const POWER_ON_MESSAGE_OFFSET: usize = 0x11C0;
53
54const POWER_ON_MESSAGE_SIZE: usize = 16;
56
57const MODEL_NAME_OFFSET: usize = 0x11D0;
59
60const MODEL_NAME_SIZE: usize = 16;
62
63const CALLSIGN_OFFSET: usize = 0x1300;
65
66const POWER_LEVEL_A_OFFSET: usize = 0x0359;
75
76const ATTENUATOR_A_OFFSET: usize = 0x035C;
78
79const DUAL_BAND_OFFSET: usize = 0x0396;
81
82const VOX_ENABLED_OFFSET: usize = 0x101B;
84
85const VOX_GAIN_OFFSET: usize = 0x101C;
87
88const LOCK_OFFSET: usize = 0x1060;
90
91const KEY_BEEP_OFFSET: usize = 0x1071;
93
94const BLUETOOTH_OFFSET: usize = 0x1078;
96
97const SQUELCH_A_OFFSET: usize = 0x100D;
104const SQUELCH_B_OFFSET: usize = 0x100E;
106const FM_NARROW_OFFSET: usize = 0x100F;
108const SSB_HIGH_CUT_OFFSET: usize = 0x1011;
110const CW_HIGH_CUT_OFFSET: usize = 0x1012;
112const AM_HIGH_CUT_OFFSET: usize = 0x1013;
114const AUTO_FILTER_OFFSET: usize = 0x100C;
116
117const SCAN_RESUME_OFFSET: usize = 0x1007;
120const DIGITAL_SCAN_RESUME_OFFSET: usize = 0x1008;
122const SCAN_RESTART_TIME_OFFSET: usize = 0x1009;
124const SCAN_RESTART_CARRIER_OFFSET: usize = 0x100A;
126
127const TIMEOUT_TIMER_OFFSET: usize = 0x1018;
130const TX_INHIBIT_OFFSET: usize = 0x1019;
132const BEAT_SHIFT_OFFSET: usize = 0x101A;
134
135const VOX_DELAY_OFFSET: usize = 0x101D;
138const VOX_TX_ON_BUSY_OFFSET: usize = 0x101E;
140
141const CW_BREAK_IN_OFFSET: usize = 0x101F;
144const CW_DELAY_TIME_OFFSET: usize = 0x1020;
146const CW_PITCH_OFFSET: usize = 0x1021;
148
149const DTMF_SPEED_OFFSET: usize = 0x1024;
152const DTMF_PAUSE_TIME_OFFSET: usize = 0x1026;
154const DTMF_TX_HOLD_OFFSET: usize = 0x1027;
156
157const REPEATER_AUTO_OFFSET_OFFSET: usize = 0x1030;
160const REPEATER_CALL_KEY_OFFSET: usize = 0x1031;
162
163const MIC_SENSITIVITY_OFFSET: usize = 0x1040;
166const PF_KEY1_OFFSET: usize = 0x1041;
168const PF_KEY2_OFFSET: usize = 0x1042;
170
171const KEY_LOCK_TYPE_OFFSET: usize = 0x1061;
174const LOCK_KEY_A_OFFSET: usize = 0x1062;
176const LOCK_KEY_B_OFFSET: usize = 0x1063;
178const LOCK_KEY_C_OFFSET: usize = 0x1064;
180const LOCK_KEY_PTT_OFFSET: usize = 0x1065;
182const APRS_LOCK_OFFSET: usize = 0x1097;
184
185const DUAL_DISPLAY_SIZE_OFFSET: usize = 0x1066;
188const DISPLAY_AREA_OFFSET: usize = 0x1067;
190const INFO_LINE_OFFSET: usize = 0x1068;
192const BACKLIGHT_CONTROL_OFFSET: usize = 0x1069;
194const BACKLIGHT_TIMER_OFFSET: usize = 0x106A;
196const DISPLAY_HOLD_TIME_OFFSET: usize = 0x106B;
198const DISPLAY_METHOD_OFFSET: usize = 0x106C;
200const POWER_ON_DISPLAY_OFFSET: usize = 0x106D;
202
203const EMR_VOLUME_LEVEL_OFFSET: usize = 0x106E;
206const AUTO_MUTE_RETURN_TIME_OFFSET: usize = 0x106F;
208const ANNOUNCE_OFFSET: usize = 0x1070;
210const BEEP_VOLUME_OFFSET: usize = 0x1072;
212const VOICE_LANGUAGE_OFFSET: usize = 0x1073;
214const VOICE_VOLUME_OFFSET: usize = 0x1074;
216const VOICE_SPEED_OFFSET: usize = 0x1075;
218const VOLUME_LOCK_OFFSET: usize = 0x1076;
220
221const SPEED_DISTANCE_UNIT_OFFSET: usize = 0x1077;
224const ALTITUDE_RAIN_UNIT_OFFSET: usize = 0x1083;
226const TEMPERATURE_UNIT_OFFSET: usize = 0x1084;
228
229const BT_AUTO_CONNECT_OFFSET: usize = 0x1079;
232
233const GPS_BT_INTERFACE_OFFSET: usize = 0x1080;
236const PC_OUTPUT_MODE_OFFSET: usize = 0x1085;
238const APRS_USB_MODE_OFFSET: usize = 0x1086;
240const USB_AUDIO_OUTPUT_OFFSET: usize = 0x1094;
242const INTERNET_LINK_OFFSET: usize = 0x1095;
244
245const POWER_ON_MESSAGE_FLAG_OFFSET: usize = 0x1087;
248const LANGUAGE_OFFSET: usize = 0x1006;
250
251const BATTERY_SAVER_OFFSET: usize = 0x10C0;
254const AUTO_POWER_OFF_OFFSET: usize = 0x10D0;
256
257const DUAL_BAND_MCP_OFFSET: usize = 0x1096;
260
261const VFO_DATA_OFFSET: usize = 0x0020;
273
274const VFO_ENTRY_COUNT: usize = 6;
276
277const VFO_ENTRY_SIZE: usize = programming::CHANNEL_RECORD_SIZE; #[derive(Debug)]
291pub struct SettingsAccess<'a> {
292 image: &'a [u8],
293}
294
295impl<'a> SettingsAccess<'a> {
296 pub(crate) const fn new(image: &'a [u8]) -> Self {
298 Self { image }
299 }
300
301 #[must_use]
305 pub fn raw(&self) -> Option<&[u8]> {
306 let end = SETTINGS_OFFSET + SETTINGS_SIZE;
307 if end <= self.image.len() {
308 Some(&self.image[SETTINGS_OFFSET..end])
309 } else {
310 None
311 }
312 }
313
314 #[must_use]
319 pub fn power_on_message(&self) -> String {
320 extract_string(self.image, POWER_ON_MESSAGE_OFFSET, POWER_ON_MESSAGE_SIZE)
321 }
322
323 #[must_use]
327 pub fn model_name(&self) -> String {
328 extract_string(self.image, MODEL_NAME_OFFSET, MODEL_NAME_SIZE)
329 }
330
331 #[must_use]
336 pub fn callsign_raw(&self, len: usize) -> Option<&[u8]> {
337 let end = CALLSIGN_OFFSET + len;
338 if end <= self.image.len() {
339 Some(&self.image[CALLSIGN_OFFSET..end])
340 } else {
341 None
342 }
343 }
344
345 #[must_use]
350 pub fn read_bytes(&self, offset: usize, len: usize) -> Option<&[u8]> {
351 let end = offset + len;
352 if end <= self.image.len() {
353 Some(&self.image[offset..end])
354 } else {
355 None
356 }
357 }
358
359 #[must_use]
367 pub fn key_beep(&self) -> bool {
368 self.image.get(KEY_BEEP_OFFSET).is_some_and(|&b| b != 0)
369 }
370
371 #[must_use]
375 pub fn beep_volume(&self) -> u8 {
376 self.image
377 .get(BEEP_VOLUME_OFFSET)
378 .copied()
379 .map_or(0, |b| b.min(7))
380 }
381
382 #[must_use]
386 pub fn backlight(&self) -> u8 {
387 self.image
388 .get(BACKLIGHT_CONTROL_OFFSET)
389 .copied()
390 .unwrap_or(0)
391 }
392
393 #[must_use]
397 pub fn auto_power_off(&self) -> AutoPowerOff {
398 match self.image.get(AUTO_POWER_OFF_OFFSET).copied().unwrap_or(0) {
399 1 => AutoPowerOff::Min30,
400 2 => AutoPowerOff::Min60,
401 3 => AutoPowerOff::Min90,
402 4 => AutoPowerOff::Min120,
403 _ => AutoPowerOff::Off,
404 }
405 }
406
407 #[must_use]
411 pub fn battery_saver(&self) -> bool {
412 self.image
413 .get(BATTERY_SAVER_OFFSET)
414 .is_some_and(|&b| b != 0)
415 }
416
417 #[must_use]
421 pub fn key_lock_type(&self) -> KeyLockType {
422 match self.image.get(KEY_LOCK_TYPE_OFFSET).copied().unwrap_or(0) {
423 1 => KeyLockType::KeyAndPtt,
424 2 => KeyLockType::KeyPttAndDial,
425 _ => KeyLockType::KeyOnly,
426 }
427 }
428
429 #[must_use]
434 pub fn display_units(&self) -> DisplayUnits {
435 let speed_distance = match self
436 .image
437 .get(SPEED_DISTANCE_UNIT_OFFSET)
438 .copied()
439 .unwrap_or(0)
440 {
441 1 => SpeedDistanceUnit::KilometersPerHour,
442 2 => SpeedDistanceUnit::Knots,
443 _ => SpeedDistanceUnit::MilesPerHour,
444 };
445
446 let altitude_rain = match self
447 .image
448 .get(ALTITUDE_RAIN_UNIT_OFFSET)
449 .copied()
450 .unwrap_or(0)
451 {
452 1 => AltitudeRainUnit::MetersMm,
453 _ => AltitudeRainUnit::FeetInch,
454 };
455
456 let temperature = match self
457 .image
458 .get(TEMPERATURE_UNIT_OFFSET)
459 .copied()
460 .unwrap_or(0)
461 {
462 1 => TemperatureUnit::Celsius,
463 _ => TemperatureUnit::Fahrenheit,
464 };
465
466 DisplayUnits {
467 speed_distance,
468 altitude_rain,
469 temperature,
470 }
471 }
472
473 #[must_use]
477 pub fn language(&self) -> Language {
478 match self.image.get(LANGUAGE_OFFSET).copied().unwrap_or(0) {
479 1 => Language::Japanese,
480 _ => Language::English,
481 }
482 }
483
484 #[must_use]
488 pub fn vox_enabled(&self) -> bool {
489 self.image.get(VOX_ENABLED_OFFSET).is_some_and(|&b| b != 0)
490 }
491
492 #[must_use]
496 pub fn vox_gain(&self) -> u8 {
497 self.image
498 .get(VOX_GAIN_OFFSET)
499 .copied()
500 .map_or(0, |b| b.min(9))
501 }
502
503 #[must_use]
507 pub fn vox_delay(&self) -> u8 {
508 self.image.get(VOX_DELAY_OFFSET).copied().unwrap_or(0)
509 }
510
511 #[must_use]
515 pub fn squelch_a(&self) -> u8 {
516 self.image
517 .get(SQUELCH_A_OFFSET)
518 .copied()
519 .map_or(0, |b| b.min(6))
520 }
521
522 #[must_use]
526 pub fn squelch_b(&self) -> u8 {
527 self.image
528 .get(SQUELCH_B_OFFSET)
529 .copied()
530 .map_or(0, |b| b.min(6))
531 }
532
533 #[must_use]
541 pub fn fm_narrow(&self) -> u8 {
542 self.image.get(FM_NARROW_OFFSET).copied().unwrap_or(0)
543 }
544
545 #[must_use]
549 pub fn ssb_high_cut(&self) -> u8 {
550 self.image.get(SSB_HIGH_CUT_OFFSET).copied().unwrap_or(0)
551 }
552
553 #[must_use]
557 pub fn cw_high_cut(&self) -> u8 {
558 self.image.get(CW_HIGH_CUT_OFFSET).copied().unwrap_or(0)
559 }
560
561 #[must_use]
565 pub fn am_high_cut(&self) -> u8 {
566 self.image.get(AM_HIGH_CUT_OFFSET).copied().unwrap_or(0)
567 }
568
569 #[must_use]
573 pub fn auto_filter(&self) -> u8 {
574 self.image.get(AUTO_FILTER_OFFSET).copied().unwrap_or(0)
575 }
576
577 #[must_use]
581 pub fn scan_resume(&self) -> u8 {
582 self.image.get(SCAN_RESUME_OFFSET).copied().unwrap_or(0)
583 }
584
585 #[must_use]
589 pub fn digital_scan_resume(&self) -> u8 {
590 self.image
591 .get(DIGITAL_SCAN_RESUME_OFFSET)
592 .copied()
593 .unwrap_or(0)
594 }
595
596 #[must_use]
600 pub fn scan_restart_time(&self) -> u8 {
601 self.image
602 .get(SCAN_RESTART_TIME_OFFSET)
603 .copied()
604 .unwrap_or(0)
605 }
606
607 #[must_use]
611 pub fn scan_restart_carrier(&self) -> u8 {
612 self.image
613 .get(SCAN_RESTART_CARRIER_OFFSET)
614 .copied()
615 .unwrap_or(0)
616 }
617
618 #[must_use]
622 pub fn timeout_timer(&self) -> u8 {
623 self.image.get(TIMEOUT_TIMER_OFFSET).copied().unwrap_or(0)
624 }
625
626 #[must_use]
630 pub fn tx_inhibit(&self) -> bool {
631 self.image.get(TX_INHIBIT_OFFSET).is_some_and(|&b| b != 0)
632 }
633
634 #[must_use]
638 pub fn beat_shift(&self) -> bool {
639 self.image.get(BEAT_SHIFT_OFFSET).is_some_and(|&b| b != 0)
640 }
641
642 #[must_use]
646 pub fn vox_tx_on_busy(&self) -> bool {
647 self.image
648 .get(VOX_TX_ON_BUSY_OFFSET)
649 .is_some_and(|&b| b != 0)
650 }
651
652 #[must_use]
656 pub fn cw_break_in(&self) -> bool {
657 self.image.get(CW_BREAK_IN_OFFSET).is_some_and(|&b| b != 0)
658 }
659
660 #[must_use]
664 pub fn cw_delay_time(&self) -> u8 {
665 self.image.get(CW_DELAY_TIME_OFFSET).copied().unwrap_or(0)
666 }
667
668 #[must_use]
672 pub fn cw_pitch(&self) -> u8 {
673 self.image.get(CW_PITCH_OFFSET).copied().unwrap_or(0)
674 }
675
676 #[must_use]
680 pub fn dtmf_speed(&self) -> u8 {
681 self.image.get(DTMF_SPEED_OFFSET).copied().unwrap_or(0)
682 }
683
684 #[must_use]
688 pub fn dtmf_pause_time(&self) -> u8 {
689 self.image.get(DTMF_PAUSE_TIME_OFFSET).copied().unwrap_or(0)
690 }
691
692 #[must_use]
696 pub fn dtmf_tx_hold(&self) -> bool {
697 self.image.get(DTMF_TX_HOLD_OFFSET).is_some_and(|&b| b != 0)
698 }
699
700 #[must_use]
704 pub fn repeater_auto_offset(&self) -> bool {
705 self.image
706 .get(REPEATER_AUTO_OFFSET_OFFSET)
707 .is_some_and(|&b| b != 0)
708 }
709
710 #[must_use]
714 pub fn repeater_call_key(&self) -> u8 {
715 self.image
716 .get(REPEATER_CALL_KEY_OFFSET)
717 .copied()
718 .unwrap_or(0)
719 }
720
721 #[must_use]
725 pub fn mic_sensitivity(&self) -> u8 {
726 self.image.get(MIC_SENSITIVITY_OFFSET).copied().unwrap_or(0)
727 }
728
729 #[must_use]
733 pub fn pf_key1(&self) -> u8 {
734 self.image.get(PF_KEY1_OFFSET).copied().unwrap_or(0)
735 }
736
737 #[must_use]
741 pub fn pf_key2(&self) -> u8 {
742 self.image.get(PF_KEY2_OFFSET).copied().unwrap_or(0)
743 }
744
745 #[must_use]
749 pub fn lock_key_a(&self) -> bool {
750 self.image.get(LOCK_KEY_A_OFFSET).is_some_and(|&b| b != 0)
751 }
752
753 #[must_use]
757 pub fn lock_key_b(&self) -> bool {
758 self.image.get(LOCK_KEY_B_OFFSET).is_some_and(|&b| b != 0)
759 }
760
761 #[must_use]
765 pub fn lock_key_c(&self) -> bool {
766 self.image.get(LOCK_KEY_C_OFFSET).is_some_and(|&b| b != 0)
767 }
768
769 #[must_use]
773 pub fn lock_key_ptt(&self) -> bool {
774 self.image.get(LOCK_KEY_PTT_OFFSET).is_some_and(|&b| b != 0)
775 }
776
777 #[must_use]
781 pub fn aprs_lock(&self) -> bool {
782 self.image.get(APRS_LOCK_OFFSET).is_some_and(|&b| b != 0)
783 }
784
785 #[must_use]
789 pub fn dual_display_size(&self) -> u8 {
790 self.image
791 .get(DUAL_DISPLAY_SIZE_OFFSET)
792 .copied()
793 .unwrap_or(0)
794 }
795
796 #[must_use]
800 pub fn display_area(&self) -> u8 {
801 self.image.get(DISPLAY_AREA_OFFSET).copied().unwrap_or(0)
802 }
803
804 #[must_use]
808 pub fn info_line(&self) -> u8 {
809 self.image.get(INFO_LINE_OFFSET).copied().unwrap_or(0)
810 }
811
812 #[must_use]
816 pub fn backlight_control(&self) -> u8 {
817 self.image
818 .get(BACKLIGHT_CONTROL_OFFSET)
819 .copied()
820 .unwrap_or(0)
821 }
822
823 #[must_use]
827 pub fn backlight_timer(&self) -> u8 {
828 self.image.get(BACKLIGHT_TIMER_OFFSET).copied().unwrap_or(0)
829 }
830
831 #[must_use]
835 pub fn display_hold_time(&self) -> u8 {
836 self.image
837 .get(DISPLAY_HOLD_TIME_OFFSET)
838 .copied()
839 .unwrap_or(0)
840 }
841
842 #[must_use]
846 pub fn display_method(&self) -> u8 {
847 self.image.get(DISPLAY_METHOD_OFFSET).copied().unwrap_or(0)
848 }
849
850 #[must_use]
854 pub fn power_on_display(&self) -> u8 {
855 self.image
856 .get(POWER_ON_DISPLAY_OFFSET)
857 .copied()
858 .unwrap_or(0)
859 }
860
861 #[must_use]
865 pub fn emr_volume_level(&self) -> u8 {
866 self.image
867 .get(EMR_VOLUME_LEVEL_OFFSET)
868 .copied()
869 .unwrap_or(0)
870 }
871
872 #[must_use]
876 pub fn auto_mute_return_time(&self) -> u8 {
877 self.image
878 .get(AUTO_MUTE_RETURN_TIME_OFFSET)
879 .copied()
880 .unwrap_or(0)
881 }
882
883 #[must_use]
887 pub fn announce(&self) -> bool {
888 self.image.get(ANNOUNCE_OFFSET).is_some_and(|&b| b != 0)
889 }
890
891 #[must_use]
895 pub fn voice_language(&self) -> u8 {
896 self.image.get(VOICE_LANGUAGE_OFFSET).copied().unwrap_or(0)
897 }
898
899 #[must_use]
903 pub fn voice_volume(&self) -> u8 {
904 self.image.get(VOICE_VOLUME_OFFSET).copied().unwrap_or(0)
905 }
906
907 #[must_use]
911 pub fn voice_speed(&self) -> u8 {
912 self.image.get(VOICE_SPEED_OFFSET).copied().unwrap_or(0)
913 }
914
915 #[must_use]
919 pub fn volume_lock(&self) -> bool {
920 self.image.get(VOLUME_LOCK_OFFSET).is_some_and(|&b| b != 0)
921 }
922
923 #[must_use]
927 pub fn bt_auto_connect(&self) -> bool {
928 self.image
929 .get(BT_AUTO_CONNECT_OFFSET)
930 .is_some_and(|&b| b != 0)
931 }
932
933 #[must_use]
937 pub fn gps_bt_interface(&self) -> u8 {
938 self.image
939 .get(GPS_BT_INTERFACE_OFFSET)
940 .copied()
941 .unwrap_or(0)
942 }
943
944 #[must_use]
948 pub fn pc_output_mode(&self) -> u8 {
949 self.image.get(PC_OUTPUT_MODE_OFFSET).copied().unwrap_or(0)
950 }
951
952 #[must_use]
956 pub fn aprs_usb_mode(&self) -> u8 {
957 self.image.get(APRS_USB_MODE_OFFSET).copied().unwrap_or(0)
958 }
959
960 #[must_use]
964 pub fn usb_audio_output(&self) -> bool {
965 self.image
966 .get(USB_AUDIO_OUTPUT_OFFSET)
967 .is_some_and(|&b| b != 0)
968 }
969
970 #[must_use]
974 pub fn internet_link(&self) -> bool {
975 self.image
976 .get(INTERNET_LINK_OFFSET)
977 .is_some_and(|&b| b != 0)
978 }
979
980 #[must_use]
984 pub fn power_on_message_flag(&self) -> bool {
985 self.image
986 .get(POWER_ON_MESSAGE_FLAG_OFFSET)
987 .is_some_and(|&b| b != 0)
988 }
989
990 #[must_use]
994 pub fn dual_band_mcp(&self) -> bool {
995 self.image
996 .get(DUAL_BAND_MCP_OFFSET)
997 .is_some_and(|&b| b != 0)
998 }
999
1000 #[must_use]
1009 pub fn power_level_a(&self) -> PowerLevel {
1010 self.image
1011 .get(POWER_LEVEL_A_OFFSET)
1012 .copied()
1013 .and_then(|b| PowerLevel::try_from(b).ok())
1014 .unwrap_or(PowerLevel::High)
1015 }
1016
1017 #[must_use]
1021 pub fn attenuator_a(&self) -> bool {
1022 self.image.get(ATTENUATOR_A_OFFSET).is_some_and(|&b| b != 0)
1023 }
1024
1025 #[must_use]
1029 pub fn dual_band(&self) -> bool {
1030 self.image.get(DUAL_BAND_OFFSET).is_some_and(|&b| b != 0)
1031 }
1032
1033 #[must_use]
1037 pub fn lock(&self) -> bool {
1038 self.image.get(LOCK_OFFSET).is_some_and(|&b| b != 0)
1039 }
1040
1041 #[must_use]
1045 pub fn bluetooth(&self) -> bool {
1046 self.image.get(BLUETOOTH_OFFSET).is_some_and(|&b| b != 0)
1047 }
1048
1049 #[must_use]
1073 pub fn vfo_raw(&self, index: usize) -> Option<&[u8]> {
1074 if index >= VFO_ENTRY_COUNT {
1075 return None;
1076 }
1077 let offset = VFO_DATA_OFFSET + index * VFO_ENTRY_SIZE;
1078 let end = offset + VFO_ENTRY_SIZE;
1079 if end <= self.image.len() {
1080 Some(&self.image[offset..end])
1081 } else {
1082 None
1083 }
1084 }
1085
1086 #[must_use]
1093 pub fn vfo_frequency(&self, index: usize) -> Option<Frequency> {
1094 let raw = self.vfo_raw(index)?;
1095 if raw.iter().all(|&b| b == 0xFF) {
1097 return None;
1098 }
1099 if raw[..4].iter().all(|&b| b == 0x00) {
1101 return None;
1102 }
1103 let freq = Frequency::from_le_bytes([raw[0], raw[1], raw[2], raw[3]]);
1104 Some(freq)
1105 }
1106
1107 #[must_use]
1115 pub fn vfo_mode(&self, index: usize) -> Option<MemoryMode> {
1116 let raw = self.vfo_raw(index)?;
1117 if raw.iter().all(|&b| b == 0xFF) {
1118 return None;
1119 }
1120 let mode_bits = (raw[0x09] >> 4) & 0x07;
1121 MemoryMode::try_from(mode_bits).ok()
1122 }
1123
1124 #[must_use]
1131 pub fn vfo_tx_offset(&self, index: usize) -> Option<Frequency> {
1132 let raw = self.vfo_raw(index)?;
1133 if raw.iter().all(|&b| b == 0xFF) {
1134 return None;
1135 }
1136 let offset = Frequency::from_le_bytes([raw[4], raw[5], raw[6], raw[7]]);
1137 Some(offset)
1138 }
1139
1140 #[must_use]
1142 pub fn vfo_count(&self) -> usize {
1143 (0..VFO_ENTRY_COUNT)
1144 .filter(|&i| self.vfo_frequency(i).is_some())
1145 .count()
1146 }
1147
1148 #[must_use]
1156 pub fn key_lock_type_raw(&self) -> u8 {
1157 self.image
1158 .get(KEY_LOCK_TYPE_OFFSET)
1159 .copied()
1160 .unwrap_or(0)
1161 .min(2)
1162 }
1163
1164 #[must_use]
1168 pub fn auto_power_off_raw(&self) -> u8 {
1169 self.image
1170 .get(AUTO_POWER_OFF_OFFSET)
1171 .copied()
1172 .unwrap_or(0)
1173 .min(4)
1174 }
1175
1176 #[must_use]
1180 pub fn speed_distance_unit_raw(&self) -> u8 {
1181 self.image
1182 .get(SPEED_DISTANCE_UNIT_OFFSET)
1183 .copied()
1184 .unwrap_or(0)
1185 .min(2)
1186 }
1187
1188 #[must_use]
1192 pub fn altitude_rain_unit_raw(&self) -> u8 {
1193 self.image
1194 .get(ALTITUDE_RAIN_UNIT_OFFSET)
1195 .copied()
1196 .unwrap_or(0)
1197 .min(1)
1198 }
1199
1200 #[must_use]
1204 pub fn temperature_unit_raw(&self) -> u8 {
1205 self.image
1206 .get(TEMPERATURE_UNIT_OFFSET)
1207 .copied()
1208 .unwrap_or(0)
1209 .min(1)
1210 }
1211}
1212
1213#[derive(Debug)]
1223pub struct SettingsWriter<'a> {
1224 image: &'a mut [u8],
1225}
1226
1227impl<'a> SettingsWriter<'a> {
1228 pub(crate) const fn new(image: &'a mut [u8]) -> Self {
1230 Self { image }
1231 }
1232
1233 pub fn set_backlight(&mut self, level: u8) {
1237 if let Some(b) = self.image.get_mut(BACKLIGHT_CONTROL_OFFSET) {
1238 *b = level;
1239 }
1240 }
1241
1242 pub fn set_backlight_control(&mut self, value: u8) {
1246 if let Some(b) = self.image.get_mut(BACKLIGHT_CONTROL_OFFSET) {
1247 *b = value;
1248 }
1249 }
1250
1251 pub fn set_backlight_timer(&mut self, value: u8) {
1255 if let Some(b) = self.image.get_mut(BACKLIGHT_TIMER_OFFSET) {
1256 *b = value;
1257 }
1258 }
1259
1260 pub fn set_beep_volume(&mut self, volume: u8) {
1266 if let Some(b) = self.image.get_mut(BEEP_VOLUME_OFFSET) {
1267 *b = volume.min(7);
1268 }
1269 }
1270
1271 pub fn set_auto_power_off(&mut self, value: AutoPowerOff) {
1275 if let Some(b) = self.image.get_mut(AUTO_POWER_OFF_OFFSET) {
1276 *b = match value {
1277 AutoPowerOff::Off => 0,
1278 AutoPowerOff::Min30 => 1,
1279 AutoPowerOff::Min60 => 2,
1280 AutoPowerOff::Min90 => 3,
1281 AutoPowerOff::Min120 => 4,
1282 };
1283 }
1284 }
1285
1286 pub fn set_battery_saver(&mut self, enabled: bool) {
1290 if let Some(b) = self.image.get_mut(BATTERY_SAVER_OFFSET) {
1291 *b = u8::from(enabled);
1292 }
1293 }
1294
1295 pub fn set_key_lock_type(&mut self, value: KeyLockType) {
1299 if let Some(b) = self.image.get_mut(KEY_LOCK_TYPE_OFFSET) {
1300 *b = match value {
1301 KeyLockType::KeyOnly => 0,
1302 KeyLockType::KeyAndPtt => 1,
1303 KeyLockType::KeyPttAndDial => 2,
1304 };
1305 }
1306 }
1307
1308 pub fn set_language(&mut self, value: Language) {
1312 if let Some(b) = self.image.get_mut(LANGUAGE_OFFSET) {
1313 *b = match value {
1314 Language::English => 0,
1315 Language::Japanese => 1,
1316 };
1317 }
1318 }
1319
1320 pub fn set_speed_distance_unit(&mut self, value: SpeedDistanceUnit) {
1324 if let Some(b) = self.image.get_mut(SPEED_DISTANCE_UNIT_OFFSET) {
1325 *b = match value {
1326 SpeedDistanceUnit::MilesPerHour => 0,
1327 SpeedDistanceUnit::KilometersPerHour => 1,
1328 SpeedDistanceUnit::Knots => 2,
1329 };
1330 }
1331 }
1332
1333 pub fn set_altitude_rain_unit(&mut self, value: AltitudeRainUnit) {
1337 if let Some(b) = self.image.get_mut(ALTITUDE_RAIN_UNIT_OFFSET) {
1338 *b = match value {
1339 AltitudeRainUnit::FeetInch => 0,
1340 AltitudeRainUnit::MetersMm => 1,
1341 };
1342 }
1343 }
1344
1345 pub fn set_temperature_unit(&mut self, value: TemperatureUnit) {
1349 if let Some(b) = self.image.get_mut(TEMPERATURE_UNIT_OFFSET) {
1350 *b = match value {
1351 TemperatureUnit::Fahrenheit => 0,
1352 TemperatureUnit::Celsius => 1,
1353 };
1354 }
1355 }
1356
1357 pub fn set_vox_delay(&mut self, delay: u8) {
1361 if let Some(b) = self.image.get_mut(VOX_DELAY_OFFSET) {
1362 *b = delay.min(30);
1363 }
1364 }
1365
1366 pub fn set_vox_tx_on_busy(&mut self, enabled: bool) {
1370 if let Some(b) = self.image.get_mut(VOX_TX_ON_BUSY_OFFSET) {
1371 *b = u8::from(enabled);
1372 }
1373 }
1374
1375 pub fn set_squelch_a(&mut self, level: u8) {
1379 if let Some(b) = self.image.get_mut(SQUELCH_A_OFFSET) {
1380 *b = level.min(6);
1381 }
1382 }
1383
1384 pub fn set_squelch_b(&mut self, level: u8) {
1388 if let Some(b) = self.image.get_mut(SQUELCH_B_OFFSET) {
1389 *b = level.min(6);
1390 }
1391 }
1392
1393 pub fn set_fm_narrow(&mut self, value: u8) {
1397 if let Some(b) = self.image.get_mut(FM_NARROW_OFFSET) {
1398 *b = value;
1399 }
1400 }
1401
1402 pub fn set_auto_filter(&mut self, value: u8) {
1406 if let Some(b) = self.image.get_mut(AUTO_FILTER_OFFSET) {
1407 *b = value;
1408 }
1409 }
1410
1411 pub fn set_scan_resume(&mut self, value: u8) {
1415 if let Some(b) = self.image.get_mut(SCAN_RESUME_OFFSET) {
1416 *b = value;
1417 }
1418 }
1419
1420 pub fn set_digital_scan_resume(&mut self, value: u8) {
1424 if let Some(b) = self.image.get_mut(DIGITAL_SCAN_RESUME_OFFSET) {
1425 *b = value;
1426 }
1427 }
1428
1429 pub fn set_timeout_timer(&mut self, value: u8) {
1433 if let Some(b) = self.image.get_mut(TIMEOUT_TIMER_OFFSET) {
1434 *b = value;
1435 }
1436 }
1437
1438 pub fn set_tx_inhibit(&mut self, enabled: bool) {
1442 if let Some(b) = self.image.get_mut(TX_INHIBIT_OFFSET) {
1443 *b = u8::from(enabled);
1444 }
1445 }
1446
1447 pub fn set_beat_shift(&mut self, enabled: bool) {
1451 if let Some(b) = self.image.get_mut(BEAT_SHIFT_OFFSET) {
1452 *b = u8::from(enabled);
1453 }
1454 }
1455
1456 pub fn set_cw_break_in(&mut self, enabled: bool) {
1460 if let Some(b) = self.image.get_mut(CW_BREAK_IN_OFFSET) {
1461 *b = u8::from(enabled);
1462 }
1463 }
1464
1465 pub fn set_cw_pitch(&mut self, value: u8) {
1469 if let Some(b) = self.image.get_mut(CW_PITCH_OFFSET) {
1470 *b = value;
1471 }
1472 }
1473
1474 pub fn set_dtmf_speed(&mut self, value: u8) {
1478 if let Some(b) = self.image.get_mut(DTMF_SPEED_OFFSET) {
1479 *b = value;
1480 }
1481 }
1482
1483 pub fn set_mic_sensitivity(&mut self, value: u8) {
1487 if let Some(b) = self.image.get_mut(MIC_SENSITIVITY_OFFSET) {
1488 *b = value;
1489 }
1490 }
1491
1492 pub fn set_pf_key1(&mut self, value: u8) {
1496 if let Some(b) = self.image.get_mut(PF_KEY1_OFFSET) {
1497 *b = value;
1498 }
1499 }
1500
1501 pub fn set_pf_key2(&mut self, value: u8) {
1505 if let Some(b) = self.image.get_mut(PF_KEY2_OFFSET) {
1506 *b = value;
1507 }
1508 }
1509
1510 pub fn set_aprs_lock(&mut self, enabled: bool) {
1514 if let Some(b) = self.image.get_mut(APRS_LOCK_OFFSET) {
1515 *b = u8::from(enabled);
1516 }
1517 }
1518
1519 pub fn set_dual_display_size(&mut self, value: u8) {
1523 if let Some(b) = self.image.get_mut(DUAL_DISPLAY_SIZE_OFFSET) {
1524 *b = value;
1525 }
1526 }
1527
1528 pub fn set_display_area(&mut self, value: u8) {
1532 if let Some(b) = self.image.get_mut(DISPLAY_AREA_OFFSET) {
1533 *b = value;
1534 }
1535 }
1536
1537 pub fn set_info_line(&mut self, value: u8) {
1541 if let Some(b) = self.image.get_mut(INFO_LINE_OFFSET) {
1542 *b = value;
1543 }
1544 }
1545
1546 pub fn set_volume_lock(&mut self, enabled: bool) {
1550 if let Some(b) = self.image.get_mut(VOLUME_LOCK_OFFSET) {
1551 *b = u8::from(enabled);
1552 }
1553 }
1554
1555 pub fn set_bt_auto_connect(&mut self, enabled: bool) {
1559 if let Some(b) = self.image.get_mut(BT_AUTO_CONNECT_OFFSET) {
1560 *b = u8::from(enabled);
1561 }
1562 }
1563
1564 pub fn set_pc_output_mode(&mut self, value: u8) {
1568 if let Some(b) = self.image.get_mut(PC_OUTPUT_MODE_OFFSET) {
1569 *b = value;
1570 }
1571 }
1572
1573 pub fn set_aprs_usb_mode(&mut self, value: u8) {
1577 if let Some(b) = self.image.get_mut(APRS_USB_MODE_OFFSET) {
1578 *b = value;
1579 }
1580 }
1581
1582 pub fn set_power_on_message_flag(&mut self, enabled: bool) {
1586 if let Some(b) = self.image.get_mut(POWER_ON_MESSAGE_FLAG_OFFSET) {
1587 *b = u8::from(enabled);
1588 }
1589 }
1590
1591 pub fn set_dual_band_mcp(&mut self, enabled: bool) {
1595 if let Some(b) = self.image.get_mut(DUAL_BAND_MCP_OFFSET) {
1596 *b = u8::from(enabled);
1597 }
1598 }
1599
1600 pub fn set_key_beep(&mut self, enabled: bool) {
1604 if let Some(b) = self.image.get_mut(KEY_BEEP_OFFSET) {
1605 *b = u8::from(enabled);
1606 }
1607 }
1608
1609 pub fn set_vox_enabled(&mut self, enabled: bool) {
1613 if let Some(b) = self.image.get_mut(VOX_ENABLED_OFFSET) {
1614 *b = u8::from(enabled);
1615 }
1616 }
1617
1618 pub fn set_vox_gain(&mut self, gain: u8) {
1624 if let Some(b) = self.image.get_mut(VOX_GAIN_OFFSET) {
1625 *b = gain.min(9);
1626 }
1627 }
1628
1629 pub fn set_lock(&mut self, locked: bool) {
1633 if let Some(b) = self.image.get_mut(LOCK_OFFSET) {
1634 *b = u8::from(locked);
1635 }
1636 }
1637
1638 pub fn set_dual_band(&mut self, enabled: bool) {
1642 if let Some(b) = self.image.get_mut(DUAL_BAND_OFFSET) {
1643 *b = u8::from(enabled);
1644 }
1645 }
1646
1647 pub fn set_attenuator_a(&mut self, enabled: bool) {
1651 if let Some(b) = self.image.get_mut(ATTENUATOR_A_OFFSET) {
1652 *b = u8::from(enabled);
1653 }
1654 }
1655
1656 pub fn set_power_level_a(&mut self, level: PowerLevel) {
1660 if let Some(b) = self.image.get_mut(POWER_LEVEL_A_OFFSET) {
1661 *b = u8::from(level);
1662 }
1663 }
1664
1665 pub fn set_bluetooth(&mut self, enabled: bool) {
1669 if let Some(b) = self.image.get_mut(BLUETOOTH_OFFSET) {
1670 *b = u8::from(enabled);
1671 }
1672 }
1673
1674 pub fn set_ssb_high_cut(&mut self, value: u8) {
1682 if let Some(b) = self.image.get_mut(SSB_HIGH_CUT_OFFSET) {
1683 *b = value;
1684 }
1685 }
1686
1687 pub fn set_cw_high_cut(&mut self, value: u8) {
1691 if let Some(b) = self.image.get_mut(CW_HIGH_CUT_OFFSET) {
1692 *b = value;
1693 }
1694 }
1695
1696 pub fn set_am_high_cut(&mut self, value: u8) {
1700 if let Some(b) = self.image.get_mut(AM_HIGH_CUT_OFFSET) {
1701 *b = value;
1702 }
1703 }
1704
1705 pub fn set_scan_restart_time(&mut self, value: u8) {
1709 if let Some(b) = self.image.get_mut(SCAN_RESTART_TIME_OFFSET) {
1710 *b = value;
1711 }
1712 }
1713
1714 pub fn set_scan_restart_carrier(&mut self, value: u8) {
1718 if let Some(b) = self.image.get_mut(SCAN_RESTART_CARRIER_OFFSET) {
1719 *b = value;
1720 }
1721 }
1722
1723 pub fn set_cw_delay_time(&mut self, value: u8) {
1727 if let Some(b) = self.image.get_mut(CW_DELAY_TIME_OFFSET) {
1728 *b = value;
1729 }
1730 }
1731
1732 pub fn set_dtmf_pause_time(&mut self, value: u8) {
1736 if let Some(b) = self.image.get_mut(DTMF_PAUSE_TIME_OFFSET) {
1737 *b = value;
1738 }
1739 }
1740
1741 pub fn set_dtmf_tx_hold(&mut self, enabled: bool) {
1745 if let Some(b) = self.image.get_mut(DTMF_TX_HOLD_OFFSET) {
1746 *b = u8::from(enabled);
1747 }
1748 }
1749
1750 pub fn set_repeater_auto_offset(&mut self, enabled: bool) {
1754 if let Some(b) = self.image.get_mut(REPEATER_AUTO_OFFSET_OFFSET) {
1755 *b = u8::from(enabled);
1756 }
1757 }
1758
1759 pub fn set_repeater_call_key(&mut self, value: u8) {
1763 if let Some(b) = self.image.get_mut(REPEATER_CALL_KEY_OFFSET) {
1764 *b = value;
1765 }
1766 }
1767
1768 pub fn set_lock_key_a(&mut self, enabled: bool) {
1772 if let Some(b) = self.image.get_mut(LOCK_KEY_A_OFFSET) {
1773 *b = u8::from(enabled);
1774 }
1775 }
1776
1777 pub fn set_lock_key_b(&mut self, enabled: bool) {
1781 if let Some(b) = self.image.get_mut(LOCK_KEY_B_OFFSET) {
1782 *b = u8::from(enabled);
1783 }
1784 }
1785
1786 pub fn set_lock_key_c(&mut self, enabled: bool) {
1790 if let Some(b) = self.image.get_mut(LOCK_KEY_C_OFFSET) {
1791 *b = u8::from(enabled);
1792 }
1793 }
1794
1795 pub fn set_lock_key_ptt(&mut self, enabled: bool) {
1799 if let Some(b) = self.image.get_mut(LOCK_KEY_PTT_OFFSET) {
1800 *b = u8::from(enabled);
1801 }
1802 }
1803
1804 pub fn set_display_hold_time(&mut self, value: u8) {
1808 if let Some(b) = self.image.get_mut(DISPLAY_HOLD_TIME_OFFSET) {
1809 *b = value;
1810 }
1811 }
1812
1813 pub fn set_display_method(&mut self, value: u8) {
1817 if let Some(b) = self.image.get_mut(DISPLAY_METHOD_OFFSET) {
1818 *b = value;
1819 }
1820 }
1821
1822 pub fn set_power_on_display(&mut self, value: u8) {
1826 if let Some(b) = self.image.get_mut(POWER_ON_DISPLAY_OFFSET) {
1827 *b = value;
1828 }
1829 }
1830
1831 pub fn set_emr_volume_level(&mut self, value: u8) {
1835 if let Some(b) = self.image.get_mut(EMR_VOLUME_LEVEL_OFFSET) {
1836 *b = value;
1837 }
1838 }
1839
1840 pub fn set_auto_mute_return_time(&mut self, value: u8) {
1844 if let Some(b) = self.image.get_mut(AUTO_MUTE_RETURN_TIME_OFFSET) {
1845 *b = value;
1846 }
1847 }
1848
1849 pub fn set_announce(&mut self, enabled: bool) {
1853 if let Some(b) = self.image.get_mut(ANNOUNCE_OFFSET) {
1854 *b = u8::from(enabled);
1855 }
1856 }
1857
1858 pub fn set_voice_language(&mut self, value: u8) {
1862 if let Some(b) = self.image.get_mut(VOICE_LANGUAGE_OFFSET) {
1863 *b = value;
1864 }
1865 }
1866
1867 pub fn set_voice_volume(&mut self, value: u8) {
1871 if let Some(b) = self.image.get_mut(VOICE_VOLUME_OFFSET) {
1872 *b = value;
1873 }
1874 }
1875
1876 pub fn set_voice_speed(&mut self, value: u8) {
1880 if let Some(b) = self.image.get_mut(VOICE_SPEED_OFFSET) {
1881 *b = value;
1882 }
1883 }
1884
1885 pub fn set_usb_audio_output(&mut self, enabled: bool) {
1889 if let Some(b) = self.image.get_mut(USB_AUDIO_OUTPUT_OFFSET) {
1890 *b = u8::from(enabled);
1891 }
1892 }
1893
1894 pub fn set_internet_link(&mut self, enabled: bool) {
1898 if let Some(b) = self.image.get_mut(INTERNET_LINK_OFFSET) {
1899 *b = u8::from(enabled);
1900 }
1901 }
1902
1903 pub fn set_gps_bt_interface(&mut self, value: u8) {
1907 if let Some(b) = self.image.get_mut(GPS_BT_INTERFACE_OFFSET) {
1908 *b = value;
1909 }
1910 }
1911
1912 pub fn set_key_lock_type_raw(&mut self, value: u8) {
1920 if let Some(b) = self.image.get_mut(KEY_LOCK_TYPE_OFFSET) {
1921 *b = value.min(2);
1922 }
1923 }
1924
1925 pub fn set_auto_power_off_raw(&mut self, value: u8) {
1929 if let Some(b) = self.image.get_mut(AUTO_POWER_OFF_OFFSET) {
1930 *b = value.min(4);
1931 }
1932 }
1933
1934 pub fn set_speed_distance_unit_raw(&mut self, value: u8) {
1938 if let Some(b) = self.image.get_mut(SPEED_DISTANCE_UNIT_OFFSET) {
1939 *b = value.min(2);
1940 }
1941 }
1942
1943 pub fn set_altitude_rain_unit_raw(&mut self, value: u8) {
1947 if let Some(b) = self.image.get_mut(ALTITUDE_RAIN_UNIT_OFFSET) {
1948 *b = value.min(1);
1949 }
1950 }
1951
1952 pub fn set_temperature_unit_raw(&mut self, value: u8) {
1956 if let Some(b) = self.image.get_mut(TEMPERATURE_UNIT_OFFSET) {
1957 *b = value.min(1);
1958 }
1959 }
1960}
1961
1962fn extract_string(image: &[u8], offset: usize, max_len: usize) -> String {
1964 let end = offset + max_len;
1965 if end > image.len() {
1966 return String::new();
1967 }
1968 let slice = &image[offset..end];
1969 let nul = slice.iter().position(|&b| b == 0).unwrap_or(max_len);
1970 String::from_utf8_lossy(&slice[..nul]).trim().to_string()
1971}
1972
1973#[cfg(test)]
1978mod tests {
1979 use super::*;
1980 use crate::protocol::programming::TOTAL_SIZE;
1981 use crate::types::settings::{
1982 AltitudeRainUnit, AutoPowerOff, KeyLockType, Language, SpeedDistanceUnit, TemperatureUnit,
1983 };
1984
1985 fn make_settings_image() -> Vec<u8> {
1986 let mut image = vec![0x00_u8; TOTAL_SIZE];
1987
1988 let msg = b"Hello D75!\0\0\0\0\0\0";
1990 image[POWER_ON_MESSAGE_OFFSET..POWER_ON_MESSAGE_OFFSET + 16].copy_from_slice(msg);
1991
1992 let model = b"TH-D75A\0\0\0\0\0\0\0\0\0";
1994 image[MODEL_NAME_OFFSET..MODEL_NAME_OFFSET + 16].copy_from_slice(model);
1995
1996 image
1997 }
1998
1999 #[test]
2000 fn settings_power_on_message() {
2001 let image = make_settings_image();
2002 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2003 let settings = mi.settings();
2004 assert_eq!(settings.power_on_message(), "Hello D75!");
2005 }
2006
2007 #[test]
2008 fn settings_model_name() {
2009 let image = make_settings_image();
2010 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2011 let settings = mi.settings();
2012 assert_eq!(settings.model_name(), "TH-D75A");
2013 }
2014
2015 #[test]
2016 fn settings_raw_not_none() {
2017 let image = make_settings_image();
2018 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2019 let settings = mi.settings();
2020 let raw = settings.raw().unwrap();
2021 assert_eq!(raw.len(), SETTINGS_SIZE);
2022 }
2023
2024 #[test]
2025 fn settings_read_bytes() {
2026 let image = make_settings_image();
2027 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2028 let settings = mi.settings();
2029 let bytes = settings.read_bytes(POWER_ON_MESSAGE_OFFSET, 10).unwrap();
2031 assert_eq!(&bytes[..10], b"Hello D75!");
2032 }
2033
2034 #[test]
2039 fn settings_key_beep() {
2040 let mut image = make_settings_image();
2041 image[KEY_BEEP_OFFSET] = 1;
2042 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2043 assert!(mi.settings().key_beep());
2044 }
2045
2046 #[test]
2047 fn settings_key_beep_off() {
2048 let image = make_settings_image();
2049 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2050 assert!(!mi.settings().key_beep());
2051 }
2052
2053 #[test]
2054 fn settings_vox() {
2055 let mut image = make_settings_image();
2056 image[VOX_ENABLED_OFFSET] = 1;
2057 image[VOX_GAIN_OFFSET] = 7;
2058 image[VOX_DELAY_OFFSET] = 5;
2059 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2060 let settings = mi.settings();
2061 assert!(settings.vox_enabled());
2062 assert_eq!(settings.vox_gain(), 7);
2063 assert_eq!(settings.vox_delay(), 5);
2064 }
2065
2066 #[test]
2067 fn settings_lock() {
2068 let mut image = make_settings_image();
2069 image[LOCK_OFFSET] = 1;
2070 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2071 assert!(mi.settings().lock());
2072 }
2073
2074 #[test]
2075 fn settings_lock_off() {
2076 let image = make_settings_image();
2077 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2078 assert!(!mi.settings().lock());
2079 }
2080
2081 #[test]
2082 fn settings_dual_band() {
2083 let mut image = make_settings_image();
2084 image[DUAL_BAND_OFFSET] = 1;
2085 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2086 assert!(mi.settings().dual_band());
2087 }
2088
2089 #[test]
2090 fn settings_dual_band_off() {
2091 let image = make_settings_image();
2092 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2093 assert!(!mi.settings().dual_band());
2094 }
2095
2096 #[test]
2097 fn settings_attenuator_a() {
2098 let mut image = make_settings_image();
2099 image[ATTENUATOR_A_OFFSET] = 1;
2100 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2101 assert!(mi.settings().attenuator_a());
2102 }
2103
2104 #[test]
2105 fn settings_attenuator_a_off() {
2106 let image = make_settings_image();
2107 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2108 assert!(!mi.settings().attenuator_a());
2109 }
2110
2111 #[test]
2112 fn settings_power_level_a() {
2113 let mut image = make_settings_image();
2114 image[POWER_LEVEL_A_OFFSET] = 2; let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2116 assert_eq!(mi.settings().power_level_a(), PowerLevel::Low);
2117 }
2118
2119 #[test]
2120 fn settings_power_level_a_default() {
2121 let image = make_settings_image();
2122 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2123 assert_eq!(mi.settings().power_level_a(), PowerLevel::High);
2125 }
2126
2127 #[test]
2128 fn settings_power_level_a_invalid_defaults_to_high() {
2129 let mut image = make_settings_image();
2130 image[POWER_LEVEL_A_OFFSET] = 0xFF;
2131 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2132 assert_eq!(mi.settings().power_level_a(), PowerLevel::High);
2133 }
2134
2135 #[test]
2136 fn settings_bluetooth() {
2137 let mut image = make_settings_image();
2138 image[BLUETOOTH_OFFSET] = 1;
2139 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2140 assert!(mi.settings().bluetooth());
2141 }
2142
2143 #[test]
2144 fn settings_bluetooth_off() {
2145 let image = make_settings_image();
2146 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2147 assert!(!mi.settings().bluetooth());
2148 }
2149
2150 #[test]
2155 fn settings_beep_volume() {
2156 let mut image = make_settings_image();
2157 image[BEEP_VOLUME_OFFSET] = 5;
2158 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2159 assert_eq!(mi.settings().beep_volume(), 5);
2160 }
2161
2162 #[test]
2163 fn settings_beep_volume_clamped() {
2164 let mut image = make_settings_image();
2165 image[BEEP_VOLUME_OFFSET] = 0xFF;
2166 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2167 assert_eq!(mi.settings().beep_volume(), 7);
2168 }
2169
2170 #[test]
2171 fn settings_backlight() {
2172 let mut image = make_settings_image();
2173 image[BACKLIGHT_CONTROL_OFFSET] = 4;
2174 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2175 assert_eq!(mi.settings().backlight(), 4);
2176 }
2177
2178 #[test]
2179 fn settings_auto_power_off() {
2180 let mut image = make_settings_image();
2181 image[AUTO_POWER_OFF_OFFSET] = 2;
2182 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2183 assert_eq!(mi.settings().auto_power_off(), AutoPowerOff::Min60);
2184 }
2185
2186 #[test]
2187 fn settings_auto_power_off_unknown_defaults_to_off() {
2188 let mut image = make_settings_image();
2189 image[AUTO_POWER_OFF_OFFSET] = 0xFF;
2190 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2191 assert_eq!(mi.settings().auto_power_off(), AutoPowerOff::Off);
2192 }
2193
2194 #[test]
2195 fn settings_battery_saver() {
2196 let mut image = make_settings_image();
2197 image[BATTERY_SAVER_OFFSET] = 1;
2198 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2199 assert!(mi.settings().battery_saver());
2200 }
2201
2202 #[test]
2203 fn settings_key_lock_type() {
2204 let mut image = make_settings_image();
2205 image[KEY_LOCK_TYPE_OFFSET] = 2;
2206 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2207 assert_eq!(mi.settings().key_lock_type(), KeyLockType::KeyPttAndDial);
2208 }
2209
2210 #[test]
2211 fn settings_display_units() {
2212 let mut image = make_settings_image();
2213 image[SPEED_DISTANCE_UNIT_OFFSET] = 1; image[ALTITUDE_RAIN_UNIT_OFFSET] = 1; image[TEMPERATURE_UNIT_OFFSET] = 1; let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2217 let units = mi.settings().display_units();
2218 assert_eq!(units.speed_distance, SpeedDistanceUnit::KilometersPerHour);
2219 assert_eq!(units.altitude_rain, AltitudeRainUnit::MetersMm);
2220 assert_eq!(units.temperature, TemperatureUnit::Celsius);
2221 }
2222
2223 #[test]
2224 fn settings_language() {
2225 let mut image = make_settings_image();
2226 image[LANGUAGE_OFFSET] = 1;
2227 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2228 assert_eq!(mi.settings().language(), Language::Japanese);
2229 }
2230
2231 #[test]
2232 fn settings_squelch() {
2233 let mut image = make_settings_image();
2234 image[SQUELCH_A_OFFSET] = 3;
2235 image[SQUELCH_B_OFFSET] = 4;
2236 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2237 let settings = mi.settings();
2238 assert_eq!(settings.squelch_a(), 3);
2239 assert_eq!(settings.squelch_b(), 4);
2240 }
2241
2242 #[test]
2243 fn settings_squelch_clamped() {
2244 let mut image = make_settings_image();
2245 image[SQUELCH_A_OFFSET] = 0xFF;
2246 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2247 assert_eq!(mi.settings().squelch_a(), 6);
2248 }
2249
2250 #[test]
2255 fn write_key_beep() {
2256 let image = make_settings_image();
2257 let mut mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2258 assert!(!mi.settings().key_beep());
2259 mi.settings_mut().set_key_beep(true);
2260 assert!(mi.settings().key_beep());
2261 mi.settings_mut().set_key_beep(false);
2262 assert!(!mi.settings().key_beep());
2263 }
2264
2265 #[test]
2266 fn write_vox_enabled() {
2267 let image = make_settings_image();
2268 let mut mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2269 assert!(!mi.settings().vox_enabled());
2270 mi.settings_mut().set_vox_enabled(true);
2271 assert!(mi.settings().vox_enabled());
2272 mi.settings_mut().set_vox_enabled(false);
2273 assert!(!mi.settings().vox_enabled());
2274 }
2275
2276 #[test]
2277 fn write_vox_gain() {
2278 let image = make_settings_image();
2279 let mut mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2280 mi.settings_mut().set_vox_gain(7);
2281 assert_eq!(mi.settings().vox_gain(), 7);
2282 }
2283
2284 #[test]
2285 fn write_vox_gain_clamped() {
2286 let image = make_settings_image();
2287 let mut mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2288 mi.settings_mut().set_vox_gain(0xFF);
2289 assert_eq!(mi.settings().vox_gain(), 9);
2290 }
2291
2292 #[test]
2293 fn write_lock() {
2294 let image = make_settings_image();
2295 let mut mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2296 assert!(!mi.settings().lock());
2297 mi.settings_mut().set_lock(true);
2298 assert!(mi.settings().lock());
2299 mi.settings_mut().set_lock(false);
2300 assert!(!mi.settings().lock());
2301 }
2302
2303 #[test]
2304 fn write_dual_band() {
2305 let image = make_settings_image();
2306 let mut mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2307 assert!(!mi.settings().dual_band());
2308 mi.settings_mut().set_dual_band(true);
2309 assert!(mi.settings().dual_band());
2310 mi.settings_mut().set_dual_band(false);
2311 assert!(!mi.settings().dual_band());
2312 }
2313
2314 #[test]
2315 fn write_attenuator_a() {
2316 let image = make_settings_image();
2317 let mut mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2318 assert!(!mi.settings().attenuator_a());
2319 mi.settings_mut().set_attenuator_a(true);
2320 assert!(mi.settings().attenuator_a());
2321 mi.settings_mut().set_attenuator_a(false);
2322 assert!(!mi.settings().attenuator_a());
2323 }
2324
2325 #[test]
2326 fn write_power_level_a() {
2327 let image = make_settings_image();
2328 let mut mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2329 mi.settings_mut().set_power_level_a(PowerLevel::Low);
2330 assert_eq!(mi.settings().power_level_a(), PowerLevel::Low);
2331 mi.settings_mut().set_power_level_a(PowerLevel::ExtraLow);
2332 assert_eq!(mi.settings().power_level_a(), PowerLevel::ExtraLow);
2333 mi.settings_mut().set_power_level_a(PowerLevel::High);
2334 assert_eq!(mi.settings().power_level_a(), PowerLevel::High);
2335 }
2336
2337 #[test]
2338 fn write_bluetooth() {
2339 let image = make_settings_image();
2340 let mut mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2341 assert!(!mi.settings().bluetooth());
2342 mi.settings_mut().set_bluetooth(true);
2343 assert!(mi.settings().bluetooth());
2344 mi.settings_mut().set_bluetooth(false);
2345 assert!(!mi.settings().bluetooth());
2346 }
2347
2348 #[test]
2349 fn write_roundtrip_all_verified() {
2350 let image = make_settings_image();
2351 let mut mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2352
2353 mi.settings_mut().set_key_beep(true);
2355 mi.settings_mut().set_vox_enabled(true);
2356 mi.settings_mut().set_vox_gain(9);
2357 mi.settings_mut().set_lock(true);
2358 mi.settings_mut().set_dual_band(true);
2359 mi.settings_mut().set_attenuator_a(true);
2360 mi.settings_mut().set_power_level_a(PowerLevel::ExtraLow);
2361 mi.settings_mut().set_bluetooth(true);
2362
2363 let s = mi.settings();
2365 assert!(s.key_beep());
2366 assert!(s.vox_enabled());
2367 assert_eq!(s.vox_gain(), 9);
2368 assert!(s.lock());
2369 assert!(s.dual_band());
2370 assert!(s.attenuator_a());
2371 assert_eq!(s.power_level_a(), PowerLevel::ExtraLow);
2372 assert!(s.bluetooth());
2373
2374 let raw = mi.as_raw();
2376 assert_eq!(raw[KEY_BEEP_OFFSET], 1);
2377 assert_eq!(raw[VOX_ENABLED_OFFSET], 1);
2378 assert_eq!(raw[VOX_GAIN_OFFSET], 9);
2379 assert_eq!(raw[LOCK_OFFSET], 1);
2380 assert_eq!(raw[DUAL_BAND_OFFSET], 1);
2381 assert_eq!(raw[ATTENUATOR_A_OFFSET], 1);
2382 assert_eq!(raw[POWER_LEVEL_A_OFFSET], 3); assert_eq!(raw[BLUETOOTH_OFFSET], 1);
2384 }
2385
2386 #[test]
2391 fn vfo_raw_accessible() {
2392 let mut image = make_settings_image();
2393 image[VFO_DATA_OFFSET..VFO_DATA_OFFSET + 4].copy_from_slice(&[0xDE, 0xAD, 0xBE, 0xEF]);
2395 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2396 let settings = mi.settings();
2397 let raw = settings.vfo_raw(0).unwrap();
2398 assert_eq!(raw.len(), VFO_ENTRY_SIZE);
2399 assert_eq!(&raw[..4], &[0xDE, 0xAD, 0xBE, 0xEF]);
2400 }
2401
2402 #[test]
2403 fn vfo_raw_out_of_range() {
2404 let image = make_settings_image();
2405 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2406 assert!(mi.settings().vfo_raw(6).is_none());
2407 }
2408
2409 #[test]
2410 fn vfo_frequency_valid() {
2411 let mut image = make_settings_image();
2412 let freq: u32 = 146_520_000;
2414 let offset = VFO_DATA_OFFSET;
2415 image[offset..offset + 4].copy_from_slice(&freq.to_le_bytes());
2416 image[offset + 8] = 0x50; let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2420 let f = mi.settings().vfo_frequency(0).unwrap();
2421 assert_eq!(f.as_hz(), 146_520_000);
2422 }
2423
2424 #[test]
2425 fn vfo_frequency_empty_entry() {
2426 let mut image = make_settings_image();
2427 let offset = VFO_DATA_OFFSET;
2429 image[offset..offset + VFO_ENTRY_SIZE].fill(0xFF);
2430
2431 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2432 assert!(mi.settings().vfo_frequency(0).is_none());
2433 }
2434
2435 #[test]
2436 fn vfo_frequency_zeroed_entry() {
2437 let image = make_settings_image(); let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2439 assert!(mi.settings().vfo_frequency(0).is_none());
2441 }
2442
2443 #[test]
2444 fn vfo_mode_fm() {
2445 let mut image = make_settings_image();
2446 let offset = VFO_DATA_OFFSET;
2447 image[offset..offset + 4].copy_from_slice(&146_520_000_u32.to_le_bytes());
2449 image[offset + 0x09] = 0x00;
2451
2452 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2453 let mode = mi.settings().vfo_mode(0).unwrap();
2454 assert_eq!(mode, MemoryMode::Fm);
2455 }
2456
2457 #[test]
2458 fn vfo_mode_am() {
2459 let mut image = make_settings_image();
2460 let offset = VFO_DATA_OFFSET;
2461 image[offset..offset + 4].copy_from_slice(&7_100_000_u32.to_le_bytes());
2462 image[offset + 0x09] = 0x20;
2464
2465 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2466 let mode = mi.settings().vfo_mode(0).unwrap();
2467 assert_eq!(mode, MemoryMode::Am);
2468 }
2469
2470 #[test]
2471 fn vfo_mode_lsb() {
2472 let mut image = make_settings_image();
2473 let offset = VFO_DATA_OFFSET;
2474 image[offset..offset + 4].copy_from_slice(&7_100_000_u32.to_le_bytes());
2475 image[offset + 0x09] = 0x30;
2477
2478 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2479 let mode = mi.settings().vfo_mode(0).unwrap();
2480 assert_eq!(mode, MemoryMode::Lsb);
2481 }
2482
2483 #[test]
2484 fn vfo_tx_offset() {
2485 let mut image = make_settings_image();
2486 let offset = VFO_DATA_OFFSET;
2487 image[offset..offset + 4].copy_from_slice(&146_520_000_u32.to_le_bytes());
2488 image[offset + 4..offset + 8].copy_from_slice(&600_000_u32.to_le_bytes());
2489
2490 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2491 let tx_off = mi.settings().vfo_tx_offset(0).unwrap();
2492 assert_eq!(tx_off.as_hz(), 600_000);
2493 }
2494
2495 #[test]
2496 fn vfo_count_none_populated() {
2497 let image = make_settings_image(); let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2499 assert_eq!(mi.settings().vfo_count(), 0);
2500 }
2501
2502 #[test]
2503 fn vfo_count_with_entries() {
2504 let mut image = make_settings_image();
2505 let offset0 = VFO_DATA_OFFSET;
2507 image[offset0..offset0 + 4].copy_from_slice(&146_520_000_u32.to_le_bytes());
2508 let offset2 = VFO_DATA_OFFSET + 2 * VFO_ENTRY_SIZE;
2509 image[offset2..offset2 + 4].copy_from_slice(&446_000_000_u32.to_le_bytes());
2510
2511 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2512 assert_eq!(mi.settings().vfo_count(), 2);
2513 }
2514
2515 #[test]
2516 fn vfo_second_entry_frequency() {
2517 let mut image = make_settings_image();
2518 let offset = VFO_DATA_OFFSET + VFO_ENTRY_SIZE; image[offset..offset + 4].copy_from_slice(&222_100_000_u32.to_le_bytes());
2520
2521 let mi = crate::memory::MemoryImage::from_raw(image).unwrap();
2522 let f = mi.settings().vfo_frequency(1).unwrap();
2523 assert_eq!(f.as_hz(), 222_100_000);
2524 }
2525}