[chirp_devel] Fwd: [PATCH 3 of 4] [RB26] Retevis RB26
Jim Unroe
Sat Apr 3 18:03:55 PDT 2021
---------- Forwarded message ---------
From: Jim Unroe <kc9hi at comcast.net>
Date: Sat, Apr 3, 2021 at 8:59 PM
Subject: [PATCH 3 of 4] [RB26] Retevis RB26
To: <Rock.Unroe at gmail.com>
# HG changeset patch
# User Jim Unroe <rock.unroe at gmail.com>
# Date 1617488017 14400
# Sat Apr 03 18:13:37 2021 -0400
# Node ID c11f6ae9ccfd9b306db3b5f1e7fe166364a822b0
# Parent 53ff4720769f9db27a2a0a56885aa505fef3da09
[RB26] Retevis RB26
This patch adds support for the Retevis RB26.
Related to #8661
diff -r 53ff4720769f -r c11f6ae9ccfd chirp/drivers/retevis_rt21.py
--- a/chirp/drivers/retevis_rt21.py Sat Apr 03 18:09:19 2021 -0400
+++ b/chirp/drivers/retevis_rt21.py Sat Apr 03 18:13:37 2021 -0400
@@ -135,16 +135,86 @@
u8 skipflags[4]; // Scan Add
"""
+MEM_FORMAT_RB26 = """
+#seekto 0x0000;
+struct {
+ lbcd rxfreq[4]; // RX Frequency 0-3
+ lbcd txfreq[4]; // TX Frequency 4-7
+ ul16 rx_tone; // PL/DPL Decode 8-9
+ ul16 tx_tone; // PL/DPL Encode A-B
+ u8 compander:1, // Compander C
+ unknown1:1, //
+ highpower:1, // Power Level
+ wide:1, // Bandwidth
+ bcl:1, // Busy Lock OFF=0 ON=1
+ unknown2:3; //
+ u8 reserved[3]; // Reserved D-F
+} memory[30];
+
+#seekto 0x002D;
+struct {
+ u8 unknown_1:1, // 002D
+ chnumberd:1, // Channel Number Disable
+ gain:1, // MIC Gain
+ savem:1, // Battery Save Mode
+ save:1, // Battery Save
+ beep:1, // Beep
+ voice:1, // Voice Prompts
+ unknown_2:1; //
+ u8 squelch; // Squelch 002E
+ u8 tot; // Time-out Timer 002F
+ u8 channel_4[13]; // 0030-003C
+ u8 unknown_3[3]; // 003D-003F
+ u8 channel_5[13]; // 0040-004C
+ u8 unknown_4; // 004D
+ u8 unknown_5[2]; // 004E-004F
+ u8 channel_6[13]; // 0050-005C
+ u8 unknown_6; // 005D
+ u8 unknown_7[2]; // 005E-005F
+ u8 channel_7[13]; // 0060-006C
+ u8 warn; // Warn Mode 006D
+ u8 pf1; // Key Set PF1 006E
+ u8 pf2; // Key Set PF2 006F
+ u8 channel_8[13]; // 0070-007C
+ u8 unknown_8; // 007D
+ u8 tail; // QT/DQT Tail(inverted) 007E
+} settings;
+
+#seekto 0x01F0;
+u8 skipflags[4]; // Scan Add
+
+#seekto 0x029F;
+struct {
+ u8 chnumber; // Channel Number 029F
+} settings2;
+
+#seekto 0x031D;
+struct {
+ u8 unused:7, // 031D
+ vox:1; // Vox
+ u8 voxl; // Vox Level 031E
+ u8 voxd; // Vox Delay 031F
+} settings3;
+"""
+
CMD_ACK = "\x06"
ALARM_LIST = ["Local Alarm", "Remote Alarm"]
BCL_LIST = ["Off", "Carrier", "QT/DQT"]
CDCSS_LIST = ["Normal Code", "Special Code 2", "Special Code 1"]
+GAIN_LIST = ["Standard", "Enhanced"]
+PFKEY_LIST = ["None", "Monitor", "Lamp", "Warn", "VOX", "VOX Delay",
+ "Key Lock", "Scan"]
+SAVE_LIST = ["Standard", "Super"]
SCRAMBLE_LIST = ["%s" % x for x in range(1, 9)]
TIMEOUTTIMER_LIST = ["%s seconds" % x for x in range(15, 615, 15)]
TOTALERT_LIST = ["Off"] + ["%s seconds" % x for x in range(1, 11)]
VOICE_LIST = ["Off", "Chinese", "English"]
+VOICE_LIST2 = ["Off", "English"]
VOX_LIST = ["OFF"] + ["%s" % x for x in range(1, 17)]
+VOXD_LIST = ["0.5", "1.0", "1.5", "2.0", "2.5", "3.0"]
+VOXL_LIST = ["OFF"] + ["%s" % x for x in range(1, 9)]
+WARN_LIST = ["OFF", "Native Warn", "Remote Warn"]
PF1_CHOICES = ["None", "Monitor", "Scan", "Scramble", "Alarm"]
PF1_VALUES = [0x0F, 0x04, 0x06, 0x08, 0x0C]
TOPKEY_CHOICES = ["None", "Alarming"]
@@ -154,11 +224,18 @@
"alarm": ALARM_LIST,
"bcl": BCL_LIST,
"cdcss": CDCSS_LIST,
+ "gain": GAIN_LIST,
+ "pfkey": PFKEY_LIST,
+ "save": SAVE_LIST,
"scramble": SCRAMBLE_LIST,
"tot": TIMEOUTTIMER_LIST,
"totalert": TOTALERT_LIST,
"voice": VOICE_LIST,
+ "voice": VOICE_LIST2,
"vox": VOX_LIST,
+ "voxd": VOXD_LIST,
+ "voxl": VOXL_LIST,
+ "warn": WARN_LIST,
}
GMRS_FREQS1 = [462.5625, 462.5875, 462.6125, 462.6375, 462.6625,
@@ -176,6 +253,8 @@
exito = False
for i in range(0, 5):
serial.write(radio._magic)
+ if radio.MODEL == "RB26":
+ serial.read(1)
ack = serial.read(1)
try:
@@ -246,6 +325,34 @@
return block_data
+def _rb26_read_block(radio, block_addr, block_size):
+ serial = radio.pipe
+
+ cmd = struct.pack(">cHb", 'R', block_addr, block_size)
+ expectedresponse = "W" + cmd[1:]
+ LOG.debug("Reading block %04x..." % (block_addr))
+
+ try:
+ serial.write(cmd)
+ response = serial.read(4 + block_size)
+ if response[:4] != expectedresponse:
+ raise Exception("Error reading block %04x." % (block_addr))
+
+ block_data = response[4:]
+
+ if block_addr != 0:
+ serial.write(CMD_ACK)
+ ack = serial.read(1)
+ except:
+ raise errors.RadioError("Failed to read block at %04x" % block_addr)
+
+ if block_addr != 0:
+ if ack != CMD_ACK:
+ raise Exception("No ACK reading block %04x." % (block_addr))
+
+ return block_data
+
+
def _write_block(radio, block_addr, block_size):
serial = radio.pipe
@@ -280,7 +387,10 @@
status.cur = addr + radio.BLOCK_SIZE
radio.status_fn(status)
- block = _read_block(radio, addr, radio.BLOCK_SIZE)
+ if radio.MODEL == "RB26":
+ block = _rb26_read_block(radio, addr, radio.BLOCK_SIZE)
+ else:
+ block = _read_block(radio, addr, radio.BLOCK_SIZE)
data += block
LOG.debug("Address: %04x" % addr)
@@ -504,6 +614,9 @@
else:
_mem = self._memobj.memory[number - 1]
+ if self._reserved:
+ _rsvd = _mem.reserved.get_raw()
+
mem.freq = int(_mem.rxfreq) * 10
# We'll consider any blank (i.e. 0MHz frequency) to be empty
@@ -520,6 +633,8 @@
LOG.debug("Initializing empty memory")
if self.MODEL == "RB17A":
_mem.set_raw("\x00" * 13 + "\x04\xFF\xFF")
+ if self.MODEL == "RB26":
+ _mem.set_raw("\x00" * 13 + _rsvd)
else:
_mem.set_raw("\x00" * 13 + "\x30\x8F\xF8")
@@ -556,6 +671,15 @@
rset = RadioSetting("cdcss", "Cdcss Mode", rs)
mem.extra.append(rset)
+ if self.MODEL == "RB26":
+ rs = RadioSettingValueBoolean(_mem.bcl)
+ rset = RadioSetting("bcl", "Busy Channel Lockout", rs)
+ mem.extra.append(rset)
+
+ rs = RadioSettingValueBoolean(_mem.compander)
+ rset = RadioSetting("compander", "Compander", rs)
+ mem.extra.append(rset)
+
if self._gmrs:
GMRS_IMMUTABLE = ["freq", "duplex", "offset"]
if mem.number >= 8 and mem.number <= 14:
@@ -619,9 +743,14 @@
else:
_mem = self._memobj.memory[mem.number - 1]
+ if self._reserved:
+ _rsvd = _mem.reserved.get_raw()
+
if mem.empty:
if self.MODEL == "RB17A":
_mem.set_raw("\xFF" * 12 + "\x00\x00\xFF\xFF")
+ elif self.MODEL == "RB26":
+ _mem.set_raw("\xFF" * 13 + _rsvd)
else:
_mem.set_raw("\xFF" * (_mem.size() / 8))
@@ -644,6 +773,8 @@
if self.MODEL == "RB17A":
_mem.set_raw("\x00" * 13 + "\x00\xFF\xFF")
+ elif self.MODEL == "RB26":
+ _mem.set_raw("\x00" * 13 + _rsvd)
else:
_mem.set_raw("\x00" * 13 + "\x30\x8F\xF8")
@@ -763,6 +894,76 @@
rset.set_apply_callback(apply_topkey_listvalue, _keys.topkey)
basic.append(rset)
+ if self.MODEL == "RB26":
+ _settings2 = self._memobj.settings2
+ _settings3 = self._memobj.settings3
+
+ rs = RadioSettingValueInteger(0, 9, _settings.squelch)
+ rset = RadioSetting("squelch", "Squelch Level", rs)
+ basic.append(rset)
+
+ rs = RadioSettingValueList(TIMEOUTTIMER_LIST,
+ TIMEOUTTIMER_LIST[_settings.tot - 1])
+ rset = RadioSetting("tot", "Time-out timer", rs)
+ basic.append(rset)
+
+ rs = RadioSettingValueList(VOICE_LIST2,
+ VOICE_LIST2[_settings.voice])
+ rset = RadioSetting("voice", "Voice Annumciation", rs)
+ basic.append(rset)
+
+ rs = RadioSettingValueBoolean(not _settings.chnumberd)
+ rset = RadioSetting("chnumberd", "Channel Number Enable", rs)
+ basic.append(rset)
+
+ rs = RadioSettingValueBoolean(_settings.save)
+ rset = RadioSetting("save", "Battery Save", rs)
+ basic.append(rset)
+
+ rs = RadioSettingValueBoolean(_settings.beep)
+ rset = RadioSetting("beep", "Beep", rs)
+ basic.append(rset)
+
+ rs = RadioSettingValueBoolean(not _settings.tail)
+ rset = RadioSetting("tail", "QT/DQT Tail", rs)
+ basic.append(rset)
+
+ rs = RadioSettingValueList(SAVE_LIST, SAVE_LIST[_settings.savem])
+ rset = RadioSetting("savem", "Battery Save Mode", rs)
+ basic.append(rset)
+
+ rs = RadioSettingValueList(GAIN_LIST, GAIN_LIST[_settings.gain])
+ rset = RadioSetting("gain", "MIC Gain", rs)
+ basic.append(rset)
+
+ rs = RadioSettingValueList(WARN_LIST, WARN_LIST[_settings.warn])
+ rset = RadioSetting("warn", "Warn Mode", rs)
+ basic.append(rset)
+
+ rs = RadioSettingValueBoolean(_settings3.vox)
+ rset = RadioSetting("settings3.vox", "Vox Function", rs)
+ basic.append(rset)
+
+ rs = RadioSettingValueList(VOXL_LIST, VOXL_LIST[_settings3.voxl])
+ rset = RadioSetting("settings3.voxl", "Vox Level", rs)
+ basic.append(rset)
+
+ rs = RadioSettingValueList(VOXD_LIST, VOXD_LIST[_settings3.voxd])
+ rset = RadioSetting("settings3.voxd", "Vox Delay", rs)
+ basic.append(rset)
+
+ rs = RadioSettingValueList(PFKEY_LIST, PFKEY_LIST[_settings.pf1])
+ rset = RadioSetting("pf1", "PF1 Key Set", rs)
+ basic.append(rset)
+
+ rs = RadioSettingValueList(PFKEY_LIST, PFKEY_LIST[_settings.pf2])
+ rset = RadioSetting("pf2", "PF2 Key Set", rs)
+ basic.append(rset)
+
+ rs = RadioSettingValueInteger(1, 30, _settings2.chnumber + 1)
+ rset = RadioSetting("settings2.chnumber", "Channel Number", rs)
+ basic.append(rset)
+
return top
def set_settings(self, settings):
@@ -787,6 +988,12 @@
element.run_apply_callback()
elif setting == "channel":
setattr(obj, setting, int(element.value) - 1)
+ elif setting == "chnumber":
+ setattr(obj, setting, int(element.value) - 1)
+ elif setting == "chnumberd":
+ setattr(obj, setting, not int(element.value))
+ elif setting == "tail":
+ setattr(obj, setting, not int(element.value))
elif setting == "tot":
setattr(obj, setting, int(element.value) + 1)
elif element.value.get_mutable():
@@ -824,6 +1031,34 @@
def process_mmap(self):
self._memobj = bitwise.parse(MEM_FORMAT_RB17A, self._mmap)
+
+ at directory.register
+class RB26Radio(RT21Radio):
+ """RETEVIS RB26"""
+ VENDOR = "Retevis"
+ MODEL = "RB26"
+ BAUD_RATE = 9600
+ BLOCK_SIZE = 0x20
+ BLOCK_SIZE_UP = 0x10
+
+ POWER_LEVELS = [chirp_common.PowerLevel("Low", watts=0.50),
+ chirp_common.PowerLevel("High", watts=3.00)]
+
+ _magic = "PHOGR" + "\x01" + "0"
+ _fingerprint = "P32073" + "\x02\xFF"
+ _upper = 30
+ _skipflags = True
+ _reserved = True
+ _gmrs = True
+
+ _ranges = [
+ (0x0000, 0x0320),
+ ]
+ _memsize = 0x0320
+
+ def process_mmap(self):
+ self._memobj = bitwise.parse(MEM_FORMAT_RB26, self._mmap)
+
@classmethod
def match_model(cls, filedata, filename):
if cls.MODEL == "RT21":
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Retevis_RB26.img
Type: application/octet-stream
Size: 965 bytes
Desc: not available
Url : http://intrepid.danplanet.com/pipermail/chirp_devel/attachments/20210403/a160f56a/attachment-0001.img
More information about the chirp_devel
mailing list