[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