[chirp_devel] Fwd: [PATCH 2 of 4] [RB17A] Retevis RB17A
Jim Unroe
Sat Apr 3 18:03:02 PDT 2021
---------- Forwarded message ---------
From: Jim Unroe <kc9hi at comcast.net>
Date: Sat, Apr 3, 2021 at 8:59 PM
Subject: [PATCH 2 of 4] [RB17A] Retevis RB17A
To: <Rock.Unroe at gmail.com>
# HG changeset patch
# User Jim Unroe <rock.unroe at gmail.com>
# Date 1617487759 14400
# Sat Apr 03 18:09:19 2021 -0400
# Node ID 53ff4720769f9db27a2a0a56885aa505fef3da09
# Parent d292f6aff5e0687bff7cf839f5de3e3164fe9f41
[RB17A] Retevis RB17A
This patch adds support for the Retevis RB17A.
Related to #8957
diff -r d292f6aff5e0 -r 53ff4720769f chirp/drivers/retevis_rt21.py
--- a/chirp/drivers/retevis_rt21.py Sat Apr 03 18:05:05 2021 -0400
+++ b/chirp/drivers/retevis_rt21.py Sat Apr 03 18:09:19 2021 -0400
@@ -83,9 +83,63 @@
u8 skipflags[2]; // SCAN_ADD
"""
+MEM_FORMAT_RB17A = """
+struct memory {
+ lbcd rxfreq[4]; // 0-3
+ lbcd txfreq[4]; // 4-7
+ ul16 rx_tone; // 8-9
+ ul16 tx_tone; // A-B
+ u8 unknown1:1, // C
+ compander:1, // Compand
+ bcl:2, // Busy Channel Lock-out
+ cdcss:1, // Cdcss Mode
+ scramble_type:3; // Scramble Type
+ u8 unknown2:4, // D
+ middlepower:1, // Power Level-Middle
+ unknown3:1, //
+ highpower:1, // Power Level-High/Low
+ wide:1; // Bandwidth
+ u8 unknown4; // E
+ u8 unknown5; // F
+};
+
+#seekto 0x0010;
+ struct memory lomems[16];
+
+#seekto 0x0200;
+ struct memory himems[14];
+
+#seekto 0x011D;
+struct {
+ u8 pf1; // 011D PF1 Key
+ u8 topkey; // 011E Top Key
+} keys;
+
+#seekto 0x012C;
+struct {
+ u8 use_scramble; // 012C Scramble Enable
+ u8 channel; // 012D Channel Number
+ u8 alarm; // 012E Alarm Type
+ u8 voice; // 012F Voice Annunciation
+ u8 tot; // 0130 Time-out Timer
+ u8 totalert; // 0131 Time-out Timer Pre-alert
+ u8 unknown2[2];
+ u8 squelch; // 0134 Squelch Level
+ u8 save; // 0135 Battery Saver
+ u8 unknown3[3];
+ u8 use_vox; // 0139 VOX Enable
+ u8 vox; // 013A VOX Gain
+} settings;
+
+#seekto 0x017E;
+u8 skipflags[4]; // Scan Add
+"""
+
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"]
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)]
@@ -93,9 +147,13 @@
VOX_LIST = ["OFF"] + ["%s" % x for x in range(1, 17)]
PF1_CHOICES = ["None", "Monitor", "Scan", "Scramble", "Alarm"]
PF1_VALUES = [0x0F, 0x04, 0x06, 0x08, 0x0C]
+TOPKEY_CHOICES = ["None", "Alarming"]
+TOPKEY_VALUES = [0xFF, 0x0C]
SETTING_LISTS = {
+ "alarm": ALARM_LIST,
"bcl": BCL_LIST,
+ "cdcss": CDCSS_LIST,
"scramble": SCRAMBLE_LIST,
"tot": TIMEOUTTIMER_LIST,
"totalert": TOTALERT_LIST,
@@ -103,6 +161,14 @@
"vox": VOX_LIST,
}
+GMRS_FREQS1 = [462.5625, 462.5875, 462.6125, 462.6375, 462.6625,
+ 462.6875, 462.7125]
+GMRS_FREQS2 = [467.5625, 467.5875, 467.6125, 467.6375, 467.6625,
+ 467.6875, 467.7125]
+GMRS_FREQS3 = [462.5500, 462.5750, 462.6000, 462.6250, 462.6500,
+ 462.6750, 462.7000, 462.7250]
+GMRS_FREQS = GMRS_FREQS1 + GMRS_FREQS2 + GMRS_FREQS3 * 2
+
def _enter_programming_mode(radio):
serial = radio.pipe
@@ -300,6 +366,52 @@
def process_mmap(self):
self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
+ def validate_memory(self, mem):
+ msgs = ""
+ msgs = chirp_common.CloneModeRadio.validate_memory(self, mem)
+
+ _msg_freq = 'Memory location cannot change frequency'
+ _msg_simplex = 'Memory location only supports Duplex:(None)'
+ _msg_duplex = 'Memory location only supports Duplex: +'
+ _msg_offset = 'Memory location only supports Offset: 5.000000'
+ _msg_nfm = 'Memory location only supports Mode: NFM'
+ _msg_txp = 'Memory location only supports Power: Low'
+
+ # GMRS models
+ if self._gmrs:
+ # range of memories with values set by FCC rules
+ if mem.freq != int(GMRS_FREQS[mem.number - 1] * 1000000):
+ # warn user can't change frequency
+ msgs.append(chirp_common.ValidationError(_msg_freq))
+
+ # channels 1 - 22 are simplex only
+ if mem.number <= 22:
+ if str(mem.duplex) != "":
+ # warn user can't change duplex
+ msgs.append(chirp_common.ValidationError(_msg_simplex))
+
+ # channels 23 - 30 are +5 MHz duplex only
+ if mem.number >= 23:
+ if str(mem.duplex) != "+":
+ # warn user can't change duplex
+ msgs.append(chirp_common.ValidationError(_msg_duplex))
+
+ if str(mem.offset) != "5000000":
+ # warn user can't change offset
+ msgs.append(chirp_common.ValidationError(_msg_offset))
+
+ # channels 8 - 14 are low power NFM only
+ if mem.number >= 8 and mem.number <= 14:
+ if mem.mode != "NFM":
+ # warn user can't change mode
+ msgs.append(chirp_common.ValidationError(_msg_nfm))
+
+ if mem.power != "Low":
+ # warn user can't change power
+ msgs.append(chirp_common.ValidationError(_msg_txp))
+
+ return msgs
+
def sync_in(self):
"""Download from radio"""
try:
@@ -380,11 +492,18 @@
LOG.debug("bytepos %s" % bytepos)
_skp = self._memobj.skipflags[bytepos]
- _mem = self._memobj.memory[number - 1]
-
mem = chirp_common.Memory()
mem.number = number
+
+ if self.MODEL == "RB17A":
+ if mem.number < 17:
+ _mem = self._memobj.lomems[number - 1]
+ else:
+ _mem = self._memobj.himems[number - 17]
+ else:
+ _mem = self._memobj.memory[number - 1]
+
mem.freq = int(_mem.rxfreq) * 10
# We'll consider any blank (i.e. 0MHz frequency) to be empty
@@ -399,7 +518,10 @@
if _mem.get_raw() == ("\xFF" * 16):
LOG.debug("Initializing empty memory")
- _mem.set_raw("\x00" * 13 + "\x30\x8F\xF8")
+ if self.MODEL == "RB17A":
+ _mem.set_raw("\x00" * 13 + "\x04\xFF\xFF")
+ else:
+ _mem.set_raw("\x00" * 13 + "\x30\x8F\xF8")
if int(_mem.rxfreq) == int(_mem.txfreq):
mem.duplex = ""
@@ -419,7 +541,7 @@
mem.extra = RadioSettingGroup("Extra", "extra")
- if self.MODEL == "RT21":
+ if self.MODEL == "RT21" or self.MODEL == "RB17A":
rs = RadioSettingValueList(BCL_LIST, BCL_LIST[_mem.bcl])
rset = RadioSetting("bcl", "Busy Channel Lockout", rs)
mem.extra.append(rset)
@@ -429,6 +551,18 @@
rset = RadioSetting("scramble_type", "Scramble Type", rs)
mem.extra.append(rset)
+ if self.MODEL == "RB17A":
+ rs = RadioSettingValueList(CDCSS_LIST, CDCSS_LIST[_mem.cdcss])
+ rset = RadioSetting("cdcss", "Cdcss Mode", rs)
+ mem.extra.append(rset)
+
+ if self._gmrs:
+ GMRS_IMMUTABLE = ["freq", "duplex", "offset"]
+ if mem.number >= 8 and mem.number <= 14:
+ mem.immutable = GMRS_IMMUTABLE + ["power", "mode"]
+ else:
+ mem.immutable = GMRS_IMMUTABLE
+
return mem
def _set_tone(self, mem, _mem):
@@ -477,13 +611,41 @@
LOG.debug("bytepos %s" % bytepos)
_skp = self._memobj.skipflags[bytepos]
- _mem = self._memobj.memory[mem.number - 1]
+ if self.MODEL == "RB17A":
+ if mem.number < 17:
+ _mem = self._memobj.lomems[mem.number - 1]
+ else:
+ _mem = self._memobj.himems[mem.number - 17]
+ else:
+ _mem = self._memobj.memory[mem.number - 1]
if mem.empty:
- _mem.set_raw("\xFF" * (_mem.size() / 8))
+ if self.MODEL == "RB17A":
+ _mem.set_raw("\xFF" * 12 + "\x00\x00\xFF\xFF")
+ else:
+ _mem.set_raw("\xFF" * (_mem.size() / 8))
+
+ if self._gmrs:
+ GMRS_FREQ = int(GMRS_FREQS[mem.number - 1] * 100000)
+ if mem.number > 22:
+ _mem.rxfreq = GMRS_FREQ
+ _mem.txfreq = int(_mem.rxfreq) + 500000
+ _mem.wide = True
+ else:
+ _mem.rxfreq = _mem.txfreq = GMRS_FREQ
+ if mem.number >= 8 and mem.number <= 14:
+ _mem.wide = False
+ _mem.highpower = False
+ else:
+ _mem.wide = True
+ _mem.highpower = True
+
return
- _mem.set_raw("\x00" * 13 + "\x00\x8F\xF8")
+ if self.MODEL == "RB17A":
+ _mem.set_raw("\x00" * 13 + "\x00\xFF\xFF")
+ else:
+ _mem.set_raw("\x00" * 13 + "\x30\x8F\xF8")
_mem.rxfreq = mem.freq / 10
@@ -523,7 +685,7 @@
basic = RadioSettingGroup("basic", "Basic Settings")
top = RadioSettings(basic)
- if self.MODEL == "RT21":
+ if self.MODEL == "RT21" or self.MODEL == "RB17A":
_keys = self._memobj.keys
rs = RadioSettingValueList(TIMEOUTTIMER_LIST,
@@ -544,6 +706,12 @@
rset = RadioSetting("voice", "Voice Annumciation", rs)
basic.append(rset)
+ if self.MODEL == "RB17A":
+ rs = RadioSettingValueList(ALARM_LIST,
+ ALARM_LIST[_settings.alarm])
+ rset = RadioSetting("alarm", "Alarm Type", rs)
+ basic.append(rset)
+
rs = RadioSettingValueBoolean(_settings.save)
rset = RadioSetting("save", "Battery Saver", rs)
basic.append(rset)
@@ -577,6 +745,24 @@
rset.set_apply_callback(apply_pf1_listvalue, _keys.pf1)
basic.append(rset)
+ def apply_topkey_listvalue(setting, obj):
+ LOG.debug("Setting value: " + str(setting.value) +
+ " from list")
+ val = str(setting.value)
+ index = TOPKEY_CHOICES.index(val)
+ val = TOPKEY_VALUES[index]
+ obj.set_value(val)
+
+ if self.MODEL == "RB17A":
+ if _keys.topkey in TOPKEY_VALUES:
+ idx = TOPKEY_VALUES.index(_keys.topkey)
+ else:
+ idx = TOPKEY_VALUES.index(0x0C)
+ rs = RadioSettingValueList(TOPKEY_CHOICES, TOPKEY_CHOICES[idx])
+ rset = RadioSetting("keys.topkey", "Top Key Function", rs)
+ rset.set_apply_callback(apply_topkey_listvalue, _keys.topkey)
+ basic.append(rset)
+
return top
def set_settings(self, settings):
@@ -599,6 +785,8 @@
if element.has_apply_callback():
LOG.debug("Using apply callback")
element.run_apply_callback()
+ elif setting == "channel":
+ setattr(obj, setting, int(element.value) - 1)
elif setting == "tot":
setattr(obj, setting, int(element.value) + 1)
elif element.value.get_mutable():
@@ -608,6 +796,34 @@
LOG.debug(element.get_name())
raise
+
+ at directory.register
+class RB17ARadio(RT21Radio):
+ """RETEVIS RB17A"""
+ VENDOR = "Retevis"
+ MODEL = "RB17A"
+ BAUD_RATE = 9600
+ BLOCK_SIZE = 0x40
+ BLOCK_SIZE_UP = 0x10
+
+ POWER_LEVELS = [chirp_common.PowerLevel("Low", watts=0.50),
+ chirp_common.PowerLevel("High", watts=5.00)]
+
+ _magic = "PROA8US"
+ _fingerprint = "P3217s\xF8\xFF"
+ _upper = 30
+ _skipflags = True
+ _reserved = False
+ _gmrs = True
+
+ _ranges = [
+ (0x0000, 0x0300),
+ ]
+ _memsize = 0x0300
+
+ def process_mmap(self):
+ self._memobj = bitwise.parse(MEM_FORMAT_RB17A, self._mmap)
+
@classmethod
def match_model(cls, filedata, filename):
if cls.MODEL == "RT21":
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Retevis_RB17A.img
Type: application/octet-stream
Size: 933 bytes
Desc: not available
Url : http://intrepid.danplanet.com/pipermail/chirp_devel/attachments/20210403/32b5dbd3/attachment-0001.img
More information about the chirp_devel
mailing list