[chirp_devel] [PATCH] [KT7900D] Add Support for QYT KT7900D and KT8900D Mobile Radios
Jim Unroe
Sun Feb 19 16:16:39 PST 2017
# HG changeset patch
# User Jim Unroe <rock.unroe at gmail.com>
# Date 1487549731 18000
# Node ID 90b02a91d80c80d843f3c1de6ff7dc5470030f3b
# Parent 0611c6c740ab74c0d939634765a52ebc2b5a8497
[KT7900D] Add Support for QYT KT7900D and KT8900D Mobile Radios
This patch modifies the btech.py driver to prepare it to support a
new series of mobile radios that have an OLED color display.
Radio models initially supported:
QYT KT7900D (quad band)
QYT KT8900D (dual band)
New Model #4269
diff -r 0611c6c740ab -r 90b02a91d80c chirp/drivers/btech.py
--- a/chirp/drivers/btech.py Thu Feb 16 18:28:49 2017 -0800
+++ b/chirp/drivers/btech.py Sun Feb 19 19:15:31 2017 -0500
@@ -1,4 +1,4 @@
-# Copyright 2016:
+# Copyright 2016-2017:
# * Pavel Milanes CO7WT, <pavelmc at gmail.com>
# * Jim Unroe KC9HI, <rock.unroe at gmail.com>
#
@@ -30,6 +30,2710 @@
RadioSettingValueFloat, RadioSettings, InvalidValueError
from textwrap import dedent
+# A note about the memmory in these radios
+#
+# The real memory of these radios extends to 0x4000
+# On read the factory software only uses up to 0x3200
+# On write it just uploads the contents up to 0x3100
+#
+# The mem beyond 0x3200 holds the ID data
+
+MEM_SIZE = 0x4000
+BLOCK_SIZE = 0x40
+TX_BLOCK_SIZE = 0x10
+ACK_CMD = "\x06"
+MODES = ["FM", "NFM"]
+SKIP_VALUES = ["S", ""]
+TONES = chirp_common.TONES
+DTCS = sorted(chirp_common.DTCS_CODES + [645])
+
+# lists related to "extra" settings
+PTTID_LIST = ["OFF", "BOT", "EOT", "BOTH"]
+PTTIDCODE_LIST = ["%s" % x for x in range(1, 16)]
+OPTSIG_LIST = ["OFF", "DTMF", "2TONE", "5TONE"]
+SPMUTE_LIST = ["Tone/DTCS", "Tone/DTCS and Optsig", "Tone/DTCS or Optsig"]
+
+# lists
+LIST_AB = ["A", "B"]
+LIST_ABCD = LIST_AB + ["C", "D"]
+LIST_ANIL = ["3", "4", "5"]
+LIST_APO = ["Off"] + ["%s minutes" % x for x in range(30, 330, 30)]
+LIST_COLOR4 = ["Off", "Blue", "Orange", "Purple"]
+LIST_COLOR8 = ["Black", "White", "Red", "Blue", "Green", "Yellow", "Indego",
+ "Purple", "Gray"]
+LIST_DTMFST = ["OFF", "Keyboard", "ANI", "Keyboad + ANI"]
+LIST_EMCTP = ["TX alarm sound", "TX ANI", "Both"]
+LIST_EMCTPX = ["Off"] + LIST_EMCTP
+LIST_LANGUA = ["English", "Chinese"]
+LIST_MDF = ["Frequency", "Channel", "Name"]
+LIST_OFF1TO9 = ["Off"] + ["%s seconds" % x for x in range(1, 10)]
+LIST_OFF1TO10 = ["Off"] + ["%s seconds" % x for x in range(1, 11)]
+LIST_OFF1TO50 = ["Off"] + ["%s seconds" % x for x in range(1, 51)]
+LIST_PONMSG = ["Full", "Message", "Battery voltage"]
+LIST_REPM = ["Off", "Carrier", "CTCSS or DCS", "Tone", "DTMF"]
+LIST_REPS = ["1000 Hz", "1450 Hz", "1750 Hz", "2100Hz"]
+LIST_RPTDL = ["Off"] + ["%s ms" % x for x in range(1, 10)]
+LIST_SCMODE = ["Off", "PTT-SC", "MEM-SC", "PON-SC"]
+LIST_SHIFT = ["Off", "+", "-"]
+LIST_SKIPTX = ["Off", "Skip 1", "Skip 2"]
+STEPS = [2.5, 5.0, 6.25, 10.0, 12.5, 25.0]
+LIST_STEP = [str(x) for x in STEPS]
+LIST_SYNC = ["Off", "AB", "CD", "AB+CD"]
+LIST_TMR = ["OFF", "M+A", "M+B", "M+C", "M+D", "M+A+B", "M+A+C", "M+A+D",
+ "M+B+C", "M+B+D", "M+C+D", "M+A+B+C", "M+A+B+D", "M+A+C+D",
+ "M+B+C+D", "A+B+C+D"]
+LIST_TOT = ["%s sec" % x for x in range(15, 615, 15)]
+LIST_TXDISP = ["Power", "Mic Volume"]
+LIST_TXP = ["High", "Low"]
+LIST_SCREV = ["TO (timeout)", "CO (carrier operated)", "SE (search)"]
+LIST_VFOMR = ["Frequency", "Channel"]
+LIST_WIDE = ["Wide", "Narrow"]
+
+# lists related to DTMF, 2TONE and 5TONE settings
+LIST_5TONE_STANDARDS = ["CCIR1", "CCIR2", "PCCIR", "ZVEI1", "ZVEI2", "ZVEI3",
+ "PZVEI", "DZVEI", "PDZVEI", "EEA", "EIA", "EURO",
+ "CCITT", "NATEL", "MODAT", "none"]
+LIST_5TONE_STANDARDS_without_none = ["CCIR1", "CCIR2", "PCCIR", "ZVEI1",
+ "ZVEI2", "ZVEI3",
+ "PZVEI", "DZVEI", "PDZVEI", "EEA", "EIA",
+ "EURO", "CCITT", "NATEL", "MODAT"]
+LIST_5TONE_STANDARD_PERIODS = ["20", "30", "40", "50", "60", "70", "80", "90",
+ "100", "110", "120", "130", "140", "150", "160",
+ "170", "180", "190", "200"]
+LIST_5TONE_DIGITS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A",
+ "B", "C", "D", "E", "F"]
+LIST_5TONE_DELAY = ["%s ms" % x for x in range(0, 1010, 10)]
+LIST_5TONE_RESET = ["%s ms" % x for x in range(100, 8100, 100)]
+LIST_5TONE_RESET_COLOR = ["%s ms" % x for x in range(100, 20100, 100)]
+LIST_DTMF_SPEED = ["%s ms" % x for x in range(50, 2010, 10)]
+LIST_DTMF_DIGITS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B",
+ "C", "D", "#", "*"]
+LIST_DTMF_VALUES = [0x0A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0D, 0x0E, 0x0F, 0x00, 0x0C, 0x0B ]
+LIST_DTMF_SPECIAL_DIGITS = [ "*", "#", "A", "B", "C", "D"]
+LIST_DTMF_SPECIAL_VALUES = [ 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00]
+LIST_DTMF_DELAY = ["%s ms" % x for x in range(100, 4100, 100)]
+CHARSET_DTMF_DIGITS = "0123456789AaBbCcDd#*"
+LIST_2TONE_DEC = ["A-B", "A-C", "A-D",
+ "B-A", "B-C", "B-D",
+ "C-A", "C-B", "C-D",
+ "D-A", "D-B", "D-C"]
+LIST_2TONE_RESPONSE = ["None", "Alert", "Transpond", "Alert+Transpond"]
+
+# This is a general serial timeout for all serial read functions.
+# Practice has show that about 0.7 sec will be enough to cover all radios.
+STIMEOUT = 0.7
+
+# this var controls the verbosity in the debug and by default it's low (False)
+# make it True and you will to get a very verbose debug.log
+debug = False
+
+# valid chars on the LCD, Note that " " (space) is stored as "\xFF"
+VALID_CHARS = chirp_common.CHARSET_ALPHANUMERIC + \
+ "`{|}!\"#$%&'()*+,-./:;<=>?@[]^_"
+
+
+##### ID strings #####################################################
+
+# BTECH UV2501 pre-production units
+UV2501pp_fp = "M2C294"
+# BTECH UV2501 pre-production units 2 + and 1st Gen radios
+UV2501pp2_fp = "M29204"
+# B-TECH UV-2501 second generation (2G) radios
+UV2501G2_fp = "BTG214"
+# B-TECH UV-2501 third generation (3G) radios
+UV2501G3_fp = "BTG324"
+
+# B-TECH UV-2501+220 pre-production units
+UV2501_220pp_fp = "M3C281"
+# extra block read for the 2501+220 pre-production units
+# the same for all of this radios so far
+UV2501_220pp_id = " 280528"
+# B-TECH UV-2501+220
+UV2501_220_fp = "M3G201"
+# new variant, let's call it Generation 2
+UV2501_220G2_fp = "BTG211"
+# B-TECH UV-2501+220 third generation (3G)
+UV2501_220G3_fp = "BTG311"
+
+# B-TECH UV-5001 pre-production units + 1st Gen radios
+UV5001pp_fp = "V19204"
+# B-TECH UV-5001 alpha units
+UV5001alpha_fp = "V28204"
+# B-TECH UV-5001 second generation (2G) radios
+UV5001G2_fp = "BTG214"
+# B-TECH UV-5001 second generation (2G2)
+UV5001G22_fp = "V2G204"
+# B-TECH UV-5001 third generation (3G)
+UV5001G3_fp = "BTG304"
+
+# special var to know when we found a BTECH Gen 3
+BTECH3 = [UV2501G3_fp, UV2501_220G3_fp, UV5001G3_fp]
+
+
+# WACCOM Mini-8900
+MINI8900_fp = "M28854"
+
+
+# QYT KT-UV980
+KTUV980_fp = "H28854"
+
+# QYT KT8900
+KT8900_fp = "M29154"
+# New generations KT8900
+KT8900_fp1 = "M2C234"
+KT8900_fp2 = "M2G1F4"
+KT8900_fp3 = "M2G2F4"
+KT8900_fp4 = "M2G304"
+KT8900_fp5 = "M2G314"
+# this radio has an extra ID
+KT8900_id = " 303688"
+
+# KT8900R
+KT8900R_fp = "M3G1F4"
+# Second Generation
+KT8900R_fp1 = "M3G214"
+# another model
+KT8900R_fp2 = "M3C234"
+# another model G4?
+KT8900R_fp3 = "M39164"
+# another model
+KT8900R_fp4 = "M3G314"
+# this radio has an extra ID
+KT8900R_id = "280528"
+
+# KT7900D (quad band)
+KT7900D_fp = "VC4004"
+
+# KT8900D (dual band)
+KT8900D_fp = "VC2002"
+
+# LUITON LT-588UV
+LT588UV_fp = "V2G1F4"
+# Added by rstrickoff gen 2 id
+LT588UV_fp1 = "V2G214"
+
+
+#### MAGICS
+# for the Waccom Mini-8900
+MSTRING_MINI8900 = "\x55\xA5\xB5\x45\x55\x45\x4d\x02"
+# for the B-TECH UV-2501+220 (including pre production ones)
+MSTRING_220 = "\x55\x20\x15\x12\x12\x01\x4d\x02"
+# for the QYT KT8900 & R
+MSTRING_KT8900 = "\x55\x20\x15\x09\x16\x45\x4D\x02"
+MSTRING_KT8900R = "\x55\x20\x15\x09\x25\x01\x4D\x02"
+# magic string for all other models
+MSTRING = "\x55\x20\x15\x09\x20\x45\x4d\x02"
+# for the QYT KT7900D & KT8900D
+MSTRING_KT8900D = "\x55\x20\x16\x08\x01\xFF\xDC\x02"
+
+
+def _clean_buffer(radio):
+ """Cleaning the read serial buffer, hard timeout to survive an infinite
+ data stream"""
+
+ # touching the serial timeout to optimize the flushing
+ # restored at the end to the default value
+ radio.pipe.timeout = 0.1
+ dump = "1"
+ datacount = 0
+
+ try:
+ while len(dump) > 0:
+ dump = radio.pipe.read(100)
+ datacount += len(dump)
+ # hard limit to survive a infinite serial data stream
+ # 5 times bigger than a normal rx block (69 bytes)
+ if datacount > 345:
+ seriale = "Please check your serial port selection."
+ raise errors.RadioError(seriale)
+
+ # restore the default serial timeout
+ radio.pipe.timeout = STIMEOUT
+
+ except Exception:
+ raise errors.RadioError("Unknown error cleaning the serial buffer")
+
+
+def _rawrecv(radio, amount):
+ """Raw read from the radio device, less intensive way"""
+
+ data = ""
+
+ try:
+ data = radio.pipe.read(amount)
+
+ # DEBUG
+ if debug is True:
+ LOG.debug("<== (%d) bytes:\n\n%s" %
+ (len(data), util.hexprint(data)))
+
+ # fail if no data is received
+ if len(data) == 0:
+ raise errors.RadioError("No data received from radio")
+
+ # notice on the logs if short
+ if len(data) < amount:
+ LOG.warn("Short reading %d bytes from the %d requested." %
+ (len(data), amount))
+
+ except:
+ raise errors.RadioError("Error reading data from radio")
+
+ return data
+
+
+def _send(radio, data):
+ """Send data to the radio device"""
+
+ try:
+ for byte in data:
+ radio.pipe.write(byte)
+ # Some OS (mainly Linux ones) are too fast on the serial and
+ # get the MCU inside the radio stuck in the early stages, this
+ # hits some models more than others.
+ #
+ # To cope with that we introduce a delay on the writes.
+ # Many option have been tested (delaying only after error occures,
+ # after short reads, only for linux, ...)
+ # Finally, a static delay was chosen as simplest of all solutions
+ # (Michael Wagner, OE4AMW)
+ # (for details, see issue 3993)
+ sleep(0.002)
+
+ # DEBUG
+ if debug is True:
+ LOG.debug("==> (%d) bytes:\n\n%s" %
+ (len(data), util.hexprint(data)))
+ except:
+ raise errors.RadioError("Error sending data to radio")
+
+
+def _make_frame(cmd, addr, length, data=""):
+ """Pack the info in the headder format"""
+ frame = "\x06" + struct.pack(">BHB", ord(cmd), addr, length)
+ # add the data if set
+ if len(data) != 0:
+ frame += data
+
+ return frame
+
+
+def _recv(radio, addr):
+ """Get data from the radio all at once to lower syscalls load"""
+
+ # Get the full 69 bytes at a time to reduce load
+ # 1 byte ACK + 4 bytes header + 64 bytes of data (BLOCK_SIZE)
+
+ # get the whole block
+ block = _rawrecv(radio, BLOCK_SIZE + 5)
+
+ # basic check
+ if len(block) < (BLOCK_SIZE + 5):
+ raise errors.RadioError("Short read of the block 0x%04x" % addr)
+
+ # checking for the ack
+ if block[0] != ACK_CMD:
+ raise errors.RadioError("Bad ack from radio in block 0x%04x" % addr)
+
+ # header validation
+ c, a, l = struct.unpack(">BHB", block[1:5])
+ if a != addr or l != BLOCK_SIZE or c != ord("X"):
+ LOG.debug("Invalid header for block 0x%04x" % addr)
+ LOG.debug("CMD: %s ADDR: %04x SIZE: %02x" % (c, a, l))
+ raise errors.RadioError("Invalid header for block 0x%04x:" % addr)
+
+ # return the data
+ return block[5:]
+
+
+def _start_clone_mode(radio, status):
+ """Put the radio in clone mode and get the ident string, 3 tries"""
+
+ # cleaning the serial buffer
+ _clean_buffer(radio)
+
+ # prep the data to show in the UI
+ status.cur = 0
+ status.msg = "Identifying the radio..."
+ status.max = 3
+ radio.status_fn(status)
+
+ try:
+ for a in range(0, status.max):
+ # Update the UI
+ status.cur = a + 1
+ radio.status_fn(status)
+
+ # send the magic word
+ _send(radio, radio._magic)
+
+ # Now you get a x06 of ACK if all goes well
+ ack = radio.pipe.read(1)
+
+ if ack == "\x06":
+ # DEBUG
+ LOG.info("Magic ACK received")
+ status.cur = status.max
+ radio.status_fn(status)
+
+ return True
+
+ return False
+
+ except errors.RadioError:
+ raise
+ except Exception, e:
+ raise errors.RadioError("Error sending Magic to radio:\n%s" % e)
+
+
+def _do_ident(radio, status, upload=False):
+ """Put the radio in PROGRAM mode & identify it"""
+ # set the serial discipline
+ radio.pipe.baudrate = 9600
+ radio.pipe.parity = "N"
+
+ # open the radio into program mode
+ if _start_clone_mode(radio, status) is False:
+ msg = "Radio did not enter clone mode"
+ # warning about old versions of QYT KT8900
+ if radio.MODEL == "KT8900":
+ msg += ". You may want to try it as a WACCOM MINI-8900, there is a"
+ msg += " known variant of this radios that is a clone of it."
+ raise errors.RadioError(msg)
+
+ # Ok, get the ident string
+ ident = _rawrecv(radio, 49)
+
+ # basic check for the ident
+ if len(ident) != 49:
+ raise errors.RadioError("Radio send a short ident block.")
+
+ # check if ident is OK
+ itis = False
+ for fp in radio._fileid:
+ if fp in ident:
+ # got it!
+ itis = True
+ # checking if we are dealing with a Gen 3 BTECH
+ if radio.VENDOR == "BTECH" and fp in BTECH3:
+ radio.btech3 = True
+
+ break
+
+ if itis is False:
+ LOG.debug("Incorrect model ID, got this:\n\n" + util.hexprint(ident))
+ raise errors.RadioError("Radio identification failed.")
+
+ # some radios needs a extra read and check for a code on it, this ones
+ # has the check value in the _id2 var, others simply False
+ if radio._id2 is not False:
+ # lower the timeout here as this radios are reseting due to timeout
+ radio.pipe.timeout = 0.05
+
+ # query & receive the extra ID
+ _send(radio, _make_frame("S", 0x3DF0, 16))
+ id2 = _rawrecv(radio, 21)
+
+ # WARNING !!!!!!
+ # different radios send a response with a different amount of data
+ # it seems that it's padded with \xff, \x20 and some times with \x00
+ # we just care about the first 16, our magic string is in there
+ if len(id2) < 16:
+ raise errors.RadioError("The extra ID is short, aborting.")
+
+ # ok, the correct string must be in the received data
+ if radio._id2 not in id2:
+ LOG.debug("Full *BAD* extra ID on the %s is: \n%s" %
+ (radio.MODEL, util.hexprint(id2)))
+ raise errors.RadioError("The extra ID is wrong, aborting.")
+
+ # this radios need a extra request/answer here on the upload
+ # the amount of data received depends of the radio type
+ #
+ # also the first block of TX must no have the ACK at the beginning
+ # see _upload for this.
+ if upload is True:
+ # send an ACK
+ _send(radio, ACK_CMD)
+
+ # the amount of data depend on the radio, so far we have two radios
+ # reading two bytes with an ACK at the end and just ONE with just
+ # one byte (QYT KT8900)
+ # the JT-6188 appears a clone of the last, but reads TWO bytes.
+ #
+ # we will read two bytes with a custom timeout to not penalize the
+ # users for this.
+ #
+ # we just check for a response and last byte being a ACK, that is
+ # the common stone for all radios (3 so far)
+ ack = _rawrecv(radio, 2)
+
+ # checking
+ if len(ack) == 0 or ack[-1:] != ACK_CMD:
+ raise errors.RadioError("Radio didn't ACK the upload")
+
+ # restore the default serial timeout
+ radio.pipe.timeout = STIMEOUT
+
+ # DEBUG
+ LOG.info("Positive ident, this is a %s %s" % (radio.VENDOR, radio.MODEL))
+
+ return True
+
+
+def _download(radio):
+ """Get the memory map"""
+
+ # UI progress
+ status = chirp_common.Status()
+
+ # put radio in program mode and identify it
+ _do_ident(radio, status)
+
+ # the models that doesn't have the extra ID have to make a dummy read here
+ if radio._id2 is False:
+ _send(radio, _make_frame("S", 0, BLOCK_SIZE))
+ discard = _rawrecv(radio, BLOCK_SIZE + 5)
+
+ if debug is True:
+ LOG.info("Dummy first block read done, got this:\n\n %s",
+ util.hexprint(discard))
+
+ # reset the progress bar in the UI
+ status.max = MEM_SIZE / BLOCK_SIZE
+ status.msg = "Cloning from radio..."
+ status.cur = 0
+ radio.status_fn(status)
+
+ # cleaning the serial buffer
+ _clean_buffer(radio)
+
+ data = ""
+ for addr in range(0, MEM_SIZE, BLOCK_SIZE):
+ # sending the read request
+ _send(radio, _make_frame("S", addr, BLOCK_SIZE))
+
+ # read
+ d = _recv(radio, addr)
+
+ # aggregate the data
+ data += d
+
+ # UI Update
+ status.cur = addr / BLOCK_SIZE
+ status.msg = "Cloning from radio..."
+ radio.status_fn(status)
+
+ return data
+
+
+def _upload(radio):
+ """Upload procedure"""
+
+ # The UPLOAD mem is restricted to lower than 0x3100,
+ # so we will overide that here localy
+ MEM_SIZE = 0x3100
+
+ # UI progress
+ status = chirp_common.Status()
+
+ # put radio in program mode and identify it
+ _do_ident(radio, status, True)
+
+ # get the data to upload to radio
+ data = radio.get_mmap()
+
+ # Reset the UI progress
+ status.max = MEM_SIZE / TX_BLOCK_SIZE
+ status.cur = 0
+ status.msg = "Cloning to radio..."
+ radio.status_fn(status)
+
+ # the radios that doesn't have the extra ID 'may' do a dummy write, I found
+ # that leveraging the bad ACK and NOT doing the dummy write is ok, as the
+ # dummy write is accepted (it actually writes to the mem!) by the radio.
+
+ # cleaning the serial buffer
+ _clean_buffer(radio)
+
+ # the fun start here
+ for addr in range(0, MEM_SIZE, TX_BLOCK_SIZE):
+ # getting the block of data to send
+ d = data[addr:addr + TX_BLOCK_SIZE]
+
+ # build the frame to send
+ frame = _make_frame("X", addr, TX_BLOCK_SIZE, d)
+
+ # first block must not send the ACK at the beginning for the
+ # ones that has the extra id, since this have to do a extra step
+ if addr == 0 and radio._id2 is not False:
+ frame = frame[1:]
+
+ # send the frame
+ _send(radio, frame)
+
+ # receiving the response
+ ack = _rawrecv(radio, 1)
+
+ # basic check
+ if len(ack) != 1:
+ raise errors.RadioError("No ACK when writing block 0x%04x" % addr)
+
+ if not ack in "\x06\x05":
+ raise errors.RadioError("Bad ACK writing block 0x%04x:" % addr)
+
+ # UI Update
+ status.cur = addr / TX_BLOCK_SIZE
+ status.msg = "Cloning to radio..."
+ radio.status_fn(status)
+
+
+def model_match(cls, data):
+ """Match the opened/downloaded image to the correct version"""
+ rid = data[0x3f70:0x3f76]
+
+ if rid in cls._fileid:
+ return True
+
+ return False
+
+
+def _decode_ranges(low, high):
+ """Unpack the data in the ranges zones in the memmap and return
+ a tuple with the integer corresponding to the Mhz it means"""
+ ilow = int(low[0]) * 100 + int(low[1]) * 10 + int(low[2])
+ ihigh = int(high[0]) * 100 + int(high[1]) * 10 + int(high[2])
+ ilow *= 1000000
+ ihigh *= 1000000
+
+ return (ilow, ihigh)
+
+
+def _split(rf, f1, f2):
+ """Returns False if the two freqs are in the same band (no split)
+ or True otherwise"""
+
+ # determine if the two freqs are in the same band
+ for low, high in rf.valid_bands:
+ if f1 >= low and f1 <= high and \
+ f2 >= low and f2 <= high:
+ # if the two freqs are on the same Band this is not a split
+ return False
+
+ # if you get here is because the freq pairs are split
+ return False
+
+
+class BTechMobileCommon(chirp_common.CloneModeRadio,
+ chirp_common.ExperimentalRadio):
+ """BTECH's UV-5001 and alike radios"""
+ VENDOR = "BTECH"
+ MODEL = ""
+ IDENT = ""
+ BANDS = 2
+ COLOR_LCD = False
+ NAME_LENGTH = 6
+ _power_levels = [chirp_common.PowerLevel("High", watts=25),
+ chirp_common.PowerLevel("Low", watts=10)]
+ _vhf_range = (130000000, 180000000)
+ _220_range = (200000000, 271000000)
+ _uhf_range = (400000000, 521000000)
+ _350_range = (350000000, 391000000)
+ _upper = 199
+ _magic = MSTRING
+ _fileid = None
+ _id2 = False
+ btech3 = False
+
+ @classmethod
+ def get_prompts(cls):
+ rp = chirp_common.RadioPrompts()
+ rp.experimental = \
+ ('This driver is experimental.\n'
+ '\n'
+ 'Please keep a copy of your memories with the original software '
+ 'if you treasure them, this driver is new and may contain'
+ ' bugs.\n'
+ '\n'
+ )
+ rp.pre_download = _(dedent("""\
+ Follow these instructions to download your info:
+
+ 1 - Turn off your radio
+ 2 - Connect your interface cable
+ 3 - Turn on your radio
+ 4 - Do the download of your radio data
+
+ """))
+ rp.pre_upload = _(dedent("""\
+ Follow these instructions to upload your info:
+
+ 1 - Turn off your radio
+ 2 - Connect your interface cable
+ 3 - Turn on your radio
+ 4 - Do the upload of your radio data
+
+ """))
+ return rp
+
+ def get_features(self):
+ """Get the radio's features"""
+
+ # we will use the following var as global
+ global POWER_LEVELS
+
+ rf = chirp_common.RadioFeatures()
+ rf.has_settings = True
+ rf.has_bank = False
+ rf.has_tuning_step = False
+ rf.can_odd_split = True
+ rf.has_name = True
+ rf.has_offset = True
+ rf.has_mode = True
+ rf.has_dtcs = True
+ rf.has_rx_dtcs = True
+ rf.has_dtcs_polarity = True
+ rf.has_ctone = True
+ rf.has_cross = True
+ rf.valid_modes = MODES
+ rf.valid_characters = VALID_CHARS
+ rf.valid_name_length = self.NAME_LENGTH
+ rf.valid_duplexes = ["", "-", "+", "split", "off"]
+ rf.valid_tmodes = ['', 'Tone', 'TSQL', 'DTCS', 'Cross']
+ rf.valid_cross_modes = [
+ "Tone->Tone",
+ "DTCS->",
+ "->DTCS",
+ "Tone->DTCS",
+ "DTCS->Tone",
+ "->Tone",
+ "DTCS->DTCS"]
+ rf.valid_skips = SKIP_VALUES
+ rf.valid_dtcs_codes = DTCS
+ rf.memory_bounds = (0, self._upper)
+
+ # power levels
+ POWER_LEVELS = self._power_levels
+ rf.valid_power_levels = POWER_LEVELS
+
+ # normal dual bands
+ rf.valid_bands = [self._vhf_range, self._uhf_range]
+
+ # 220 band
+ if self.BANDS == 3 or self.BANDS == 4:
+ rf.valid_bands.append(self._220_range)
+
+ # 350 band
+ if self.BANDS == 4:
+ rf.valid_bands.append(self._350_range)
+
+ return rf
+
+ def sync_in(self):
+ """Download from radio"""
+ data = _download(self)
+ self._mmap = memmap.MemoryMap(data)
+ self.process_mmap()
+
+ def sync_out(self):
+ """Upload to radio"""
+ try:
+ _upload(self)
+ except errors.RadioError:
+ raise
+ except Exception, e:
+ raise errors.RadioError("Error: %s" % e)
+
+ def get_raw_memory(self, number):
+ return repr(self._memobj.memory[number])
+
+ def _decode_tone(self, val):
+ """Parse the tone data to decode from mem, it returns:
+ Mode (''|DTCS|Tone), Value (None|###), Polarity (None,N,R)"""
+ pol = None
+
+ if val in [0, 65535]:
+ return '', None, None
+ elif val > 0x0258:
+ a = val / 10.0
+ return 'Tone', a, pol
+ else:
+ if val > 0x69:
+ index = val - 0x6A
+ pol = "R"
+ else:
+ index = val - 1
+ pol = "N"
+
+ tone = DTCS[index]
+ return 'DTCS', tone, pol
+
+ def _encode_tone(self, memval, mode, val, pol):
+ """Parse the tone data to encode from UI to mem"""
+ if mode == '' or mode is None:
+ memval.set_raw("\x00\x00")
+ elif mode == 'Tone':
+ memval.set_value(val * 10)
+ elif mode == 'DTCS':
+ # detect the index in the DTCS list
+ try:
+ index = DTCS.index(val)
+ if pol == "N":
+ index += 1
+ else:
+ index += 0x6A
+ memval.set_value(index)
+ except:
+ msg = "Digital Tone '%d' is not supported" % value
+ LOG.error(msg)
+ raise errors.RadioError(msg)
+ else:
+ msg = "Internal error: invalid mode '%s'" % mode
+ LOG.error(msg)
+ raise errors.InvalidDataError(msg)
+
+ def get_memory(self, number):
+ """Get the mem representation from the radio image"""
+ _mem = self._memobj.memory[number]
+ _names = self._memobj.names[number]
+
+ # Create a high-level memory object to return to the UI
+ mem = chirp_common.Memory()
+
+ # Memory number
+ mem.number = number
+
+ if _mem.get_raw()[0] == "\xFF":
+ mem.empty = True
+ return mem
+
+ # Freq and offset
+ mem.freq = int(_mem.rxfreq) * 10
+ # tx freq can be blank
+ if _mem.get_raw()[4] == "\xFF":
+ # TX freq not set
+ mem.offset = 0
+ mem.duplex = "off"
+ else:
+ # TX freq set
+ offset = (int(_mem.txfreq) * 10) - mem.freq
+ if offset != 0:
+ if _split(self.get_features(), mem.freq, int(_mem.txfreq) * 10):
+ mem.duplex = "split"
+ mem.offset = int(_mem.txfreq) * 10
+ elif offset < 0:
+ mem.offset = abs(offset)
+ mem.duplex = "-"
+ elif offset > 0:
+ mem.offset = offset
+ mem.duplex = "+"
+ else:
+ mem.offset = 0
+
+ # name TAG of the channel
+ mem.name = str(_names.name).rstrip("\xFF").replace("\xFF", " ")
+
+ # power
+ mem.power = POWER_LEVELS[int(_mem.power)]
+
+ # wide/narrow
+ mem.mode = MODES[int(_mem.wide)]
+
+ # skip
+ mem.skip = SKIP_VALUES[_mem.add]
+
+ # tone data
+ rxtone = txtone = None
+ txtone = self._decode_tone(_mem.txtone)
+ rxtone = self._decode_tone(_mem.rxtone)
+ chirp_common.split_tone_decode(mem, txtone, rxtone)
+
+ # Extra
+ mem.extra = RadioSettingGroup("extra", "Extra")
+
+ scramble = RadioSetting("scramble", "Scramble",
+ RadioSettingValueBoolean(bool(
+ _mem.scramble)))
+ mem.extra.append(scramble)
+
+ bcl = RadioSetting("bcl", "Busy channel lockout",
+ RadioSettingValueBoolean(bool(_mem.bcl)))
+ mem.extra.append(bcl)
+
+ pttid = RadioSetting("pttid", "PTT ID",
+ RadioSettingValueList(PTTID_LIST,
+ PTTID_LIST[_mem.pttid]))
+ mem.extra.append(pttid)
+
+ # validating scode
+ scode = _mem.scode if _mem.scode != 15 else 0
+ pttidcode = RadioSetting("scode", "PTT ID signal code",
+ RadioSettingValueList(
+ PTTIDCODE_LIST,
+ PTTIDCODE_LIST[scode]))
+ mem.extra.append(pttidcode)
+
+ optsig = RadioSetting("optsig", "Optional signaling",
+ RadioSettingValueList(
+ OPTSIG_LIST,
+ OPTSIG_LIST[_mem.optsig]))
+ mem.extra.append(optsig)
+
+ spmute = RadioSetting("spmute", "Speaker mute",
+ RadioSettingValueList(
+ SPMUTE_LIST,
+ SPMUTE_LIST[_mem.spmute]))
+ mem.extra.append(spmute)
+
+ return mem
+
+ def set_memory(self, mem):
+ """Set the memory data in the eeprom img from the UI"""
+ # get the eprom representation of this channel
+ _mem = self._memobj.memory[mem.number]
+ _names = self._memobj.names[mem.number]
+
+ mem_was_empty = False
+ # same method as used in get_memory for determining if mem is empty
+ # doing this BEFORE overwriting it with new values ...
+ if _mem.get_raw()[0] == "\xFF":
+ LOG.debug("This mem was empty before")
+ mem_was_empty = True
+
+ # if empty memmory
+ if mem.empty:
+ # the channel itself
+ _mem.set_raw("\xFF" * 16)
+ # the name tag
+ _names.set_raw("\xFF" * 16)
+ return
+
+ # frequency
+ _mem.rxfreq = mem.freq / 10
+
+ # duplex
+ if mem.duplex == "+":
+ _mem.txfreq = (mem.freq + mem.offset) / 10
+ elif mem.duplex == "-":
+ _mem.txfreq = (mem.freq - mem.offset) / 10
+ elif mem.duplex == "off":
+ for i in _mem.txfreq:
+ i.set_raw("\xFF")
+ elif mem.duplex == "split":
+ _mem.txfreq = mem.offset / 10
+ else:
+ _mem.txfreq = mem.freq / 10
+
+ # tone data
+ ((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)
+
+ # name TAG of the channel
+ if len(mem.name) < self.NAME_LENGTH:
+ # we must pad to self.NAME_LENGTH chars, " " = "\xFF"
+ mem.name = str(mem.name).ljust(self.NAME_LENGTH, " ")
+ _names.name = str(mem.name).replace(" ", "\xFF")
+
+ # power, # default power level is high
+ _mem.power = 0 if mem.power is None else POWER_LEVELS.index(mem.power)
+
+ # wide/narrow
+ _mem.wide = MODES.index(mem.mode)
+
+ # scan add property
+ _mem.add = SKIP_VALUES.index(mem.skip)
+
+ # reseting unknowns, this have to be set by hand
+ _mem.unknown0 = 0
+ _mem.unknown1 = 0
+ _mem.unknown2 = 0
+ _mem.unknown3 = 0
+ _mem.unknown4 = 0
+ _mem.unknown5 = 0
+ _mem.unknown6 = 0
+
+ # extra settings
+ if len(mem.extra) > 0:
+ # there are setting, parse
+ LOG.debug("Extra-Setting supplied. Setting them.")
+ for setting in mem.extra:
+ setattr(_mem, setting.get_name(), setting.value)
+ else:
+ if mem.empty:
+ LOG.debug("New mem is empty.")
+ else:
+ LOG.debug("New mem is NOT empty")
+ # set extra-settings to default ONLY when apreviously empty or
+ # deleted memory was edited to prevent errors such as #4121
+ if mem_was_empty :
+ LOG.debug("old mem was empty. Setting default for extras.")
+ _mem.spmute = 0
+ _mem.optsig = 0
+ _mem.scramble = 0
+ _mem.bcl = 0
+ _mem.pttid = 0
+ _mem.scode = 0
+
+ return mem
+
+ def get_settings(self):
+ """Translate the bit in the mem_struct into settings in the UI"""
+ _mem = self._memobj
+ basic = RadioSettingGroup("basic", "Basic Settings")
+ advanced = RadioSettingGroup("advanced", "Advanced Settings")
+ other = RadioSettingGroup("other", "Other Settings")
+ work = RadioSettingGroup("work", "Work Mode Settings")
+ top = RadioSettings(basic, advanced, other, work)
+
+ # Basic
+ if self.COLOR_LCD:
+ tmr = RadioSetting("settings.tmr", "Transceiver multi-receive",
+ RadioSettingValueList(
+ LIST_TMR,
+ LIST_TMR[_mem.settings.tmr]))
+ basic.append(tmr)
+ else:
+ tdr = RadioSetting("settings.tdr", "Transceiver dual receive",
+ RadioSettingValueBoolean(_mem.settings.tdr))
+ basic.append(tdr)
+
+ sql = RadioSetting("settings.sql", "Squelch level",
+ RadioSettingValueInteger(0, 9, _mem.settings.sql))
+ basic.append(sql)
+
+ tot = RadioSetting("settings.tot", "Time out timer",
+ RadioSettingValueList(
+ LIST_TOT,
+ LIST_TOT[_mem.settings.tot]))
+ basic.append(tot)
+
+ if self.VENDOR == "BTECH" or self.COLOR_LCD:
+ apo = RadioSetting("settings.apo", "Auto power off timer",
+ RadioSettingValueList(
+ LIST_APO,
+ LIST_APO[_mem.settings.apo]))
+ basic.append(apo)
+ else:
+ toa = RadioSetting("settings.apo", "Time out alert timer",
+ RadioSettingValueList(
+ LIST_OFF1TO10,
+ LIST_OFF1TO10[_mem.settings.apo]))
+ basic.append(toa)
+
+ abr = RadioSetting("settings.abr", "Backlight timer",
+ RadioSettingValueList(
+ LIST_OFF1TO50,
+ LIST_OFF1TO50[_mem.settings.abr]))
+ basic.append(abr)
+
+ beep = RadioSetting("settings.beep", "Key beep",
+ RadioSettingValueBoolean(_mem.settings.beep))
+ basic.append(beep)
+
+ dtmfst = RadioSetting("settings.dtmfst", "DTMF side tone",
+ RadioSettingValueList(
+ LIST_DTMFST,
+ LIST_DTMFST[_mem.settings.dtmfst]))
+ basic.append(dtmfst)
+
+ if not self.COLOR_LCD:
+ prisc = RadioSetting("settings.prisc", "Priority scan",
+ RadioSettingValueBoolean(
+ _mem.settings.prisc))
+ basic.append(prisc)
+
+ prich = RadioSetting("settings.prich", "Priority channel",
+ RadioSettingValueInteger(0, 199,
+ _mem.settings.prich))
+ basic.append(prich)
+
+ screv = RadioSetting("settings.screv", "Scan resume method",
+ RadioSettingValueList(
+ LIST_SCREV,
+ LIST_SCREV[_mem.settings.screv]))
+ basic.append(screv)
+
+ pttlt = RadioSetting("settings.pttlt", "PTT transmit delay",
+ RadioSettingValueInteger(0, 30,
+ _mem.settings.pttlt))
+ basic.append(pttlt)
+
+ emctp = RadioSetting("settings.emctp", "Alarm mode",
+ RadioSettingValueList(
+ LIST_EMCTP,
+ LIST_EMCTP[_mem.settings.emctp]))
+ basic.append(emctp)
+
+ emcch = RadioSetting("settings.emcch", "Alarm channel",
+ RadioSettingValueInteger(0, 199,
+ _mem.settings.emcch))
+ basic.append(emcch)
+
+ if self.COLOR_LCD:
+ if _mem.settings.sigbp > 0x01:
+ val = 0x00
+ else:
+ val = _mem.settings.sigbp
+ sigbp = RadioSetting("settings.sigbp", "Roger beep",
+ RadioSettingValueBoolean(val))
+ basic.append(sigbp)
+ else:
+ ringt = RadioSetting("settings.ringt", "Ring time",
+ RadioSettingValueList(
+ LIST_OFF1TO9,
+ LIST_OFF1TO9[_mem.settings.ringt]))
+ basic.append(ringt)
+
+ camdf = RadioSetting("settings.camdf", "Display mode A",
+ RadioSettingValueList(
+ LIST_MDF,
+ LIST_MDF[_mem.settings.camdf]))
+ basic.append(camdf)
+
+ cbmdf = RadioSetting("settings.cbmdf", "Display mode B",
+ RadioSettingValueList(
+ LIST_MDF,
+ LIST_MDF[_mem.settings.cbmdf]))
+ basic.append(cbmdf)
+
+ if self.COLOR_LCD:
+ ccmdf = RadioSetting("settings.ccmdf", "Display mode C",
+ RadioSettingValueList(
+ LIST_MDF,
+ LIST_MDF[_mem.settings.ccmdf]))
+ basic.append(ccmdf)
+
+ cdmdf = RadioSetting("settings.cdmdf", "Display mode D",
+ RadioSettingValueList(
+ LIST_MDF,
+ LIST_MDF[_mem.settings.cdmdf]))
+ basic.append(cdmdf)
+
+ langua = RadioSetting("settings.langua", "Language",
+ RadioSettingValueList(
+ LIST_LANGUA,
+ LIST_LANGUA[_mem.settings.langua]))
+ basic.append(langua)
+
+ if self.VENDOR == "BTECH":
+ sync = RadioSetting("settings.sync", "A/B channel sync",
+ RadioSettingValueBoolean(
+ _mem.settings.sync))
+ basic.append(sync)
+ else:
+ autolk = RadioSetting("settings.sync", "Auto keylock",
+ RadioSettingValueBoolean(
+ _mem.settings.sync))
+ basic.append(autolk)
+
+ if not self.COLOR_LCD:
+ ponmsg = RadioSetting("settings.ponmsg", "Power-on message",
+ RadioSettingValueList(
+ LIST_PONMSG,
+ LIST_PONMSG[_mem.settings.ponmsg]))
+ basic.append(ponmsg)
+
+ if self.COLOR_LCD:
+ mainfc = RadioSetting("settings.mainfc",
+ "Main LCD foreground color",
+ RadioSettingValueList(
+ LIST_COLOR8,
+ LIST_COLOR8[_mem.settings.mainfc]))
+ basic.append(mainfc)
+
+ mainbc = RadioSetting("settings.mainbc",
+ "Main LCD background color",
+ RadioSettingValueList(
+ LIST_COLOR8,
+ LIST_COLOR8[_mem.settings.mainbc]))
+ basic.append(mainbc)
+
+ menufc = RadioSetting("settings.menufc", "Menu foreground color",
+ RadioSettingValueList(
+ LIST_COLOR8,
+ LIST_COLOR8[_mem.settings.menufc]))
+ basic.append(menufc)
+
+ menubc = RadioSetting("settings.menubc", "Menu background color",
+ RadioSettingValueList(
+ LIST_COLOR8,
+ LIST_COLOR8[_mem.settings.menubc]))
+ basic.append(menubc)
+
+ stafc = RadioSetting("settings.stafc",
+ "Top status foreground color",
+ RadioSettingValueList(
+ LIST_COLOR8,
+ LIST_COLOR8[_mem.settings.stafc]))
+ basic.append(stafc)
+
+ stabc = RadioSetting("settings.stabc",
+ "Top status background color",
+ RadioSettingValueList(
+ LIST_COLOR8,
+ LIST_COLOR8[_mem.settings.stabc]))
+ basic.append(stabc)
+
+ sigfc = RadioSetting("settings.sigfc",
+ "Bottom status foreground color",
+ RadioSettingValueList(
+ LIST_COLOR8,
+ LIST_COLOR8[_mem.settings.sigfc]))
+ basic.append(sigfc)
+
+ sigbc = RadioSetting("settings.sigbc",
+ "Bottom status background color",
+ RadioSettingValueList(
+ LIST_COLOR8,
+ LIST_COLOR8[_mem.settings.sigbc]))
+ basic.append(sigbc)
+
+ rxfc = RadioSetting("settings.rxfc", "Receiving character color",
+ RadioSettingValueList(
+ LIST_COLOR8,
+ LIST_COLOR8[_mem.settings.rxfc]))
+ basic.append(rxfc)
+
+ txfc = RadioSetting("settings.txfc",
+ "Transmitting character color",
+ RadioSettingValueList(
+ LIST_COLOR8,
+ LIST_COLOR8[_mem.settings.txfc]))
+ basic.append(txfc)
+
+ txdisp = RadioSetting("settings.txdisp",
+ "Transmitting status display",
+ RadioSettingValueList(
+ LIST_TXDISP,
+ LIST_TXDISP[_mem.settings.txdisp]))
+ basic.append(txdisp)
+ else:
+ wtled = RadioSetting("settings.wtled", "Standby backlight Color",
+ RadioSettingValueList(
+ LIST_COLOR4,
+ LIST_COLOR4[_mem.settings.wtled]))
+ basic.append(wtled)
+
+ rxled = RadioSetting("settings.rxled", "RX backlight Color",
+ RadioSettingValueList(
+ LIST_COLOR4,
+ LIST_COLOR4[_mem.settings.rxled]))
+ basic.append(rxled)
+
+ txled = RadioSetting("settings.txled", "TX backlight Color",
+ RadioSettingValueList(
+ LIST_COLOR4,
+ LIST_COLOR4[_mem.settings.txled]))
+ basic.append(txled)
+
+ anil = RadioSetting("settings.anil", "ANI length",
+ RadioSettingValueList(
+ LIST_ANIL,
+ LIST_ANIL[_mem.settings.anil]))
+ basic.append(anil)
+
+ reps = RadioSetting("settings.reps", "Relay signal (tone burst)",
+ RadioSettingValueList(
+ LIST_REPS,
+ LIST_REPS[_mem.settings.reps]))
+ basic.append(reps)
+
+ repm = RadioSetting("settings.repm", "Relay condition",
+ RadioSettingValueList(
+ LIST_REPM,
+ LIST_REPM[_mem.settings.repm]))
+ basic.append(repm)
+
+ if self.VENDOR == "BTECH" or self.COLOR_LCD:
+ if self.COLOR_LCD:
+ tmrmr = RadioSetting("settings.tmrmr", "TMR return time",
+ RadioSettingValueList(
+ LIST_OFF1TO50,
+ LIST_OFF1TO50[_mem.settings.tmrmr]))
+ basic.append(tmrmr)
+ else:
+ tdrab = RadioSetting("settings.tdrab", "TDR return time",
+ RadioSettingValueList(
+ LIST_OFF1TO50,
+ LIST_OFF1TO50[_mem.settings.tdrab]))
+ basic.append(tdrab)
+
+ ste = RadioSetting("settings.ste", "Squelch tail eliminate",
+ RadioSettingValueBoolean(_mem.settings.ste))
+ basic.append(ste)
+
+ rpste = RadioSetting("settings.rpste", "Repeater STE",
+ RadioSettingValueList(
+ LIST_OFF1TO9,
+ LIST_OFF1TO9[_mem.settings.rpste]))
+ basic.append(rpste)
+
+ rptdl = RadioSetting("settings.rptdl", "Repeater STE delay",
+ RadioSettingValueList(
+ LIST_RPTDL,
+ LIST_RPTDL[_mem.settings.rptdl]))
+ basic.append(rptdl)
+
+ if str(_mem.fingerprint.fp) in BTECH3:
+ mgain = RadioSetting("settings.mgain", "Mic gain",
+ RadioSettingValueInteger(0, 120,
+ _mem.settings.mgain))
+ basic.append(mgain)
+
+ if str(_mem.fingerprint.fp) in BTECH3 or self.COLOR_LCD:
+ dtmfg = RadioSetting("settings.dtmfg", "DTMF gain",
+ RadioSettingValueInteger(0, 60,
+ _mem.settings.dtmfg))
+ basic.append(dtmfg)
+
+ # Advanced
+ def _filter(name):
+ filtered = ""
+ for char in str(name):
+ if char in VALID_CHARS:
+ filtered += char
+ else:
+ filtered += " "
+ return filtered
+
+ _msg = self._memobj.poweron_msg
+ if self.COLOR_LCD:
+ line1 = RadioSetting("poweron_msg.line1",
+ "Power-on message line 1",
+ RadioSettingValueString(0, 8, _filter(
+ _msg.line1)))
+ advanced.append(line1)
+ line2 = RadioSetting("poweron_msg.line2",
+ "Power-on message line 2",
+ RadioSettingValueString(0, 8, _filter(
+ _msg.line2)))
+ advanced.append(line2)
+ line3 = RadioSetting("poweron_msg.line3",
+ "Power-on message line 3",
+ RadioSettingValueString(0, 8, _filter(
+ _msg.line3)))
+ advanced.append(line3)
+ line4 = RadioSetting("poweron_msg.line4",
+ "Power-on message line 4",
+ RadioSettingValueString(0, 8, _filter(
+ _msg.line4)))
+ advanced.append(line4)
+ line5 = RadioSetting("poweron_msg.line5",
+ "Power-on message line 5",
+ RadioSettingValueString(0, 8, _filter(
+ _msg.line5)))
+ advanced.append(line5)
+ line6 = RadioSetting("poweron_msg.line6",
+ "Power-on message line 6",
+ RadioSettingValueString(0, 8, _filter(
+ _msg.line6)))
+ advanced.append(line6)
+ line7 = RadioSetting("poweron_msg.line7",
+ "Power-on message line 7",
+ RadioSettingValueString(0, 8, _filter(
+ _msg.line7)))
+ advanced.append(line7)
+ line8 = RadioSetting("poweron_msg.line8", "Static message",
+ RadioSettingValueString(0, 8, _filter(
+ _msg.line8)))
+ advanced.append(line8)
+ else:
+ line1 = RadioSetting("poweron_msg.line1",
+ "Power-on message line 1",
+ RadioSettingValueString(0, 6, _filter(
+ _msg.line1)))
+ advanced.append(line1)
+ line2 = RadioSetting("poweron_msg.line2",
+ "Power-on message line 2",
+ RadioSettingValueString(0, 6, _filter(
+ _msg.line2)))
+ advanced.append(line2)
+
+ if self.MODEL in ("UV-2501", "UV-5001"):
+ vfomren = RadioSetting("settings2.vfomren", "VFO/MR switching",
+ RadioSettingValueBoolean(
+ _mem.settings2.vfomren))
+ advanced.append(vfomren)
+
+ reseten = RadioSetting("settings2.reseten", "RESET",
+ RadioSettingValueBoolean(
+ _mem.settings2.reseten))
+ advanced.append(reseten)
+
+ menuen = RadioSetting("settings2.menuen", "Menu",
+ RadioSettingValueBoolean(
+ _mem.settings2.menuen))
+ advanced.append(menuen)
+
+ # Other
+ def convert_bytes_to_limit(bytes):
+ limit = ""
+ for byte in bytes:
+ if byte < 10:
+ limit += chr(byte + 0x30)
+ else:
+ break
+ return limit
+
+ if self.MODEL in ["UV-2501+220", "KT8900R"]:
+ _ranges = self._memobj.ranges220
+ ranges = "ranges220"
+ else:
+ _ranges = self._memobj.ranges
+ ranges = "ranges"
+
+ _limit = convert_bytes_to_limit(_ranges.vhf_low)
+ val = RadioSettingValueString(0, 3, _limit)
+ val.set_mutable(False)
+ vhf_low = RadioSetting("%s.vhf_low" % ranges, "VHF low", val)
+ other.append(vhf_low)
+
+ _limit = convert_bytes_to_limit(_ranges.vhf_high)
+ val = RadioSettingValueString(0, 3, _limit)
+ val.set_mutable(False)
+ vhf_high = RadioSetting("%s.vhf_high" % ranges, "VHF high", val)
+ other.append(vhf_high)
+
+ if self.BANDS == 3 or self.BANDS == 4:
+ _limit = convert_bytes_to_limit(_ranges.vhf2_low)
+ val = RadioSettingValueString(0, 3, _limit)
+ val.set_mutable(False)
+ vhf2_low = RadioSetting("%s.vhf2_low" % ranges, "VHF2 low", val)
+ other.append(vhf2_low)
+
+ _limit = convert_bytes_to_limit(_ranges.vhf2_high)
+ val = RadioSettingValueString(0, 3, _limit)
+ val.set_mutable(False)
+ vhf2_high = RadioSetting("%s.vhf2_high" % ranges, "VHF2 high", val)
+ other.append(vhf2_high)
+
+ _limit = convert_bytes_to_limit(_ranges.uhf_low)
+ val = RadioSettingValueString(0, 3, _limit)
+ val.set_mutable(False)
+ uhf_low = RadioSetting("%s.uhf_low" % ranges, "UHF low", val)
+ other.append(uhf_low)
+
+ _limit = convert_bytes_to_limit(_ranges.uhf_high)
+ val = RadioSettingValueString(0, 3, _limit)
+ val.set_mutable(False)
+ uhf_high = RadioSetting("%s.uhf_high" % ranges, "UHF high", val)
+ other.append(uhf_high)
+
+ if self.BANDS == 4:
+ _limit = convert_bytes_to_limit(_ranges.uhf2_low)
+ val = RadioSettingValueString(0, 3, _limit)
+ val.set_mutable(False)
+ uhf2_low = RadioSetting("%s.uhf2_low" % ranges, "UHF2 low", val)
+ other.append(uhf2_low)
+
+ _limit = convert_bytes_to_limit(_ranges.uhf2_high)
+ val = RadioSettingValueString(0, 3, _limit)
+ val.set_mutable(False)
+ uhf2_high = RadioSetting("%s.uhf2_high" % ranges, "UHF2 high", val)
+ other.append(uhf2_high)
+
+ val = RadioSettingValueString(0, 6, _filter(_mem.fingerprint.fp))
+ val.set_mutable(False)
+ fp = RadioSetting("fingerprint.fp", "Fingerprint", val)
+ other.append(fp)
+
+
+ # Work
+ if self.COLOR_LCD:
+ dispab = RadioSetting("settings2.dispab", "Display",
+ RadioSettingValueList(
+ LIST_ABCD,
+ LIST_ABCD[_mem.settings2.dispab]))
+ work.append(dispab)
+ else:
+ dispab = RadioSetting("settings2.dispab", "Display",
+ RadioSettingValueList(
+ LIST_AB,
+ LIST_AB[_mem.settings2.dispab]))
+ work.append(dispab)
+
+ if self.COLOR_LCD:
+ vfomra = RadioSetting("settings2.vfomra", "VFO/MR A mode",
+ RadioSettingValueList(
+ LIST_VFOMR,
+ LIST_VFOMR[_mem.settings2.vfomra]))
+ work.append(vfomra)
+
+ vfomrb = RadioSetting("settings2.vfomrb", "VFO/MR B mode",
+ RadioSettingValueList(
+ LIST_VFOMR,
+ LIST_VFOMR[_mem.settings2.vfomrb]))
+ work.append(vfomrb)
+
+ vfomrc = RadioSetting("settings2.vfomrc", "VFO/MR C mode",
+ RadioSettingValueList(
+ LIST_VFOMR,
+ LIST_VFOMR[_mem.settings2.vfomrc]))
+ work.append(vfomrc)
+
+ vfomrd = RadioSetting("settings2.vfomrd", "VFO/MR D mode",
+ RadioSettingValueList(
+ LIST_VFOMR,
+ LIST_VFOMR[_mem.settings2.vfomrd]))
+ work.append(vfomrd)
+ else:
+ vfomr = RadioSetting("settings2.vfomr", "VFO/MR mode",
+ RadioSettingValueList(
+ LIST_VFOMR,
+ LIST_VFOMR[_mem.settings2.vfomr]))
+ work.append(vfomr)
+
+
+ keylock = RadioSetting("settings2.keylock", "Keypad lock",
+ RadioSettingValueBoolean(_mem.settings2.keylock))
+ work.append(keylock)
+
+ mrcha = RadioSetting("settings2.mrcha", "MR A channel",
+ RadioSettingValueInteger(0, 199,
+ _mem.settings2.mrcha))
+ work.append(mrcha)
+
+ mrchb = RadioSetting("settings2.mrchb", "MR B channel",
+ RadioSettingValueInteger(0, 199,
+ _mem.settings2.mrchb))
+ work.append(mrchb)
+
+ if self.COLOR_LCD:
+ mrchc = RadioSetting("settings2.mrchc", "MR C channel",
+ RadioSettingValueInteger(0, 199,
+ _mem.settings2.mrchc))
+ work.append(mrchc)
+
+ mrchd = RadioSetting("settings2.mrchd", "MR D channel",
+ RadioSettingValueInteger(0, 199,
+ _mem.settings2.mrchd))
+ work.append(mrchd)
+
+ def convert_bytes_to_freq(bytes):
+ real_freq = 0
+ for byte in bytes:
+ real_freq = (real_freq * 10) + byte
+ return chirp_common.format_freq(real_freq * 10)
+
+ def my_validate(value):
+ _vhf_lower = int(convert_bytes_to_limit(_ranges.vhf_low))
+ _vhf_upper = int(convert_bytes_to_limit(_ranges.vhf_high))
+ _uhf_lower = int(convert_bytes_to_limit(_ranges.uhf_low))
+ _uhf_upper = int(convert_bytes_to_limit(_ranges.uhf_high))
+ if self.BANDS == 3 or self.BANDS == 4:
+ _vhf2_lower = int(convert_bytes_to_limit(_ranges.vhf2_low))
+ _vhf2_upper = int(convert_bytes_to_limit(_ranges.vhf2_high))
+ if self.BANDS == 4:
+ _uhf2_lower = int(convert_bytes_to_limit(_ranges.uhf2_low))
+ _uhf2_upper = int(convert_bytes_to_limit(_ranges.uhf2_high))
+
+ value = chirp_common.parse_freq(value)
+ msg = ("Can't be less then %i.0000")
+ if value > 99000000 and value < _vhf_lower * 1000000:
+ raise InvalidValueError(msg % (_vhf_lower))
+ msg = ("Can't be betweeb %i.9975-%i.0000")
+ if self.BANDS == 2:
+ if (_vhf_upper + 1) * 1000000 <= value and \
+ value < _uhf_lower * 1000000:
+ raise InvalidValueError(msg % (_vhf_upper, _uhf_lower))
+ if self.BANDS == 3:
+ if (_vhf_upper + 1) * 1000000 <= value and \
+ value < _vhf2_lower * 1000000:
+ raise InvalidValueError(msg % (_vhf_upper, _vhf2_lower))
+ if (_vhf2_upper + 1) * 1000000 <= value and \
+ value < _uhf_lower * 1000000:
+ raise InvalidValueError(msg % (_vhf2_upper, _uhf_lower))
+ if self.BANDS == 4:
+ if (_vhf_upper + 1) * 1000000 <= value and \
+ value < _vhf2_lower * 1000000:
+ raise InvalidValueError(msg % (_vhf_upper, _vhf2_lower))
+ if (_vhf2_upper + 1) * 1000000 <= value and \
+ value < _uhf2_lower * 1000000:
+ raise InvalidValueError(msg % (_vhf2_upper, _uhf2_lower))
+ if (_uhf2_upper + 1) * 1000000 <= value and \
+ value < _uhf_lower * 1000000:
+ raise InvalidValueError(msg % (_uhf2_upper, _uhf_lower))
+ msg = ("Can't be greater then %i.9975")
+ if value > 99000000 and value >= _uhf_upper * 1000000:
+ raise InvalidValueError(msg % (_uhf_upper))
+ return chirp_common.format_freq(value)
+
+ def apply_freq(setting, obj):
+ value = chirp_common.parse_freq(str(setting.value)) / 10
+ for i in range(7, -1, -1):
+ obj.freq[i] = value % 10
+ value /= 10
+
+ val1a = RadioSettingValueString(0, 10, convert_bytes_to_freq(
+ _mem.vfo.a.freq))
+ val1a.set_validate_callback(my_validate)
+ vfoafreq = RadioSetting("vfo.a.freq", "VFO A frequency", val1a)
+ vfoafreq.set_apply_callback(apply_freq, _mem.vfo.a)
+ work.append(vfoafreq)
+
+ val1b = RadioSettingValueString(0, 10, convert_bytes_to_freq(
+ _mem.vfo.b.freq))
+ val1b.set_validate_callback(my_validate)
+ vfobfreq = RadioSetting("vfo.b.freq", "VFO B frequency", val1b)
+ vfobfreq.set_apply_callback(apply_freq, _mem.vfo.b)
+ work.append(vfobfreq)
+
+ if self.COLOR_LCD:
+ val1c = RadioSettingValueString(0, 10, convert_bytes_to_freq(
+ _mem.vfo.c.freq))
+ val1c.set_validate_callback(my_validate)
+ vfocfreq = RadioSetting("vfo.c.freq", "VFO C frequency", val1c)
+ vfocfreq.set_apply_callback(apply_freq, _mem.vfo.c)
+ work.append(vfocfreq)
+
+ val1d = RadioSettingValueString(0, 10, convert_bytes_to_freq(
+ _mem.vfo.d.freq))
+ val1d.set_validate_callback(my_validate)
+ vfodfreq = RadioSetting("vfo.d.freq", "VFO D frequency", val1d)
+ vfodfreq.set_apply_callback(apply_freq, _mem.vfo.d)
+ work.append(vfodfreq)
+
+ vfoashiftd = RadioSetting("vfo.a.shiftd", "VFO A shift",
+ RadioSettingValueList(
+ LIST_SHIFT,
+ LIST_SHIFT[_mem.vfo.a.shiftd]))
+ work.append(vfoashiftd)
+
+ vfobshiftd = RadioSetting("vfo.b.shiftd", "VFO B shift",
+ RadioSettingValueList(
+ LIST_SHIFT,
+ LIST_SHIFT[_mem.vfo.b.shiftd]))
+ work.append(vfobshiftd)
+
+ if self.COLOR_LCD:
+ vfocshiftd = RadioSetting("vfo.c.shiftd", "VFO C shift",
+ RadioSettingValueList(
+ LIST_SHIFT,
+ LIST_SHIFT[_mem.vfo.c.shiftd]))
+ work.append(vfocshiftd)
+
+ vfodshiftd = RadioSetting("vfo.d.shiftd", "VFO D shift",
+ RadioSettingValueList(
+ LIST_SHIFT,
+ LIST_SHIFT[_mem.vfo.d.shiftd]))
+ work.append(vfodshiftd)
+
+ def convert_bytes_to_offset(bytes):
+ real_offset = 0
+ for byte in bytes:
+ real_offset = (real_offset * 10) + byte
+ return chirp_common.format_freq(real_offset * 1000)
+
+ def apply_offset(setting, obj):
+ value = chirp_common.parse_freq(str(setting.value)) / 1000
+ for i in range(5, -1, -1):
+ obj.offset[i] = value % 10
+ value /= 10
+
+ if self.COLOR_LCD:
+ val1a = RadioSettingValueString(0, 10, convert_bytes_to_offset(
+ _mem.vfo.a.offset))
+ vfoaoffset = RadioSetting("vfo.a.offset",
+ "VFO A offset (0.000-999.999)", val1a)
+ vfoaoffset.set_apply_callback(apply_offset, _mem.vfo.a)
+ work.append(vfoaoffset)
+
+ val1b = RadioSettingValueString(0, 10, convert_bytes_to_offset(
+ _mem.vfo.b.offset))
+ vfoboffset = RadioSetting("vfo.b.offset",
+ "VFO B offset (0.000-999.999)", val1b)
+ vfoboffset.set_apply_callback(apply_offset, _mem.vfo.b)
+ work.append(vfoboffset)
+
+ val1c = RadioSettingValueString(0, 10, convert_bytes_to_offset(
+ _mem.vfo.c.offset))
+ vfocoffset = RadioSetting("vfo.c.offset",
+ "VFO C offset (0.000-999.999)", val1c)
+ vfocoffset.set_apply_callback(apply_offset, _mem.vfo.c)
+ work.append(vfocoffset)
+
+ val1d = RadioSettingValueString(0, 10, convert_bytes_to_offset(
+ _mem.vfo.d.offset))
+ vfodoffset = RadioSetting("vfo.d.offset",
+ "VFO D offset (0.000-999.999)", val1d)
+ vfodoffset.set_apply_callback(apply_offset, _mem.vfo.d)
+ work.append(vfodoffset)
+ else:
+ val1a = RadioSettingValueString(0, 10, convert_bytes_to_offset(
+ _mem.vfo.a.offset))
+ vfoaoffset = RadioSetting("vfo.a.offset",
+ "VFO A offset (0.000-99.999)", val1a)
+ vfoaoffset.set_apply_callback(apply_offset, _mem.vfo.a)
+ work.append(vfoaoffset)
+
+ val1b = RadioSettingValueString(0, 10, convert_bytes_to_offset(
+ _mem.vfo.b.offset))
+ vfoboffset = RadioSetting("vfo.b.offset",
+ "VFO B offset (0.000-99.999)", val1b)
+ vfoboffset.set_apply_callback(apply_offset, _mem.vfo.b)
+ work.append(vfoboffset)
+
+
+ vfoatxp = RadioSetting("vfo.a.power", "VFO A power",
+ RadioSettingValueList(
+ LIST_TXP,
+ LIST_TXP[_mem.vfo.a.power]))
+ work.append(vfoatxp)
+
+ vfobtxp = RadioSetting("vfo.b.power", "VFO B power",
+ RadioSettingValueList(
+ LIST_TXP,
+ LIST_TXP[_mem.vfo.b.power]))
+ work.append(vfobtxp)
+
+ if self.COLOR_LCD:
+ vfoctxp = RadioSetting("vfo.c.power", "VFO C power",
+ RadioSettingValueList(
+ LIST_TXP,
+ LIST_TXP[_mem.vfo.c.power]))
+ work.append(vfoctxp)
+
+ vfodtxp = RadioSetting("vfo.d.power", "VFO D power",
+ RadioSettingValueList(
+ LIST_TXP,
+ LIST_TXP[_mem.vfo.d.power]))
+ work.append(vfodtxp)
+
+ vfoawide = RadioSetting("vfo.a.wide", "VFO A bandwidth",
+ RadioSettingValueList(
+ LIST_WIDE,
+ LIST_WIDE[_mem.vfo.a.wide]))
+ work.append(vfoawide)
+
+ vfobwide = RadioSetting("vfo.b.wide", "VFO B bandwidth",
+ RadioSettingValueList(
+ LIST_WIDE,
+ LIST_WIDE[_mem.vfo.b.wide]))
+ work.append(vfobwide)
+
+ if self.COLOR_LCD:
+ vfocwide = RadioSetting("vfo.c.wide", "VFO C bandwidth",
+ RadioSettingValueList(
+ LIST_WIDE,
+ LIST_WIDE[_mem.vfo.c.wide]))
+ work.append(vfocwide)
+
+ vfodwide = RadioSetting("vfo.d.wide", "VFO D bandwidth",
+ RadioSettingValueList(
+ LIST_WIDE,
+ LIST_WIDE[_mem.vfo.d.wide]))
+ work.append(vfodwide)
+
+ vfoastep = RadioSetting("vfo.a.step", "VFO A step",
+ RadioSettingValueList(
+ LIST_STEP,
+ LIST_STEP[_mem.vfo.a.step]))
+ work.append(vfoastep)
+
+ vfobstep = RadioSetting("vfo.b.step", "VFO B step",
+ RadioSettingValueList(
+ LIST_STEP,
+ LIST_STEP[_mem.vfo.b.step]))
+ work.append(vfobstep)
+
+ if self.COLOR_LCD:
+ vfocstep = RadioSetting("vfo.c.step", "VFO C step",
+ RadioSettingValueList(
+ LIST_STEP,
+ LIST_STEP[_mem.vfo.c.step]))
+ work.append(vfocstep)
+
+ vfodstep = RadioSetting("vfo.d.step", "VFO D step",
+ RadioSettingValueList(
+ LIST_STEP,
+ LIST_STEP[_mem.vfo.d.step]))
+ work.append(vfodstep)
+
+ vfoaoptsig = RadioSetting("vfo.a.optsig", "VFO A optional signal",
+ RadioSettingValueList(
+ OPTSIG_LIST,
+ OPTSIG_LIST[_mem.vfo.a.optsig]))
+ work.append(vfoaoptsig)
+
+ vfoboptsig = RadioSetting("vfo.b.optsig", "VFO B optional signal",
+ RadioSettingValueList(
+ OPTSIG_LIST,
+ OPTSIG_LIST[_mem.vfo.b.optsig]))
+ work.append(vfoboptsig)
+
+ if self.COLOR_LCD:
+ vfocoptsig = RadioSetting("vfo.c.optsig", "VFO C optional signal",
+ RadioSettingValueList(
+ OPTSIG_LIST,
+ OPTSIG_LIST[_mem.vfo.c.optsig]))
+ work.append(vfocoptsig)
+
+ vfodoptsig = RadioSetting("vfo.d.optsig", "VFO D optional signal",
+ RadioSettingValueList(
+ OPTSIG_LIST,
+ OPTSIG_LIST[_mem.vfo.d.optsig]))
+ work.append(vfodoptsig)
+
+ vfoaspmute = RadioSetting("vfo.a.spmute", "VFO A speaker mute",
+ RadioSettingValueList(
+ SPMUTE_LIST,
+ SPMUTE_LIST[_mem.vfo.a.spmute]))
+ work.append(vfoaspmute)
+
+ vfobspmute = RadioSetting("vfo.b.spmute", "VFO B speaker mute",
+ RadioSettingValueList(
+ SPMUTE_LIST,
+ SPMUTE_LIST[_mem.vfo.b.spmute]))
+ work.append(vfobspmute)
+
+ if self.COLOR_LCD:
+ vfocspmute = RadioSetting("vfo.c.spmute", "VFO C speaker mute",
+ RadioSettingValueList(
+ SPMUTE_LIST,
+ SPMUTE_LIST[_mem.vfo.c.spmute]))
+ work.append(vfocspmute)
+
+ vfodspmute = RadioSetting("vfo.d.spmute", "VFO D speaker mute",
+ RadioSettingValueList(
+ SPMUTE_LIST,
+ SPMUTE_LIST[_mem.vfo.d.spmute]))
+ work.append(vfodspmute)
+
+ vfoascr = RadioSetting("vfo.a.scramble", "VFO A scramble",
+ RadioSettingValueBoolean(
+ _mem.vfo.a.scramble))
+ work.append(vfoascr)
+
+ vfobscr = RadioSetting("vfo.b.scramble", "VFO B scramble",
+ RadioSettingValueBoolean(
+ _mem.vfo.b.scramble))
+ work.append(vfobscr)
+
+ if self.COLOR_LCD:
+ vfocscr = RadioSetting("vfo.c.scramble", "VFO C scramble",
+ RadioSettingValueBoolean(
+ _mem.vfo.c.scramble))
+ work.append(vfocscr)
+
+ vfodscr = RadioSetting("vfo.d.scramble", "VFO D scramble",
+ RadioSettingValueBoolean(
+ _mem.vfo.d.scramble))
+ work.append(vfodscr)
+
+ vfoascode = RadioSetting("vfo.a.scode", "VFO A PTT-ID",
+ RadioSettingValueList(
+ PTTIDCODE_LIST,
+ PTTIDCODE_LIST[_mem.vfo.a.scode]))
+ work.append(vfoascode)
+
+ vfobscode = RadioSetting("vfo.b.scode", "VFO B PTT-ID",
+ RadioSettingValueList(
+ PTTIDCODE_LIST,
+ PTTIDCODE_LIST[_mem.vfo.b.scode]))
+ work.append(vfobscode)
+
+ if self.COLOR_LCD:
+ vfocscode = RadioSetting("vfo.c.scode", "VFO C PTT-ID",
+ RadioSettingValueList(
+ PTTIDCODE_LIST,
+ PTTIDCODE_LIST[_mem.vfo.c.scode]))
+ work.append(vfocscode)
+
+ vfodscode = RadioSetting("vfo.d.scode", "VFO D PTT-ID",
+ RadioSettingValueList(
+ PTTIDCODE_LIST,
+ PTTIDCODE_LIST[_mem.vfo.d.scode]))
+ work.append(vfodscode)
+
+ pttid = RadioSetting("settings.pttid", "PTT ID",
+ RadioSettingValueList(
+ PTTID_LIST,
+ PTTID_LIST[_mem.settings.pttid]))
+ work.append(pttid)
+
+ if not self.COLOR_LCD:
+ #FM presets
+ fm_presets = RadioSettingGroup("fm_presets", "FM Presets")
+ top.append(fm_presets)
+
+ def fm_validate(value):
+ if value == 0:
+ return chirp_common.format_freq(value)
+ if not (87.5 <= value and value <= 108.0): # 87.5-108MHz
+ msg = ("FM-Preset-Frequency: Must be between 87.5 and 108 MHz")
+ raise InvalidValueError(msg)
+ return value
+
+ def apply_fm_preset_name(setting, obj):
+ valstring = str (setting.value)
+ for i in range(0,6):
+ if valstring[i] in VALID_CHARS:
+ obj[i] = valstring[i]
+ else:
+ obj[i] = '0xff'
+
+ def apply_fm_freq(setting, obj):
+ value = chirp_common.parse_freq(str(setting.value)) / 10
+ for i in range(7, -1, -1):
+ obj.freq[i] = value % 10
+ value /= 10
+
+ _presets = self._memobj.fm_radio_preset
+ i = 1
+ for preset in _presets:
+ line = RadioSetting("fm_presets_"+ str(i),
+ "Station name " + str(i),
+ RadioSettingValueString(0, 6, _filter(
+ preset.broadcast_station_name)))
+ line.set_apply_callback(apply_fm_preset_name,
+ preset.broadcast_station_name)
+
+ val = RadioSettingValueFloat(0, 108,
+ convert_bytes_to_freq(
+ preset.freq))
+ fmfreq = RadioSetting("fm_presets_"+ str(i) + "_freq",
+ "Frequency "+ str(i), val)
+ val.set_validate_callback(fm_validate)
+ fmfreq.set_apply_callback(apply_fm_freq, preset)
+ fm_presets.append(line)
+ fm_presets.append(fmfreq)
+
+ i = i + 1
+
+ # DTMF-Setting
+ dtmf_enc_settings = RadioSettingGroup ("dtmf_enc_settings",
+ "DTMF Encoding Settings")
+ dtmf_dec_settings = RadioSettingGroup ("dtmf_dec_settings",
+ "DTMF Decoding Settings")
+ top.append(dtmf_enc_settings)
+ top.append(dtmf_dec_settings)
+ txdisable = RadioSetting("dtmf_settings.txdisable",
+ "TX-Disable",
+ RadioSettingValueBoolean(
+ _mem.dtmf_settings.txdisable))
+ dtmf_enc_settings.append(txdisable)
+
+ rxdisable = RadioSetting("dtmf_settings.rxdisable",
+ "RX-Disable",
+ RadioSettingValueBoolean(
+ _mem.dtmf_settings.rxdisable))
+ dtmf_enc_settings.append(rxdisable)
+
+ dtmfspeed_on = RadioSetting(
+ "dtmf_settings.dtmfspeed_on",
+ "DTMF Speed (On Time)",
+ RadioSettingValueList(LIST_DTMF_SPEED,
+ LIST_DTMF_SPEED[
+ _mem.dtmf_settings.dtmfspeed_on]))
+ dtmf_enc_settings.append(dtmfspeed_on)
+
+ dtmfspeed_off = RadioSetting(
+ "dtmf_settings.dtmfspeed_off",
+ "DTMF Speed (Off Time)",
+ RadioSettingValueList(LIST_DTMF_SPEED,
+ LIST_DTMF_SPEED[
+ _mem.dtmf_settings.dtmfspeed_off]))
+ dtmf_enc_settings.append(dtmfspeed_off)
+
+ def memory2string(dmtf_mem):
+ dtmf_string = ""
+ for digit in dmtf_mem:
+ if digit != 255:
+ index = LIST_DTMF_VALUES.index(digit)
+ dtmf_string = dtmf_string + LIST_DTMF_DIGITS[index]
+ return dtmf_string
+
+ def apply_dmtf_frame(setting, obj):
+ LOG.debug("Setting DTMF-Code: " + str(setting.value) )
+ val_string = str(setting.value)
+ for i in range(0,16):
+ obj[i] = 255
+ i = 0
+ for current_char in val_string:
+ current_char = current_char.upper()
+ index = LIST_DTMF_DIGITS.index(current_char)
+ obj[i] = LIST_DTMF_VALUES[ index ]
+ i = i + 1
+
+ codes = self._memobj.dtmf_codes
+ i = 1
+ for dtmfcode in codes:
+ val = RadioSettingValueString(0, 16,
+ memory2string(dtmfcode.code),
+ False, CHARSET_DTMF_DIGITS)
+ line = RadioSetting("dtmf_code_" + str(i) + "_code",
+ "DMTF Code " + str(i), val)
+ line.set_apply_callback(apply_dmtf_frame, dtmfcode.code)
+ dtmf_enc_settings.append(line)
+ i = i + 1
+
+ line = RadioSetting("dtmf_settings.mastervice",
+ "Master and Vice ID",
+ RadioSettingValueBoolean(
+ _mem.dtmf_settings.mastervice))
+ dtmf_dec_settings.append(line)
+
+ val = RadioSettingValueString(0, 16,
+ memory2string(
+ _mem.dtmf_settings.masterid),
+ False, CHARSET_DTMF_DIGITS)
+ line = RadioSetting("dtmf_settings.masterid",
+ "Master Control ID ", val)
+ line.set_apply_callback(apply_dmtf_frame,
+ _mem.dtmf_settings.masterid)
+ dtmf_dec_settings.append(line)
+
+ line = RadioSetting("dtmf_settings.minspection",
+ "Master Inspection",
+ RadioSettingValueBoolean(
+ _mem.dtmf_settings.minspection))
+ dtmf_dec_settings.append(line)
+
+ line = RadioSetting("dtmf_settings.mmonitor",
+ "Master Monitor",
+ RadioSettingValueBoolean(
+ _mem.dtmf_settings.mmonitor))
+ dtmf_dec_settings.append(line)
+
+ line = RadioSetting("dtmf_settings.mstun",
+ "Master Stun",
+ RadioSettingValueBoolean(
+ _mem.dtmf_settings.mstun))
+ dtmf_dec_settings.append(line)
+
+ line = RadioSetting("dtmf_settings.mkill",
+ "Master Kill",
+ RadioSettingValueBoolean(
+ _mem.dtmf_settings.mkill))
+ dtmf_dec_settings.append(line)
+
+ line = RadioSetting("dtmf_settings.mrevive",
+ "Master Revive",
+ RadioSettingValueBoolean(
+ _mem.dtmf_settings.mrevive))
+ dtmf_dec_settings.append(line)
+
+ val = RadioSettingValueString(0, 16,
+ memory2string(
+ _mem.dtmf_settings.viceid),
+ False, CHARSET_DTMF_DIGITS)
+ line = RadioSetting("dtmf_settings.viceid",
+ "Vice Control ID ", val)
+ line.set_apply_callback(apply_dmtf_frame,
+ _mem.dtmf_settings.viceid)
+ dtmf_dec_settings.append(line)
+
+ line = RadioSetting("dtmf_settings.vinspection",
+ "Vice Inspection",
+ RadioSettingValueBoolean(
+ _mem.dtmf_settings.vinspection))
+ dtmf_dec_settings.append(line)
+
+ line = RadioSetting("dtmf_settings.vmonitor",
+ "Vice Monitor",
+ RadioSettingValueBoolean(
+ _mem.dtmf_settings.vmonitor))
+ dtmf_dec_settings.append(line)
+
+ line = RadioSetting("dtmf_settings.vstun",
+ "Vice Stun",
+ RadioSettingValueBoolean(
+ _mem.dtmf_settings.vstun))
+ dtmf_dec_settings.append(line)
+
+ line = RadioSetting("dtmf_settings.vkill",
+ "Vice Kill",
+ RadioSettingValueBoolean(
+ _mem.dtmf_settings.vkill))
+ dtmf_dec_settings.append(line)
+
+ line = RadioSetting("dtmf_settings.vrevive",
+ "Vice Revive",
+ RadioSettingValueBoolean(
+ _mem.dtmf_settings.vrevive))
+ dtmf_dec_settings.append(line)
+
+ val = RadioSettingValueString(0, 16,
+ memory2string(
+ _mem.dtmf_settings.inspection),
+ False, CHARSET_DTMF_DIGITS)
+ line = RadioSetting("dtmf_settings.inspection",
+ "Inspection", val)
+ line.set_apply_callback(apply_dmtf_frame,
+ _mem.dtmf_settings.inspection)
+ dtmf_dec_settings.append(line)
+
+ val = RadioSettingValueString(0, 16,
+ memory2string(
+ _mem.dtmf_settings.alarmcode),
+ False, CHARSET_DTMF_DIGITS)
+ line = RadioSetting("dtmf_settings.alarmcode",
+ "Alarm", val)
+ line.set_apply_callback(apply_dmtf_frame,
+ _mem.dtmf_settings.alarmcode)
+ dtmf_dec_settings.append(line)
+
+ val = RadioSettingValueString(0, 16,
+ memory2string(
+ _mem.dtmf_settings.kill),
+ False, CHARSET_DTMF_DIGITS)
+ line = RadioSetting("dtmf_settings.kill",
+ "Kill", val)
+ line.set_apply_callback(apply_dmtf_frame,
+ _mem.dtmf_settings.kill)
+ dtmf_dec_settings.append(line)
+
+ val = RadioSettingValueString(0, 16,
+ memory2string(
+ _mem.dtmf_settings.monitor),
+ False, CHARSET_DTMF_DIGITS)
+ line = RadioSetting("dtmf_settings.monitor",
+ "Monitor", val)
+ line.set_apply_callback(apply_dmtf_frame,
+ _mem.dtmf_settings.monitor)
+ dtmf_dec_settings.append(line)
+
+ val = RadioSettingValueString(0, 16,
+ memory2string(
+ _mem.dtmf_settings.stun),
+ False, CHARSET_DTMF_DIGITS)
+ line = RadioSetting("dtmf_settings.stun",
+ "Stun", val)
+ line.set_apply_callback(apply_dmtf_frame,
+ _mem.dtmf_settings.stun)
+ dtmf_dec_settings.append(line)
+
+ val = RadioSettingValueString(0, 16,
+ memory2string(
+ _mem.dtmf_settings.revive),
+ False, CHARSET_DTMF_DIGITS)
+ line = RadioSetting("dtmf_settings.revive",
+ "Revive", val)
+ line.set_apply_callback(apply_dmtf_frame,
+ _mem.dtmf_settings.revive)
+ dtmf_dec_settings.append(line)
+
+ def apply_dmtf_listvalue(setting, obj):
+ LOG.debug("Setting value: "+ str(setting.value) + " from list")
+ val = str(setting.value)
+ index = LIST_DTMF_SPECIAL_DIGITS.index(val)
+ val = LIST_DTMF_SPECIAL_VALUES[index]
+ obj.set_value(val)
+
+ idx = LIST_DTMF_SPECIAL_VALUES.index(_mem.dtmf_settings.groupcode)
+ line = RadioSetting(
+ "dtmf_settings.groupcode",
+ "Group Code",
+ RadioSettingValueList(LIST_DTMF_SPECIAL_DIGITS,
+ LIST_DTMF_SPECIAL_DIGITS[idx]))
+ line.set_apply_callback(apply_dmtf_listvalue,
+ _mem.dtmf_settings.groupcode)
+ dtmf_dec_settings.append(line)
+
+ idx = LIST_DTMF_SPECIAL_VALUES.index(_mem.dtmf_settings.spacecode)
+ line = RadioSetting(
+ "dtmf_settings.spacecode",
+ "Space Code",
+ RadioSettingValueList(LIST_DTMF_SPECIAL_DIGITS,
+ LIST_DTMF_SPECIAL_DIGITS[idx]))
+ line.set_apply_callback(apply_dmtf_listvalue,
+ _mem.dtmf_settings.spacecode)
+ dtmf_dec_settings.append(line)
+
+ if self.COLOR_LCD:
+ line = RadioSetting(
+ "dtmf_settings.resettime",
+ "Reset time",
+ RadioSettingValueList(LIST_5TONE_RESET_COLOR,
+ LIST_5TONE_RESET_COLOR[
+ _mem.dtmf_settings.resettime]))
+ dtmf_dec_settings.append(line)
+ else:
+ line = RadioSetting(
+ "dtmf_settings.resettime",
+ "Reset time",
+ RadioSettingValueList(LIST_5TONE_RESET,
+ LIST_5TONE_RESET[
+ _mem.dtmf_settings.resettime]))
+ dtmf_dec_settings.append(line)
+
+ line = RadioSetting(
+ "dtmf_settings.delayproctime",
+ "Delay processing time",
+ RadioSettingValueList(LIST_DTMF_DELAY,
+ LIST_DTMF_DELAY[
+ _mem.dtmf_settings.delayproctime]))
+ dtmf_dec_settings.append(line)
+
+
+ # 5 Tone Settings
+ stds_5tone = RadioSettingGroup ("stds_5tone", "Standards")
+ codes_5tone = RadioSettingGroup ("codes_5tone", "Codes")
+
+ group_5tone = RadioSettingGroup ("group_5tone", "5 Tone Settings")
+ group_5tone.append(stds_5tone)
+ group_5tone.append(codes_5tone)
+
+ top.append(group_5tone)
+
+ def apply_list_value(setting, obj):
+ options = setting.value.get_options()
+ obj.set_value ( options.index(str(setting.value)) )
+
+ _5tone_standards = self._memobj._5tone_std_settings
+ i = 0
+ for standard in _5tone_standards:
+ std_5tone = RadioSettingGroup ("std_5tone_" + str(i),
+ LIST_5TONE_STANDARDS[i])
+ stds_5tone.append(std_5tone)
+
+ period = standard.period
+ if period == 255:
+ LOG.debug("Period for " + LIST_5TONE_STANDARDS[i] +
+ " is not yet configured. Setting to 70ms.")
+ period = 5
+
+ if period <= len( LIST_5TONE_STANDARD_PERIODS ):
+ line = RadioSetting(
+ "_5tone_std_settings_" + str(i) + "_period",
+ "Period (ms)", RadioSettingValueList
+ (LIST_5TONE_STANDARD_PERIODS,
+ LIST_5TONE_STANDARD_PERIODS[period]))
+ line.set_apply_callback(apply_list_value, standard.period)
+ std_5tone.append(line)
+ else:
+ LOG.debug("Invalid value for 5tone period! Disabling.")
+
+ group_tone = standard.group_tone
+ if group_tone == 255:
+ LOG.debug("Group-Tone for " + LIST_5TONE_STANDARDS[i] +
+ " is not yet configured. Setting to A.")
+ group_tone = 10
+
+ if group_tone <= len( LIST_5TONE_DIGITS ):
+ line = RadioSetting(
+ "_5tone_std_settings_" + str(i) + "_grouptone",
+ "Group Tone",
+ RadioSettingValueList(LIST_5TONE_DIGITS,
+ LIST_5TONE_DIGITS[
+ group_tone]))
+ line.set_apply_callback(apply_list_value,
+ standard.group_tone)
+ std_5tone.append(line)
+ else:
+ LOG.debug("Invalid value for 5tone digit! Disabling.")
+
+ repeat_tone = standard.repeat_tone
+ if repeat_tone == 255:
+ LOG.debug("Repeat-Tone for " + LIST_5TONE_STANDARDS[i] +
+ " is not yet configured. Setting to E.")
+ repeat_tone = 14
+
+ if repeat_tone <= len( LIST_5TONE_DIGITS ):
+ line = RadioSetting(
+ "_5tone_std_settings_" + str(i) + "_repttone",
+ "Repeat Tone",
+ RadioSettingValueList(LIST_5TONE_DIGITS,
+ LIST_5TONE_DIGITS[
+ repeat_tone]))
+ line.set_apply_callback(apply_list_value,
+ standard.repeat_tone)
+ std_5tone.append(line)
+ else:
+ LOG.debug("Invalid value for 5tone digit! Disabling.")
+ i = i + 1
+
+ def my_apply_5tonestdlist_value(setting, obj):
+ if LIST_5TONE_STANDARDS.index(str(setting.value)) == 15:
+ obj.set_value(0xFF)
+ else:
+ obj.set_value( LIST_5TONE_STANDARDS.
+ index(str(setting.value)) )
+
+ def apply_5tone_frame(setting, obj):
+ LOG.debug("Setting 5 Tone: " + str(setting.value) )
+ valstring = str(setting.value)
+ if len(valstring) == 0:
+ for i in range(0,5):
+ obj[i] = 255
+ else:
+ validFrame = True
+ for i in range(0,5):
+ currentChar = valstring[i].upper()
+ if currentChar in LIST_5TONE_DIGITS:
+ obj[i] = LIST_5TONE_DIGITS.index(currentChar)
+ else:
+ validFrame = False
+ LOG.debug("invalid char: " + str(currentChar))
+ if not validFrame:
+ LOG.debug("setting whole frame to FF" )
+ for i in range(0,5):
+ obj[i] = 255
+
+ def validate_5tone_frame(value):
+ if (len(str(value)) != 5) and (len(str(value)) != 0) :
+ msg = ("5 Tone must have 5 digits or 0 digits")
+ raise InvalidValueError(msg)
+ for digit in str(value):
+ if digit.upper() not in LIST_5TONE_DIGITS:
+ msg = (str(digit) + " is not a valid digit for 5tones")
+ raise InvalidValueError(msg)
+ return value
+
+ def frame2string(frame):
+ frameString = ""
+ for digit in frame:
+ if digit != 255:
+ frameString = frameString + LIST_5TONE_DIGITS[digit]
+ return frameString
+
+ _5tone_codes = self._memobj._5tone_codes
+ i = 1
+ for code in _5tone_codes:
+ code_5tone = RadioSettingGroup ("code_5tone_" + str(i),
+ "5 Tone code " + str(i))
+ codes_5tone.append(code_5tone)
+ if (code.standard == 255 ):
+ currentVal = 15
+ else:
+ currentVal = code.standard
+ line = RadioSetting("_5tone_code_" + str(i) + "_std",
+ " Standard",
+ RadioSettingValueList(LIST_5TONE_STANDARDS,
+ LIST_5TONE_STANDARDS[
+ currentVal]) )
+ line.set_apply_callback(my_apply_5tonestdlist_value,
+ code.standard)
+ code_5tone.append(line)
+
+ val = RadioSettingValueString(0, 6,
+ frame2string(code.frame1), False)
+ line = RadioSetting("_5tone_code_" + str(i) + "_frame1",
+ " Frame 1", val)
+ val.set_validate_callback(validate_5tone_frame)
+ line.set_apply_callback(apply_5tone_frame, code.frame1)
+ code_5tone.append(line)
+
+ val = RadioSettingValueString(0, 6,
+ frame2string(code.frame2), False)
+ line = RadioSetting("_5tone_code_" + str(i) + "_frame2",
+ " Frame 2", val)
+ val.set_validate_callback(validate_5tone_frame)
+ line.set_apply_callback(apply_5tone_frame, code.frame2)
+ code_5tone.append(line)
+
+ val = RadioSettingValueString(0, 6,
+ frame2string(code.frame3), False)
+ line = RadioSetting("_5tone_code_" + str(i) + "_frame3",
+ " Frame 3", val)
+ val.set_validate_callback(validate_5tone_frame)
+ line.set_apply_callback(apply_5tone_frame, code.frame3)
+ code_5tone.append(line)
+ i = i + 1
+
+ _5_tone_decode1 = RadioSetting(
+ "_5tone_settings._5tone_decode_call_frame1",
+ "5 Tone decode call Frame 1",
+ RadioSettingValueBoolean(
+ _mem._5tone_settings._5tone_decode_call_frame1))
+ group_5tone.append(_5_tone_decode1)
+
+ _5_tone_decode2 = RadioSetting(
+ "_5tone_settings._5tone_decode_call_frame2",
+ "5 Tone decode call Frame 2",
+ RadioSettingValueBoolean(
+ _mem._5tone_settings._5tone_decode_call_frame2))
+ group_5tone.append(_5_tone_decode2)
+
+ _5_tone_decode3 = RadioSetting(
+ "_5tone_settings._5tone_decode_call_frame3",
+ "5 Tone decode call Frame 3",
+ RadioSettingValueBoolean(
+ _mem._5tone_settings._5tone_decode_call_frame3))
+ group_5tone.append(_5_tone_decode3)
+
+ _5_tone_decode_disp1 = RadioSetting(
+ "_5tone_settings._5tone_decode_disp_frame1",
+ "5 Tone decode disp Frame 1",
+ RadioSettingValueBoolean(
+ _mem._5tone_settings._5tone_decode_disp_frame1))
+ group_5tone.append(_5_tone_decode_disp1)
+
+ _5_tone_decode_disp2 = RadioSetting(
+ "_5tone_settings._5tone_decode_disp_frame2",
+ "5 Tone decode disp Frame 2",
+ RadioSettingValueBoolean(
+ _mem._5tone_settings._5tone_decode_disp_frame2))
+ group_5tone.append(_5_tone_decode_disp2)
+
+ _5_tone_decode_disp3 = RadioSetting(
+ "_5tone_settings._5tone_decode_disp_frame3",
+ "5 Tone decode disp Frame 3",
+ RadioSettingValueBoolean(
+ _mem._5tone_settings._5tone_decode_disp_frame3))
+ group_5tone.append(_5_tone_decode_disp3)
+
+ decode_standard = _mem._5tone_settings.decode_standard
+ if decode_standard == 255:
+ decode_standard = 0
+ if decode_standard <= len (LIST_5TONE_STANDARDS_without_none) :
+ line = RadioSetting("_5tone_settings.decode_standard",
+ "5 Tone-decode Standard",
+ RadioSettingValueList(
+ LIST_5TONE_STANDARDS_without_none,
+ LIST_5TONE_STANDARDS_without_none[
+ decode_standard]))
+ group_5tone.append(line)
+ else:
+ LOG.debug("Invalid decode std...")
+
+ _5tone_delay1 = _mem._5tone_settings._5tone_delay1
+ if _5tone_delay1 == 255:
+ _5tone_delay1 = 20
+
+ if _5tone_delay1 <= len( LIST_5TONE_DELAY ):
+ list = RadioSettingValueList(LIST_5TONE_DELAY,
+ LIST_5TONE_DELAY[
+ _5tone_delay1])
+ line = RadioSetting("_5tone_settings._5tone_delay1",
+ "5 Tone Delay Frame 1", list)
+ group_5tone.append(line)
+ else:
+ LOG.debug("Invalid value for 5tone delay (frame1) ! Disabling.")
+
+ _5tone_delay2 = _mem._5tone_settings._5tone_delay2
+ if _5tone_delay2 == 255:
+ _5tone_delay2 = 20
+ LOG.debug("5 Tone delay unconfigured! Resetting to 200ms.")
+
+ if _5tone_delay2 <= len( LIST_5TONE_DELAY ):
+ list = RadioSettingValueList(LIST_5TONE_DELAY,
+ LIST_5TONE_DELAY[
+ _5tone_delay2])
+ line = RadioSetting("_5tone_settings._5tone_delay2",
+ "5 Tone Delay Frame 2", list)
+ group_5tone.append(line)
+ else:
+ LOG.debug("Invalid value for 5tone delay (frame2)! Disabling.")
+
+ _5tone_delay3 = _mem._5tone_settings._5tone_delay3
+ if _5tone_delay3 == 255:
+ _5tone_delay3 = 20
+ LOG.debug("5 Tone delay unconfigured! Resetting to 200ms.")
+
+ if _5tone_delay3 <= len( LIST_5TONE_DELAY ):
+ list = RadioSettingValueList(LIST_5TONE_DELAY,
+ LIST_5TONE_DELAY[
+ _5tone_delay3])
+ line = RadioSetting("_5tone_settings._5tone_delay3",
+ "5 Tone Delay Frame 3", list )
+ group_5tone.append(line)
+ else:
+ LOG.debug("Invalid value for 5tone delay (frame3)! Disabling.")
+
+ ext_length = _mem._5tone_settings._5tone_first_digit_ext_length
+ if ext_length == 255:
+ ext_length = 0
+ LOG.debug("1st Tone ext lenght unconfigured! Resetting to 0")
+
+ if ext_length <= len(
+ LIST_5TONE_DELAY ):
+ list = RadioSettingValueList(
+ LIST_5TONE_DELAY,
+ LIST_5TONE_DELAY[
+ ext_length])
+ line = RadioSetting(
+ "_5tone_settings._5tone_first_digit_ext_length",
+ "First digit extend length", list)
+ group_5tone.append(line)
+ else:
+ LOG.debug("Invalid value for 5tone ext length! Disabling.")
+
+ decode_reset_time = _mem._5tone_settings.decode_reset_time
+ if decode_reset_time == 255:
+ decode_reset_time = 59
+ LOG.debug("Decode reset time unconfigured. resetting.")
+ if decode_reset_time <= len(LIST_5TONE_RESET):
+ list = RadioSettingValueList(
+ LIST_5TONE_RESET,
+ LIST_5TONE_RESET[
+ decode_reset_time])
+ line = RadioSetting("_5tone_settings.decode_reset_time",
+ "Decode reset time", list)
+ group_5tone.append(line)
+ else:
+ LOG.debug("Invalid value decode reset time! Disabling.")
+
+ # 2 Tone
+ encode_2tone = RadioSettingGroup ("encode_2tone", "2 Tone Encode")
+ decode_2tone = RadioSettingGroup ("decode_2tone", "2 Code Decode")
+
+ top.append(encode_2tone)
+ top.append(decode_2tone)
+
+ duration_1st_tone = self._memobj._2tone.duration_1st_tone
+ if duration_1st_tone == 255:
+ LOG.debug("Duration of first 2 Tone digit is not yet " +
+ "configured. Setting to 600ms")
+ duration_1st_tone = 60
+
+ if duration_1st_tone <= len( LIST_5TONE_DELAY ):
+ line = RadioSetting("_2tone.duration_1st_tone",
+ "Duration 1st Tone",
+ RadioSettingValueList(LIST_5TONE_DELAY,
+ LIST_5TONE_DELAY[
+ duration_1st_tone]))
+ encode_2tone.append(line)
+
+ duration_2nd_tone = self._memobj._2tone.duration_2nd_tone
+ if duration_2nd_tone == 255:
+ LOG.debug("Duration of second 2 Tone digit is not yet " +
+ "configured. Setting to 600ms")
+ duration_2nd_tone = 60
+
+ if duration_2nd_tone <= len( LIST_5TONE_DELAY ):
+ line = RadioSetting("_2tone.duration_2nd_tone",
+ "Duration 2nd Tone",
+ RadioSettingValueList(LIST_5TONE_DELAY,
+ LIST_5TONE_DELAY[
+ duration_2nd_tone]))
+ encode_2tone.append(line)
+
+ duration_gap = self._memobj._2tone.duration_gap
+ if duration_gap == 255:
+ LOG.debug("Duration of gap is not yet " +
+ "configured. Setting to 300ms")
+ duration_gap = 30
+
+ if duration_gap <= len( LIST_5TONE_DELAY ):
+ line = RadioSetting("_2tone.duration_gap", "Duration of gap",
+ RadioSettingValueList(LIST_5TONE_DELAY,
+ LIST_5TONE_DELAY[
+ duration_gap]))
+ encode_2tone.append(line)
+
+ def _2tone_validate(value):
+ if value == 0:
+ return 65535
+ if value == 65535:
+ return value
+ if not (300 <= value and value <= 3000):
+ msg = ("2 Tone Frequency: Must be between 300 and 3000 Hz")
+ raise InvalidValueError(msg)
+ return value
+
+ def apply_2tone_freq(setting, obj):
+ val = int(setting.value)
+ if (val == 0) or (val == 65535):
+ obj.set_value(65535)
+ else:
+ obj.set_value(val)
+
+ i = 1
+ for code in self._memobj._2tone._2tone_encode:
+ code_2tone = RadioSettingGroup ("code_2tone_" + str(i),
+ "Encode Code " + str(i))
+ encode_2tone.append(code_2tone)
+
+ tmp = code.freq1
+ if tmp == 65535:
+ tmp = 0
+ val1 = RadioSettingValueInteger(0, 65535, tmp)
+ freq1 = RadioSetting("2tone_code_"+ str(i) + "_freq1",
+ "Frequency 1", val1)
+ val1.set_validate_callback(_2tone_validate)
+ freq1.set_apply_callback(apply_2tone_freq, code.freq1)
+ code_2tone.append(freq1)
+
+ tmp = code.freq2
+ if tmp == 65535:
+ tmp = 0
+ val2 = RadioSettingValueInteger(0, 65535, tmp)
+ freq2 = RadioSetting("2tone_code_"+ str(i) + "_freq2",
+ "Frequency 2", val2)
+ val2.set_validate_callback(_2tone_validate)
+ freq2.set_apply_callback(apply_2tone_freq, code.freq2)
+ code_2tone.append(freq2)
+
+ i = i + 1
+
+ decode_reset_time = _mem._2tone.reset_time
+ if decode_reset_time == 255:
+ decode_reset_time = 59
+ LOG.debug("Decode reset time unconfigured. resetting.")
+ if decode_reset_time <= len(LIST_5TONE_RESET):
+ list = RadioSettingValueList(
+ LIST_5TONE_RESET,
+ LIST_5TONE_RESET[
+ decode_reset_time])
+ line = RadioSetting("_2tone.reset_time",
+ "Decode reset time", list)
+ decode_2tone.append(line)
+ else:
+ LOG.debug("Invalid value decode reset time! Disabling.")
+
+ def apply_2tone_freq_pair(setting, obj):
+ val = int(setting.value)
+ derived_val = 65535
+ frqname = str(setting._name[-5:])
+ derivedname = "derived_from_" + frqname
+
+ if (val == 0):
+ val = 65535
+ derived_val = 65535
+ else:
+ derived_val = int(round(2304000.0/val))
+
+ obj[frqname].set_value( val )
+ obj[derivedname].set_value( derived_val )
+
+ LOG.debug("Apply " + frqname + ": " + str(val) + " | "
+ + derivedname + ": " + str(derived_val))
+
+ i = 1
+ for decode_code in self._memobj._2tone._2tone_decode:
+ _2tone_dec_code = RadioSettingGroup ("code_2tone_" + str(i),
+ "Decode Code " + str(i))
+ decode_2tone.append(_2tone_dec_code)
+
+ j = 1
+ for dec in decode_code.decs:
+ val = dec.dec
+ if val == 255:
+ LOG.debug("Dec for Code " + str(i) + " Dec " + str(j) +
+ " is not yet configured. Setting to 0.")
+ val = 0
+
+ if val <= len( LIST_2TONE_DEC ):
+ line = RadioSetting(
+ "_2tone_dec_settings_" + str(i) + "_dec_" + str(j),
+ "Dec " + str(j), RadioSettingValueList
+ (LIST_2TONE_DEC,
+ LIST_2TONE_DEC[val]))
+ line.set_apply_callback(apply_list_value, dec.dec)
+ _2tone_dec_code.append(line)
+ else:
+ LOG.debug("Invalid value for 2tone dec! Disabling.")
+
+ val = dec.response
+ if val == 255:
+ LOG.debug("Response for Code " + str(i) + " Dec " + str(j)+
+ " is not yet configured. Setting to 0.")
+ val = 0
+
+ if val <= len( LIST_2TONE_RESPONSE ):
+ line = RadioSetting(
+ "_2tone_dec_settings_" + str(i) + "_resp_" + str(j),
+ "Response " + str(j), RadioSettingValueList
+ (LIST_2TONE_RESPONSE,
+ LIST_2TONE_RESPONSE[val]))
+ line.set_apply_callback(apply_list_value, dec.response)
+ _2tone_dec_code.append(line)
+ else:
+ LOG.debug("Invalid value for 2tone response! Disabling.")
+
+ val = dec.alert
+ if val == 255:
+ LOG.debug("Alert for Code " + str(i) + " Dec " + str(j) +
+ " is not yet configured. Setting to 0.")
+ val = 0
+
+ if val <= len( PTTIDCODE_LIST ):
+ line = RadioSetting(
+ "_2tone_dec_settings_" + str(i) + "_alert_" + str(j),
+ "Alert " + str(j), RadioSettingValueList
+ (PTTIDCODE_LIST,
+ PTTIDCODE_LIST[val]))
+ line.set_apply_callback(apply_list_value, dec.alert)
+ _2tone_dec_code.append(line)
+ else:
+ LOG.debug("Invalid value for 2tone alert! Disabling.")
+ j = j + 1
+
+ freq = self._memobj._2tone.freqs[i-1]
+ for char in ['A', 'B', 'C', 'D']:
+ setting_name = "freq" + str(char)
+
+ tmp = freq[setting_name]
+ if tmp == 65535:
+ tmp = 0
+ if tmp != 0:
+ expected = int(round(2304000.0/tmp))
+ from_mem = freq["derived_from_" + setting_name]
+ if expected != from_mem:
+ LOG.error("Expected " + str(expected) +
+ " but read " + str(from_mem ) +
+ ". Disabling 2Tone Decode Freqs!")
+ break
+ val = RadioSettingValueInteger(0, 65535, tmp)
+ frq = RadioSetting("2tone_dec_"+ str(i) + "_freq" + str(char),
+ ("Decode Frequency " +str(char)), val)
+ val.set_validate_callback(_2tone_validate)
+ frq.set_apply_callback(apply_2tone_freq_pair, freq)
+ _2tone_dec_code.append(frq)
+
+ i = i + 1
+
+ return top
+
+ def set_settings(self, settings):
+ _settings = self._memobj.settings
+ for element in settings:
+ if not isinstance(element, RadioSetting):
+ if element.get_name() == "fm_preset":
+ self._set_fm_preset(element)
+ else:
+ self.set_settings(element)
+ continue
+ else:
+ try:
+ name = element.get_name()
+ if "." in name:
+ bits = name.split(".")
+ obj = self._memobj
+ for bit in bits[:-1]:
+ if "/" in bit:
+ bit, index = bit.split("/", 1)
+ index = int(index)
+ obj = getattr(obj, bit)[index]
+ else:
+ obj = getattr(obj, bit)
+ setting = bits[-1]
+ else:
+ obj = _settings
+ setting = element.get_name()
+
+ if element.has_apply_callback():
+ LOG.debug("Using apply callback")
+ element.run_apply_callback()
+ 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) == MEM_SIZE:
+ match_size = True
+
+ # testing the firmware model fingerprint
+ match_model = model_match(cls, filedata)
+
+ if match_size and match_model:
+ return True
+ else:
+ return False
+
+
MEM_FORMAT = """
#seekto 0x0000;
struct {
@@ -127,9 +2831,8 @@
struct settings_vfo {
u8 freq[8];
- u8 unknown1;
- u8 offset[4];
- u8 unknown2[3];
+ u8 offset[6];
+ u8 unknown2[2];
ul16 rxtone;
ul16 txtone;
u8 scode;
@@ -316,696 +3019,12 @@
"""
-# A note about the memmory in these radios
-#
-# The real memory of these radios extends to 0x4000
-# On read the factory software only uses up to 0x3200
-# On write it just uploads the contents up to 0x3100
-#
-# The mem beyond 0x3200 holds the ID data
-
-MEM_SIZE = 0x4000
-BLOCK_SIZE = 0x40
-TX_BLOCK_SIZE = 0x10
-ACK_CMD = "\x06"
-MODES = ["FM", "NFM"]
-SKIP_VALUES = ["S", ""]
-TONES = chirp_common.TONES
-DTCS = sorted(chirp_common.DTCS_CODES + [645])
-NAME_LENGTH = 6
-PTTID_LIST = ["OFF", "BOT", "EOT", "BOTH"]
-PTTIDCODE_LIST = ["%s" % x for x in range(1, 16)]
-OPTSIG_LIST = ["OFF", "DTMF", "2TONE", "5TONE"]
-SPMUTE_LIST = ["Tone/DTCS", "Tone/DTCS and Optsig", "Tone/DTCS or Optsig"]
-
-LIST_TOT = ["%s sec" % x for x in range(15, 615, 15)]
-LIST_TOA = ["Off"] + ["%s seconds" % x for x in range(1, 11)]
-LIST_APO = ["Off"] + ["%s minutes" % x for x in range(30, 330, 30)]
-LIST_ABR = ["Off"] + ["%s seconds" % x for x in range(1, 51)]
-LIST_DTMFST = ["OFF", "Keyboard", "ANI", "Keyboad + ANI"]
-LIST_SCREV = ["TO (timeout)", "CO (carrier operated)", "SE (search)"]
-LIST_EMCTP = ["TX alarm sound", "TX ANI", "Both"]
-LIST_RINGT = ["Off"] + ["%s seconds" % x for x in range(1, 10)]
-LIST_MDF = ["Frequency", "Channel", "Name"]
-LIST_PONMSG = ["Full", "Message", "Battery voltage"]
-LIST_COLOR = ["Off", "Blue", "Orange", "Purple"]
-LIST_REPS = ["1000 Hz", "1450 Hz", "1750 Hz", "2100Hz"]
-LIST_REPM = ["Off", "Carrier", "CTCSS or DCS", "Tone", "DTMF"]
-LIST_RPTDL = ["Off"] + ["%s ms" % x for x in range(1, 10)]
-LIST_ANIL = ["3", "4", "5"]
-LIST_AB = ["A", "B"]
-LIST_VFOMR = ["Frequency", "Channel"]
-LIST_SHIFT = ["Off", "+", "-"]
-LIST_TXP = ["High", "Low"]
-LIST_WIDE = ["Wide", "Narrow"]
-STEPS = [2.5, 5.0, 6.25, 10.0, 12.5, 25.0]
-LIST_STEP = [str(x) for x in STEPS]
-LIST_5TONE_STANDARDS = ["CCIR1", "CCIR2", "PCCIR", "ZVEI1", "ZVEI2", "ZVEI3",
- "PZVEI", "DZVEI", "PDZVEI", "EEA", "EIA", "EURO",
- "CCITT", "NATEL", "MODAT", "none"]
-LIST_5TONE_STANDARDS_without_none = ["CCIR1", "CCIR2", "PCCIR", "ZVEI1",
- "ZVEI2", "ZVEI3",
- "PZVEI", "DZVEI", "PDZVEI", "EEA", "EIA",
- "EURO", "CCITT", "NATEL", "MODAT"]
-LIST_5TONE_STANDARD_PERIODS = ["20", "30", "40", "50", "60", "70", "80", "90",
- "100", "110", "120", "130", "140", "150", "160",
- "170", "180", "190", "200"]
-LIST_5TONE_DIGITS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A",
- "B", "C", "D", "E", "F"]
-LIST_5TONE_DELAY = ["%s ms" % x for x in range(0, 1010, 10)]
-LIST_5TONE_RESET = ["%s ms" % x for x in range(100, 8100, 100)]
-LIST_DTMF_SPEED = ["%s ms" % x for x in range(50, 2010, 10)]
-LIST_DTMF_DIGITS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B",
- "C", "D", "#", "*"]
-LIST_DTMF_VALUES = [0x0A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
- 0x0D, 0x0E, 0x0F, 0x00, 0x0C, 0x0B ]
-LIST_DTMF_SPECIAL_DIGITS = [ "*", "#", "A", "B", "C", "D"]
-LIST_DTMF_SPECIAL_VALUES = [ 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00]
-LIST_DTMF_DELAY = ["%s ms" % x for x in range(100, 4100, 100)]
-CHARSET_DTMF_DIGITS = "0123456789AaBbCcDd#*"
-LIST_2TONE_DEC = ["A-B", "A-C", "A-D",
- "B-A", "B-C", "B-D",
- "C-A", "C-B", "C-D",
- "D-A", "D-B", "D-C"]
-LIST_2TONE_RESPONSE = ["None", "Alert", "Transpond", "Alert+Transpond"]
-
-# This is a general serial timeout for all serial read functions.
-# Practice has show that about 0.7 sec will be enough to cover all radios.
-STIMEOUT = 0.7
-
-# this var controls the verbosity in the debug and by default it's low (False)
-# make it True and you will to get a very verbose debug.log
-debug = False
-
-# Power Levels
-NORMAL_POWER_LEVELS = [chirp_common.PowerLevel("High", watts=25),
- chirp_common.PowerLevel("Low", watts=10)]
-UV5001_POWER_LEVELS = [chirp_common.PowerLevel("High", watts=50),
- chirp_common.PowerLevel("Low", watts=10)]
-
-# this must be defined globaly
-POWER_LEVELS = None
-
-# valid chars on the LCD, Note that " " (space) is stored as "\xFF"
-VALID_CHARS = chirp_common.CHARSET_ALPHANUMERIC + \
- "`{|}!\"#$%&'()*+,-./:;<=>?@[]^_"
-
-
-##### ID strings #####################################################
-
-# BTECH UV2501 pre-production units
-UV2501pp_fp = "M2C294"
-# BTECH UV2501 pre-production units 2 + and 1st Gen radios
-UV2501pp2_fp = "M29204"
-# B-TECH UV-2501 second generation (2G) radios
-UV2501G2_fp = "BTG214"
-# B-TECH UV-2501 third generation (3G) radios
-UV2501G3_fp = "BTG324"
-
-# B-TECH UV-2501+220 pre-production units
-UV2501_220pp_fp = "M3C281"
-# extra block read for the 2501+220 pre-production units
-# the same for all of this radios so far
-UV2501_220pp_id = " 280528"
-# B-TECH UV-2501+220
-UV2501_220_fp = "M3G201"
-# new variant, let's call it Generation 2
-UV2501_220G2_fp = "BTG211"
-# B-TECH UV-2501+220 third generation (3G)
-UV2501_220G3_fp = "BTG311"
-
-# B-TECH UV-5001 pre-production units + 1st Gen radios
-UV5001pp_fp = "V19204"
-# B-TECH UV-5001 alpha units
-UV5001alpha_fp = "V28204"
-# B-TECH UV-5001 second generation (2G) radios
-UV5001G2_fp = "BTG214"
-# B-TECH UV-5001 second generation (2G2)
-UV5001G22_fp = "V2G204"
-# B-TECH UV-5001 third generation (3G)
-UV5001G3_fp = "BTG304"
-
-# special var to know when we found a BTECH Gen 3
-BTECH3 = [UV2501G3_fp, UV2501_220G3_fp, UV5001G3_fp]
-
-
-# WACCOM Mini-8900
-MINI8900_fp = "M28854"
-
-
-# QYT KT-UV980
-KTUV980_fp = "H28854"
-
-# QYT KT8900
-KT8900_fp = "M29154"
-# New generations KT8900
-KT8900_fp1 = "M2C234"
-KT8900_fp2 = "M2G1F4"
-KT8900_fp3 = "M2G2F4"
-KT8900_fp4 = "M2G304"
-KT8900_fp5 = "M2G314"
-# this radio has an extra ID
-KT8900_id = " 303688"
-
-# KT8900R
-KT8900R_fp = "M3G1F4"
-# Second Generation
-KT8900R_fp1 = "M3G214"
-# another model
-KT8900R_fp2 = "M3C234"
-# another model G4?
-KT8900R_fp3 = "M39164"
-# another model
-KT8900R_fp4 = "M3G314"
-# this radio has an extra ID
-KT8900R_id = "280528"
-
-
-# LUITON LT-588UV
-LT588UV_fp = "V2G1F4"
-# Added by rstrickoff gen 2 id
-LT588UV_fp1 = "V2G214"
-
-
-#### MAGICS
-# for the Waccom Mini-8900
-MSTRING_MINI8900 = "\x55\xA5\xB5\x45\x55\x45\x4d\x02"
-# for the B-TECH UV-2501+220 (including pre production ones)
-MSTRING_220 = "\x55\x20\x15\x12\x12\x01\x4d\x02"
-# for the QYT KT8900 & R
-MSTRING_KT8900 = "\x55\x20\x15\x09\x16\x45\x4D\x02"
-MSTRING_KT8900R = "\x55\x20\x15\x09\x25\x01\x4D\x02"
-# magic string for all other models
-MSTRING = "\x55\x20\x15\x09\x20\x45\x4d\x02"
-
-
-def _clean_buffer(radio):
- """Cleaning the read serial buffer, hard timeout to survive an infinite
- data stream"""
-
- # touching the serial timeout to optimize the flushing
- # restored at the end to the default value
- radio.pipe.timeout = 0.1
- dump = "1"
- datacount = 0
-
- try:
- while len(dump) > 0:
- dump = radio.pipe.read(100)
- datacount += len(dump)
- # hard limit to survive a infinite serial data stream
- # 5 times bigger than a normal rx block (69 bytes)
- if datacount > 345:
- seriale = "Please check your serial port selection."
- raise errors.RadioError(seriale)
-
- # restore the default serial timeout
- radio.pipe.timeout = STIMEOUT
-
- except Exception:
- raise errors.RadioError("Unknown error cleaning the serial buffer")
-
-
-def _rawrecv(radio, amount):
- """Raw read from the radio device, less intensive way"""
-
- data = ""
-
- try:
- data = radio.pipe.read(amount)
-
- # DEBUG
- if debug is True:
- LOG.debug("<== (%d) bytes:\n\n%s" %
- (len(data), util.hexprint(data)))
-
- # fail if no data is received
- if len(data) == 0:
- raise errors.RadioError("No data received from radio")
-
- # notice on the logs if short
- if len(data) < amount:
- LOG.warn("Short reading %d bytes from the %d requested." %
- (len(data), amount))
-
- except:
- raise errors.RadioError("Error reading data from radio")
-
- return data
-
-
-def _send(radio, data):
- """Send data to the radio device"""
-
- try:
- for byte in data:
- radio.pipe.write(byte)
- # Some OS (mainly Linux ones) are too fast on the serial and
- # get the MCU inside the radio stuck in the early stages, this
- # hits some models more than others.
- #
- # To cope with that we introduce a delay on the writes.
- # Many option have been tested (delaying only after error occures, after short reads, only for linux, ...)
- # Finally, a static delay was chosen as simplest of all solutions (Michael Wagner, OE4AMW)
- # (for details, see issue 3993)
- sleep(0.002)
-
- # DEBUG
- if debug is True:
- LOG.debug("==> (%d) bytes:\n\n%s" %
- (len(data), util.hexprint(data)))
- except:
- raise errors.RadioError("Error sending data to radio")
-
-
-def _make_frame(cmd, addr, length, data=""):
- """Pack the info in the headder format"""
- frame = "\x06" + struct.pack(">BHB", ord(cmd), addr, length)
- # add the data if set
- if len(data) != 0:
- frame += data
-
- return frame
-
-
-def _recv(radio, addr):
- """Get data from the radio all at once to lower syscalls load"""
-
- # Get the full 69 bytes at a time to reduce load
- # 1 byte ACK + 4 bytes header + 64 bytes of data (BLOCK_SIZE)
-
- # get the whole block
- block = _rawrecv(radio, BLOCK_SIZE + 5)
-
- # basic check
- if len(block) < (BLOCK_SIZE + 5):
- raise errors.RadioError("Short read of the block 0x%04x" % addr)
-
- # checking for the ack
- if block[0] != ACK_CMD:
- raise errors.RadioError("Bad ack from radio in block 0x%04x" % addr)
-
- # header validation
- c, a, l = struct.unpack(">BHB", block[1:5])
- if a != addr or l != BLOCK_SIZE or c != ord("X"):
- LOG.debug("Invalid header for block 0x%04x" % addr)
- LOG.debug("CMD: %s ADDR: %04x SIZE: %02x" % (c, a, l))
- raise errors.RadioError("Invalid header for block 0x%04x:" % addr)
-
- # return the data
- return block[5:]
-
-
-def _start_clone_mode(radio, status):
- """Put the radio in clone mode and get the ident string, 3 tries"""
-
- # cleaning the serial buffer
- _clean_buffer(radio)
-
- # prep the data to show in the UI
- status.cur = 0
- status.msg = "Identifying the radio..."
- status.max = 3
- radio.status_fn(status)
-
- try:
- for a in range(0, status.max):
- # Update the UI
- status.cur = a + 1
- radio.status_fn(status)
-
- # send the magic word
- _send(radio, radio._magic)
-
- # Now you get a x06 of ACK if all goes well
- ack = radio.pipe.read(1)
-
- if ack == "\x06":
- # DEBUG
- LOG.info("Magic ACK received")
- status.cur = status.max
- radio.status_fn(status)
-
- return True
-
- return False
-
- except errors.RadioError:
- raise
- except Exception, e:
- raise errors.RadioError("Error sending Magic to radio:\n%s" % e)
-
-
-def _do_ident(radio, status, upload=False):
- """Put the radio in PROGRAM mode & identify it"""
- # set the serial discipline
- radio.pipe.baudrate = 9600
- radio.pipe.parity = "N"
-
- # open the radio into program mode
- if _start_clone_mode(radio, status) is False:
- msg = "Radio did not enter clone mode"
- # warning about old versions of QYT KT8900
- if radio.MODEL == "KT8900":
- msg += ". You may want to try it as a WACCOM MINI-8900, there is a"
- msg += " known variant of this radios that is a clone of it."
- raise errors.RadioError(msg)
-
- # Ok, get the ident string
- ident = _rawrecv(radio, 49)
-
- # basic check for the ident
- if len(ident) != 49:
- raise errors.RadioError("Radio send a short ident block.")
-
- # check if ident is OK
- itis = False
- for fp in radio._fileid:
- if fp in ident:
- # got it!
- itis = True
- # checking if we are dealing with a Gen 3 BTECH
- if radio.VENDOR == "BTECH" and fp in BTECH3:
- radio.btech3 = True
-
- break
-
- if itis is False:
- LOG.debug("Incorrect model ID, got this:\n\n" + util.hexprint(ident))
- raise errors.RadioError("Radio identification failed.")
-
- # some radios needs a extra read and check for a code on it, this ones
- # has the check value in the _id2 var, others simply False
- if radio._id2 is not False:
- # lower the timeout here as this radios are reseting due to timeout
- radio.pipe.timeout = 0.05
-
- # query & receive the extra ID
- _send(radio, _make_frame("S", 0x3DF0, 16))
- id2 = _rawrecv(radio, 21)
-
- # WARNING !!!!!!
- # different radios send a response with a different amount of data
- # it seems that it's padded with \xff, \x20 and some times with \x00
- # we just care about the first 16, our magic string is in there
- if len(id2) < 16:
- raise errors.RadioError("The extra ID is short, aborting.")
-
- # ok, the correct string must be in the received data
- if radio._id2 not in id2:
- LOG.debug("Full *BAD* extra ID on the %s is: \n%s" %
- (radio.MODEL, util.hexprint(id2)))
- raise errors.RadioError("The extra ID is wrong, aborting.")
-
- # this radios need a extra request/answer here on the upload
- # the amount of data received depends of the radio type
- #
- # also the first block of TX must no have the ACK at the beginning
- # see _upload for this.
- if upload is True:
- # send an ACK
- _send(radio, ACK_CMD)
-
- # the amount of data depend on the radio, so far we have two radios
- # reading two bytes with an ACK at the end and just ONE with just
- # one byte (QYT KT8900)
- # the JT-6188 appears a clone of the last, but reads TWO bytes.
- #
- # we will read two bytes with a custom timeout to not penalize the
- # users for this.
- #
- # we just check for a response and last byte being a ACK, that is
- # the common stone for all radios (3 so far)
- ack = _rawrecv(radio, 2)
-
- # checking
- if len(ack) == 0 or ack[-1:] != ACK_CMD:
- raise errors.RadioError("Radio didn't ACK the upload")
-
- # restore the default serial timeout
- radio.pipe.timeout = STIMEOUT
-
- # DEBUG
- LOG.info("Positive ident, this is a %s %s" % (radio.VENDOR, radio.MODEL))
-
- return True
-
-
-def _download(radio):
- """Get the memory map"""
-
- # UI progress
- status = chirp_common.Status()
-
- # put radio in program mode and identify it
- _do_ident(radio, status)
-
- # the models that doesn't have the extra ID have to make a dummy read here
- if radio._id2 is False:
- _send(radio, _make_frame("S", 0, BLOCK_SIZE))
- discard = _rawrecv(radio, BLOCK_SIZE + 5)
-
- if debug is True:
- LOG.info("Dummy first block read done, got this:\n\n %s",
- util.hexprint(discard))
-
- # reset the progress bar in the UI
- status.max = MEM_SIZE / BLOCK_SIZE
- status.msg = "Cloning from radio..."
- status.cur = 0
- radio.status_fn(status)
-
- # cleaning the serial buffer
- _clean_buffer(radio)
-
- data = ""
- for addr in range(0, MEM_SIZE, BLOCK_SIZE):
- # sending the read request
- _send(radio, _make_frame("S", addr, BLOCK_SIZE))
-
- # read
- d = _recv(radio, addr)
-
- # aggregate the data
- data += d
-
- # UI Update
- status.cur = addr / BLOCK_SIZE
- status.msg = "Cloning from radio..."
- radio.status_fn(status)
-
- return data
-
-
-def _upload(radio):
- """Upload procedure"""
-
- # The UPLOAD mem is restricted to lower than 0x3100,
- # so we will overide that here localy
- MEM_SIZE = 0x3100
-
- # UI progress
- status = chirp_common.Status()
-
- # put radio in program mode and identify it
- _do_ident(radio, status, True)
-
- # get the data to upload to radio
- data = radio.get_mmap()
-
- # Reset the UI progress
- status.max = MEM_SIZE / TX_BLOCK_SIZE
- status.cur = 0
- status.msg = "Cloning to radio..."
- radio.status_fn(status)
-
- # the radios that doesn't have the extra ID 'may' do a dummy write, I found
- # that leveraging the bad ACK and NOT doing the dummy write is ok, as the
- # dummy write is accepted (it actually writes to the mem!) by the radio.
-
- # cleaning the serial buffer
- _clean_buffer(radio)
-
- # the fun start here
- for addr in range(0, MEM_SIZE, TX_BLOCK_SIZE):
- # getting the block of data to send
- d = data[addr:addr + TX_BLOCK_SIZE]
-
- # build the frame to send
- frame = _make_frame("X", addr, TX_BLOCK_SIZE, d)
-
- # first block must not send the ACK at the beginning for the
- # ones that has the extra id, since this have to do a extra step
- if addr == 0 and radio._id2 is not False:
- frame = frame[1:]
-
- # send the frame
- _send(radio, frame)
-
- # receiving the response
- ack = _rawrecv(radio, 1)
-
- # basic check
- if len(ack) != 1:
- raise errors.RadioError("No ACK when writing block 0x%04x" % addr)
-
- if not ack in "\x06\x05":
- raise errors.RadioError("Bad ACK writing block 0x%04x:" % addr)
-
- # UI Update
- status.cur = addr / TX_BLOCK_SIZE
- status.msg = "Cloning to radio..."
- radio.status_fn(status)
-
-
-def model_match(cls, data):
- """Match the opened/downloaded image to the correct version"""
- rid = data[0x3f70:0x3f76]
-
- if rid in cls._fileid:
- return True
-
- return False
-
-
-def _decode_ranges(low, high):
- """Unpack the data in the ranges zones in the memmap and return
- a tuple with the integer corresponding to the Mhz it means"""
- ilow = int(low[0]) * 100 + int(low[1]) * 10 + int(low[2])
- ihigh = int(high[0]) * 100 + int(high[1]) * 10 + int(high[2])
- ilow *= 1000000
- ihigh *= 1000000
-
- return (ilow, ihigh)
-
-
-def _split(rf, f1, f2):
- """Returns False if the two freqs are in the same band (no split)
- or True otherwise"""
-
- # determine if the two freqs are in the same band
- for low, high in rf.valid_bands:
- if f1 >= low and f1 <= high and \
- f2 >= low and f2 <= high:
- # if the two freqs are on the same Band this is not a split
- return False
-
- # if you get here is because the freq pairs are split
- return False
-
-
-class BTech(chirp_common.CloneModeRadio, chirp_common.ExperimentalRadio):
+
+class BTech(BTechMobileCommon):
"""BTECH's UV-5001 and alike radios"""
- VENDOR = "BTECH"
- MODEL = ""
- IDENT = ""
- _vhf_range = (130000000, 180000000)
- _220_range = (210000000, 231000000)
- _uhf_range = (400000000, 521000000)
- _upper = 199
- _magic = MSTRING
- _fileid = None
- _id2 = False
- btech3 = False
-
- @classmethod
- def get_prompts(cls):
- rp = chirp_common.RadioPrompts()
- rp.experimental = \
- ('This driver is experimental.\n'
- '\n'
- 'Please keep a copy of your memories with the original software '
- 'if you treasure them, this driver is new and may contain'
- ' bugs.\n'
- '\n'
- )
- rp.pre_download = _(dedent("""\
- Follow these instructions to download your info:
-
- 1 - Turn off your radio
- 2 - Connect your interface cable
- 3 - Turn on your radio
- 4 - Do the download of your radio data
-
- """))
- rp.pre_upload = _(dedent("""\
- Follow these instructions to upload your info:
-
- 1 - Turn off your radio
- 2 - Connect your interface cable
- 3 - Turn on your radio
- 4 - Do the upload of your radio data
-
- """))
- return rp
-
- def get_features(self):
- """Get the radio's features"""
-
- # we will use the following var as global
- global POWER_LEVELS
-
- rf = chirp_common.RadioFeatures()
- rf.has_settings = True
- rf.has_bank = False
- rf.has_tuning_step = False
- rf.can_odd_split = True
- rf.has_name = True
- rf.has_offset = True
- rf.has_mode = True
- rf.has_dtcs = True
- rf.has_rx_dtcs = True
- rf.has_dtcs_polarity = True
- rf.has_ctone = True
- rf.has_cross = True
- rf.valid_modes = MODES
- rf.valid_characters = VALID_CHARS
- rf.valid_name_length = NAME_LENGTH
- rf.valid_duplexes = ["", "-", "+", "split", "off"]
- rf.valid_tmodes = ['', 'Tone', 'TSQL', 'DTCS', 'Cross']
- rf.valid_cross_modes = [
- "Tone->Tone",
- "DTCS->",
- "->DTCS",
- "Tone->DTCS",
- "DTCS->Tone",
- "->Tone",
- "DTCS->DTCS"]
- rf.valid_skips = SKIP_VALUES
- rf.valid_dtcs_codes = DTCS
- rf.memory_bounds = (0, self._upper)
-
- # power levels
- if self.MODEL == "UV-5001":
- POWER_LEVELS = UV5001_POWER_LEVELS # Higher power (50W)
- else:
- POWER_LEVELS = NORMAL_POWER_LEVELS # Lower power (25W)
-
- rf.valid_power_levels = POWER_LEVELS
-
- # bands
- rf.valid_bands = [self._vhf_range, self._uhf_range]
-
- # 2501+220 & KT8900R
- if self.MODEL in ["UV-2501+220", "KT8900R"]:
- rf.valid_bands.append(self._220_range)
-
- return rf
-
- def sync_in(self):
- """Download from radio"""
- data = _download(self)
- self._mmap = memmap.MemoryMap(data)
- self.process_mmap()
-
- def sync_out(self):
- """Upload to radio"""
- try:
- _upload(self)
- except errors.RadioError:
- raise
- except Exception, e:
- raise errors.RadioError("Error: %s" % e)
+ BANDS = 2
+ COLOR_LCD = False
+ NAME_LENGTH = 6
def set_options(self):
"""This is to read the options from the image and set it in the
@@ -1048,1546 +3067,6 @@
# load specific parameters from the radio image
self.set_options()
- def get_raw_memory(self, number):
- return repr(self._memobj.memory[number])
-
- def _decode_tone(self, val):
- """Parse the tone data to decode from mem, it returns:
- Mode (''|DTCS|Tone), Value (None|###), Polarity (None,N,R)"""
- pol = None
-
- if val in [0, 65535]:
- return '', None, None
- elif val > 0x0258:
- a = val / 10.0
- return 'Tone', a, pol
- else:
- if val > 0x69:
- index = val - 0x6A
- pol = "R"
- else:
- index = val - 1
- pol = "N"
-
- tone = DTCS[index]
- return 'DTCS', tone, pol
-
- def _encode_tone(self, memval, mode, val, pol):
- """Parse the tone data to encode from UI to mem"""
- if mode == '' or mode is None:
- memval.set_raw("\x00\x00")
- elif mode == 'Tone':
- memval.set_value(val * 10)
- elif mode == 'DTCS':
- # detect the index in the DTCS list
- try:
- index = DTCS.index(val)
- if pol == "N":
- index += 1
- else:
- index += 0x6A
- memval.set_value(index)
- except:
- msg = "Digital Tone '%d' is not supported" % value
- LOG.error(msg)
- raise errors.RadioError(msg)
- else:
- msg = "Internal error: invalid mode '%s'" % mode
- LOG.error(msg)
- raise errors.InvalidDataError(msg)
-
- def get_memory(self, number):
- """Get the mem representation from the radio image"""
- _mem = self._memobj.memory[number]
- _names = self._memobj.names[number]
-
- # Create a high-level memory object to return to the UI
- mem = chirp_common.Memory()
-
- # Memory number
- mem.number = number
-
- if _mem.get_raw()[0] == "\xFF":
- mem.empty = True
- return mem
-
- # Freq and offset
- mem.freq = int(_mem.rxfreq) * 10
- # tx freq can be blank
- if _mem.get_raw()[4] == "\xFF":
- # TX freq not set
- mem.offset = 0
- mem.duplex = "off"
- else:
- # TX freq set
- offset = (int(_mem.txfreq) * 10) - mem.freq
- if offset != 0:
- if _split(self.get_features(), mem.freq, int(_mem.txfreq) * 10):
- mem.duplex = "split"
- mem.offset = int(_mem.txfreq) * 10
- elif offset < 0:
- mem.offset = abs(offset)
- mem.duplex = "-"
- elif offset > 0:
- mem.offset = offset
- mem.duplex = "+"
- else:
- mem.offset = 0
-
- # name TAG of the channel
- mem.name = str(_names.name).rstrip("\xFF").replace("\xFF", " ")
-
- # power
- mem.power = POWER_LEVELS[int(_mem.power)]
-
- # wide/narrow
- mem.mode = MODES[int(_mem.wide)]
-
- # skip
- mem.skip = SKIP_VALUES[_mem.add]
-
- # tone data
- rxtone = txtone = None
- txtone = self._decode_tone(_mem.txtone)
- rxtone = self._decode_tone(_mem.rxtone)
- chirp_common.split_tone_decode(mem, txtone, rxtone)
-
- # Extra
- mem.extra = RadioSettingGroup("extra", "Extra")
-
- scramble = RadioSetting("scramble", "Scramble",
- RadioSettingValueBoolean(bool(_mem.scramble)))
- mem.extra.append(scramble)
-
- bcl = RadioSetting("bcl", "Busy channel lockout",
- RadioSettingValueBoolean(bool(_mem.bcl)))
- mem.extra.append(bcl)
-
- pttid = RadioSetting("pttid", "PTT ID",
- RadioSettingValueList(PTTID_LIST,
- PTTID_LIST[_mem.pttid]))
- mem.extra.append(pttid)
-
- # validating scode
- scode = _mem.scode if _mem.scode != 15 else 0
- pttidcode = RadioSetting("scode", "PTT ID signal code",
- RadioSettingValueList(
- PTTIDCODE_LIST,
- PTTIDCODE_LIST[scode]))
- mem.extra.append(pttidcode)
-
- optsig = RadioSetting("optsig", "Optional signaling",
- RadioSettingValueList(
- OPTSIG_LIST,
- OPTSIG_LIST[_mem.optsig]))
- mem.extra.append(optsig)
-
- spmute = RadioSetting("spmute", "Speaker mute",
- RadioSettingValueList(
- SPMUTE_LIST,
- SPMUTE_LIST[_mem.spmute]))
- mem.extra.append(spmute)
-
- return mem
-
- def set_memory(self, mem):
- """Set the memory data in the eeprom img from the UI"""
- # get the eprom representation of this channel
- _mem = self._memobj.memory[mem.number]
- _names = self._memobj.names[mem.number]
-
- mem_was_empty = False
- # same method as used in get_memory for determining if mem is empty
- # doing this BEFORE overwriting it with new values ...
- if _mem.get_raw()[0] == "\xFF":
- LOG.debug("This mem was empty before")
- mem_was_empty = True
-
- # if empty memmory
- if mem.empty:
- # the channel itself
- _mem.set_raw("\xFF" * 16)
- # the name tag
- _names.set_raw("\xFF" * 16)
- return
-
- # frequency
- _mem.rxfreq = mem.freq / 10
-
- # duplex
- if mem.duplex == "+":
- _mem.txfreq = (mem.freq + mem.offset) / 10
- elif mem.duplex == "-":
- _mem.txfreq = (mem.freq - mem.offset) / 10
- elif mem.duplex == "off":
- for i in _mem.txfreq:
- i.set_raw("\xFF")
- elif mem.duplex == "split":
- _mem.txfreq = mem.offset / 10
- else:
- _mem.txfreq = mem.freq / 10
-
- # tone data
- ((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)
-
- # name TAG of the channel
- if len(mem.name) < NAME_LENGTH:
- # we must pad to NAME_LENGTH chars, " " = "\xFF"
- mem.name = str(mem.name).ljust(NAME_LENGTH, " ")
- _names.name = str(mem.name).replace(" ", "\xFF")
-
- # power, # default power level is high
- _mem.power = 0 if mem.power is None else POWER_LEVELS.index(mem.power)
-
- # wide/narrow
- _mem.wide = MODES.index(mem.mode)
-
- # scan add property
- _mem.add = SKIP_VALUES.index(mem.skip)
-
- # reseting unknowns, this have to be set by hand
- _mem.unknown0 = 0
- _mem.unknown1 = 0
- _mem.unknown2 = 0
- _mem.unknown3 = 0
- _mem.unknown4 = 0
- _mem.unknown5 = 0
- _mem.unknown6 = 0
-
- # extra settings
- if len(mem.extra) > 0:
- # there are setting, parse
- LOG.debug("Extra-Setting supplied. Setting them.")
- for setting in mem.extra:
- setattr(_mem, setting.get_name(), setting.value)
- else:
- if mem.empty:
- LOG.debug("New mem is empty.")
- else:
- LOG.debug("New mem is NOT empty")
- # set extra-settings to default ONLY when apreviously empty or
- # deleted memory was edited to prevent errors such as #4121
- if mem_was_empty :
- LOG.debug("old mem was empty. Setting default for extras.")
- _mem.spmute = 0
- _mem.optsig = 0
- _mem.scramble = 0
- _mem.bcl = 0
- _mem.pttid = 0
- _mem.scode = 0
-
- return mem
-
- def get_settings(self):
- """Translate the bit in the mem_struct into settings in the UI"""
- _mem = self._memobj
- basic = RadioSettingGroup("basic", "Basic Settings")
- advanced = RadioSettingGroup("advanced", "Advanced Settings")
- other = RadioSettingGroup("other", "Other Settings")
- work = RadioSettingGroup("work", "Work Mode Settings")
- fm_presets = RadioSettingGroup("fm_presets", "FM Presets")
- top = RadioSettings(basic, advanced, other, work, fm_presets)
-
- # Basic
- tdr = RadioSetting("settings.tdr", "Transceiver dual receive",
- RadioSettingValueBoolean(_mem.settings.tdr))
- basic.append(tdr)
-
- sql = RadioSetting("settings.sql", "Squelch level",
- RadioSettingValueInteger(0, 9, _mem.settings.sql))
- basic.append(sql)
-
- tot = RadioSetting("settings.tot", "Time out timer",
- RadioSettingValueList(LIST_TOT, LIST_TOT[
- _mem.settings.tot]))
- basic.append(tot)
-
- if self.MODEL in ("UV-2501", "UV-2501+220", "UV-5001"):
- apo = RadioSetting("settings.apo", "Auto power off timer",
- RadioSettingValueList(LIST_APO, LIST_APO[
- _mem.settings.apo]))
- basic.append(apo)
- else:
- toa = RadioSetting("settings.apo", "Time out alert timer",
- RadioSettingValueList(LIST_TOA, LIST_TOA[
- _mem.settings.apo]))
- basic.append(toa)
-
- abr = RadioSetting("settings.abr", "Backlight timer",
- RadioSettingValueList(LIST_ABR, LIST_ABR[
- _mem.settings.abr]))
- basic.append(abr)
-
- beep = RadioSetting("settings.beep", "Key beep",
- RadioSettingValueBoolean(_mem.settings.beep))
- basic.append(beep)
-
- dtmfst = RadioSetting("settings.dtmfst", "DTMF side tone",
- RadioSettingValueList(LIST_DTMFST, LIST_DTMFST[
- _mem.settings.dtmfst]))
- basic.append(dtmfst)
-
- prisc = RadioSetting("settings.prisc", "Priority scan",
- RadioSettingValueBoolean(_mem.settings.prisc))
- basic.append(prisc)
-
- prich = RadioSetting("settings.prich", "Priority channel",
- RadioSettingValueInteger(0, 199,
- _mem.settings.prich))
- basic.append(prich)
-
- screv = RadioSetting("settings.screv", "Scan resume method",
- RadioSettingValueList(LIST_SCREV, LIST_SCREV[
- _mem.settings.screv]))
- basic.append(screv)
-
- pttlt = RadioSetting("settings.pttlt", "PTT transmit delay",
- RadioSettingValueInteger(0, 30,
- _mem.settings.pttlt))
- basic.append(pttlt)
-
- emctp = RadioSetting("settings.emctp", "Alarm mode",
- RadioSettingValueList(LIST_EMCTP, LIST_EMCTP[
- _mem.settings.emctp]))
- basic.append(emctp)
-
- emcch = RadioSetting("settings.emcch", "Alarm channel",
- RadioSettingValueInteger(0, 199,
- _mem.settings.emcch))
- basic.append(emcch)
-
- ringt = RadioSetting("settings.ringt", "Ring time",
- RadioSettingValueList(LIST_RINGT, LIST_RINGT[
- _mem.settings.ringt]))
- basic.append(ringt)
-
- camdf = RadioSetting("settings.camdf", "Display mode A",
- RadioSettingValueList(LIST_MDF, LIST_MDF[
- _mem.settings.camdf]))
- basic.append(camdf)
-
- cbmdf = RadioSetting("settings.cbmdf", "Display mode B",
- RadioSettingValueList(LIST_MDF, LIST_MDF[
- _mem.settings.cbmdf]))
- basic.append(cbmdf)
-
- if self.MODEL in ("UV-2501", "UV-2501+220", "UV-5001"):
- sync = RadioSetting("settings.sync", "A/B channel sync",
- RadioSettingValueBoolean(_mem.settings.sync))
- basic.append(sync)
- else:
- autolk = RadioSetting("settings.sync", "Auto keylock",
- RadioSettingValueBoolean(_mem.settings.sync))
- basic.append(autolk)
-
- ponmsg = RadioSetting("settings.ponmsg", "Power-on message",
- RadioSettingValueList(LIST_PONMSG, LIST_PONMSG[
- _mem.settings.ponmsg]))
- basic.append(ponmsg)
-
- wtled = RadioSetting("settings.wtled", "Standby backlight Color",
- RadioSettingValueList(LIST_COLOR, LIST_COLOR[
- _mem.settings.wtled]))
- basic.append(wtled)
-
- rxled = RadioSetting("settings.rxled", "RX backlight Color",
- RadioSettingValueList(LIST_COLOR, LIST_COLOR[
- _mem.settings.rxled]))
- basic.append(rxled)
-
- txled = RadioSetting("settings.txled", "TX backlight Color",
- RadioSettingValueList(LIST_COLOR, LIST_COLOR[
- _mem.settings.txled]))
- basic.append(txled)
-
- anil = RadioSetting("settings.anil", "ANI length",
- RadioSettingValueList(LIST_ANIL, LIST_ANIL[
- _mem.settings.anil]))
- basic.append(anil)
-
- reps = RadioSetting("settings.reps", "Relay signal (tone burst)",
- RadioSettingValueList(LIST_REPS, LIST_REPS[
- _mem.settings.reps]))
- basic.append(reps)
-
- repm = RadioSetting("settings.repm", "Relay condition",
- RadioSettingValueList(LIST_REPM, LIST_REPM[
- _mem.settings.repm]))
- basic.append(repm)
-
- if self.MODEL in ("UV-2501", "UV-2501+220", "UV-5001"):
- tdrab = RadioSetting("settings.tdrab", "TDR return time",
- RadioSettingValueList(LIST_ABR, LIST_ABR[
- _mem.settings.tdrab]))
- basic.append(tdrab)
-
- ste = RadioSetting("settings.ste", "Squelch tail eliminate",
- RadioSettingValueBoolean(_mem.settings.ste))
- basic.append(ste)
-
- rpste = RadioSetting("settings.rpste", "Repeater STE",
- RadioSettingValueList(LIST_RINGT, LIST_RINGT[
- _mem.settings.rpste]))
- basic.append(rpste)
-
- rptdl = RadioSetting("settings.rptdl", "Repeater STE delay",
- RadioSettingValueList(LIST_RPTDL, LIST_RPTDL[
- _mem.settings.rptdl]))
- basic.append(rptdl)
-
- if str(_mem.fingerprint.fp) in BTECH3:
-
- mgain = RadioSetting("settings.mgain", "Mic gain",
- RadioSettingValueInteger(0, 120,
- _mem.settings.mgain))
- basic.append(mgain)
-
- dtmfg = RadioSetting("settings.dtmfg", "DTMF gain",
- RadioSettingValueInteger(0, 60,
- _mem.settings.dtmfg))
- basic.append(dtmfg)
-
- # Advanced
- def _filter(name):
- filtered = ""
- for char in str(name):
- if char in VALID_CHARS:
- filtered += char
- else:
- filtered += " "
- return filtered
-
- _msg = self._memobj.poweron_msg
- line1 = RadioSetting("poweron_msg.line1", "Power-on message line 1",
- RadioSettingValueString(0, 6, _filter(
- _msg.line1)))
- advanced.append(line1)
- line2 = RadioSetting("poweron_msg.line2", "Power-on message line 2",
- RadioSettingValueString(0, 6, _filter(
- _msg.line2)))
- advanced.append(line2)
-
- if self.MODEL in ("UV-2501", "UV-5001"):
- vfomren = RadioSetting("settings2.vfomren", "VFO/MR switching",
- RadioSettingValueBoolean(
- _mem.settings2.vfomren))
- advanced.append(vfomren)
-
- reseten = RadioSetting("settings2.reseten", "RESET",
- RadioSettingValueBoolean(
- _mem.settings2.reseten))
- advanced.append(reseten)
-
- menuen = RadioSetting("settings2.menuen", "Menu",
- RadioSettingValueBoolean(
- _mem.settings2.menuen))
- advanced.append(menuen)
-
- # Other
- def convert_bytes_to_limit(bytes):
- limit = ""
- for byte in bytes:
- if byte < 10:
- limit += chr(byte + 0x30)
- else:
- break
- return limit
-
- if self.MODEL in ["UV-2501+220", "KT8900R"]:
- _ranges = self._memobj.ranges220
- ranges = "ranges220"
- else:
- _ranges = self._memobj.ranges
- ranges = "ranges"
-
- _limit = convert_bytes_to_limit(_ranges.vhf_low)
- val = RadioSettingValueString(0, 3, _limit)
- val.set_mutable(False)
- vhf_low = RadioSetting("%s.vhf_low" % ranges, "VHF low", val)
- other.append(vhf_low)
-
- _limit = convert_bytes_to_limit(_ranges.vhf_high)
- val = RadioSettingValueString(0, 3, _limit)
- val.set_mutable(False)
- vhf_high = RadioSetting("%s.vhf_high" % ranges, "VHF high", val)
- other.append(vhf_high)
-
- if self.MODEL in ["UV-2501+220", "KT8900R"]:
- _limit = convert_bytes_to_limit(_ranges.vhf2_low)
- val = RadioSettingValueString(0, 3, _limit)
- val.set_mutable(False)
- vhf2_low = RadioSetting("%s.vhf2_low" % ranges, "VHF2 low", val)
- other.append(vhf2_low)
-
- _limit = convert_bytes_to_limit(_ranges.vhf2_high)
- val = RadioSettingValueString(0, 3, _limit)
- val.set_mutable(False)
- vhf2_high = RadioSetting("%s.vhf2_high" % ranges, "VHF2 high", val)
- other.append(vhf2_high)
-
- _limit = convert_bytes_to_limit(_ranges.uhf_low)
- val = RadioSettingValueString(0, 3, _limit)
- val.set_mutable(False)
- uhf_low = RadioSetting("%s.uhf_low" % ranges, "UHF low", val)
- other.append(uhf_low)
-
- _limit = convert_bytes_to_limit(_ranges.uhf_high)
- val = RadioSettingValueString(0, 3, _limit)
- val.set_mutable(False)
- uhf_high = RadioSetting("%s.uhf_high" % ranges, "UHF high", val)
- other.append(uhf_high)
-
- val = RadioSettingValueString(0, 6, _filter(_mem.fingerprint.fp))
- val.set_mutable(False)
- fp = RadioSetting("fingerprint.fp", "Fingerprint", val)
- other.append(fp)
-
- # Work
- dispab = RadioSetting("settings2.dispab", "Display",
- RadioSettingValueList(LIST_AB,LIST_AB[
- _mem.settings2.dispab]))
- work.append(dispab)
-
- vfomr = RadioSetting("settings2.vfomr", "VFO/MR mode",
- RadioSettingValueList(LIST_VFOMR,LIST_VFOMR[
- _mem.settings2.vfomr]))
- work.append(vfomr)
-
- keylock = RadioSetting("settings2.keylock", "Keypad lock",
- RadioSettingValueBoolean(_mem.settings2.keylock))
- work.append(keylock)
-
- mrcha = RadioSetting("settings2.mrcha", "MR A channel",
- RadioSettingValueInteger(0, 199,
- _mem.settings2.mrcha))
- work.append(mrcha)
-
- mrchb = RadioSetting("settings2.mrchb", "MR B channel",
- RadioSettingValueInteger(0, 199,
- _mem.settings2.mrchb))
- work.append(mrchb)
-
- def convert_bytes_to_freq(bytes):
- real_freq = 0
- for byte in bytes:
- real_freq = (real_freq * 10) + byte
- return chirp_common.format_freq(real_freq * 10)
-
- def my_validate(value):
- value = chirp_common.parse_freq(value)
- if "+220" in self.MODEL:
- if 180000000 <= value and value < 210000000:
- msg = ("Can't be between 180.00000-210.00000")
- raise InvalidValueError(msg)
- elif 231000000 <= value and value < 400000000:
- msg = ("Can't be between 231.00000-400.00000")
- raise InvalidValueError(msg)
- elif "8900R" in self.MODEL:
- if 180000000 <= value and value < 240000000:
- msg = ("Can't be between 180.00000-240.00000")
- raise InvalidValueError(msg)
- elif 271000000 <= value and value < 400000000:
- msg = ("Can't be between 271.00000-400.00000")
- raise InvalidValueError(msg)
- elif 180000000 <= value and value < 400000000:
- msg = ("Can't be between 180.00000-400.00000")
- raise InvalidValueError(msg)
- return chirp_common.format_freq(value)
-
- def apply_freq(setting, obj):
- value = chirp_common.parse_freq(str(setting.value)) / 10
- for i in range(7, -1, -1):
- obj.freq[i] = value % 10
- value /= 10
-
- val1a = RadioSettingValueString(0, 10, convert_bytes_to_freq(
- _mem.vfo.a.freq))
- val1a.set_validate_callback(my_validate)
- vfoafreq = RadioSetting("vfo.a.freq", "VFO A frequency", val1a)
- vfoafreq.set_apply_callback(apply_freq, _mem.vfo.a)
- work.append(vfoafreq)
-
- val1b = RadioSettingValueString(0, 10, convert_bytes_to_freq(
- _mem.vfo.b.freq))
- val1b.set_validate_callback(my_validate)
- vfobfreq = RadioSetting("vfo.b.freq", "VFO B frequency", val1b)
- vfobfreq.set_apply_callback(apply_freq, _mem.vfo.b)
- work.append(vfobfreq)
-
- vfoashiftd = RadioSetting("vfo.a.shiftd", "VFO A shift",
- RadioSettingValueList(LIST_SHIFT, LIST_SHIFT[
- _mem.vfo.a.shiftd]))
- work.append(vfoashiftd)
-
- vfobshiftd = RadioSetting("vfo.b.shiftd", "VFO B shift",
- RadioSettingValueList(LIST_SHIFT, LIST_SHIFT[
- _mem.vfo.b.shiftd]))
- work.append(vfobshiftd)
-
- def convert_bytes_to_offset(bytes):
- real_offset = 0
- for byte in bytes:
- real_offset = (real_offset * 10) + byte
- return chirp_common.format_freq(real_offset * 10000)
-
- def apply_offset(setting, obj):
- value = chirp_common.parse_freq(str(setting.value)) / 10000
- for i in range(3, -1, -1):
- obj.offset[i] = value % 10
- value /= 10
-
- val1a = RadioSettingValueString(0, 10, convert_bytes_to_offset(
- _mem.vfo.a.offset))
- vfoaoffset = RadioSetting("vfo.a.offset",
- "VFO A offset (0.00-99.95)", val1a)
- vfoaoffset.set_apply_callback(apply_offset, _mem.vfo.a)
- work.append(vfoaoffset)
-
- val1b = RadioSettingValueString(0, 10, convert_bytes_to_offset(
- _mem.vfo.b.offset))
- vfoboffset = RadioSetting("vfo.b.offset",
- "VFO B offset (0.00-99.95)", val1b)
- vfoboffset.set_apply_callback(apply_offset, _mem.vfo.b)
- work.append(vfoboffset)
-
- vfoatxp = RadioSetting("vfo.a.power", "VFO A power",
- RadioSettingValueList(LIST_TXP,LIST_TXP[
- _mem.vfo.a.power]))
- work.append(vfoatxp)
-
- vfobtxp = RadioSetting("vfo.b.power", "VFO B power",
- RadioSettingValueList(LIST_TXP,LIST_TXP[
- _mem.vfo.b.power]))
- work.append(vfobtxp)
-
- vfoawide = RadioSetting("vfo.a.wide", "VFO A bandwidth",
- RadioSettingValueList(LIST_WIDE,LIST_WIDE[
- _mem.vfo.a.wide]))
- work.append(vfoawide)
-
- vfobwide = RadioSetting("vfo.b.wide", "VFO B bandwidth",
- RadioSettingValueList(LIST_WIDE,LIST_WIDE[
- _mem.vfo.b.wide]))
- work.append(vfobwide)
-
- vfoastep = RadioSetting("vfo.a.step", "VFO A step",
- RadioSettingValueList(LIST_STEP,LIST_STEP[
- _mem.vfo.a.step]))
- work.append(vfoastep)
-
- vfobstep = RadioSetting("vfo.b.step", "VFO B step",
- RadioSettingValueList(LIST_STEP,LIST_STEP[
- _mem.vfo.b.step]))
- work.append(vfobstep)
-
- vfoaoptsig = RadioSetting("vfo.a.optsig", "VFO A optional signal",
- RadioSettingValueList(OPTSIG_LIST,
- OPTSIG_LIST[_mem.vfo.a.optsig]))
- work.append(vfoaoptsig)
-
- vfoboptsig = RadioSetting("vfo.b.optsig", "VFO B optional signal",
- RadioSettingValueList(OPTSIG_LIST,
- OPTSIG_LIST[_mem.vfo.b.optsig]))
- work.append(vfoboptsig)
-
- vfoaspmute = RadioSetting("vfo.a.spmute", "VFO A speaker mute",
- RadioSettingValueList(SPMUTE_LIST,
- SPMUTE_LIST[_mem.vfo.a.spmute]))
- work.append(vfoaspmute)
-
- vfobspmute = RadioSetting("vfo.b.spmute", "VFO B speaker mute",
- RadioSettingValueList(SPMUTE_LIST,
- SPMUTE_LIST[_mem.vfo.b.spmute]))
- work.append(vfobspmute)
-
- vfoascr = RadioSetting("vfo.a.scramble", "VFO A scramble",
- RadioSettingValueBoolean(_mem.vfo.a.scramble))
- work.append(vfoascr)
-
- vfobscr = RadioSetting("vfo.b.scramble", "VFO B scramble",
- RadioSettingValueBoolean(_mem.vfo.b.scramble))
- work.append(vfobscr)
-
- vfoascode = RadioSetting("vfo.a.scode", "VFO A PTT-ID",
- RadioSettingValueList(PTTIDCODE_LIST,
- PTTIDCODE_LIST[_mem.vfo.a.scode]))
- work.append(vfoascode)
-
- vfobscode = RadioSetting("vfo.b.scode", "VFO B PTT-ID",
- RadioSettingValueList(PTTIDCODE_LIST,
- PTTIDCODE_LIST[_mem.vfo.b.scode]))
- work.append(vfobscode)
-
- pttid = RadioSetting("settings.pttid", "PTT ID",
- RadioSettingValueList(PTTID_LIST,
- PTTID_LIST[_mem.settings.pttid]))
- work.append(pttid)
-
- #FM presets
- def fm_validate(value):
- if value == 0:
- return chirp_common.format_freq(value)
- if not (87.5 <= value and value <= 108.0): # 87.5-108MHz
- msg = ("FM-Preset-Frequency: Must be between 87.5 and 108 MHz")
- raise InvalidValueError(msg)
- return value
-
- def apply_fm_preset_name(setting, obj):
- valstring = str (setting.value)
- for i in range(0,6):
- if valstring[i] in VALID_CHARS:
- obj[i] = valstring[i]
- else:
- obj[i] = '0xff'
-
- def apply_fm_freq(setting, obj):
- value = chirp_common.parse_freq(str(setting.value)) / 10
- for i in range(7, -1, -1):
- obj.freq[i] = value % 10
- value /= 10
-
- _presets = self._memobj.fm_radio_preset
- i = 1
- for preset in _presets:
- line = RadioSetting("fm_presets_"+ str(i), "Station name " + str(i),
- RadioSettingValueString(0, 6, _filter(
- preset.broadcast_station_name)))
- line.set_apply_callback(apply_fm_preset_name,
- preset.broadcast_station_name)
-
- val = RadioSettingValueFloat(0, 108, convert_bytes_to_freq(preset.freq))
- fmfreq = RadioSetting("fm_presets_"+ str(i) + "_freq", "Frequency "+ str(i), val)
- val.set_validate_callback(fm_validate)
- fmfreq.set_apply_callback(apply_fm_freq, preset)
- fm_presets.append(line)
- fm_presets.append(fmfreq)
-
- i = i + 1
-
- # DTMF-Setting
- dtmf_enc_settings = RadioSettingGroup ("dtmf_enc_settings",
- "DTMF Encoding Settings")
- dtmf_dec_settings = RadioSettingGroup ("dtmf_dec_settings",
- "DTMF Decoding Settings")
- top.append(dtmf_enc_settings)
- top.append(dtmf_dec_settings)
- txdisable = RadioSetting("dtmf_settings.txdisable",
- "TX-Disable",
- RadioSettingValueBoolean(
- _mem.dtmf_settings.txdisable))
- dtmf_enc_settings.append(txdisable)
-
- rxdisable = RadioSetting("dtmf_settings.rxdisable",
- "RX-Disable",
- RadioSettingValueBoolean(
- _mem.dtmf_settings.rxdisable))
- dtmf_enc_settings.append(rxdisable)
-
- dtmfspeed_on = RadioSetting(
- "dtmf_settings.dtmfspeed_on",
- "DTMF Speed (On Time)",
- RadioSettingValueList(LIST_DTMF_SPEED,
- LIST_DTMF_SPEED[
- _mem.dtmf_settings.dtmfspeed_on]))
- dtmf_enc_settings.append(dtmfspeed_on)
-
- dtmfspeed_off = RadioSetting(
- "dtmf_settings.dtmfspeed_off",
- "DTMF Speed (Off Time)",
- RadioSettingValueList(LIST_DTMF_SPEED,
- LIST_DTMF_SPEED[
- _mem.dtmf_settings.dtmfspeed_off]))
- dtmf_enc_settings.append(dtmfspeed_off)
-
- def memory2string(dmtf_mem):
- dtmf_string = ""
- for digit in dmtf_mem:
- if digit != 255:
- index = LIST_DTMF_VALUES.index(digit)
- dtmf_string = dtmf_string + LIST_DTMF_DIGITS[index]
- return dtmf_string
-
- def apply_dmtf_frame(setting, obj):
- LOG.debug("Setting DTMF-Code: " + str(setting.value) )
- val_string = str(setting.value)
- for i in range(0,16):
- obj[i] = 255
- i = 0
- for current_char in val_string:
- current_char = current_char.upper()
- index = LIST_DTMF_DIGITS.index(current_char)
- obj[i] = LIST_DTMF_VALUES[ index ]
- i = i + 1
-
- codes = self._memobj.dtmf_codes
- i = 1
- for dtmfcode in codes:
- val = RadioSettingValueString(0, 16,
- memory2string(dtmfcode.code),
- False, CHARSET_DTMF_DIGITS)
- line = RadioSetting("dtmf_code_" + str(i) + "_code",
- "DMTF Code " + str(i), val)
- line.set_apply_callback(apply_dmtf_frame, dtmfcode.code)
- dtmf_enc_settings.append(line)
- i = i + 1
-
- line = RadioSetting("dtmf_settings.mastervice",
- "Master and Vice ID",
- RadioSettingValueBoolean(
- _mem.dtmf_settings.mastervice))
- dtmf_dec_settings.append(line)
-
- val = RadioSettingValueString(0, 16,
- memory2string(
- _mem.dtmf_settings.masterid),
- False, CHARSET_DTMF_DIGITS)
- line = RadioSetting("dtmf_settings.masterid",
- "Master Control ID ", val)
- line.set_apply_callback(apply_dmtf_frame,
- _mem.dtmf_settings.masterid)
- dtmf_dec_settings.append(line)
-
- line = RadioSetting("dtmf_settings.minspection",
- "Master Inspection",
- RadioSettingValueBoolean(
- _mem.dtmf_settings.minspection))
- dtmf_dec_settings.append(line)
-
- line = RadioSetting("dtmf_settings.mmonitor",
- "Master Monitor",
- RadioSettingValueBoolean(
- _mem.dtmf_settings.mmonitor))
- dtmf_dec_settings.append(line)
-
- line = RadioSetting("dtmf_settings.mstun",
- "Master Stun",
- RadioSettingValueBoolean(
- _mem.dtmf_settings.mstun))
- dtmf_dec_settings.append(line)
-
- line = RadioSetting("dtmf_settings.mkill",
- "Master Kill",
- RadioSettingValueBoolean(
- _mem.dtmf_settings.mkill))
- dtmf_dec_settings.append(line)
-
- line = RadioSetting("dtmf_settings.mrevive",
- "Master Revive",
- RadioSettingValueBoolean(
- _mem.dtmf_settings.mrevive))
- dtmf_dec_settings.append(line)
-
- val = RadioSettingValueString(0, 16,
- memory2string(
- _mem.dtmf_settings.viceid),
- False, CHARSET_DTMF_DIGITS)
- line = RadioSetting("dtmf_settings.viceid",
- "Vice Control ID ", val)
- line.set_apply_callback(apply_dmtf_frame,
- _mem.dtmf_settings.viceid)
- dtmf_dec_settings.append(line)
-
- line = RadioSetting("dtmf_settings.vinspection",
- "Vice Inspection",
- RadioSettingValueBoolean(
- _mem.dtmf_settings.vinspection))
- dtmf_dec_settings.append(line)
-
- line = RadioSetting("dtmf_settings.vmonitor",
- "Vice Monitor",
- RadioSettingValueBoolean(
- _mem.dtmf_settings.vmonitor))
- dtmf_dec_settings.append(line)
-
- line = RadioSetting("dtmf_settings.vstun",
- "Vice Stun",
- RadioSettingValueBoolean(
- _mem.dtmf_settings.vstun))
- dtmf_dec_settings.append(line)
-
- line = RadioSetting("dtmf_settings.vkill",
- "Vice Kill",
- RadioSettingValueBoolean(
- _mem.dtmf_settings.vkill))
- dtmf_dec_settings.append(line)
-
- line = RadioSetting("dtmf_settings.vrevive",
- "Vice Revive",
- RadioSettingValueBoolean(
- _mem.dtmf_settings.vrevive))
- dtmf_dec_settings.append(line)
-
- val = RadioSettingValueString(0, 16,
- memory2string(
- _mem.dtmf_settings.inspection),
- False, CHARSET_DTMF_DIGITS)
- line = RadioSetting("dtmf_settings.inspection",
- "Inspection", val)
- line.set_apply_callback(apply_dmtf_frame,
- _mem.dtmf_settings.inspection)
- dtmf_dec_settings.append(line)
-
- val = RadioSettingValueString(0, 16,
- memory2string(
- _mem.dtmf_settings.alarmcode),
- False, CHARSET_DTMF_DIGITS)
- line = RadioSetting("dtmf_settings.alarmcode",
- "Alarm", val)
- line.set_apply_callback(apply_dmtf_frame,
- _mem.dtmf_settings.alarmcode)
- dtmf_dec_settings.append(line)
-
- val = RadioSettingValueString(0, 16,
- memory2string(
- _mem.dtmf_settings.kill),
- False, CHARSET_DTMF_DIGITS)
- line = RadioSetting("dtmf_settings.kill",
- "Kill", val)
- line.set_apply_callback(apply_dmtf_frame,
- _mem.dtmf_settings.kill)
- dtmf_dec_settings.append(line)
-
- val = RadioSettingValueString(0, 16,
- memory2string(
- _mem.dtmf_settings.monitor),
- False, CHARSET_DTMF_DIGITS)
- line = RadioSetting("dtmf_settings.monitor",
- "Monitor", val)
- line.set_apply_callback(apply_dmtf_frame,
- _mem.dtmf_settings.monitor)
- dtmf_dec_settings.append(line)
-
- val = RadioSettingValueString(0, 16,
- memory2string(
- _mem.dtmf_settings.stun),
- False, CHARSET_DTMF_DIGITS)
- line = RadioSetting("dtmf_settings.stun",
- "Stun", val)
- line.set_apply_callback(apply_dmtf_frame,
- _mem.dtmf_settings.stun)
- dtmf_dec_settings.append(line)
-
- val = RadioSettingValueString(0, 16,
- memory2string(
- _mem.dtmf_settings.revive),
- False, CHARSET_DTMF_DIGITS)
- line = RadioSetting("dtmf_settings.revive",
- "Revive", val)
- line.set_apply_callback(apply_dmtf_frame,
- _mem.dtmf_settings.revive)
- dtmf_dec_settings.append(line)
-
- def apply_dmtf_listvalue(setting, obj):
- LOG.debug("Setting value: "+ str(setting.value) + " from list")
- val = str(setting.value)
- index = LIST_DTMF_SPECIAL_DIGITS.index(val)
- val = LIST_DTMF_SPECIAL_VALUES[index]
- obj.set_value(val)
-
- idx = LIST_DTMF_SPECIAL_VALUES.index(_mem.dtmf_settings.groupcode)
- line = RadioSetting(
- "dtmf_settings.groupcode",
- "Group Code",
- RadioSettingValueList(LIST_DTMF_SPECIAL_DIGITS,
- LIST_DTMF_SPECIAL_DIGITS[idx]))
- line.set_apply_callback(apply_dmtf_listvalue,
- _mem.dtmf_settings.groupcode)
- dtmf_dec_settings.append(line)
-
- idx = LIST_DTMF_SPECIAL_VALUES.index(_mem.dtmf_settings.spacecode)
- line = RadioSetting(
- "dtmf_settings.spacecode",
- "Space Code",
- RadioSettingValueList(LIST_DTMF_SPECIAL_DIGITS,
- LIST_DTMF_SPECIAL_DIGITS[idx]))
- line.set_apply_callback(apply_dmtf_listvalue,
- _mem.dtmf_settings.spacecode)
- dtmf_dec_settings.append(line)
-
- line = RadioSetting(
- "dtmf_settings.resettime",
- "Reset time",
- RadioSettingValueList(LIST_5TONE_RESET,
- LIST_5TONE_RESET[
- _mem.dtmf_settings.resettime]))
- dtmf_dec_settings.append(line)
-
- line = RadioSetting(
- "dtmf_settings.delayproctime",
- "Delay processing time",
- RadioSettingValueList(LIST_DTMF_DELAY,
- LIST_DTMF_DELAY[
- _mem.dtmf_settings.delayproctime]))
- dtmf_dec_settings.append(line)
-
-
- # 5 Tone Settings
- stds_5tone = RadioSettingGroup ("stds_5tone", "Standards")
- codes_5tone = RadioSettingGroup ("codes_5tone", "Codes")
-
- group_5tone = RadioSettingGroup ("group_5tone", "5 Tone Settings")
- group_5tone.append(stds_5tone)
- group_5tone.append(codes_5tone)
-
- top.append(group_5tone)
-
- def apply_list_value(setting, obj):
- options = setting.value.get_options()
- obj.set_value ( options.index(str(setting.value)) )
-
- _5tone_standards = self._memobj._5tone_std_settings
- i = 0
- for standard in _5tone_standards:
- std_5tone = RadioSettingGroup ("std_5tone_" + str(i),
- LIST_5TONE_STANDARDS[i])
- stds_5tone.append(std_5tone)
-
- period = standard.period
- if period == 255:
- LOG.debug("Period for " + LIST_5TONE_STANDARDS[i] +
- " is not yet configured. Setting to 70ms.")
- period = 5
-
- if period <= len( LIST_5TONE_STANDARD_PERIODS ):
- line = RadioSetting(
- "_5tone_std_settings_" + str(i) + "_period",
- "Period (ms)", RadioSettingValueList
- (LIST_5TONE_STANDARD_PERIODS,
- LIST_5TONE_STANDARD_PERIODS[period]))
- line.set_apply_callback(apply_list_value, standard.period)
- std_5tone.append(line)
- else:
- LOG.debug("Invalid value for 5tone period! Disabling.")
-
- group_tone = standard.group_tone
- if group_tone == 255:
- LOG.debug("Group-Tone for " + LIST_5TONE_STANDARDS[i] +
- " is not yet configured. Setting to A.")
- group_tone = 10
-
- if group_tone <= len( LIST_5TONE_DIGITS ):
- line = RadioSetting(
- "_5tone_std_settings_" + str(i) + "_grouptone",
- "Group Tone",
- RadioSettingValueList(LIST_5TONE_DIGITS,
- LIST_5TONE_DIGITS[
- group_tone]))
- line.set_apply_callback(apply_list_value,
- standard.group_tone)
- std_5tone.append(line)
- else:
- LOG.debug("Invalid value for 5tone digit! Disabling.")
-
- repeat_tone = standard.repeat_tone
- if repeat_tone == 255:
- LOG.debug("Repeat-Tone for " + LIST_5TONE_STANDARDS[i] +
- " is not yet configured. Setting to E.")
- repeat_tone = 14
-
- if repeat_tone <= len( LIST_5TONE_DIGITS ):
- line = RadioSetting(
- "_5tone_std_settings_" + str(i) + "_repttone",
- "Repeat Tone",
- RadioSettingValueList(LIST_5TONE_DIGITS,
- LIST_5TONE_DIGITS[
- repeat_tone]))
- line.set_apply_callback(apply_list_value,
- standard.repeat_tone)
- std_5tone.append(line)
- else:
- LOG.debug("Invalid value for 5tone digit! Disabling.")
- i = i + 1
-
- def my_apply_5tonestdlist_value(setting, obj):
- if LIST_5TONE_STANDARDS.index(str(setting.value)) == 15:
- obj.set_value(0xFF)
- else:
- obj.set_value( LIST_5TONE_STANDARDS.
- index(str(setting.value)) )
-
- def apply_5tone_frame(setting, obj):
- LOG.debug("Setting 5 Tone: " + str(setting.value) )
- valstring = str(setting.value)
- if len(valstring) == 0:
- for i in range(0,5):
- obj[i] = 255
- else:
- validFrame = True
- for i in range(0,5):
- currentChar = valstring[i].upper()
- if currentChar in LIST_5TONE_DIGITS:
- obj[i] = LIST_5TONE_DIGITS.index(currentChar)
- else:
- validFrame = False
- LOG.debug("invalid char: " + str(currentChar))
- if not validFrame:
- LOG.debug("setting whole frame to FF" )
- for i in range(0,5):
- obj[i] = 255
-
- def validate_5tone_frame(value):
- if (len(str(value)) != 5) and (len(str(value)) != 0) :
- msg = ("5 Tone must have 5 digits or 0 digits")
- raise InvalidValueError(msg)
- for digit in str(value):
- if digit.upper() not in LIST_5TONE_DIGITS:
- msg = (str(digit) + " is not a valid digit for 5tones")
- raise InvalidValueError(msg)
- return value
-
- def frame2string(frame):
- frameString = ""
- for digit in frame:
- if digit != 255:
- frameString = frameString + LIST_5TONE_DIGITS[digit]
- return frameString
-
- _5tone_codes = self._memobj._5tone_codes
- i = 1
- for code in _5tone_codes:
- code_5tone = RadioSettingGroup ("code_5tone_" + str(i),
- "5 Tone code " + str(i))
- codes_5tone.append(code_5tone)
- if (code.standard == 255 ):
- currentVal = 15
- else:
- currentVal = code.standard
- line = RadioSetting("_5tone_code_" + str(i) + "_std",
- " Standard",
- RadioSettingValueList(LIST_5TONE_STANDARDS,
- LIST_5TONE_STANDARDS[
- currentVal]) )
- line.set_apply_callback(my_apply_5tonestdlist_value,
- code.standard)
- code_5tone.append(line)
-
- val = RadioSettingValueString(0, 6,
- frame2string(code.frame1), False)
- line = RadioSetting("_5tone_code_" + str(i) + "_frame1",
- " Frame 1", val)
- val.set_validate_callback(validate_5tone_frame)
- line.set_apply_callback(apply_5tone_frame, code.frame1)
- code_5tone.append(line)
-
- val = RadioSettingValueString(0, 6,
- frame2string(code.frame2), False)
- line = RadioSetting("_5tone_code_" + str(i) + "_frame2",
- " Frame 2", val)
- val.set_validate_callback(validate_5tone_frame)
- line.set_apply_callback(apply_5tone_frame, code.frame2)
- code_5tone.append(line)
-
- val = RadioSettingValueString(0, 6,
- frame2string(code.frame3), False)
- line = RadioSetting("_5tone_code_" + str(i) + "_frame3",
- " Frame 3", val)
- val.set_validate_callback(validate_5tone_frame)
- line.set_apply_callback(apply_5tone_frame, code.frame3)
- code_5tone.append(line)
- i = i + 1
-
- _5_tone_decode1 = RadioSetting(
- "_5tone_settings._5tone_decode_call_frame1",
- "5 Tone decode call Frame 1",
- RadioSettingValueBoolean(
- _mem._5tone_settings._5tone_decode_call_frame1))
- group_5tone.append(_5_tone_decode1)
-
- _5_tone_decode2 = RadioSetting(
- "_5tone_settings._5tone_decode_call_frame2",
- "5 Tone decode call Frame 2",
- RadioSettingValueBoolean(
- _mem._5tone_settings._5tone_decode_call_frame2))
- group_5tone.append(_5_tone_decode2)
-
- _5_tone_decode3 = RadioSetting(
- "_5tone_settings._5tone_decode_call_frame3",
- "5 Tone decode call Frame 3",
- RadioSettingValueBoolean(
- _mem._5tone_settings._5tone_decode_call_frame3))
- group_5tone.append(_5_tone_decode3)
-
- _5_tone_decode_disp1 = RadioSetting(
- "_5tone_settings._5tone_decode_disp_frame1",
- "5 Tone decode disp Frame 1",
- RadioSettingValueBoolean(
- _mem._5tone_settings._5tone_decode_disp_frame1))
- group_5tone.append(_5_tone_decode_disp1)
-
- _5_tone_decode_disp2 = RadioSetting(
- "_5tone_settings._5tone_decode_disp_frame2",
- "5 Tone decode disp Frame 2",
- RadioSettingValueBoolean(
- _mem._5tone_settings._5tone_decode_disp_frame2))
- group_5tone.append(_5_tone_decode_disp2)
-
- _5_tone_decode_disp3 = RadioSetting(
- "_5tone_settings._5tone_decode_disp_frame3",
- "5 Tone decode disp Frame 3",
- RadioSettingValueBoolean(
- _mem._5tone_settings._5tone_decode_disp_frame3))
- group_5tone.append(_5_tone_decode_disp3)
-
- decode_standard = _mem._5tone_settings.decode_standard
- if decode_standard == 255:
- decode_standard = 0
- if decode_standard <= len (LIST_5TONE_STANDARDS_without_none) :
- line = RadioSetting("_5tone_settings.decode_standard",
- "5 Tone-decode Standard",
- RadioSettingValueList(
- LIST_5TONE_STANDARDS_without_none,
- LIST_5TONE_STANDARDS_without_none[
- decode_standard]))
- group_5tone.append(line)
- else:
- LOG.debug("Invalid decode std...")
-
- _5tone_delay1 = _mem._5tone_settings._5tone_delay1
- if _5tone_delay1 == 255:
- _5tone_delay1 = 20
-
- if _5tone_delay1 <= len( LIST_5TONE_DELAY ):
- list = RadioSettingValueList(LIST_5TONE_DELAY,
- LIST_5TONE_DELAY[
- _5tone_delay1])
- line = RadioSetting("_5tone_settings._5tone_delay1",
- "5 Tone Delay Frame 1", list)
- group_5tone.append(line)
- else:
- LOG.debug("Invalid value for 5tone delay (frame1) ! Disabling.")
-
- _5tone_delay2 = _mem._5tone_settings._5tone_delay2
- if _5tone_delay2 == 255:
- _5tone_delay2 = 20
- LOG.debug("5 Tone delay unconfigured! Resetting to 200ms.")
-
- if _5tone_delay2 <= len( LIST_5TONE_DELAY ):
- list = RadioSettingValueList(LIST_5TONE_DELAY,
- LIST_5TONE_DELAY[
- _5tone_delay2])
- line = RadioSetting("_5tone_settings._5tone_delay2",
- "5 Tone Delay Frame 2", list)
- group_5tone.append(line)
- else:
- LOG.debug("Invalid value for 5tone delay (frame2)! Disabling.")
-
- _5tone_delay3 = _mem._5tone_settings._5tone_delay3
- if _5tone_delay3 == 255:
- _5tone_delay3 = 20
- LOG.debug("5 Tone delay unconfigured! Resetting to 200ms.")
-
- if _5tone_delay3 <= len( LIST_5TONE_DELAY ):
- list = RadioSettingValueList(LIST_5TONE_DELAY,
- LIST_5TONE_DELAY[
- _5tone_delay3])
- line = RadioSetting("_5tone_settings._5tone_delay3",
- "5 Tone Delay Frame 3", list )
- group_5tone.append(line)
- else:
- LOG.debug("Invalid value for 5tone delay (frame3)! Disabling.")
-
- ext_length = _mem._5tone_settings._5tone_first_digit_ext_length
- if ext_length == 255:
- ext_length = 0
- LOG.debug("1st Tone ext lenght unconfigured! Resetting to 0")
-
- if ext_length <= len(
- LIST_5TONE_DELAY ):
- list = RadioSettingValueList(
- LIST_5TONE_DELAY,
- LIST_5TONE_DELAY[
- ext_length])
- line = RadioSetting(
- "_5tone_settings._5tone_first_digit_ext_length",
- "First digit extend length", list)
- group_5tone.append(line)
- else:
- LOG.debug("Invalid value for 5tone ext length! Disabling.")
-
- decode_reset_time = _mem._5tone_settings.decode_reset_time
- if decode_reset_time == 255:
- decode_reset_time = 59
- LOG.debug("Decode reset time unconfigured. resetting.")
- if decode_reset_time <= len(LIST_5TONE_RESET):
- list = RadioSettingValueList(
- LIST_5TONE_RESET,
- LIST_5TONE_RESET[
- decode_reset_time])
- line = RadioSetting("_5tone_settings.decode_reset_time",
- "Decode reset time", list)
- group_5tone.append(line)
- else:
- LOG.debug("Invalid value decode reset time! Disabling.")
-
- # 2 Tone
- encode_2tone = RadioSettingGroup ("encode_2tone", "2 Tone Encode")
- decode_2tone = RadioSettingGroup ("decode_2tone", "2 Code Decode")
-
- top.append(encode_2tone)
- top.append(decode_2tone)
-
- duration_1st_tone = self._memobj._2tone.duration_1st_tone
- if duration_1st_tone == 255:
- LOG.debug("Duration of first 2 Tone digit is not yet " +
- "configured. Setting to 600ms")
- duration_1st_tone = 60
-
- if duration_1st_tone <= len( LIST_5TONE_DELAY ):
- line = RadioSetting("_2tone.duration_1st_tone",
- "Duration 1st Tone",
- RadioSettingValueList(LIST_5TONE_DELAY,
- LIST_5TONE_DELAY[
- duration_1st_tone]))
- encode_2tone.append(line)
-
- duration_2nd_tone = self._memobj._2tone.duration_2nd_tone
- if duration_2nd_tone == 255:
- LOG.debug("Duration of second 2 Tone digit is not yet " +
- "configured. Setting to 600ms")
- duration_2nd_tone = 60
-
- if duration_2nd_tone <= len( LIST_5TONE_DELAY ):
- line = RadioSetting("_2tone.duration_2nd_tone",
- "Duration 2nd Tone",
- RadioSettingValueList(LIST_5TONE_DELAY,
- LIST_5TONE_DELAY[
- duration_2nd_tone]))
- encode_2tone.append(line)
-
- duration_gap = self._memobj._2tone.duration_gap
- if duration_gap == 255:
- LOG.debug("Duration of gap is not yet " +
- "configured. Setting to 300ms")
- duration_gap = 30
-
- if duration_gap <= len( LIST_5TONE_DELAY ):
- line = RadioSetting("_2tone.duration_gap", "Duration of gap",
- RadioSettingValueList(LIST_5TONE_DELAY,
- LIST_5TONE_DELAY[
- duration_gap]))
- encode_2tone.append(line)
-
- def _2tone_validate(value):
- if value == 0:
- return 65535
- if value == 65535:
- return value
- if not (300 <= value and value <= 3000):
- msg = ("2 Tone Frequency: Must be between 300 and 3000 Hz")
- raise InvalidValueError(msg)
- return value
-
- def apply_2tone_freq(setting, obj):
- val = int(setting.value)
- if (val == 0) or (val == 65535):
- obj.set_value(65535)
- else:
- obj.set_value(val)
-
- i = 1
- for code in self._memobj._2tone._2tone_encode:
- code_2tone = RadioSettingGroup ("code_2tone_" + str(i),
- "Encode Code " + str(i))
- encode_2tone.append(code_2tone)
-
- tmp = code.freq1
- if tmp == 65535:
- tmp = 0
- val1 = RadioSettingValueInteger(0, 65535, tmp)
- freq1 = RadioSetting("2tone_code_"+ str(i) + "_freq1",
- "Frequency 1", val1)
- val1.set_validate_callback(_2tone_validate)
- freq1.set_apply_callback(apply_2tone_freq, code.freq1)
- code_2tone.append(freq1)
-
- tmp = code.freq2
- if tmp == 65535:
- tmp = 0
- val2 = RadioSettingValueInteger(0, 65535, tmp)
- freq2 = RadioSetting("2tone_code_"+ str(i) + "_freq2",
- "Frequency 2", val2)
- val2.set_validate_callback(_2tone_validate)
- freq2.set_apply_callback(apply_2tone_freq, code.freq2)
- code_2tone.append(freq2)
-
- i = i + 1
-
- decode_reset_time = _mem._2tone.reset_time
- if decode_reset_time == 255:
- decode_reset_time = 59
- LOG.debug("Decode reset time unconfigured. resetting.")
- if decode_reset_time <= len(LIST_5TONE_RESET):
- list = RadioSettingValueList(
- LIST_5TONE_RESET,
- LIST_5TONE_RESET[
- decode_reset_time])
- line = RadioSetting("_2tone.reset_time",
- "Decode reset time", list)
- decode_2tone.append(line)
- else:
- LOG.debug("Invalid value decode reset time! Disabling.")
-
- def apply_2tone_freq_pair(setting, obj):
- val = int(setting.value)
- derived_val = 65535
- frqname = str(setting._name[-5:])
- derivedname = "derived_from_" + frqname
-
- if (val == 0):
- val = 65535
- derived_val = 65535
- else:
- derived_val = int(round(2304000.0/val))
-
- obj[frqname].set_value( val )
- obj[derivedname].set_value( derived_val )
-
- LOG.debug("Apply " + frqname + ": " + str(val) + " | "
- + derivedname + ": " + str(derived_val))
-
- i = 1
- for decode_code in self._memobj._2tone._2tone_decode:
- _2tone_dec_code = RadioSettingGroup ("code_2tone_" + str(i),
- "Decode Code " + str(i))
- decode_2tone.append(_2tone_dec_code)
-
- j = 1
- for dec in decode_code.decs:
- val = dec.dec
- if val == 255:
- LOG.debug("Dec for Code " + str(i) + " Dec " + str(j) +
- " is not yet configured. Setting to 0.")
- val = 0
-
- if val <= len( LIST_2TONE_DEC ):
- line = RadioSetting(
- "_2tone_dec_settings_" + str(i) + "_dec_" + str(j),
- "Dec " + str(j), RadioSettingValueList
- (LIST_2TONE_DEC,
- LIST_2TONE_DEC[val]))
- line.set_apply_callback(apply_list_value, dec.dec)
- _2tone_dec_code.append(line)
- else:
- LOG.debug("Invalid value for 2tone dec! Disabling.")
-
- val = dec.response
- if val == 255:
- LOG.debug("Response for Code " + str(i) + " Dec " + str(j)+
- " is not yet configured. Setting to 0.")
- val = 0
-
- if val <= len( LIST_2TONE_RESPONSE ):
- line = RadioSetting(
- "_2tone_dec_settings_" + str(i) + "_resp_" + str(j),
- "Response " + str(j), RadioSettingValueList
- (LIST_2TONE_RESPONSE,
- LIST_2TONE_RESPONSE[val]))
- line.set_apply_callback(apply_list_value, dec.response)
- _2tone_dec_code.append(line)
- else:
- LOG.debug("Invalid value for 2tone response! Disabling.")
-
- val = dec.alert
- if val == 255:
- LOG.debug("Alert for Code " + str(i) + " Dec " + str(j) +
- " is not yet configured. Setting to 0.")
- val = 0
-
- if val <= len( PTTIDCODE_LIST ):
- line = RadioSetting(
- "_2tone_dec_settings_" + str(i) + "_alert_" + str(j),
- "Alert " + str(j), RadioSettingValueList
- (PTTIDCODE_LIST,
- PTTIDCODE_LIST[val]))
- line.set_apply_callback(apply_list_value, dec.alert)
- _2tone_dec_code.append(line)
- else:
- LOG.debug("Invalid value for 2tone alert! Disabling.")
- j = j + 1
-
- freq = self._memobj._2tone.freqs[i-1]
- for char in ['A', 'B', 'C', 'D']:
- setting_name = "freq" + str(char)
-
- tmp = freq[setting_name]
- if tmp == 65535:
- tmp = 0
- if tmp != 0:
- expected = int(round(2304000.0/tmp))
- from_mem = freq["derived_from_" + setting_name]
- if expected != from_mem:
- LOG.error("Expected " + str(expected) +
- " but read " + str(from_mem ) +
- ". Disabling 2Tone Decode Freqs!")
- break
- val = RadioSettingValueInteger(0, 65535, tmp)
- frq = RadioSetting("2tone_dec_"+ str(i) + "_freq" + str(char),
- ("Decode Frequency " +str(char)), val)
- val.set_validate_callback(_2tone_validate)
- frq.set_apply_callback(apply_2tone_freq_pair, freq)
- _2tone_dec_code.append(frq)
-
- i = i + 1
-
- return top
-
- def set_settings(self, settings):
- _settings = self._memobj.settings
- for element in settings:
- if not isinstance(element, RadioSetting):
- if element.get_name() == "fm_preset":
- self._set_fm_preset(element)
- else:
- self.set_settings(element)
- continue
- else:
- try:
- name = element.get_name()
- if "." in name:
- bits = name.split(".")
- obj = self._memobj
- for bit in bits[:-1]:
- if "/" in bit:
- bit, index = bit.split("/", 1)
- index = int(index)
- obj = getattr(obj, bit)[index]
- else:
- obj = getattr(obj, bit)
- setting = bits[-1]
- else:
- obj = _settings
- setting = element.get_name()
-
- if element.has_apply_callback():
- LOG.debug("Using apply callback")
- element.run_apply_callback()
- 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) == MEM_SIZE:
- match_size = True
-
- # testing the firmware model fingerprint
- match_model = model_match(cls, filedata)
-
- if match_size and match_model:
- return True
- else:
- return False
-
# Declaring Aliases (Clones of the real radios)
class JT2705M(chirp_common.Alias):
@@ -2630,6 +3109,7 @@
class UV2501_220(BTech):
"""Baofeng Tech UV2501+220"""
MODEL = "UV-2501+220"
+ BANDS = 3
_magic = MSTRING_220
_id2 = UV2501_220pp_id
_fileid = [UV2501_220G3_fp,
@@ -2647,6 +3127,8 @@
UV5001G2_fp,
UV5001alpha_fp,
UV5001pp_fp]
+ _power_levels = [chirp_common.PowerLevel("High", watts=50),
+ chirp_common.PowerLevel("Low", watts=10)]
@directory.register
@@ -2698,6 +3180,7 @@
"""QYT KT8900R"""
VENDOR = "QYT"
MODEL = "KT8900R"
+ BANDS = 3
_vhf_range = (136000000, 175000000)
_220_range = (240000000, 271000000)
_uhf_range = (400000000, 481000000)
@@ -2720,3 +3203,377 @@
_magic = MSTRING_KT8900
_fileid = [LT588UV_fp,
LT588UV_fp1]
+ _power_levels = [chirp_common.PowerLevel("High", watts=60),
+ chirp_common.PowerLevel("Low", watts=10)]
+
+
+COLOR_MEM_FORMAT = """
+#seekto 0x0000;
+struct {
+ lbcd rxfreq[4];
+ lbcd txfreq[4];
+ ul16 rxtone;
+ ul16 txtone;
+ u8 unknown0:4,
+ scode:4;
+ u8 unknown1:2,
+ spmute:1,
+ unknown2:3,
+ optsig:2;
+ u8 unknown3:3,
+ scramble:1,
+ unknown4:3,
+ power:1;
+ u8 unknown5:1,
+ wide:1,
+ unknown6:2,
+ bcl:1,
+ add:1,
+ pttid:2;
+} memory[200];
+
+#seekto 0x0E00;
+struct {
+ u8 tmr;
+ u8 unknown1;
+ u8 sql;
+ u8 unknown2[2];
+ u8 tot;
+ u8 apo;
+ u8 unknown3;
+ u8 abr;
+ u8 beep;
+ u8 unknown4[4];
+ u8 dtmfst;
+ u8 unknown5[2];
+ u8 screv;
+ u8 unknown6[2];
+ u8 pttid;
+ u8 pttlt;
+ u8 unknown7;
+ u8 emctp;
+ u8 emcch;
+ u8 sigbp;
+ u8 unknown8;
+ u8 camdf;
+ u8 cbmdf;
+ u8 ccmdf;
+ u8 cdmdf;
+ u8 langua;
+ u8 sync; // BTech radios use this as the display sync
+ // setting, other radios use this as the auto
+ // keypad lock setting
+ u8 mainfc;
+ u8 mainbc;
+ u8 menufc;
+ u8 menubc;
+ u8 stafc;
+ u8 stabc;
+ u8 sigfc;
+ u8 sigbc;
+ u8 rxfc;
+ u8 txfc;
+ u8 txdisp;
+ u8 unknown9[5];
+ u8 anil;
+ u8 reps;
+ u8 repm;
+ u8 tmrmr;
+ u8 ste;
+ u8 rpste;
+ u8 rptdl;
+ u8 dtmfg;
+ u8 mgain;
+ u8 skiptx;
+ u8 scmode;
+} settings;
+
+#seekto 0x0E80;
+struct {
+ u8 unknown1;
+ u8 vfomr;
+ u8 keylock;
+ u8 unknown2;
+ u8 unknown3:4,
+ vfomren:1,
+ unknown4:1,
+ reseten:1,
+ menuen:1;
+ u8 unknown5[11];
+ u8 dispab;
+ u8 unknown6[2];
+ u8 menu;
+ u8 unknown7[7];
+ u8 vfomra;
+ u8 vfomrb;
+ u8 vfomrc;
+ u8 vfomrd;
+ u8 mrcha;
+ u8 mrchb;
+ u8 mrchc;
+ u8 mrchd;
+} settings2;
+
+struct settings_vfo {
+ u8 freq[8];
+ u8 offset[6];
+ u8 unknown2[2];
+ ul16 rxtone;
+ ul16 txtone;
+ u8 scode;
+ u8 spmute;
+ u8 optsig;
+ u8 scramble;
+ u8 wide;
+ u8 power;
+ u8 shiftd;
+ u8 step;
+ u8 unknown3[4];
+};
+
+#seekto 0x0F00;
+struct {
+ struct settings_vfo a;
+ struct settings_vfo b;
+ struct settings_vfo c;
+ struct settings_vfo d;
+} vfo;
+
+#seekto 0x0F80;
+struct {
+ char line1[8];
+ char line2[8];
+ char line3[8];
+ char line4[8];
+ char line5[8];
+ char line6[8];
+ char line7[8];
+ char line8[8];
+} poweron_msg;
+
+#seekto 0x1000;
+struct {
+ char name[8];
+ u8 unknown1[8];
+} names[200];
+
+#seekto 0x2400;
+struct {
+ u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
+ u8 group_tone;
+ u8 repeat_tone;
+ u8 unused[13];
+} _5tone_std_settings[15];
+
+#seekto 0x2500;
+struct {
+ u8 frame1[5];
+ u8 frame2[5];
+ u8 frame3[5];
+ u8 standard; // one out of LIST_5TONE_STANDARDS
+} _5tone_codes[15];
+
+#seekto 0x25F0;
+struct {
+ u8 _5tone_delay1; // * 10ms
+ u8 _5tone_delay2; // * 10ms
+ u8 _5tone_delay3; // * 10ms
+ u8 _5tone_first_digit_ext_length;
+ u8 unknown1;
+ u8 unknown2;
+ u8 unknown3;
+ u8 unknown4;
+ u8 decode_standard;
+ u8 unknown5:5,
+ _5tone_decode_call_frame3:1,
+ _5tone_decode_call_frame2:1,
+ _5tone_decode_call_frame1:1;
+ u8 unknown6:5,
+ _5tone_decode_disp_frame3:1,
+ _5tone_decode_disp_frame2:1,
+ _5tone_decode_disp_frame1:1;
+ u8 decode_reset_time; // * 100 + 100ms
+} _5tone_settings;
+
+#seekto 0x2900;
+struct {
+ u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
+} dtmf_codes[15];
+
+#seekto 0x29F0;
+struct {
+ u8 dtmfspeed_on; //list with 50..2000ms in steps of 10
+ u8 dtmfspeed_off; //list with 50..2000ms in steps of 10
+ u8 unknown0[14];
+ u8 inspection[16];
+ u8 monitor[16];
+ u8 alarmcode[16];
+ u8 stun[16];
+ u8 kill[16];
+ u8 revive[16];
+ u8 unknown1[16];
+ u8 unknown2[16];
+ u8 unknown3[16];
+ u8 unknown4[16];
+ u8 unknown5[16];
+ u8 unknown6[16];
+ u8 unknown7[16];
+ u8 masterid[16];
+ u8 viceid[16];
+ u8 unused01:7,
+ mastervice:1;
+ u8 unused02:3,
+ mrevive:1,
+ mkill:1,
+ mstun:1,
+ mmonitor:1,
+ minspection:1;
+ u8 unused03:3,
+ vrevive:1,
+ vkill:1,
+ vstun:1,
+ vmonitor:1,
+ vinspection:1;
+ u8 unused04:6,
+ txdisable:1,
+ rxdisable:1;
+ u8 groupcode;
+ u8 spacecode;
+ u8 delayproctime; // * 100 + 100ms
+ u8 resettime; // * 100 + 100ms
+} dtmf_settings;
+
+#seekto 0x2D00;
+struct {
+ struct {
+ ul16 freq1;
+ u8 unused01[6];
+ ul16 freq2;
+ u8 unused02[6];
+ } _2tone_encode[15];
+ u8 duration_1st_tone; // *10ms
+ u8 duration_2nd_tone; // *10ms
+ u8 duration_gap; // *10ms
+ u8 unused03[13];
+ struct {
+ struct {
+ u8 dec; // one out of LIST_2TONE_DEC
+ u8 response; // one out of LIST_2TONE_RESPONSE
+ u8 alert; // 1-16
+ } decs[4];
+ u8 unused04[4];
+ } _2tone_decode[15];
+ u8 unused05[16];
+
+ struct {
+ ul16 freqA;
+ ul16 freqB;
+ ul16 freqC;
+ ul16 freqD;
+ // unknown what those values mean, but they are
+ // derived from configured frequencies
+ ul16 derived_from_freqA; // 2304000/freqA
+ ul16 derived_from_freqB; // 2304000/freqB
+ ul16 derived_from_freqC; // 2304000/freqC
+ ul16 derived_from_freqD; // 2304000/freqD
+ }freqs[15];
+ u8 reset_time; // * 100 + 100ms - 100-8000ms
+} _2tone;
+
+#seekto 0x3D80;
+struct {
+ u8 vhf_low[3];
+ u8 vhf_high[3];
+ u8 unknown1[4];
+ u8 unknown2[6];
+ u8 vhf2_low[3];
+ u8 vhf2_high[3];
+ u8 unknown3[4];
+ u8 unknown4[6];
+ u8 uhf_low[3];
+ u8 uhf_high[3];
+ u8 unknown5[4];
+ u8 unknown6[6];
+ u8 uhf2_low[3];
+ u8 uhf2_high[3];
+} ranges;
+
+#seekto 0x3F70;
+struct {
+ char fp[6];
+} fingerprint;
+
+"""
+
+
+class BTechColor(BTechMobileCommon):
+ """BTECH's Color LCD Mobile and alike radios"""
+ COLOR_LCD = True
+ NAME_LENGTH = 8
+
+ def process_mmap(self):
+ """Process the mem map into the mem object"""
+
+ # Get it
+ self._memobj = bitwise.parse(COLOR_MEM_FORMAT, self._mmap)
+
+ # load specific parameters from the radio image
+ self.set_options()
+
+ def set_options(self):
+ """This is to read the options from the image and set it in the
+ environment, for now just the limits of the freqs in the VHF/UHF
+ ranges"""
+
+ # setting the correct ranges for each radio type
+ ranges = self._memobj.ranges
+
+ # the normal dual bands
+ vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
+ uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
+
+ # DEBUG
+ LOG.info("Radio ranges: VHF %d to %d" % vhf)
+ LOG.info("Radio ranges: UHF %d to %d" % uhf)
+
+ # the additional bands
+ if self.MODEL in ["KT8900D", ]:
+ # 200Mhz band
+ vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high)
+ LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2)
+ self._220_range = vhf2
+
+ # 350Mhz band
+ uhf2 = _decode_ranges(ranges.uhf2_low, ranges.uhf2_high)
+ LOG.info("Radio ranges: UHF(350) %d to %d" % uhf2)
+ self._350_range = uhf2
+
+ # set the class with the real data
+ self._vhf_range = vhf
+ self._uhf_range = uhf
+
+
+ at directory.register
+class KT7900D(BTechColor):
+ """QYT KT7900D"""
+ VENDOR = "QYT"
+ MODEL = "KT7900D"
+ BANDS = 4
+ _vhf_range = (136000000, 175000000)
+ _220_range = (200000000, 271000000)
+ _uhf_range = (400000000, 481000000)
+ _350_range = (350000000, 371000000)
+ _magic = MSTRING_KT8900D
+ _fileid = [KT7900D_fp, ]
+
+
+ at directory.register
+class KT8900D(BTechColor):
+ """QYT KT8900D"""
+ VENDOR = "QYT"
+ MODEL = "KT8900D"
+ BANDS = 2
+ _vhf_range = (136000000, 175000000)
+ _uhf_range = (400000000, 481000000)
+ _magic = MSTRING_KT8900D
+ _fileid = [KT8900D_fp, ]
More information about the chirp_devel
mailing list