mirror of
https://github.com/kk7ds/chirp.git
synced 2024-09-21 10:37:15 +00:00
[RB27B] Add Retevis RB27B
This patch adds support for the Retevis RB27B FRS radio. addresses #9551
This commit is contained in:
parent
c84e9dec8c
commit
1bf6288f05
@ -54,7 +54,7 @@ struct {
|
|||||||
isnarrow:1, // W/N: 1 = Narrow, 0 = Wide
|
isnarrow:1, // W/N: 1 = Narrow, 0 = Wide
|
||||||
lowpower:1; // TX Power: 1 = Low, 0 = High
|
lowpower:1; // TX Power: 1 = Low, 0 = High
|
||||||
u8 unknown3[3]; //
|
u8 unknown3[3]; //
|
||||||
} memory[99];
|
} memory[%d];
|
||||||
|
|
||||||
#seekto 0x0630;
|
#seekto 0x0630;
|
||||||
struct {
|
struct {
|
||||||
@ -79,6 +79,12 @@ struct {
|
|||||||
u8 wx; // NOAA WX ch#
|
u8 wx; // NOAA WX ch#
|
||||||
u8 area; // Area Selected
|
u8 area; // Area Selected
|
||||||
} settings;
|
} settings;
|
||||||
|
|
||||||
|
#seekto 0x0D00;
|
||||||
|
struct {
|
||||||
|
char name[6];
|
||||||
|
u8 unknown1[2];
|
||||||
|
} names[%d];
|
||||||
"""
|
"""
|
||||||
|
|
||||||
CMD_ACK = "\x06"
|
CMD_ACK = "\x06"
|
||||||
@ -93,6 +99,7 @@ AREA_LIST = ["China", "Japan", "Korea", "Malaysia", "American",
|
|||||||
MDF_LIST = ["Frequency", "Channel #", "Name"]
|
MDF_LIST = ["Frequency", "Channel #", "Name"]
|
||||||
RING_LIST = ["OFF"] + ["%s" % x for x in range(1, 11)]
|
RING_LIST = ["OFF"] + ["%s" % x for x in range(1, 11)]
|
||||||
TOT_LIST = ["OFF"] + ["%s seconds" % x for x in range(30, 210, 30)]
|
TOT_LIST = ["OFF"] + ["%s seconds" % x for x in range(30, 210, 30)]
|
||||||
|
TOT2_LIST = TOT_LIST[1:]
|
||||||
VOICE_LIST = ["Off", "Chinese", "English"]
|
VOICE_LIST = ["Off", "Chinese", "English"]
|
||||||
VOX_LIST = ["OFF"] + ["%s" % x for x in range(1, 6)]
|
VOX_LIST = ["OFF"] + ["%s" % x for x in range(1, 6)]
|
||||||
WORKMODE_LIST = ["General", "PMR"]
|
WORKMODE_LIST = ["General", "PMR"]
|
||||||
@ -116,6 +123,7 @@ SETTING_LISTS = {
|
|||||||
"mdf": MDF_LIST,
|
"mdf": MDF_LIST,
|
||||||
"ring": RING_LIST,
|
"ring": RING_LIST,
|
||||||
"tot": TOT_LIST,
|
"tot": TOT_LIST,
|
||||||
|
"tot": TOT2_LIST,
|
||||||
"voice": VOICE_LIST,
|
"voice": VOICE_LIST,
|
||||||
"vox": VOX_LIST,
|
"vox": VOX_LIST,
|
||||||
"workmode": WORKMODE_LIST,
|
"workmode": WORKMODE_LIST,
|
||||||
@ -286,16 +294,23 @@ class BFT8Radio(chirp_common.CloneModeRadio):
|
|||||||
BLOCK_SIZE = BLOCK_SIZE_UP = 0x10
|
BLOCK_SIZE = BLOCK_SIZE_UP = 0x10
|
||||||
ODD_SPLIT = True
|
ODD_SPLIT = True
|
||||||
HAS_NAMES = False
|
HAS_NAMES = False
|
||||||
|
NAME_LENGTH = 0
|
||||||
|
VALID_CHARS = ""
|
||||||
|
CH_OFFSET = False
|
||||||
SKIP_VALUES = []
|
SKIP_VALUES = []
|
||||||
DTCS_CODES = sorted(chirp_common.DTCS_CODES)
|
DTCS_CODES = sorted(chirp_common.DTCS_CODES)
|
||||||
|
|
||||||
POWER_LEVELS = [chirp_common.PowerLevel("High", watts=2.00),
|
POWER_LEVELS = [chirp_common.PowerLevel("High", watts=2.00),
|
||||||
chirp_common.PowerLevel("Low", watts=0.50)]
|
chirp_common.PowerLevel("Low", watts=0.50)]
|
||||||
|
VALID_BANDS = [(400000000, 470000000)]
|
||||||
|
|
||||||
_magic = "\x02" + "PROGRAM"
|
_magic = "\x02" + "PROGRAM"
|
||||||
_fingerprint = "\x2E" + "BF-T6" + "\x2E"
|
_fingerprint = "\x2E" + "BF-T6" + "\x2E"
|
||||||
_upper = 99
|
_upper = 99
|
||||||
_frs = _upper == 22
|
_mem_params = (_upper, # number of channels
|
||||||
|
_upper # number of names
|
||||||
|
)
|
||||||
|
_frs = False
|
||||||
|
|
||||||
_ranges = [
|
_ranges = [
|
||||||
(0x0000, 0x0B60),
|
(0x0000, 0x0B60),
|
||||||
@ -310,8 +325,10 @@ class BFT8Radio(chirp_common.CloneModeRadio):
|
|||||||
rf.has_cross = True
|
rf.has_cross = True
|
||||||
rf.has_rx_dtcs = True
|
rf.has_rx_dtcs = True
|
||||||
rf.has_tuning_step = False
|
rf.has_tuning_step = False
|
||||||
rf.can_odd_split = self.ODD_SPLIT
|
|
||||||
rf.has_name = self.HAS_NAMES
|
rf.has_name = self.HAS_NAMES
|
||||||
|
rf.can_odd_split = self.ODD_SPLIT
|
||||||
|
rf.valid_name_length = self.NAME_LENGTH
|
||||||
|
rf.valid_characters = self.VALID_CHARS
|
||||||
rf.valid_skips = self.SKIP_VALUES
|
rf.valid_skips = self.SKIP_VALUES
|
||||||
rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
|
rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
|
||||||
rf.valid_cross_modes = ["Tone->Tone", "Tone->DTCS", "DTCS->Tone",
|
rf.valid_cross_modes = ["Tone->Tone", "Tone->DTCS", "DTCS->Tone",
|
||||||
@ -322,12 +339,12 @@ class BFT8Radio(chirp_common.CloneModeRadio):
|
|||||||
rf.valid_modes = ["FM", "NFM"] # 25 kHz, 12.5 KHz.
|
rf.valid_modes = ["FM", "NFM"] # 25 kHz, 12.5 KHz.
|
||||||
rf.memory_bounds = (1, self._upper)
|
rf.memory_bounds = (1, self._upper)
|
||||||
rf.valid_tuning_steps = [2.5, 5., 6.25, 10., 12.5, 25.]
|
rf.valid_tuning_steps = [2.5, 5., 6.25, 10., 12.5, 25.]
|
||||||
rf.valid_bands = [(400000000, 470000000)]
|
rf.valid_bands = self.VALID_BANDS
|
||||||
|
|
||||||
return rf
|
return rf
|
||||||
|
|
||||||
def process_mmap(self):
|
def process_mmap(self):
|
||||||
self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
|
self._memobj = bitwise.parse(MEM_FORMAT % self._mem_params, self._mmap)
|
||||||
|
|
||||||
def validate_memory(self, mem):
|
def validate_memory(self, mem):
|
||||||
msgs = ""
|
msgs = ""
|
||||||
@ -418,8 +435,13 @@ class BFT8Radio(chirp_common.CloneModeRadio):
|
|||||||
def _get_mem(self, number):
|
def _get_mem(self, number):
|
||||||
return self._memobj.memory[number - 1]
|
return self._memobj.memory[number - 1]
|
||||||
|
|
||||||
|
def _get_nam(self, number):
|
||||||
|
return self._memobj.names[number - 1]
|
||||||
|
|
||||||
def get_memory(self, number):
|
def get_memory(self, number):
|
||||||
_mem = self._get_mem(number)
|
_mem = self._get_mem(number)
|
||||||
|
if self.HAS_NAMES:
|
||||||
|
_nam = self._get_nam(number)
|
||||||
|
|
||||||
mem = chirp_common.Memory()
|
mem = chirp_common.Memory()
|
||||||
|
|
||||||
@ -450,6 +472,15 @@ class BFT8Radio(chirp_common.CloneModeRadio):
|
|||||||
# wide/narrow
|
# wide/narrow
|
||||||
mem.mode = _mem.isnarrow and "NFM" or "FM"
|
mem.mode = _mem.isnarrow and "NFM" or "FM"
|
||||||
|
|
||||||
|
mem.skip = _mem.skip and "S" or ""
|
||||||
|
|
||||||
|
if self.HAS_NAMES:
|
||||||
|
for char in _nam.name:
|
||||||
|
if str(char) == "\xFF":
|
||||||
|
char = " " # The OEM software may have 0xFF mid-name
|
||||||
|
mem.name += str(char)
|
||||||
|
mem.name = mem.name.rstrip()
|
||||||
|
|
||||||
# tone data
|
# tone data
|
||||||
self._get_tone(mem, _mem)
|
self._get_tone(mem, _mem)
|
||||||
|
|
||||||
@ -491,8 +522,13 @@ class BFT8Radio(chirp_common.CloneModeRadio):
|
|||||||
def _set_mem(self, number):
|
def _set_mem(self, number):
|
||||||
return self._memobj.memory[number - 1]
|
return self._memobj.memory[number - 1]
|
||||||
|
|
||||||
|
def _set_nam(self, number):
|
||||||
|
return self._memobj.names[number - 1]
|
||||||
|
|
||||||
def set_memory(self, mem):
|
def set_memory(self, mem):
|
||||||
_mem = self._set_mem(mem.number)
|
_mem = self._set_mem(mem.number)
|
||||||
|
if self.HAS_NAMES:
|
||||||
|
_nam = self._set_nam(mem.number)
|
||||||
|
|
||||||
# if empty memmory
|
# if empty memmory
|
||||||
if mem.empty:
|
if mem.empty:
|
||||||
@ -508,6 +544,10 @@ class BFT8Radio(chirp_common.CloneModeRadio):
|
|||||||
else:
|
else:
|
||||||
_mem.set_raw("\xFF" * 8 + "\x00" * 4 + "\x03" + "\xFF" * 3)
|
_mem.set_raw("\xFF" * 8 + "\x00" * 4 + "\x03" + "\xFF" * 3)
|
||||||
|
|
||||||
|
if self.HAS_NAMES:
|
||||||
|
for i in range(0, self.NAME_LENGTH):
|
||||||
|
_nam.name[i].set_raw("\xFF")
|
||||||
|
|
||||||
return mem
|
return mem
|
||||||
|
|
||||||
_mem.set_raw("\x00" * 13 + "\xFF" * 3)
|
_mem.set_raw("\x00" * 13 + "\xFF" * 3)
|
||||||
@ -530,6 +570,15 @@ class BFT8Radio(chirp_common.CloneModeRadio):
|
|||||||
# wide/narrow
|
# wide/narrow
|
||||||
_mem.isnarrow = mem.mode == "NFM"
|
_mem.isnarrow = mem.mode == "NFM"
|
||||||
|
|
||||||
|
_mem.skip = mem.skip == "S"
|
||||||
|
|
||||||
|
if self.HAS_NAMES:
|
||||||
|
for i in range(self.NAME_LENGTH):
|
||||||
|
try:
|
||||||
|
_nam.name[i] = mem.name[i]
|
||||||
|
except IndexError:
|
||||||
|
_nam.name[i] = "\xFF"
|
||||||
|
|
||||||
# tone data
|
# tone data
|
||||||
self._set_tone(mem, _mem)
|
self._set_tone(mem, _mem)
|
||||||
|
|
||||||
@ -551,8 +600,13 @@ class BFT8Radio(chirp_common.CloneModeRadio):
|
|||||||
rset = RadioSetting("squelch", "Squelch Level", rs)
|
rset = RadioSetting("squelch", "Squelch Level", rs)
|
||||||
basic.append(rset)
|
basic.append(rset)
|
||||||
|
|
||||||
# Menu 11
|
model_list = ["RB27B", ]
|
||||||
rs = RadioSettingValueList(TOT_LIST, TOT_LIST[_settings.tot])
|
if self.MODEL in model_list:
|
||||||
|
# Menu 09 (RB27x)
|
||||||
|
rs = RadioSettingValueList(TOT2_LIST, TOT2_LIST[_settings.tot])
|
||||||
|
else:
|
||||||
|
# Menu 11
|
||||||
|
rs = RadioSettingValueList(TOT_LIST, TOT_LIST[_settings.tot])
|
||||||
rset = RadioSetting("tot", "Time-out timer", rs)
|
rset = RadioSetting("tot", "Time-out timer", rs)
|
||||||
basic.append(rset)
|
basic.append(rset)
|
||||||
|
|
||||||
@ -561,7 +615,7 @@ class BFT8Radio(chirp_common.CloneModeRadio):
|
|||||||
rset = RadioSetting("vox", "VOX Level", rs)
|
rset = RadioSetting("vox", "VOX Level", rs)
|
||||||
basic.append(rset)
|
basic.append(rset)
|
||||||
|
|
||||||
# Menu 15 (BF-T8)
|
# Menu 15 (BF-T8) / 12 (RB-27/RB627)
|
||||||
rs = RadioSettingValueList(VOICE_LIST, VOICE_LIST[_settings.voice])
|
rs = RadioSettingValueList(VOICE_LIST, VOICE_LIST[_settings.voice])
|
||||||
rset = RadioSetting("voice", "Voice", rs)
|
rset = RadioSetting("voice", "Voice", rs)
|
||||||
basic.append(rset)
|
basic.append(rset)
|
||||||
@ -571,12 +625,12 @@ class BFT8Radio(chirp_common.CloneModeRadio):
|
|||||||
rset = RadioSetting("bcl", "Busy Channel Lockout", rs)
|
rset = RadioSetting("bcl", "Busy Channel Lockout", rs)
|
||||||
basic.append(rset)
|
basic.append(rset)
|
||||||
|
|
||||||
# Menu 10
|
# Menu 10 / 08 (RB27/RB627)
|
||||||
rs = RadioSettingValueBoolean(_settings.save)
|
rs = RadioSettingValueBoolean(_settings.save)
|
||||||
rset = RadioSetting("save", "Battery Saver", rs)
|
rset = RadioSetting("save", "Battery Saver", rs)
|
||||||
basic.append(rset)
|
basic.append(rset)
|
||||||
|
|
||||||
# Menu 08
|
# Menu 08 / 07 (RB-27/RB627)
|
||||||
rs = RadioSettingValueBoolean(_settings.tdr)
|
rs = RadioSettingValueBoolean(_settings.tdr)
|
||||||
rset = RadioSetting("tdr", "Dual Watch", rs)
|
rset = RadioSetting("tdr", "Dual Watch", rs)
|
||||||
basic.append(rset)
|
basic.append(rset)
|
||||||
@ -591,7 +645,7 @@ class BFT8Radio(chirp_common.CloneModeRadio):
|
|||||||
rset = RadioSetting("abr", "Back Light", rs)
|
rset = RadioSetting("abr", "Back Light", rs)
|
||||||
basic.append(rset)
|
basic.append(rset)
|
||||||
|
|
||||||
# Menu 13
|
# Menu 13 / 11 (RB-27/RB627)
|
||||||
rs = RadioSettingValueList(RING_LIST, RING_LIST[_settings.ring])
|
rs = RadioSettingValueList(RING_LIST, RING_LIST[_settings.ring])
|
||||||
rset = RadioSetting("ring", "Ring", rs)
|
rset = RadioSetting("ring", "Ring", rs)
|
||||||
basic.append(rset)
|
basic.append(rset)
|
||||||
@ -602,11 +656,17 @@ class BFT8Radio(chirp_common.CloneModeRadio):
|
|||||||
|
|
||||||
#
|
#
|
||||||
|
|
||||||
rs = RadioSettingValueInteger(1, self._upper, _settings.mra)
|
if self.CH_OFFSET:
|
||||||
|
rs = RadioSettingValueInteger(1, self._upper, _settings.mra + 1)
|
||||||
|
else:
|
||||||
|
rs = RadioSettingValueInteger(1, self._upper, _settings.mra)
|
||||||
rset = RadioSetting("mra", "MR A Channel #", rs)
|
rset = RadioSetting("mra", "MR A Channel #", rs)
|
||||||
basic.append(rset)
|
basic.append(rset)
|
||||||
|
|
||||||
rs = RadioSettingValueInteger(1, self._upper, _settings.mrb)
|
if self.CH_OFFSET:
|
||||||
|
rs = RadioSettingValueInteger(1, self._upper, _settings.mrb + 1)
|
||||||
|
else:
|
||||||
|
rs = RadioSettingValueInteger(1, self._upper, _settings.mrb)
|
||||||
rset = RadioSetting("mrb", "MR B Channel #", rs)
|
rset = RadioSetting("mrb", "MR B Channel #", rs)
|
||||||
basic.append(rset)
|
basic.append(rset)
|
||||||
|
|
||||||
@ -635,7 +695,8 @@ class BFT8Radio(chirp_common.CloneModeRadio):
|
|||||||
rset.set_apply_callback(myset_freq, _settings, "fmcur", 10)
|
rset.set_apply_callback(myset_freq, _settings, "fmcur", 10)
|
||||||
basic.append(rset)
|
basic.append(rset)
|
||||||
|
|
||||||
if not self._frs:
|
model_list = ["BF-T8", "BF-U9", "AR-8"]
|
||||||
|
if self.MODEL in model_list:
|
||||||
rs = RadioSettingValueList(WORKMODE_LIST,
|
rs = RadioSettingValueList(WORKMODE_LIST,
|
||||||
WORKMODE_LIST[_settings.workmode])
|
WORKMODE_LIST[_settings.workmode])
|
||||||
rset = RadioSetting("workmode", "Work Mode", rs)
|
rset = RadioSetting("workmode", "Work Mode", rs)
|
||||||
@ -668,6 +729,10 @@ class BFT8Radio(chirp_common.CloneModeRadio):
|
|||||||
if element.has_apply_callback():
|
if element.has_apply_callback():
|
||||||
LOG.debug("Using apply callback")
|
LOG.debug("Using apply callback")
|
||||||
element.run_apply_callback()
|
element.run_apply_callback()
|
||||||
|
elif setting == "mra" and self.CH_OFFSET:
|
||||||
|
setattr(obj, setting, int(element.value) - 1)
|
||||||
|
elif setting == "mrb" and self.CH_OFFSET:
|
||||||
|
setattr(obj, setting, int(element.value) - 1)
|
||||||
elif setting == "ste":
|
elif setting == "ste":
|
||||||
setattr(obj, setting, not int(element.value))
|
setattr(obj, setting, not int(element.value))
|
||||||
elif element.value.get_mutable():
|
elif element.value.get_mutable():
|
||||||
@ -705,4 +770,28 @@ class RetevisRT16(BFT8Radio):
|
|||||||
MODEL = "RT16"
|
MODEL = "RT16"
|
||||||
|
|
||||||
_upper = 22
|
_upper = 22
|
||||||
_frs = _upper == 22
|
_frs = True
|
||||||
|
|
||||||
|
|
||||||
|
@directory.register
|
||||||
|
class RetevisRT27B(BFT8Radio):
|
||||||
|
VENDOR = "Retevis"
|
||||||
|
MODEL = "RB27B"
|
||||||
|
DTCS_CODES = sorted(chirp_common.DTCS_CODES + [645])
|
||||||
|
HAS_NAMES = True
|
||||||
|
NAME_LENGTH = 6
|
||||||
|
VALID_CHARS = chirp_common.CHARSET_UPPER_NUMERIC + "-"
|
||||||
|
CH_OFFSET = True
|
||||||
|
SKIP_VALUES = ["", "S"]
|
||||||
|
POWER_LEVELS = [chirp_common.PowerLevel("High", watts=2.00),
|
||||||
|
chirp_common.PowerLevel("Low", watts=0.50)]
|
||||||
|
VALID_BANDS = [(400000000, 520000000)]
|
||||||
|
|
||||||
|
_upper = 22
|
||||||
|
_frs = True
|
||||||
|
|
||||||
|
_ranges = [
|
||||||
|
(0x0000, 0x0640),
|
||||||
|
(0x0D00, 0x1040),
|
||||||
|
]
|
||||||
|
_memsize = 0x1040
|
||||||
|
BIN
tests/images/Retevis_RB27B.img
Normal file
BIN
tests/images/Retevis_RB27B.img
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user