|
# Copyright 2016 Jim Unroe <rock.unroe@gmail.com>
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import time
|
|
import os
|
|
import struct
|
|
import re
|
|
import logging
|
|
|
|
from chirp import chirp_common, directory, memmap
|
|
from chirp import bitwise, errors, util
|
|
from chirp.settings import RadioSetting, RadioSettingGroup, \
|
|
RadioSettingValueInteger, RadioSettingValueList, \
|
|
RadioSettingValueBoolean, RadioSettingValueString, \
|
|
RadioSettings
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
MEM_FORMAT = """
|
|
struct memory {
|
|
lbcd rxfreq[4];
|
|
lbcd txfreq[4];
|
|
lbcd rxtone[2];
|
|
lbcd txtone[2];
|
|
u8 unknown1;
|
|
u8 pttid:2, // PTT-ID
|
|
unknown2:1,
|
|
signaling:1, // Signaling(ANI)
|
|
unknown3:1,
|
|
bcl:1, // Busy Channel Lockout
|
|
unknown4:2;
|
|
u8 unknown5:3,
|
|
highpower:1, // Power Level
|
|
isnarrow:1, // Bandwidth
|
|
scan:1, // Scan Add
|
|
unknown6:2;
|
|
u8 unknown7;
|
|
};
|
|
|
|
#seekto 0x0010;
|
|
struct memory channels[128];
|
|
|
|
#seekto 0x0810;
|
|
struct memory vfo_a;
|
|
struct memory vfo_b;
|
|
|
|
#seekto 0x0830;
|
|
struct {
|
|
u8 unknown_0830_1:4, // 0x0830
|
|
color:2, // Background Color
|
|
dst:1, // DTMF Side Tone
|
|
txsel:1; // Priority TX Channel Select
|
|
u8 scans:2, // Scan Mode - 0x0831
|
|
unknown_0831:1,
|
|
autolk:1, // Auto Key Lock
|
|
save:1, // Battery Save
|
|
beep:1, // Key Beep
|
|
voice:2; // Voice Prompt
|
|
u8 vfomr_fm:1, // FM Radio Display Mode - 0x0832
|
|
led:2, // Background Light
|
|
unknown_0832_2:1,
|
|
dw:1, // FM Radio Dual Watch
|
|
name:1, // Display Names
|
|
vfomr_a:2; // Display Mode A
|
|
u8 opnset:2, // Power On Message - 0x0833
|
|
unknown_0833_1:3,
|
|
dwait:1, // Dual Standby
|
|
vfomr_b:2; // Display Mode B
|
|
u8 mrcha; // mr a ch num - 0x0834
|
|
u8 mrchb; // mr b ch num - 0x0835
|
|
u8 fmch; // fm radio ch num - 0x0836
|
|
u8 unknown_0837_1:1, // 0x0837
|
|
ste:1, // Squelch Tail Eliminate
|
|
roger:1, // Roger Beep
|
|
unknown_0837_2:1,
|
|
vox:4; // VOX
|
|
u8 step:4, // Step - 0x0838
|
|
unknown_0838_1:4;
|
|
u8 squelch; // Squelch - 0x0839
|
|
u8 tot; // Time Out Timer - 0x083A
|
|
u8 rptmod:1, // Repeater Mode - 0x083B
|
|
volmod:2, // Volume Mode
|
|
rptptt:1, // Repeater PTT Switch
|
|
rptspk:1, // Repeater Speaker
|
|
relay:3; // Cross Band Repeater Enable
|
|
u8 unknown_083C:4, // 0x083C
|
|
rptrl:4; // Repeater TX Delay
|
|
u8 pf1:4, // Function Key 1 - 0x083D
|
|
pf2:4; // Function Key 2
|
|
u8 vot; // VOX Delay Time - 0x083E
|
|
} settings;
|
|
|
|
#seekto 0x0848;
|
|
struct {
|
|
char line1[7];
|
|
} poweron_msg;
|
|
|
|
struct limit {
|
|
bbcd lower[2];
|
|
bbcd upper[2];
|
|
};
|
|
|
|
#seekto 0x0850;
|
|
struct {
|
|
struct limit vhf;
|
|
struct limit uhf;
|
|
} limits;
|
|
|
|
struct fmmemory {
|
|
lbcd fmfreq[4];
|
|
};
|
|
|
|
#seekto 0x085E;
|
|
struct fmmemory fmchannels[25];
|
|
struct fmmemory fmvfo;
|
|
|
|
#seekto 0x08D0;
|
|
struct {
|
|
char name[7]; // Channel Names
|
|
u8 unknown2[1];
|
|
} names[128];
|
|
|
|
#seekto 0x0D20;
|
|
u8 usedflags[16];
|
|
u8 scanflags[16];
|
|
|
|
#seekto 0x0E50;
|
|
struct memory rpt_vfo;
|
|
struct memory rpt_1;
|
|
struct memory rpt_2;
|
|
struct memory rpt_3;
|
|
struct memory rpt_4;
|
|
struct memory rpt_5;
|
|
|
|
#seekto 0x0EB0;
|
|
struct {
|
|
u8 unknown:2,
|
|
rpt_5_enable:1,
|
|
rpt_4_enable:1,
|
|
rpt_3_enable:1,
|
|
rpt_2_enable:1,
|
|
rpt_1_enable:1,
|
|
rpt_vfo_enable:1;
|
|
} special_enables;
|
|
|
|
#seekto 0x0FA0;
|
|
struct {
|
|
u8 unknown_0FA0_1:4,
|
|
dispab:1, // select a/b
|
|
unknown_0FA0_2:3;
|
|
} settings2;
|
|
"""
|
|
|
|
CMD_ACK = "\x06"
|
|
BLOCK_SIZE = 0x10
|
|
|
|
RT23_POWER_LEVELS = [chirp_common.PowerLevel("Low", watts=1.00),
|
|
chirp_common.PowerLevel("High", watts=2.50)]
|
|
|
|
|
|
RT23_DTCS = sorted(chirp_common.DTCS_CODES +
|
|
[17, 50, 55, 135, 217, 254, 305, 645, 765])
|
|
|
|
RT23_CHARSET = chirp_common.CHARSET_UPPER_NUMERIC + \
|
|
":;<=>?@ !\"#$%&'()*+,-./"
|
|
|
|
LIST_SIGNALING = ["No", "DTMF"]
|
|
LIST_STEP = ["2.50K", "5.00K", "6.25K", "10.00K", "12,50K", "20.00K", "25.00K",
|
|
"50.00K"]
|
|
####LIST_OFFON = ["OFF", "ON"]
|
|
LIST_VOX = ["OFF"] + ["%s" % x for x in range(1, 6)]
|
|
LIST_TOT = ["OFF"] + ["%s seconds" % x for x in range(30, 300, 30)]
|
|
LIST_COLOR = ["Blue", "Orange", "Purple"]
|
|
LIST_VOT = ["0.5S", "1.0S", "1.5S", "2.0S", "3.0S"]
|
|
LIST_LED = ["Off", "On", "Auto"]
|
|
LIST_VOICE = ["Off", "Chinese", "English"]
|
|
LIST_OPNSET = ["Full", "Voltage", "Message"]
|
|
LIST_SCANS = ["Time Operated", "Carrier Operated", "Search"]
|
|
|
|
LIST_RPTRL = ["0.5S", "1.0S", "1.5S", "2.0S", "2.5S", "3.0S", "3.5S", "4.0S",
|
|
"4.5S"]
|
|
LIST_RPTMOD = ["Single", "Double"]
|
|
LIST_VOLMOD = ["Off", "Sub", "Main"]
|
|
LIST_TXSEL = ["Edit", "Busy"]
|
|
|
|
LIST_VFOMR = ["VFO", "MR(Frequency)", "MR(Channel #/Name)"]
|
|
LIST_VFOMRFM = ["VFO", "Channel"]
|
|
|
|
LIST_PTTID = ["Off", "BOT", "EOT", "Both"]
|
|
|
|
LIST_PFKEY = [
|
|
"Radio",
|
|
"Sub-channel Sent",
|
|
"Scan",
|
|
"Alarm",
|
|
"DTMF",
|
|
"Squelch Off Momentarily",
|
|
"Battery Power Indicator",
|
|
"Tone 1750",
|
|
"Tone 2100",
|
|
"Tone 1000",
|
|
"Tone 1450"]
|
|
|
|
RT23_SPECIAL = ["rpt_vfo", "rpt_1", "rpt_2", "rpt_3", "rpt_4", "rpt_5",
|
|
"vfo_a", "vfo_b"]
|
|
|
|
|
|
def _rt23_enter_programming_mode(radio):
|
|
serial = radio.pipe
|
|
|
|
#magic = "PROIUAM"
|
|
try:
|
|
serial.write("PROIUAM")
|
|
#for j in range(0, len(magic)):
|
|
# time.sleep(0.002)
|
|
# serial.write(magic[j])
|
|
ack = serial.read(1)
|
|
except:
|
|
raise errors.RadioError("Error communicating with radio")
|
|
|
|
if not ack:
|
|
raise errors.RadioError("No response from radio")
|
|
elif ack != CMD_ACK:
|
|
raise errors.RadioError("Radio refused to enter programming mode")
|
|
|
|
try:
|
|
serial.write("\x02")
|
|
ident = serial.read(8)
|
|
except:
|
|
raise errors.RadioError("Error communicating with radio")
|
|
|
|
if not ident.startswith("P31183"):
|
|
LOG.debug(util.hexprint(ident))
|
|
raise errors.RadioError("Radio returned unknown identification string")
|
|
|
|
try:
|
|
serial.write(CMD_ACK)
|
|
ack = serial.read(1)
|
|
except:
|
|
raise errors.RadioError("Error communicating with radio")
|
|
|
|
if ack != CMD_ACK:
|
|
raise errors.RadioError("Radio refused to enter programming mode")
|
|
|
|
|
|
def _rt23_exit_programming_mode(radio):
|
|
serial = radio.pipe
|
|
try:
|
|
serial.write("E")
|
|
except:
|
|
raise errors.RadioError("Radio refused to exit programming mode")
|
|
|
|
|
|
def _rt23_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 + 1)
|
|
if response[:4] != expectedresponse:
|
|
raise Exception("Error reading block %04x." % (block_addr))
|
|
|
|
chunk = response[4:]
|
|
|
|
cs = 0
|
|
for byte in chunk[:-1]:
|
|
cs += ord(byte)
|
|
if ord(chunk[-1]) != (cs & 0xFF):
|
|
raise Exception("Block failed checksum!")
|
|
|
|
block_data = chunk[:-1]
|
|
except:
|
|
raise errors.RadioError("Failed to read block at %04x" % block_addr)
|
|
|
|
return block_data
|
|
|
|
|
|
def _rt23_write_block(radio, block_addr, block_size):
|
|
serial = radio.pipe
|
|
|
|
cmd = struct.pack(">cHb", 'W', block_addr, BLOCK_SIZE)
|
|
data = radio.get_mmap()[block_addr:block_addr + BLOCK_SIZE]
|
|
cs = 0
|
|
for byte in data:
|
|
cs += ord(byte)
|
|
data += chr(cs & 0xFF)
|
|
|
|
LOG.debug("Writing Data:")
|
|
LOG.debug(util.hexprint(cmd + data))
|
|
|
|
try:
|
|
#serial.write(cmd + data)
|
|
for j in range(0, len(cmd)):
|
|
time.sleep(0.002)
|
|
serial.write(cmd[j])
|
|
for j in range(0, len(data)):
|
|
time.sleep(0.002)
|
|
serial.write(data[j])
|
|
if serial.read(1) != CMD_ACK:
|
|
raise Exception("No ACK")
|
|
except:
|
|
raise errors.RadioError("Failed to send block "
|
|
"to radio at %04x" % block_addr)
|
|
|
|
|
|
def do_download(radio):
|
|
LOG.debug("download")
|
|
_rt23_enter_programming_mode(radio)
|
|
|
|
data = ""
|
|
|
|
status = chirp_common.Status()
|
|
status.msg = "Cloning from radio"
|
|
|
|
status.cur = 0
|
|
status.max = radio._memsize
|
|
|
|
for addr in range(0, radio._memsize, BLOCK_SIZE):
|
|
status.cur = addr + BLOCK_SIZE
|
|
radio.status_fn(status)
|
|
|
|
####print "Address: %s" % addr
|
|
block = _rt23_read_block(radio, addr, BLOCK_SIZE)
|
|
if addr == 0 and block.startswith("\xFF" * 6):
|
|
block = "P31183" + "\xFF" * 10
|
|
data += block
|
|
|
|
LOG.debug("Address: %04x" % addr)
|
|
LOG.debug(util.hexprint(block))
|
|
|
|
####data += radio.MODEL.ljust(8)
|
|
|
|
_rt23_exit_programming_mode(radio)
|
|
|
|
return memmap.MemoryMap(data)
|
|
|
|
|
|
def do_upload(radio):
|
|
status = chirp_common.Status()
|
|
status.msg = "Uploading to radio"
|
|
|
|
_rt23_enter_programming_mode(radio)
|
|
|
|
status.cur = 0
|
|
status.max = radio._memsize
|
|
|
|
for start_addr, end_addr in radio._ranges:
|
|
for addr in range(start_addr, end_addr, BLOCK_SIZE):
|
|
status.cur = addr + BLOCK_SIZE
|
|
radio.status_fn(status)
|
|
_rt23_write_block(radio, addr, BLOCK_SIZE)
|
|
|
|
####_rt23_exit_programming_mode(radio)
|
|
|
|
|
|
def model_match(cls, data):
|
|
"""Match the opened/downloaded image to the correct version"""
|
|
|
|
if len(data) == 0x1000:
|
|
rid = data[0x0000:0x0006]
|
|
return rid == "P31183"
|
|
else:
|
|
return False
|
|
|
|
|
|
@directory.register
|
|
class RT23Radio(chirp_common.CloneModeRadio):
|
|
"""RETEVIS RT23"""
|
|
VENDOR = "Retevis"
|
|
MODEL = "RT23"
|
|
BAUD_RATE = 9600
|
|
|
|
_ranges = [
|
|
(0x0000, 0x0EC0),
|
|
]
|
|
_memsize = 0x1000
|
|
|
|
def get_features(self):
|
|
rf = chirp_common.RadioFeatures()
|
|
rf.has_settings = True
|
|
rf.has_bank = False
|
|
rf.has_ctone = True
|
|
rf.has_cross = True
|
|
rf.has_rx_dtcs = True
|
|
rf.has_tuning_step = False
|
|
rf.can_odd_split = True
|
|
rf.valid_name_length = 7
|
|
rf.valid_characters = RT23_CHARSET
|
|
rf.has_name = True
|
|
rf.valid_skips = ["", "S"]
|
|
rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
|
|
rf.valid_cross_modes = ["Tone->Tone", "Tone->DTCS", "DTCS->Tone",
|
|
"->Tone", "->DTCS", "DTCS->", "DTCS->DTCS"]
|
|
rf.valid_power_levels = RT23_POWER_LEVELS
|
|
rf.valid_duplexes = ["", "-", "+", "split", "off"]
|
|
rf.valid_modes = ["FM", "NFM"] # 25 KHz, 12.5 KHz.
|
|
rf.memory_bounds = (1, 128)
|
|
rf.valid_bands = [
|
|
(136000000, 174000000),
|
|
(400000000, 480000000)]
|
|
rf.valid_special_chans = RT23_SPECIAL
|
|
|
|
return rf
|
|
|
|
def process_mmap(self):
|
|
self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
|
|
|
|
def sync_in(self):
|
|
self._mmap = do_download(self)
|
|
self.process_mmap()
|
|
|
|
def sync_out(self):
|
|
do_upload(self)
|
|
|
|
def get_raw_memory(self, number):
|
|
return repr(self._memobj.memory[number - 1])
|
|
|
|
def decode_tone(self, val):
|
|
"""Parse the tone data to decode from mem, it returns:
|
|
Mode (''|DTCS|Tone), Value (None|###), Polarity (None,N,R)"""
|
|
if val.get_raw() == "\xFF\xFF":
|
|
return '', None, None
|
|
|
|
val = int(val)
|
|
if val >= 12000:
|
|
a = val - 12000
|
|
return 'DTCS', a, 'R'
|
|
elif val >= 8000:
|
|
a = val - 8000
|
|
return 'DTCS', a, 'N'
|
|
else:
|
|
a = val / 10.0
|
|
return 'Tone', a, None
|
|
|
|
def encode_tone(self, memval, mode, value, pol):
|
|
"""Parse the tone data to encode from UI to mem"""
|
|
if mode == '':
|
|
memval[0].set_raw(0xFF)
|
|
memval[1].set_raw(0xFF)
|
|
elif mode == 'Tone':
|
|
memval.set_value(int(value * 10))
|
|
elif mode == 'DTCS':
|
|
flag = 0x80 if pol == 'N' else 0xC0
|
|
memval.set_value(value)
|
|
memval[1].set_bits(flag)
|
|
else:
|
|
raise Exception("Internal error: invalid mode `%s'" % mode)
|
|
|
|
def get_memory(self, number):
|
|
mem = chirp_common.Memory()
|
|
if isinstance(number, str):
|
|
# special channel
|
|
_mem = getattr(self._memobj, number)
|
|
_nam = None
|
|
mem.number = - len(RT23_SPECIAL) + RT23_SPECIAL.index(number)
|
|
mem.extd_number = number
|
|
_scn = None
|
|
_usd = None
|
|
if re.match('^rpt', mem.extd_number):
|
|
_special_enables = self._memobj.special_enables
|
|
isused = getattr(_special_enables,
|
|
mem.extd_number + "_enable")
|
|
else:
|
|
isused = True
|
|
isscan = True
|
|
else:
|
|
# regular memory
|
|
_mem = self._memobj.channels[number-1]
|
|
_nam = self._memobj.names[number - 1]
|
|
mem.number = number
|
|
bitpos = (1 << ((number - 1) % 8))
|
|
bytepos = ((number - 1) / 8)
|
|
_scn = self._memobj.scanflags[bytepos]
|
|
_usd = self._memobj.usedflags[bytepos]
|
|
isused = bitpos & int(_usd)
|
|
isscan = bitpos & int(_scn)
|
|
|
|
if not isused:
|
|
mem.empty = True
|
|
return mem
|
|
|
|
mem.freq = int(_mem.rxfreq) * 10
|
|
|
|
# We'll consider any blank (i.e. 0MHz frequency) to be empty
|
|
if mem.freq == 0:
|
|
mem.empty = True
|
|
return mem
|
|
|
|
if _mem.rxfreq.get_raw() == "\xFF\xFF\xFF\xFF":
|
|
mem.empty = True
|
|
return mem
|
|
|
|
if _mem.get_raw() == ("\xFF" * 16):
|
|
LOG.debug("Initializing empty memory")
|
|
_mem.set_raw("\x00" * 16)
|
|
|
|
if int(_mem.rxfreq) == int(_mem.txfreq):
|
|
mem.duplex = ""
|
|
mem.offset = 0
|
|
else:
|
|
mem.duplex = int(_mem.rxfreq) > int(_mem.txfreq) and "-" or "+"
|
|
mem.offset = abs(int(_mem.rxfreq) - int(_mem.txfreq)) * 10
|
|
|
|
if _nam:
|
|
for char in _nam.name:
|
|
if str(char) == "\xFF":
|
|
char = " "
|
|
mem.name += str(char)
|
|
mem.name = mem.name.rstrip()
|
|
|
|
mem.mode = _mem.isnarrow and "NFM" or "FM"
|
|
|
|
rxtone = txtone = None
|
|
txtone = self.decode_tone(_mem.txtone)
|
|
rxtone = self.decode_tone(_mem.rxtone)
|
|
chirp_common.split_tone_decode(mem, txtone, rxtone)
|
|
|
|
mem.power = RT23_POWER_LEVELS[_mem.highpower]
|
|
|
|
if _scn:
|
|
if not isscan:
|
|
mem.skip = "S"
|
|
|
|
mem.extra = RadioSettingGroup("Extra", "extra")
|
|
|
|
rs = RadioSetting("bcl", "BCL",
|
|
RadioSettingValueBoolean(_mem.bcl))
|
|
mem.extra.append(rs)
|
|
|
|
rs = RadioSetting("pttid", "PTT ID",
|
|
RadioSettingValueList(
|
|
LIST_PTTID, LIST_PTTID[_mem.pttid]))
|
|
mem.extra.append(rs)
|
|
|
|
rs = RadioSetting("signaling", "Optional Signaling",
|
|
RadioSettingValueList(LIST_SIGNALING,
|
|
LIST_SIGNALING[_mem.signaling]))
|
|
mem.extra.append(rs)
|
|
|
|
return mem
|
|
|
|
def set_memory(self, mem):
|
|
LOG.debug("Setting %i(%s)" % (mem.number, mem.extd_number))
|
|
if mem.number < 0:
|
|
# special channels
|
|
val = str(RT23_SPECIAL[mem.number + len(RT23_SPECIAL)])
|
|
_mem = getattr(self._memobj, val)
|
|
#_nam = None
|
|
#_scn = None
|
|
#_usd = None
|
|
if re.match('^rpt', val):
|
|
_special_enables = self._memobj.special_enables
|
|
setattr(_special_enables, val + "_enable", True)
|
|
else:
|
|
_mem = self._memobj.channels[mem.number - 1]
|
|
_nam = self._memobj.names[mem.number - 1]
|
|
bitpos = (1 << ((mem.number - 1) % 8))
|
|
bytepos = ((mem.number - 1) / 8)
|
|
_scn = self._memobj.scanflags[bytepos]
|
|
_usd = self._memobj.usedflags[bytepos]
|
|
|
|
if mem.empty:
|
|
_mem.set_raw("\xFF" * 16)
|
|
if mem.number < 0:
|
|
val = str(RT23_SPECIAL[mem.number + 6])
|
|
_special_enables = self._memobj.special_enables
|
|
setattr(_special_enables, val + "_enable", False)
|
|
return
|
|
else:
|
|
_nam.name = ("\xFF" * 7)
|
|
_usd &= ~bitpos
|
|
_scn &= ~bitpos
|
|
return
|
|
else:
|
|
if mem.number < 0:
|
|
val = str(RT23_SPECIAL[mem.number + 6])
|
|
_special_enables = self._memobj.special_enables
|
|
setattr(_special_enables, val + "_enable", True)
|
|
else:
|
|
_usd |= bitpos
|
|
|
|
if _mem.get_raw() == ("\xFF" * 16):
|
|
LOG.debug("Initializing empty memory")
|
|
_mem.set_raw("\x00" * 16)
|
|
if _scn:
|
|
_scn |= bitpos
|
|
|
|
##_mem.set_raw("\x00" * 16)
|
|
|
|
_mem.rxfreq = mem.freq / 10
|
|
|
|
if mem.duplex == "off":
|
|
for i in range(0, 4):
|
|
_mem.txfreq[i].set_raw("\xFF")
|
|
elif mem.duplex == "split":
|
|
_mem.txfreq = mem.offset / 10
|
|
elif mem.duplex == "+":
|
|
_mem.txfreq = (mem.freq + mem.offset) / 10
|
|
elif mem.duplex == "-":
|
|
_mem.txfreq = (mem.freq - mem.offset) / 10
|
|
else:
|
|
_mem.txfreq = mem.freq / 10
|
|
|
|
if mem.number >= 0:
|
|
_namelength = self.get_features().valid_name_length
|
|
for i in range(_namelength):
|
|
try:
|
|
_nam.name[i] = mem.name[i]
|
|
except IndexError:
|
|
_nam.name[i] = "\xFF"
|
|
|
|
####_mem.scan = mem.skip != "S"
|
|
if mem.skip == "S":
|
|
_scn &= ~bitpos
|
|
else:
|
|
_scn |= bitpos
|
|
_mem.isnarrow = mem.mode == "NFM"
|
|
|
|
((txmode, txtone, txpol), (rxmode, rxtone, rxpol)) = \
|
|
chirp_common.split_tone_encode(mem)
|
|
self.encode_tone(_mem.txtone, txmode, txtone, txpol)
|
|
self.encode_tone(_mem.rxtone, rxmode, rxtone, rxpol)
|
|
|
|
_mem.highpower = mem.power == RT23_POWER_LEVELS[1]
|
|
|
|
for setting in mem.extra:
|
|
setattr(_mem, setting.get_name(), setting.value)
|
|
|
|
def get_settings(self):
|
|
####_keys = self._memobj.keys
|
|
_settings = self._memobj.settings
|
|
_mem = self._memobj
|
|
basic = RadioSettingGroup("basic", "Basic Settings")
|
|
advanced = RadioSettingGroup("advanced", "Advanced Settings")
|
|
other = RadioSettingGroup("other", "Other Settings")
|
|
workmode = RadioSettingGroup("workmode", "Workmode Settings")
|
|
fmradio = RadioSettingGroup("fmradio", "FM Radio Settings")
|
|
top = RadioSettings(basic, advanced, other, workmode, fmradio)
|
|
|
|
#step = RadioSetting("step", "Step", RadioSettingValueList(
|
|
# LIST_STEP, LIST_STEP[_settings.step]))
|
|
#basic.append(step)
|
|
|
|
save = RadioSetting("save", "Battery Saver",
|
|
RadioSettingValueBoolean(_settings.save))
|
|
basic.append(save)
|
|
|
|
vox = RadioSetting("vox", "VOX Gain",
|
|
RadioSettingValueList(
|
|
LIST_VOX, LIST_VOX[_settings.vox]))
|
|
basic.append(vox)
|
|
|
|
squelch = RadioSetting("squelch", "Squelch Level",
|
|
RadioSettingValueInteger(
|
|
0, 9, _settings.squelch))
|
|
basic.append(squelch)
|
|
|
|
relay = RadioSetting("relay", "Repeater",
|
|
RadioSettingValueBoolean(_settings.relay))
|
|
basic.append(relay)
|
|
|
|
tot = RadioSetting("tot", "Time-out timer", RadioSettingValueList(
|
|
LIST_TOT, LIST_TOT[_settings.tot]))
|
|
basic.append(tot)
|
|
|
|
beep = RadioSetting("beep", "Key Beep",
|
|
RadioSettingValueBoolean(_settings.beep))
|
|
basic.append(beep)
|
|
|
|
color = RadioSetting("color", "Background Color", RadioSettingValueList(
|
|
LIST_COLOR, LIST_COLOR[_settings.color - 1]))
|
|
basic.append(color)
|
|
|
|
vot = RadioSetting("vot", "VOX Delay Time", RadioSettingValueList(
|
|
LIST_VOT, LIST_VOT[_settings.vot]))
|
|
basic.append(vot)
|
|
|
|
dwait = RadioSetting("dwait", "Dual Standby",
|
|
RadioSettingValueBoolean(_settings.dwait))
|
|
basic.append(dwait)
|
|
|
|
led = RadioSetting("led", "Background Light", RadioSettingValueList(
|
|
LIST_LED, LIST_LED[_settings.led]))
|
|
basic.append(led)
|
|
|
|
voice = RadioSetting("voice", "Voice Prompt", RadioSettingValueList(
|
|
LIST_VOICE, LIST_VOICE[_settings.voice]))
|
|
basic.append(voice)
|
|
|
|
roger = RadioSetting("roger", "Roger Beep",
|
|
RadioSettingValueBoolean(_settings.roger))
|
|
basic.append(roger)
|
|
|
|
autolk = RadioSetting("autolk", "Auto Key Lock",
|
|
RadioSettingValueBoolean(_settings.autolk))
|
|
basic.append(autolk)
|
|
|
|
opnset = RadioSetting("opnset", "Power On Message",
|
|
RadioSettingValueList(
|
|
LIST_OPNSET, LIST_OPNSET[_settings.opnset]))
|
|
basic.append(opnset)
|
|
|
|
def _filter(name):
|
|
filtered = ""
|
|
for char in str(name):
|
|
if char in chirp_common.CHARSET_ASCII:
|
|
filtered += char
|
|
else:
|
|
filtered += " "
|
|
return filtered
|
|
|
|
_msg = self._memobj.poweron_msg
|
|
ponmsg = RadioSetting("poweron_msg.line1", "Power-On Message 1",
|
|
RadioSettingValueString(
|
|
0, 7, _filter(_msg.line1)))
|
|
basic.append(ponmsg)
|
|
|
|
|
|
scans = RadioSetting("scans", "Scan Mode", RadioSettingValueList(
|
|
LIST_SCANS, LIST_SCANS[_settings.scans]))
|
|
basic.append(scans)
|
|
|
|
dw = RadioSetting("dw", "FM Radio Dual Watch",
|
|
RadioSettingValueBoolean(_settings.dw))
|
|
basic.append(dw)
|
|
|
|
name = RadioSetting("name", "Display Names",
|
|
RadioSettingValueBoolean(_settings.name))
|
|
basic.append(name)
|
|
|
|
rptrl = RadioSetting("rptrl", "Repeater TX Delay", RadioSettingValueList(
|
|
LIST_RPTRL, LIST_RPTRL[_settings.rptrl]))
|
|
basic.append(rptrl)
|
|
|
|
rptspk = RadioSetting("rptspk", "Repeater Speaker",
|
|
RadioSettingValueBoolean(_settings.rptspk))
|
|
basic.append(rptspk)
|
|
|
|
rptptt = RadioSetting("rptptt", "Repeater PTT Switch",
|
|
RadioSettingValueBoolean(_settings.rptptt))
|
|
basic.append(rptptt)
|
|
|
|
rptmod = RadioSetting("rptmod", "Repeater Mode",
|
|
RadioSettingValueList(
|
|
LIST_RPTMOD, LIST_RPTMOD[_settings.rptmod]))
|
|
basic.append(rptmod)
|
|
|
|
volmod = RadioSetting("volmod", "Volume Mode",
|
|
RadioSettingValueList(
|
|
LIST_VOLMOD, LIST_VOLMOD[_settings.volmod]))
|
|
basic.append(volmod)
|
|
|
|
dst = RadioSetting("dst", "DTMF Side Tone",
|
|
RadioSettingValueBoolean(_settings.dst))
|
|
basic.append(dst)
|
|
|
|
txsel = RadioSetting("txsel", "Priority TX Channel",
|
|
RadioSettingValueList(
|
|
LIST_TXSEL, LIST_TXSEL[_settings.txsel]))
|
|
basic.append(txsel)
|
|
|
|
ste = RadioSetting("ste", "Squelch Tail Eliminate",
|
|
RadioSettingValueBoolean(_settings.ste))
|
|
basic.append(ste)
|
|
|
|
#advanced
|
|
if _settings.pf1 > 0x0A:
|
|
val = 0x00
|
|
else:
|
|
val = _settings.pf1
|
|
pf1 = RadioSetting("pf1", "PF1 Key",
|
|
RadioSettingValueList(
|
|
LIST_PFKEY, LIST_PFKEY[val]))
|
|
advanced.append(pf1)
|
|
|
|
if _settings.pf2 > 0x0A:
|
|
val = 0x00
|
|
else:
|
|
val = _settings.pf2
|
|
pf2 = RadioSetting("pf2", "PF2 Key",
|
|
RadioSettingValueList(
|
|
LIST_PFKEY, LIST_PFKEY[val]))
|
|
advanced.append(pf2)
|
|
|
|
# other
|
|
_limit = str(int(_mem.limits.vhf.lower) / 10)
|
|
val = RadioSettingValueString(0, 3, _limit)
|
|
val.set_mutable(False)
|
|
rs = RadioSetting("limits.vhf.lower", "VHF low", val)
|
|
other.append(rs)
|
|
|
|
_limit = str(int(_mem.limits.vhf.upper) / 10)
|
|
val = RadioSettingValueString(0, 3, _limit)
|
|
val.set_mutable(False)
|
|
rs = RadioSetting("limits.vhf.upper", "VHF high", val)
|
|
other.append(rs)
|
|
|
|
_limit = str(int(_mem.limits.uhf.lower) / 10)
|
|
val = RadioSettingValueString(0, 3, _limit)
|
|
val.set_mutable(False)
|
|
rs = RadioSetting("limits.uhf.lower", "UHF low", val)
|
|
other.append(rs)
|
|
|
|
_limit = str(int(_mem.limits.uhf.upper) / 10)
|
|
val = RadioSettingValueString(0, 3, _limit)
|
|
val.set_mutable(False)
|
|
rs = RadioSetting("limits.uhf.upper", "UHF high", val)
|
|
other.append(rs)
|
|
|
|
#work mode
|
|
vfomr_a = RadioSetting("vfomr_a", "Display Mode A",
|
|
RadioSettingValueList(
|
|
LIST_VFOMR, LIST_VFOMR[_settings.vfomr_a]))
|
|
workmode.append(vfomr_a)
|
|
|
|
vfomr_b = RadioSetting("vfomr_b", "Display Mode B",
|
|
RadioSettingValueList(
|
|
LIST_VFOMR, LIST_VFOMR[_settings.vfomr_b]))
|
|
workmode.append(vfomr_b)
|
|
|
|
mrcha = RadioSetting("mrcha", "Channel # A",
|
|
RadioSettingValueInteger(
|
|
1, 128, _settings.mrcha))
|
|
workmode.append(mrcha)
|
|
|
|
mrchb = RadioSetting("mrchb", "Channel # B",
|
|
RadioSettingValueInteger(
|
|
1, 128, _settings.mrchb))
|
|
workmode.append(mrchb)
|
|
|
|
#fm radio
|
|
vfomr_fm = RadioSetting("vfomr_fm", "FM Radio Display Mode",
|
|
RadioSettingValueList(
|
|
LIST_VFOMRFM, LIST_VFOMRFM[_settings.vfomr_fm]))
|
|
fmradio.append(vfomr_fm)
|
|
|
|
fmch = RadioSetting("fmch", "FM Radio Channel #",
|
|
RadioSettingValueInteger(
|
|
1, 25, _settings.fmch))
|
|
fmradio.append(fmch)
|
|
|
|
return top
|
|
|
|
def set_settings(self, settings):
|
|
for element in settings:
|
|
if not isinstance(element, RadioSetting):
|
|
self.set_settings(element)
|
|
continue
|
|
else:
|
|
try:
|
|
if "." in element.get_name():
|
|
bits = element.get_name().split(".")
|
|
obj = self._memobj
|
|
for bit in bits[:-1]:
|
|
obj = getattr(obj, bit)
|
|
setting = bits[-1]
|
|
else:
|
|
obj = self._memobj.settings
|
|
setting = element.get_name()
|
|
|
|
if element.has_apply_callback():
|
|
LOG.debug("Using apply callback")
|
|
element.run_apply_callback()
|
|
elif setting == "color":
|
|
setattr(obj, setting, int(element.value) + 1)
|
|
elif element.value.get_mutable():
|
|
LOG.debug("Setting %s = %s" % (setting, element.value))
|
|
setattr(obj, setting, element.value)
|
|
except Exception, e:
|
|
LOG.debug(element.get_name())
|
|
raise
|
|
|
|
@classmethod
|
|
def match_model(cls, filedata, filename):
|
|
match_size = False
|
|
match_model = False
|
|
|
|
# testing the file data size
|
|
if len(filedata) in [0x1000, ]:
|
|
match_size = True
|
|
|
|
# testing the model fingerprint
|
|
match_model = model_match(cls, filedata)
|
|
|
|
if match_size and match_model:
|
|
return True
|
|
else:
|
|
return False
|