diff --git a/chirp/drivers/th_uv88.py b/chirp/drivers/th_uv88.py
index 80a05766..b8bc709a 100644
--- a/chirp/drivers/th_uv88.py
+++ b/chirp/drivers/th_uv88.py
@@ -98,7 +98,61 @@ struct {
ab:1, // * A, B
unk_bit1_0:2; //
} workmodesettings;
+"""
+MEM_FORMAT_PT2 = """
+// #seekto 0x1180;
+struct {
+ u8 bitmap[26]; // one bit for each channel marked in use
+} chan_avail;
+
+#seekto 0x11A0;
+struct {
+ u8 bitmap[26]; // one bit for each channel skipped
+} chan_skip;
+
+#seekto 0x191E;
+struct {
+ u8 unknown191e:4, //
+ region:4; // 0x191E Radio Region (read only)
+ // 0 = Unlocked TX: 136-174 MHz / 400-480 MHz
+ // 2-3 = Unknown
+ // 3 = EU TX: 144-146 MHz / 430-440 MHz
+ // 4 = US TX: 144-148 MHz / 420-450 MHz
+ // 5-15 = Unknown
+} settings2;
+
+#seekto 0x1940;
+struct {
+ char name1[15]; // Intro Screen Line 1 (16 alpha text characters)
+ u8 unk1;
+ char name2[15]; // Intro Screen Line 2 (16 alpha text characters)
+ u8 unk2;
+} openradioname;
+
+struct fm_chn {
+ ul32 rxfreq;
+};
+
+// #seekto 0x1960;
+struct chname chan_name[199];
+
+#seekto 0x2180;
+struct fm_chn fm_stations[24];
+
+// #seekto 0x021E0;
+struct {
+ u8 fmset[4];
+} fmmap;
+
+// #seekto 0x21E4;
+struct {
+ ul32 fmcur;
+} fmfrqs;
+
+"""
+
+THUV88_SETTINGS = """
#seekto 0x1160;
struct {
u8 introScreen1[12]; // 0x1160 *Intro Screen Line 1(truncated to 12 alpha
@@ -151,56 +205,60 @@ struct {
u8 unk4; // 0x1179
u8 name2[6]; // 0x117A unused
} basicsettings;
+"""
-// #seekto 0x1180;
+RA89_SETTINGS = """
+#seekto 0x1160;
struct {
- u8 bitmap[26]; // one bit for each channel marked in use
-} chan_avail;
-
-#seekto 0x11A0;
-struct {
- u8 bitmap[26]; // one bit for each channel skipped
-} chan_skip;
-
-#seekto 0x191E;
-struct {
- u8 unknown191e:4, //
- region:4; // 0x191E Radio Region (read only)
- // 0 = Unlocked TX: 136-174 MHz / 400-480 MHz
- // 2-3 = Unknown
- // 3 = EU TX: 144-146 MHz / 430-440 MHz
- // 4 = US TX: 144-148 MHz / 420-450 MHz
- // 5-15 = Unknown
-} settings2;
-
-#seekto 0x1940;
-struct {
- char name1[15]; // Intro Screen Line 1 (16 alpha text characters)
- u8 unk1;
- char name2[15]; // Intro Screen Line 2 (16 alpha text characters)
- u8 unk2;
-} openradioname;
-
-struct fm_chn {
- ul32 rxfreq;
-};
-
-// #seekto 0x1960;
-struct chname chan_name[199];
-
-#seekto 0x2180;
-struct fm_chn fm_stations[24];
-
-// #seekto 0x21E0;
-struct {
- u8 fmset[4];
-} fmmap;
-
-// #seekto 0x21E4;
-struct {
- ul32 fmcur;
-} fmfrqs;
-
+ u8 sideKey2:4, // 0x1160 side key 2
+ sideKey1:4; // side key 1
+ u8 unknownBytes[11]; // 0x1161 - 0x116B
+ u8 offFreqVoltage : 3, // 0x116C unknown referred to in code but not on
+ // screen
+ unk_bit4 : 1, //
+ sqlLevel : 4; // [05] *OFF, 1-9
+ u8 beep : 1, // 0x116D [09] *OFF, On
+ callKind : 2, // code says 1750,2100,1000,1450 as options
+ // not on screen
+ introScreen: 2, // [20] *OFF, Voltage, Char String
+ unkstr2: 2, //
+ txChSelect : 1; // [02] *Last CH, Main CH
+ u8 autoPowOff : 3, // 0x116E not on screen? OFF, 30Min, 1HR, 2HR
+ unk : 1, //
+ tot : 4; // [11] *OFF, 30 Second, 60 Second, 90 Second,
+ // ... , 270 Second
+ u8 unk_bit7:1, // 0x116F
+ roger:1, // [14] *OFF, On
+ dailDef:1, // Unknown - 'Volume, Frequency'
+ language:1, // English only
+ endToneElim:2, // *Frequency, 120, 180, 240 (RA89)
+ unkCheckBox1:1, //
+ unkCheckBox2:1; //
+ u8 scanType: 2, // 0x1170 [17] *Off, On, 5s, 10s, 15s, 20s, 25s, 30s
+ disMode : 2, // [33] *Frequency, Channel, Name
+ ledMode: 4; // [07] *Off, On, 5s, 10s, 15s, 20s, 25s, 30s
+ u8 unky; // 0x1171
+ u8 str6; // 0x1172 Has flags to do with logging - factory
+ // enabled (bits 16,64,128)
+ u8 unk; // 0x1173
+ u8 swAudio : 1, // 0x1174 [19] *OFF, On
+ radioMoni : 1, // [34] *OFF, On
+ keylock : 1, // [18] *OFF, On
+ dualWait : 1, // [06] *OFF, On
+ unk_bit3 : 1, //
+ light : 3; // [08] *1, 2, 3, 4, 5, 6, 7
+ u8 voxSw : 1, // 0x1175 [13] *OFF, On
+ voxDelay: 4, // *0.5S, 1.0S, 1.5S, 2.0S, 2.5S, 3.0S, 3.5S,
+ // 4.0S, 4.5S, 5.0S
+ voxLevel : 3; // [03] *1, 2, 3, 4, 5, 6, 7
+ u8 str9 : 4, // 0x1176
+ saveMode : 2, // [16] *OFF, 1:1, 1:2, 1:4
+ keyMode : 2; // [32] *ALL, PTT, KEY, Key & Side Key
+ u8 unk2; // 0x1177
+ u8 unk3; // 0x1178
+ u8 unk4; // 0x1179
+ u8 name2[6]; // 0x117A unused
+} basicsettings;
"""
MEM_SIZE = 0x22A0
@@ -561,7 +619,8 @@ class THUV88Radio(chirp_common.CloneModeRadio):
def process_mmap(self):
"""Process the mem map into the mem object"""
- self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
+ mem_format = MEM_FORMAT + THUV88_SETTINGS + MEM_FORMAT_PT2
+ self._memobj = bitwise.parse(mem_format, self._mmap)
def get_raw_memory(self, number):
return repr(self._memobj.memory[number - 1])
@@ -819,7 +878,10 @@ class THUV88Radio(chirp_common.CloneModeRadio):
basic.append(rset)
# Menu 07 - LED Mode
- options = ["Off", "On", "Auto"]
+ if self.MODEL == "RA89":
+ options = ["Off", "On", "5s", "10s", "15s", "20s", "25s", "30s"]
+ else:
+ options = ["Off", "On", "Auto"]
rx = RadioSettingValueList(options, options[_settings.ledMode])
rset = RadioSetting("basicsettings.ledMode", "LED Display Mode", rx)
basic.append(rset)
@@ -906,7 +968,10 @@ class THUV88Radio(chirp_common.CloneModeRadio):
group.append(advanced)
# software only
- options = ['Off', 'Frequency']
+ if self.MODEL == "RA89":
+ options = ['Frequency', '120', '180', '240']
+ else:
+ options = ['Off', 'Frequency']
rx = RadioSettingValueList(options, options[_settings.endToneElim])
rset = RadioSetting("basicsettings.endToneElim", "End Tone Elim", rx)
advanced.append(rset)
@@ -958,6 +1023,17 @@ class THUV88Radio(chirp_common.CloneModeRadio):
rset = RadioSetting("settings2.region", "Region", rx)
advanced.append(rset)
+ if self.MODEL == "RA89":
+ options = ["VOX", "Dual Wait", "Scan", "Moni", "1750 Tone",
+ "Flashlight", "Power Level", "Alarm"]
+ rx = RadioSettingValueList(options, options[_settings.sideKey1])
+ rset = RadioSetting("basicsettings.sideKey1", "Side Key 1", rx)
+ advanced.append(rset)
+
+ rx = RadioSettingValueList(options, options[_settings.sideKey2])
+ rset = RadioSetting("basicsettings.sideKey2", "Side Key 2", rx)
+ advanced.append(rset)
+
workmode = RadioSettingGroup("workmode", "Work Mode Settings")
group.append(workmode)
@@ -971,7 +1047,7 @@ class THUV88Radio(chirp_common.CloneModeRadio):
options = ["Frequency", "Channel"]
rx = RadioSettingValueList(options, options[_workmode.vfomrmodeb])
rset = RadioSetting("workmodesettings.vfomrmodeb",
- "VFO/MR Mode B (BQ1.41+)", rx)
+ "VFO/MR Mode B", rx)
workmode.append(rset)
# Toggle with [A/B] key
@@ -1103,6 +1179,23 @@ class RT85(THUV88Radio):
MODEL = "RT85"
+@directory.register
+class RA89(THUV88Radio):
+ VENDOR = "Retevis"
+ MODEL = "RA89"
+
+ _magic0 = b"\xFE\xFE\xEE\xEF\xE0" + b"UV99" + b"\xFD"
+ _magic2 = b"\xFE\xFE\xEE\xEF\xE2" + b"UV99" + b"\xFD"
+ _magic3 = b"\xFE\xFE\xEE\xEF\xE3" + b"UV99" + b"\xFD"
+ _magic5 = b"\xFE\xFE\xEE\xEF\xE5" + b"UV99" + b"\xFD"
+ _fingerprint = b"\xFE\xFE\xEF\xEE\xE1" + b"UV99"
+
+ def process_mmap(self):
+ """Process the mem map into the mem object"""
+ mem_format = MEM_FORMAT + RA89_SETTINGS + MEM_FORMAT_PT2
+ self._memobj = bitwise.parse(mem_format, self._mmap)
+
+
@directory.register
class QRZ1(THUV88Radio):
VENDOR = "Explorer"
diff --git a/tests/Python3_Driver_Testing.md b/tests/Python3_Driver_Testing.md
index 517962ed..5b3dfccb 100644
--- a/tests/Python3_Driver_Testing.md
+++ b/tests/Python3_Driver_Testing.md
@@ -262,6 +262,7 @@
| Retevis_RA685 | [@KC9HI](https://github.com/KC9HI) | 19-Nov-2022 | Yes | 0.25% |
| Retevis_RA79 | [@KC9HI](https://github.com/KC9HI) | 27-Oct-2023 | Yes | |
| Retevis_RA85 | [@KC9HI](https://github.com/KC9HI) | 19-Nov-2022 | Yes | 0.05% |
+| Retevis_RA89 | [@KC9HI](https://github.com/KC9HI) | 23-Nov-2023 | Yes | |
| Retevis_RB15 | [@KC9HI](https://github.com/KC9HI) | 18-Dec-2022 | Yes | 0.01% |
| Retevis_RB17 | [@KC9HI](https://github.com/KC9HI) | 28-Nov-2022 | Yes | 0.02% |
| Retevis_RB17A | [@KC9HI](https://github.com/KC9HI) | 30-Nov-2022 | Yes | 0.01% |
@@ -427,11 +428,11 @@
| Zastone_ZT-X6 | [Implied by Retevis_RT22](#user-content-Retevis_RT22) | 9-Dec-2022 | Yes | 0.11% |
## Stats
-**Drivers:** 424
+**Drivers:** 425
-**Tested:** 87% (369/55) (93% of usage stats)
+**Tested:** 87% (370/55) (93% of usage stats)
-**Byte clean:** 90% (384/40)
+**Byte clean:** 90% (385/40)
## Meaning of this testing
diff --git a/tests/images/Retevis_RA89.img b/tests/images/Retevis_RA89.img
new file mode 100644
index 00000000..a33e97f3
Binary files /dev/null and b/tests/images/Retevis_RA89.img differ
diff --git a/tests/py3_driver_testers.txt b/tests/py3_driver_testers.txt
index 334d586d..f23cee84 100644
--- a/tests/py3_driver_testers.txt
+++ b/tests/py3_driver_testers.txt
@@ -231,6 +231,7 @@ Retevis_H777_Plus,@KC9HI,4-Dec-2022
Retevis_RA685,@KC9HI,19-Nov-2022
Retevis_RA79,@KC9HI,27-Oct-2023
Retevis_RA85,@KC9HI,19-Nov-2022
+Retevis_RA89,@KC9HI,23-Nov-2023
Retevis_RB15,@KC9HI,18-Dec-2022
Retevis_RB17,@KC9HI,28-Nov-2022
Retevis_RB17A,@KC9HI,30-Nov-2022