[chirp_devel] [PATCH] [New Model] Support for the BTECH Mobile Radios, fixes issue #3015
# HG changeset patch # User Jim Unroe rock.unroe@gmail.com # Date 1458694045 14400 # Node ID e6f8013627457dba4eaa5dac4a026d2a455a6e3d # Parent 86fcbefbcf670ca1ace59c34262550c5cae4a792 [New Model] Support for the BTECH Mobile Radios, fixes issue #3015
This patch adds "basic support" for the the following radios:
BTECH UV-5001, UV-2501 and UV-2501+220 WACCOM MINI-8900 Plus
"Basic support" is a complete implementaton of the per-channel settings, including:
Speaker mute Scramble Busy channel lockout PTT ID PTT ID signal code Optional signaling
also related to #2673
diff -r 86fcbefbcf67 -r e6f801362745 chirp/drivers/btech.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chirp/drivers/btech.py Tue Mar 22 20:47:25 2016 -0400 @@ -0,0 +1,1009 @@ +# Copyright 2016: +# * Pavel Milanes CO7WT, co7wt@frcuba.co.cu pavelmc@gmail.com +# * Jim Unroe KC9HI, rock.unroe@gmail.com +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. + +import time +import struct +import logging + +LOG = logging.getLogger(__name__) + +from chirp import chirp_common, directory, memmap +from chirp import bitwise, errors, util +from chirp.settings import RadioSettingGroup, RadioSetting, \ + RadioSettingValueBoolean, RadioSettingValueList, \ + RadioSettingValueString, RadioSettingValueInteger, \ + RadioSettings +from textwrap import dedent + +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 0x1000; +struct { + char name[6]; + u8 unknown1[10]; +} names[200]; + +#seekto 0x3C90; +struct { + u8 vhf_low[3]; + u8 vhf_high[3]; + u8 uhf_low[3]; + u8 uhf_high[3]; +} ranges; + +// the 2501+220 has a different zone for storing ranges + +#seekto 0x3CD0; +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]; +} ranges220; + +""" + +# 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"] + +# 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+220 pre-production units +UV2501_220pp_fp = "M3C281" +# extra block read for the 2501+220 pre-production units +UV2501_220pp_id = " 280528" +# B-TECH UV-2501+220 +UV2501_220_fp = "M3G201" +# extra block read for the 2501+220 +# the extra block is the same as the pp unit + + +# 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 +# !!!! This is the same as the UV-2501 (2G) Radios !!!! +UV5001G2_fp = "BTG214" +# B-TECH UV-5001 second generation (2G2) +UV5001G22_fp = "V2G204" + + +# WACCOM Mini-8900 +MINI8900_fp = "M28854" + + +#### 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" +# magic string for all other models +MSTRING = "\x55\x20\x15\x09\x20\x45\x4d\x02" + + +def _rawrecv(radio, amount): + """Raw read from the radio device, new approach, this time a byte at + a time as the original driver, the receive data has to be atomic""" + data = "" + + try: + tdiff = 0 + start = time.time() + maxtime = amount * 0.020 + + while len(data) < amount and tdiff < maxtime: + d = radio.pipe.read(1) + if len(d) == 1: + data += d + + # Delta time + tdiff = time.time() - start + + # DEBUG + if debug is True: + LOG.debug("time diff %.04f maxtime %.04f, data: %d" % + (tdiff, maxtime, len(data))) + + # DEBUG + if debug is True: + LOG.debug("<== (%d) bytes:\n\n%s" % + (len(data), util.hexprint(data))) + + if len(data) < amount: + LOG.error("Short reading %d bytes from the %d requested." % + (len(data), amount)) + + except: + raise errors.RadioError("Error reading data from radio") + + return data + + +def _rawsend(radio, data): + """Raw send to the radio device""" + try: + for byte in data: + radio.pipe.write(byte) + time.sleep(0.003) + + # 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 _send(radio, frame, pause=0): + """Generic send data to the radio""" + _rawsend(radio, frame) + + # make a *optional* pause, to allow to build for an answer + if pause != 0: + time.sleep(pause) + + +def _recv(radio, addr): + """Get data from the radio """ + # 1 byte ACK + + # 4 bytes header + + # data of length of data (as I see always 0x40 = 64 bytes) + + # catching ack + ack = _rawrecv(radio, 1) + + # checking for a response + if len(ack) != 1: + msg = "No response in the read of the block #0x%04x" % addr + LOG.error(msg) + raise errors.RadioError(msg) + + # valid data + if ack != ACK_CMD: + msg = "Bad ack received from radio in block 0x%04x" % addr + LOG.error(msg) + LOG.debug("Bad ACK was 0x%02x" % ord(ack)) + raise errors.RadioError(msg) + + # Get the header + basic sanitize + hdr = _rawrecv(radio, 4) + if len(hdr) != 4: + msg = "Short header for block: 0x%04x" % addr + LOG.error(msg) + raise errors.RadioError(msg) + + # receive and validate the header + c, a, l = struct.unpack(">BHB", hdr) + if a != addr or l != BLOCK_SIZE or c != ord("X"): + msg = "Invalid answer for block 0x%04x:" % addr + LOG.error(msg) + LOG.debug("CMD: %s ADDR: %04x SIZE: %02x" % (c, a, l)) + raise errors.RadioError(msg) + + # Get the data + data = _rawrecv(radio, l) + + # basic validation + if len(data) != l: + msg = "Short block of data in block #0x%04x" % addr + LOG.error(msg) + raise errors.RadioError(msg) + + return data + + +def _do_magic(radio, status): + """Try to put the radio in program mode and get the ident string + it will make multiple tries""" + + # how many tries + tries = 5 + + # prep the data to show in the UI + status.cur = 0 + status.msg = "Identifying the radio..." + status.max = len(radio._magic) * tries + radio.status_fn(status) + mc = 0 + + try: + # do the magic + for magic in radio._magic: + # we try a few times + for a in range(0, tries): + # Update the UI + status.cur = (mc * tries) + a + radio.status_fn(status) + + # cleaning the serial buffer, try wrapped + try: + radio.pipe.flushInput() + except: + msg = "Error with a serial rx buffer flush at _do_magic" + LOG.error(msg) + raise errors.RadioError(msg) + + # send the magic a byte at a time + for byte in magic: + ack = _rawrecv(radio, 1) + _send(radio, byte) + + # A explicit time delay, with a longer one for the UV-5001 + if "5001" in radio.MODEL: + time.sleep(0.5) + else: + time.sleep(0.1) + + # Now you get a x06 of ACK if all goes well + ack = _rawrecv(radio, 1) + + if ack == "\x06": + # DEBUG + LOG.info("Magic ACK received") + status.msg = "Positive Ident!" + status.cur = status.max + radio.status_fn(status) + + return True + + # increment the count of magics to send, this is for the UI status + mc += 1 + + # wait between tries for different MAGICs to allow the radio to + # timeout, this is an experimental fature for the 5001 alpha that + # has the same ident as the MINI8900, raise it if it don't work + time.sleep(5) + + except errors.RadioError: + raise + except Exception, e: + msg = "Unknown error sending Magic to radio:\n%s" % e + raise errors.RadioError(msg) + + return False + + +def _do_ident(radio, status): + """Put the radio in PROGRAM mode & identify it""" + # set the serial discipline + radio.pipe.setBaudrate(9600) + radio.pipe.setParity("N") + radio.pipe.setTimeout(0.005) + # cleaning the serial buffer, try wrapped + try: + radio.pipe.flushInput() + except: + msg = "Error with a serial rx buffer flush at _do_ident" + LOG.error(msg) + raise errors.RadioError(msg) + + # do the magic trick + if _do_magic(radio, status) is False: + msg = "Radio did not respond to magic string, check your cable." + LOG.error(msg) + raise errors.RadioError(msg) + + # Ok, get the ident string + ident = _rawrecv(radio, 49) + + # basic check for the ident + if len(ident) != 49: + msg = "Radio send a sort ident block, you need to increase maxtime." + LOG.error(msg) + raise errors.RadioError(msg) + + # check if ident is OK + itis = False + for fp in radio._fileid: + if fp in ident: + itis = True + break + + if itis is False: + # bad ident + msg = "Incorrect model ID, got this:\n\n" + msg += util.hexprint(ident) + LOG.debug(msg) + raise errors.RadioError("Radio identification failed.") + + # DEBUG + LOG.info("Positive ident, this is a %s" % radio.MODEL) + + # Ok, we have a radio in the other end, we need a pause here + time.sleep(0.01) + + # the 2501+220 has one more check: + # reading the block 0x3DF0 to see if it's a code inside + if "+220" in radio.MODEL: + # DEBUG + LOG.debug("This is a BTECH UV-2501+220, requesting the extra ID") + # send the read request + _send(radio, _make_frame("S", 0x3DF0, 16), 0.04) + id2 = _rawrecv(radio, 20) + # WARNING !!!!!! + # Different versions send as 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: + msg = "The extra UV-2501+220 ID is short, aborting." + # DEBUG + LOG.error(msg) + raise errors.RadioError(msg) + + # ok, check for it, any of the correct ID must be in the received data + itis = False + for eid in radio._id2: + if eid in id2: + # DEBUG + LOG.info("Confirmed, this is a BTECH UV-2501+220") + # set the flag and exit + itis = True + break + + # It is a UV-2501+220? + if itis is False: + msg = "The extra UV-2501+220 ID is wrong, aborting." + # DEBUG + LOG.error(msg) + LOG.debug("Full extra ID on the 2501+220 is: \n%s" % + util.hexprint(id2)) + raise errors.RadioError(msg) + + 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 first dummy packet for all model but the 2501+220 + if not "+220" in radio.MODEL: + # In the logs we have found that the first block is discarded + # this is the \x05 in ack one, so we will simulate it here + _send(radio, _make_frame("S", 0, BLOCK_SIZE), 0.1) + discard = _rawrecv(radio, BLOCK_SIZE) + + if debug is True: + LOG.info("Dummy first block read done, got this:\n\n") + LOG.debug(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) + + data = "" + for addr in range(0, MEM_SIZE, BLOCK_SIZE): + # flush input, as per the original driver behavior, try wrapped + try: + radio.pipe.flushInput() + except: + msg = "Error with a serial rx buffer flush at _download" + LOG.error(msg) + raise errors.RadioError(msg) + + # sending the read request + _send(radio, _make_frame("S", addr, BLOCK_SIZE), 0.1) + + # 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) + + # 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 fun start here + for addr in range(0, MEM_SIZE, TX_BLOCK_SIZE): + # flush input, as per the original driver behavior, try wrapped + try: + radio.pipe.flushInput() + except: + msg = "Error with a serial rx buffer flush at _upload" + LOG.error(msg) + raise errors.RadioError(msg) + + # sending the data + d = data[addr:addr + TX_BLOCK_SIZE] + _send(radio, _make_frame("X", addr, TX_BLOCK_SIZE, d), 0.015) + + # receiving the response + ack = _rawrecv(radio, 1) + + # basic check + if len(ack) != 1: + msg = "No response in the write of block #0x%04x" % addr + LOG.error(msg) + raise errors.RadioError(msg) + + if not ack in "\x06\x05": + msg = "Bad ack writing block 0x%04x:" % addr + LOG.info(msg) + raise errors.RadioError(msg) + + # 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) + + +class btech(chirp_common.CloneModeRadio, chirp_common.ExperimentalRadio): + """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 = None + _fileid = None + + @classmethod + def get_prompts(cls): + rp = chirp_common.RadioPrompts() + rp.experimental = \ + ('This driver is experimental and for personal use only.\n' + '\n' + 'Please keep a copy of your memories with the original software ' + 'if you treasure them, this is the first release and may contain' + ' bugs.\n' + '\n' + 'You will miss the setting tab, we are working on it. Your ' + 'success/failure story is appreciated, visit the Chirp's ' + 'website and drop us a comment or just say THANKS if it works ' + 'for you.\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 = False + 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 + if self.MODEL == "UV-2501+220": + 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) + + 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 + if self.MODEL == "UV-2501+220": + # the model 2501+220 has a segment in 220 + # and a different position in the memmap + ranges = self._memobj.ranges220 + else: + 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) + + # 220Mhz case + if self.MODEL == "UV-2501+220": + vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high) + LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2) + self._220_range = vhf2 + + # set the class with the real data + self._vhf_range = vhf + self._uhf_range = uhf + + def process_mmap(self): + """Process the mem map into the mem object""" + + # Get it + self._memobj = bitwise.parse(MEM_FORMAT, self._mmap) + + # 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 offset > 70000000: # 70 Mhz + 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") + + spmute = RadioSetting("spmute", "Speaker mute", + RadioSettingValueBoolean(bool(_mem.spmute))) + mem.extra.append(spmute) + + 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) + + pttidcode = RadioSetting("scode", "PTT ID signal code", + RadioSettingValueList( + PTTIDCODE_LIST, + PTTIDCODE_LIST[_mem.scode])) + mem.extra.append(pttidcode) + + optsig = RadioSetting("optsig", "Optional signaling", + RadioSettingValueList( + OPTSIG_LIST, + OPTSIG_LIST[_mem.optsig])) + mem.extra.append(optsig) + + 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] + + # 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 + for setting in mem.extra: + setattr(_mem, setting.get_name(), setting.value) + else: + # there is no extra settings, load defaults + _mem.spmute = 0 + _mem.optsig = 0 + _mem.scramble = 0 + _mem.bcl = 0 + _mem.pttid = 0 + _mem.scode = 0 + + return mem + + @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 + + +# Note: +# the order in the lists in the _magic, IDENT and _fileid is important +# we put the most common units first, the policy is as follows: + +# - First latest (newer) units, as they will be the most common +# - Second the former latest version, and recursively... +# - At the end the pre-production units (pp) as this will be unique + +@directory.register +class UV2501(btech): + """Baofeng Tech UV2501""" + MODEL = "UV-2501" + _magic = [MSTRING, ] + _fileid = [UV2501G2_fp, UV2501pp2_fp, UV2501pp_fp] + + +@directory.register +class UV2501_220(btech): + """Baofeng Tech UV2501+220""" + MODEL = "UV-2501+220" + _magic = [MSTRING_220, ] + _fileid = [UV2501_220_fp, UV2501_220pp_fp] + _id2 = [UV2501_220pp_id, ] + + +@directory.register +class UV5001(btech): + """Baofeng Tech UV5001""" + MODEL = "UV-5001" + _magic = [MSTRING, MSTRING_MINI8900] + _fileid = [UV5001G22_fp, UV5001G2_fp, UV5001alpha_fp, UV5001pp_fp] + + +@directory.register +class MINI8900(btech): + """WACCOM MINI-8900""" + VENDOR = "WACCOM" + MODEL = "MINI-8900" + _magic = [MSTRING_MINI8900, ] + _fileid = [MINI8900_fp, ] diff -r 86fcbefbcf67 -r e6f801362745 tests/images/BTECH_UV-2501+220.img Binary file tests/images/BTECH_UV-2501+220.img has changed diff -r 86fcbefbcf67 -r e6f801362745 tests/images/BTECH_UV-5001.img Binary file tests/images/BTECH_UV-5001.img has changed diff -r 86fcbefbcf67 -r e6f801362745 tests/images/WACCOM_MINI-8900.img Binary file tests/images/WACCOM_MINI-8900.img has changed
Here are the images that go with this patch.
Jim
On Tue, Mar 22, 2016 at 8:48 PM, Jim Unroe rock.unroe@gmail.com wrote:
# HG changeset patch # User Jim Unroe rock.unroe@gmail.com # Date 1458694045 14400 # Node ID e6f8013627457dba4eaa5dac4a026d2a455a6e3d # Parent 86fcbefbcf670ca1ace59c34262550c5cae4a792 [New Model] Support for the BTECH Mobile Radios, fixes issue #3015
This patch adds "basic support" for the the following radios:
BTECH UV-5001, UV-2501 and UV-2501+220 WACCOM MINI-8900 Plus
"Basic support" is a complete implementaton of the per-channel settings, including:
Speaker mute Scramble Busy channel lockout PTT ID PTT ID signal code Optional signaling
also related to #2673
diff -r 86fcbefbcf67 -r e6f801362745 chirp/drivers/btech.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chirp/drivers/btech.py Tue Mar 22 20:47:25 2016 -0400 @@ -0,0 +1,1009 @@ +# Copyright 2016: +# * Pavel Milanes CO7WT, co7wt@frcuba.co.cu pavelmc@gmail.com +# * Jim Unroe KC9HI, rock.unroe@gmail.com +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/.
+import time +import struct +import logging
+LOG = logging.getLogger(__name__)
+from chirp import chirp_common, directory, memmap +from chirp import bitwise, errors, util +from chirp.settings import RadioSettingGroup, RadioSetting, \
- RadioSettingValueBoolean, RadioSettingValueList, \
- RadioSettingValueString, RadioSettingValueInteger, \
- RadioSettings
+from textwrap import dedent
+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 0x1000; +struct {
- char name[6];
- u8 unknown1[10];
+} names[200];
+#seekto 0x3C90; +struct {
- u8 vhf_low[3];
- u8 vhf_high[3];
- u8 uhf_low[3];
- u8 uhf_high[3];
+} ranges;
+// the 2501+220 has a different zone for storing ranges
+#seekto 0x3CD0; +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];
+} ranges220;
+"""
+# 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"]
+# 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+220 pre-production units +UV2501_220pp_fp = "M3C281" +# extra block read for the 2501+220 pre-production units +UV2501_220pp_id = " 280528" +# B-TECH UV-2501+220 +UV2501_220_fp = "M3G201" +# extra block read for the 2501+220 +# the extra block is the same as the pp unit
+# 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 +# !!!! This is the same as the UV-2501 (2G) Radios !!!! +UV5001G2_fp = "BTG214" +# B-TECH UV-5001 second generation (2G2) +UV5001G22_fp = "V2G204"
+# WACCOM Mini-8900 +MINI8900_fp = "M28854"
+#### 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" +# magic string for all other models +MSTRING = "\x55\x20\x15\x09\x20\x45\x4d\x02"
+def _rawrecv(radio, amount):
- """Raw read from the radio device, new approach, this time a byte at
- a time as the original driver, the receive data has to be atomic"""
- data = ""
- try:
tdiff = 0
start = time.time()
maxtime = amount * 0.020
while len(data) < amount and tdiff < maxtime:
d = radio.pipe.read(1)
if len(d) == 1:
data += d
# Delta time
tdiff = time.time() - start
# DEBUG
if debug is True:
LOG.debug("time diff %.04f maxtime %.04f, data: %d" %
(tdiff, maxtime, len(data)))
# DEBUG
if debug is True:
LOG.debug("<== (%d) bytes:\n\n%s" %
(len(data), util.hexprint(data)))
if len(data) < amount:
LOG.error("Short reading %d bytes from the %d requested." %
(len(data), amount))
- except:
raise errors.RadioError("Error reading data from radio")
- return data
+def _rawsend(radio, data):
- """Raw send to the radio device"""
- try:
for byte in data:
radio.pipe.write(byte)
time.sleep(0.003)
# 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 _send(radio, frame, pause=0):
- """Generic send data to the radio"""
- _rawsend(radio, frame)
- # make a *optional* pause, to allow to build for an answer
- if pause != 0:
time.sleep(pause)
+def _recv(radio, addr):
- """Get data from the radio """
- # 1 byte ACK +
- # 4 bytes header +
- # data of length of data (as I see always 0x40 = 64 bytes)
- # catching ack
- ack = _rawrecv(radio, 1)
- # checking for a response
- if len(ack) != 1:
msg = "No response in the read of the block #0x%04x" % addr
LOG.error(msg)
raise errors.RadioError(msg)
- # valid data
- if ack != ACK_CMD:
msg = "Bad ack received from radio in block 0x%04x" % addr
LOG.error(msg)
LOG.debug("Bad ACK was 0x%02x" % ord(ack))
raise errors.RadioError(msg)
- # Get the header + basic sanitize
- hdr = _rawrecv(radio, 4)
- if len(hdr) != 4:
msg = "Short header for block: 0x%04x" % addr
LOG.error(msg)
raise errors.RadioError(msg)
- # receive and validate the header
- c, a, l = struct.unpack(">BHB", hdr)
- if a != addr or l != BLOCK_SIZE or c != ord("X"):
msg = "Invalid answer for block 0x%04x:" % addr
LOG.error(msg)
LOG.debug("CMD: %s ADDR: %04x SIZE: %02x" % (c, a, l))
raise errors.RadioError(msg)
- # Get the data
- data = _rawrecv(radio, l)
- # basic validation
- if len(data) != l:
msg = "Short block of data in block #0x%04x" % addr
LOG.error(msg)
raise errors.RadioError(msg)
- return data
+def _do_magic(radio, status):
- """Try to put the radio in program mode and get the ident string
- it will make multiple tries"""
- # how many tries
- tries = 5
- # prep the data to show in the UI
- status.cur = 0
- status.msg = "Identifying the radio..."
- status.max = len(radio._magic) * tries
- radio.status_fn(status)
- mc = 0
- try:
# do the magic
for magic in radio._magic:
# we try a few times
for a in range(0, tries):
# Update the UI
status.cur = (mc * tries) + a
radio.status_fn(status)
# cleaning the serial buffer, try wrapped
try:
radio.pipe.flushInput()
except:
msg = "Error with a serial rx buffer flush at _do_magic"
LOG.error(msg)
raise errors.RadioError(msg)
# send the magic a byte at a time
for byte in magic:
ack = _rawrecv(radio, 1)
_send(radio, byte)
# A explicit time delay, with a longer one for the UV-5001
if "5001" in radio.MODEL:
time.sleep(0.5)
else:
time.sleep(0.1)
# Now you get a x06 of ACK if all goes well
ack = _rawrecv(radio, 1)
if ack == "\x06":
# DEBUG
LOG.info("Magic ACK received")
status.msg = "Positive Ident!"
status.cur = status.max
radio.status_fn(status)
return True
# increment the count of magics to send, this is for the UI status
mc += 1
# wait between tries for different MAGICs to allow the radio to
# timeout, this is an experimental fature for the 5001 alpha that
# has the same ident as the MINI8900, raise it if it don't work
time.sleep(5)
- except errors.RadioError:
raise
- except Exception, e:
msg = "Unknown error sending Magic to radio:\n%s" % e
raise errors.RadioError(msg)
- return False
+def _do_ident(radio, status):
- """Put the radio in PROGRAM mode & identify it"""
- # set the serial discipline
- radio.pipe.setBaudrate(9600)
- radio.pipe.setParity("N")
- radio.pipe.setTimeout(0.005)
- # cleaning the serial buffer, try wrapped
- try:
radio.pipe.flushInput()
- except:
msg = "Error with a serial rx buffer flush at _do_ident"
LOG.error(msg)
raise errors.RadioError(msg)
- # do the magic trick
- if _do_magic(radio, status) is False:
msg = "Radio did not respond to magic string, check your cable."
LOG.error(msg)
raise errors.RadioError(msg)
- # Ok, get the ident string
- ident = _rawrecv(radio, 49)
- # basic check for the ident
- if len(ident) != 49:
msg = "Radio send a sort ident block, you need to increase maxtime."
LOG.error(msg)
raise errors.RadioError(msg)
- # check if ident is OK
- itis = False
- for fp in radio._fileid:
if fp in ident:
itis = True
break
- if itis is False:
# bad ident
msg = "Incorrect model ID, got this:\n\n"
msg += util.hexprint(ident)
LOG.debug(msg)
raise errors.RadioError("Radio identification failed.")
- # DEBUG
- LOG.info("Positive ident, this is a %s" % radio.MODEL)
- # Ok, we have a radio in the other end, we need a pause here
- time.sleep(0.01)
- # the 2501+220 has one more check:
- # reading the block 0x3DF0 to see if it's a code inside
- if "+220" in radio.MODEL:
# DEBUG
LOG.debug("This is a BTECH UV-2501+220, requesting the extra ID")
# send the read request
_send(radio, _make_frame("S", 0x3DF0, 16), 0.04)
id2 = _rawrecv(radio, 20)
# WARNING !!!!!!
# Different versions send as 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:
msg = "The extra UV-2501+220 ID is short, aborting."
# DEBUG
LOG.error(msg)
raise errors.RadioError(msg)
# ok, check for it, any of the correct ID must be in the received data
itis = False
for eid in radio._id2:
if eid in id2:
# DEBUG
LOG.info("Confirmed, this is a BTECH UV-2501+220")
# set the flag and exit
itis = True
break
# It is a UV-2501+220?
if itis is False:
msg = "The extra UV-2501+220 ID is wrong, aborting."
# DEBUG
LOG.error(msg)
LOG.debug("Full extra ID on the 2501+220 is: \n%s" %
util.hexprint(id2))
raise errors.RadioError(msg)
- 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 first dummy packet for all model but the 2501+220
- if not "+220" in radio.MODEL:
# In the logs we have found that the first block is discarded
# this is the \x05 in ack one, so we will simulate it here
_send(radio, _make_frame("S", 0, BLOCK_SIZE), 0.1)
discard = _rawrecv(radio, BLOCK_SIZE)
if debug is True:
LOG.info("Dummy first block read done, got this:\n\n")
LOG.debug(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)
- data = ""
- for addr in range(0, MEM_SIZE, BLOCK_SIZE):
# flush input, as per the original driver behavior, try wrapped
try:
radio.pipe.flushInput()
except:
msg = "Error with a serial rx buffer flush at _download"
LOG.error(msg)
raise errors.RadioError(msg)
# sending the read request
_send(radio, _make_frame("S", addr, BLOCK_SIZE), 0.1)
# 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)
- # 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 fun start here
- for addr in range(0, MEM_SIZE, TX_BLOCK_SIZE):
# flush input, as per the original driver behavior, try wrapped
try:
radio.pipe.flushInput()
except:
msg = "Error with a serial rx buffer flush at _upload"
LOG.error(msg)
raise errors.RadioError(msg)
# sending the data
d = data[addr:addr + TX_BLOCK_SIZE]
_send(radio, _make_frame("X", addr, TX_BLOCK_SIZE, d), 0.015)
# receiving the response
ack = _rawrecv(radio, 1)
# basic check
if len(ack) != 1:
msg = "No response in the write of block #0x%04x" % addr
LOG.error(msg)
raise errors.RadioError(msg)
if not ack in "\x06\x05":
msg = "Bad ack writing block 0x%04x:" % addr
LOG.info(msg)
raise errors.RadioError(msg)
# 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)
+class btech(chirp_common.CloneModeRadio, chirp_common.ExperimentalRadio):
- """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 = None
- _fileid = None
- @classmethod
- def get_prompts(cls):
rp = chirp_common.RadioPrompts()
rp.experimental = \
('This driver is experimental and for personal use only.\n'
'\n'
'Please keep a copy of your memories with the original software '
'if you treasure them, this is the first release and may contain'
' bugs.\n'
'\n'
'You will miss the setting tab, we are working on it. Your '
'success/failure story is appreciated, visit the Chirp\'s '
'website and drop us a comment or just say THANKS if it works '
'for you.\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 = False
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
if self.MODEL == "UV-2501+220":
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)
- 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
if self.MODEL == "UV-2501+220":
# the model 2501+220 has a segment in 220
# and a different position in the memmap
ranges = self._memobj.ranges220
else:
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)
# 220Mhz case
if self.MODEL == "UV-2501+220":
vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high)
LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2)
self._220_range = vhf2
# set the class with the real data
self._vhf_range = vhf
self._uhf_range = uhf
- def process_mmap(self):
"""Process the mem map into the mem object"""
# Get it
self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
# 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 offset > 70000000: # 70 Mhz
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")
spmute = RadioSetting("spmute", "Speaker mute",
RadioSettingValueBoolean(bool(_mem.spmute)))
mem.extra.append(spmute)
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)
pttidcode = RadioSetting("scode", "PTT ID signal code",
RadioSettingValueList(
PTTIDCODE_LIST,
PTTIDCODE_LIST[_mem.scode]))
mem.extra.append(pttidcode)
optsig = RadioSetting("optsig", "Optional signaling",
RadioSettingValueList(
OPTSIG_LIST,
OPTSIG_LIST[_mem.optsig]))
mem.extra.append(optsig)
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]
# 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
for setting in mem.extra:
setattr(_mem, setting.get_name(), setting.value)
else:
# there is no extra settings, load defaults
_mem.spmute = 0
_mem.optsig = 0
_mem.scramble = 0
_mem.bcl = 0
_mem.pttid = 0
_mem.scode = 0
return mem
- @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
+# Note: +# the order in the lists in the _magic, IDENT and _fileid is important +# we put the most common units first, the policy is as follows:
+# - First latest (newer) units, as they will be the most common +# - Second the former latest version, and recursively... +# - At the end the pre-production units (pp) as this will be unique
+@directory.register +class UV2501(btech):
- """Baofeng Tech UV2501"""
- MODEL = "UV-2501"
- _magic = [MSTRING, ]
- _fileid = [UV2501G2_fp, UV2501pp2_fp, UV2501pp_fp]
+@directory.register +class UV2501_220(btech):
- """Baofeng Tech UV2501+220"""
- MODEL = "UV-2501+220"
- _magic = [MSTRING_220, ]
- _fileid = [UV2501_220_fp, UV2501_220pp_fp]
- _id2 = [UV2501_220pp_id, ]
+@directory.register +class UV5001(btech):
- """Baofeng Tech UV5001"""
- MODEL = "UV-5001"
- _magic = [MSTRING, MSTRING_MINI8900]
- _fileid = [UV5001G22_fp, UV5001G2_fp, UV5001alpha_fp, UV5001pp_fp]
+@directory.register +class MINI8900(btech):
- """WACCOM MINI-8900"""
- VENDOR = "WACCOM"
- MODEL = "MINI-8900"
- _magic = [MSTRING_MINI8900, ]
- _fileid = [MINI8900_fp, ]
diff -r 86fcbefbcf67 -r e6f801362745 tests/images/BTECH_UV-2501+220.img Binary file tests/images/BTECH_UV-2501+220.img has changed diff -r 86fcbefbcf67 -r e6f801362745 tests/images/BTECH_UV-5001.img Binary file tests/images/BTECH_UV-5001.img has changed diff -r 86fcbefbcf67 -r e6f801362745 tests/images/WACCOM_MINI-8900.img Binary file tests/images/WACCOM_MINI-8900.img has changed
Hi Jim and Pavel,
Thanks for your work on this, I know it's in high demand and has been a while coming. I have some concerns, detailed below:
+def _rawrecv(radio, amount):
- # Get the header + basic sanitize
- hdr = _rawrecv(radio, 4)
- if len(hdr) != 4:
msg = "Short header for block: 0x%04x" % addr
LOG.error(msg)
raise errors.RadioError(msg)
- # receive and validate the header
- c, a, l = struct.unpack(">BHB", hdr)
- if a != addr or l != BLOCK_SIZE or c != ord("X"):
msg = "Invalid answer for block 0x%04x:" % addr
LOG.error(msg)
LOG.debug("CMD: %s ADDR: %04x SIZE: %02x" % (c, a, l))
raise errors.RadioError(msg)
- # Get the data
- data = _rawrecv(radio, l)
This kind of fine-grained reading is hard on performance -- it would be better if you can read larger chunks of data, buffer it and split it up as you find frames.
+def _do_magic(radio, status):
- """Try to put the radio in program mode and get the ident string
- it will make multiple tries"""
I think maybe renaming this at some point would be good.
- # how many tries
- tries = 5
- # prep the data to show in the UI
- status.cur = 0
- status.msg = "Identifying the radio..."
- status.max = len(radio._magic) * tries
- radio.status_fn(status)
- mc = 0
- try:
# do the magic
for magic in radio._magic:
# we try a few times
for a in range(0, tries):
This is super deeply nested. I think this should be broken up into easier-to-understand pieces.
# Update the UI
status.cur = (mc * tries) + a
radio.status_fn(status)
# cleaning the serial buffer, try wrapped
try:
radio.pipe.flushInput()
except:
msg = "Error with a serial rx buffer flush at _do_magic"
LOG.error(msg)
raise errors.RadioError(msg)
# send the magic a byte at a time
for byte in magic:
ack = _rawrecv(radio, 1)
_send(radio, byte)
# A explicit time delay, with a longer one for the UV-5001
if "5001" in radio.MODEL:
time.sleep(0.5)
else:
time.sleep(0.1)
I don't think this makes sense. Sleeping before a read (especially of one byte) is kinda weird. Can't you just set your serial timeout to cover both of these cases and then read the byte, handling the timeout if it happens?
# wait between tries for different MAGICs to allow the radio to
# timeout, this is an experimental fature for the 5001 alpha that
# has the same ident as the MINI8900, raise it if it don't work
time.sleep(5)
Yikes, five seconds? I guess it seems like we should be able to avoid this with different model subclasses.
+def _do_ident(radio, status):
- """Put the radio in PROGRAM mode & identify it"""
- # set the serial discipline
- radio.pipe.setBaudrate(9600)
- radio.pipe.setParity("N")
- radio.pipe.setTimeout(0.005)
I feel like a timeout this low at 9600 baud is probably not very useful. The inter-byte timing of 9600N81 is about 1ms and your timeout here is 5ms. Other drivers that communicate at this (and faster) speeds are using timeouts on the order of 100-250ms.
Is there some detail you can offer here?
- # cleaning the serial buffer, try wrapped
- try:
radio.pipe.flushInput()
- except:
msg = "Error with a serial rx buffer flush at _do_ident"
LOG.error(msg)
This should be a LOG.exception() right? Presumably this means something very strange happened. Also, why do you think this is necessary?
- # basic check for the ident
- if len(ident) != 49:
msg = "Radio send a sort ident block, you need to increase maxtime."
This goes to the UI, right? What is maxtime? Does it refer to the value in _rawrecv()?
+class btech(chirp_common.CloneModeRadio, chirp_common.ExperimentalRadio):
This should be:
class BTech(...
or something. Starting a class name with a lowercase letter is not really in line with anyone's style.
- @classmethod
- def get_prompts(cls):
rp = chirp_common.RadioPrompts()
rp.experimental = \
('This driver is experimental and for personal use only.\n'
You can't make this requirement of the user based on the license. Please remove it.
'\n'
'Please keep a copy of your memories with the original software '
'if you treasure them, this is the first release and may contain'
' bugs.\n'
'\n'
'You will miss the setting tab, we are working on it. Your '
'success/failure story is appreciated, visit the Chirp\'s '
'website and drop us a comment or just say THANKS if it works '
'for you.\n'
This is probably a little too verbose for the experimental warning.
+# Note: +# the order in the lists in the _magic, IDENT and _fileid is important +# we put the most common units first, the policy is as follows:
+# - First latest (newer) units, as they will be the most common +# - Second the former latest version, and recursively... +# - At the end the pre-production units (pp) as this will be unique
This concerns me a bit. Is there any chance that the value you're using for identification is actually just a mutable value and we'll be chasing valid values forever?
I went ahead and applied this because I know that it's high-demand and that the Jetstream work will likely depend on this for collaboration. I've also put the test images into the tree.
I don't think this should go out to users with the experimental warning the way it is, so I will fix that up myself.
I hope that there will be some further iteration on the timing stuff, possibly with David's help from the Jetstream side.
Thanks!
--Dan
Hi Dan et al
See below
El 22/03/16 a las 21:48, Dan Smith via chirp_devel escribió:
Hi Jim and Pavel,
Thanks for your work on this, I know it's in high demand and has been a while coming. I have some concerns, detailed below:.
+def*_recv*(radio, amount):
- # Get the header + basic sanitize
- hdr = _rawrecv(radio, 4)
- if len(hdr) != 4:
msg = "Short header for block: 0x%04x" % addr
LOG.error(msg)
raise errors.RadioError(msg)
- # receive and validate the header
- c, a, l = struct.unpack(">BHB", hdr)
- if a != addr or l != BLOCK_SIZE or c != ord("X"):
msg = "Invalid answer for block 0x%04x:" % addr
LOG.error(msg)
LOG.debug("CMD: %s ADDR: %04x SIZE: %02x" % (c, a, l))
raise errors.RadioError(msg)
- # Get the data
- data = _rawrecv(radio, l)
This kind of fine-grained reading is hard on performance -- it would be better if you can read larger chunks of data, buffer it and split it up as you find frames.
Yes, good to know about the performance issue. Also how can I measure that performance impact to compare for future improvements?
I think you are mentioning the _recv() procedure instead the _rawrecv()
We originally wrote the driver that way but we found that the driver was pretty unstable and not because of the driver it self but the radios having and not consistent delays on the answer to PC commands.
Most of the time we got reads with not the amount of data we must have and the driver failing, the other alternative was to use big time.sleep() here and there and we have then a long delayed reads and uploads with occasional resets of the radio.
And that non consistent delays (pauses between write data from radio to PC, mostly) are worst once we got different variants of the *same* radios to test (pre-production, alphas, 1G, 2G) not to mention other models.
The only way we found to manage that was the _rawrecv() procedure that you see now with precise time.sleep() in some places calculated from a timing profile for every radios to found the sweet spot for all of them.
We developed yesterday a closed test with some users and that allow us to tweak more precisely the constant to form the maxtime var in the _rawrec procedure.
Thinking about it now, maybe we can write now the _recv() procedure with the "read big chunks" strategy, the key is the _rawrecv() procedure that will allow us to do that, but we have a time to test that on the field.
+def _do_magic(radio, status):
- """Try to put the radio in program mode and get the ident string
- it will make multiple tries"""
I think maybe renaming this at some point would be good.
From Aladdin story, this procedure manages the magic phrase to "open" the radios to programming mode.
Any suggestion?
- # how many tries
- tries = 5
- # prep the data to show in the UI
- status.cur = 0
- status.msg = "Identifying the radio..."
- status.max = len(radio._magic) * tries
- radio.status_fn(status)
- mc = 0
- try:
# do the magic
for magic in radio._magic:
# we try a few times
for a in range(0, tries):
This is super deeply nested. I think this should be broken up into easier-to-understand pieces.
Roger, I will do.
# Update the UI
status.cur = (mc * tries) + a
radio.status_fn(status)
# cleaning the serial buffer, try wrapped
try:
radio.pipe.flushInput()
except:
msg = "Error with a serial rx buffer flush at _do_magic"
LOG.error(msg)
raise errors.RadioError(msg)
# send the magic a byte at a time
for byte in magic:
ack = _rawrecv(radio, 1)
_send(radio, byte)
# A explicit time delay, with a longer one for the UV-5001
if "5001" in radio.MODEL:
time.sleep(0.5)
else:
time.sleep(0.1)
I don't think this makes sense. Sleeping before a read (especially of one byte) is kinda weird. Can't you just set your serial timeout to cover both of these cases and then read the byte, handling the timeout if it happens?
Yes, you are right, it can be.
But by the way the _rawrec() works we must have the serial timeout to a minimum value, that's why have used a time.sleep() trick instead.
If we use direct serial reads from radio.pipe it can work with the serial timeout, but will require more testing and time.
# wait between tries for different MAGICs to allow the radio to
# timeout, this is an experimental fature for the 5001 alpha that
# has the same ident as the MINI8900, raise it if it don't work
time.sleep(5)
Yikes, five seconds? I guess it seems like we should be able to avoid this with different model subclasses.
Believe it or not it don't work below 3 secs for ones and 5 secs for others, but at this point you don't know about who is who... so 5 secs. (this has a trick, keep reading)
Even so, there is a UV-5001 G2v2 (marked as G22 in the code, and yes, second revision of the second generation) that uses the magic from the Waccom Mini 8900 instead the former one for the other members of the UV-5001 designation. So we have to test both magic words to open it, and the users is unaware of t he version of the radio.
It's like the radios has a serial buffer that timeout after 3/5 secs, when you send a wrong magic you have to wait for the serial buffer on the radio to timeout before sending the correct one, otherwise the radio will ignore the correct magic....
As a workaround we have arranged the order of the tries from the most used units first to the least used units last (read the comments at the end before the declaration of the specific models) to get a higher chance of get it on the first tries; just for the UV-5001 brand there is 5 radio variants in the market now.
+def _do_ident(radio, status):
- """Put the radio in PROGRAM mode & identify it"""
- # set the serial discipline
- radio.pipe.setBaudrate(9600)
- radio.pipe.setParity("N")
- radio.pipe.setTimeout(0.005)
I feel like a timeout this low at 9600 baud is probably not very useful. The inter-byte timing of 9600N81 is about 1ms and your timeout here is 5ms. Other drivers that communicate at this (and faster) speeds are using timeouts on the order of 100-250ms.
Is there some detail you can offer here?
Yes, I know that, let me explain:
*Note:* Jim, please help me here if I make some mistake or syntactic error that spoil the explanation please (sorry I'm native Spanish and English is kind of self learned)
The radios has an uneven (not constant time) delays in the response of our queries starting to count from the end of the sent data, and even on the segments of the replies (ack, pause, header, pause, data...) even on the data stream the radio make pauses!
This has give us some big head aches, if you use serial timeouts then you get from time to time a shorter data block and the driver fails, that's no reliable, for one radio it _/could/_ work, but for 10 variants of the same radio structure the thing get nasty.
No matter how you tweak the serial timeouts or use time.sleep(), to much and the radio resets, to low and the radio give a short block, when you test different generations of the same radios and different models you get an very unstable driver, as all they have different time requirements.
At least the OEM has managed to get one software tuned for every few specific models, but we want one driver for all of them.
So to manage that we have to go back to the design table and I designed the actual _rawrecv() procedure that reads the data a byte at a time timing out on the desired amount of data on the buffer or a master timeout related to how many bytes we want to read, and to work that way we have to set the serial timeout as low as possible. Implementing a serial timeout by software.
This way and with the few pre-calculated time.sleep() distributed on the _do_magic, _download & _upload (from statistics from real data for all the radio models/variants), we have a very stable driver for all models and with a single tune point for any needed time tweak; the constant in this line on the _rawrecv() procedure:
maxtime = amount * 0.020
In fact that value in our tests and the final "release candidate" was 0.008 sec tops for every character we have to read from the radio.
After a closed test we find that some FTDI chips on windows needs 0.015 and on Macs needs up to 0.020 to get the diver working reliably.
A resume after reading all this looking for typos: the _rawrecv() procedure implements it's own kind of "software serial timeout" that allow us to be correct and fast and works with all radio models from Btech and the Waccom Mini 8900; but for that to works the real serial timeout must be low enough to pass a single char.
And this trick will allow us to fix things like this one just reported here http://chirp.danplanet.com/issues/3507 by just rising the value of the constant on the mentioned line without loosing performance (I'm pretty sure with 99% confidence)
I hope I have explained it clearly.
- # cleaning the serial buffer, try wrapped
- try:
radio.pipe.flushInput()
- except:
msg = "Error with a serial rx buffer flush at _do_ident"
LOG.error(msg)
This should be a LOG.exception() right? Presumably this means something very strange happened. Also, why do you think this is necessary?
The radio.pipe.flushInput() and the flushOutput() makes the test bed of Chirp fail unless it's try wrapped, note the end of the comment on the start of the block of code. "try wrapped".
The radio can send garbage if you wait to long listening on the serial line, it's not the interface but the radio, I have serial logs to prove that it does it with the OEM software, from where I copied the trick of cleaning the serial buffer.
If you don't make a periodic flushInput() before each block of reads you get from time to time garbage in the next read block, and that makes the driver fail.
- # basic check for the ident
- if len(ident) != 49:
msg = "Radio send a sort ident block, you need to increase maxtime."
This goes to the UI, right? What is maxtime? Does it refer to the value in _rawrecv()?
Yes it's a debug message for the developer, not the user.
Remember, I code here in Cuba, send the code by email to Jim in USA, Jim test with the radios and fix what he can (help him from explicit debug messages like this), he then pass logs, code & comments to me bye email, I code... (loop)
This was very useful then, must be changed now, will be changed.
+class btech(chirp_common.CloneModeRadio, chirp_common.ExperimentalRadio):
This should be:
class BTech(...
or something. Starting a class name with a lowercase letter is not really in line with anyone's style.
Good & sorry, I come from web development (ASP & PHP with auto learned skills) and now into Python, I must learn some tricks yet, thanks for guiding me with details like this. I will correct it.
- @classmethod
- def get_prompts(cls):
rp = chirp_common.RadioPrompts()
rp.experimental = \
('This driver is experimental and for personal use only.\n'
You can't make this requirement of the user based on the license. Please remove it.
Right, will do it.
'\n'
'Please keep a copy of your memories with the original software '
'if you treasure them, this is the first release and may contain'
' bugs.\n'
'\n'
'You will miss the setting tab, we are working on it. Your '
'success/failure story is appreciated, visit the Chirp\'s '
'website and drop us a comment or just say THANKS if it works '
'for you.\n'
This is probably a little too verbose for the experimental warning.
Get it.
+# Note: +# the order in the lists in the _magic, IDENT and _fileid is important +# we put the most common units first, the policy is as follows:
+# - First latest (newer) units, as they will be the most common +# - Second the former latest version, and recursively... +# - At the end the pre-production units (pp) as this will be unique
This concerns me a bit. Is there any chance that the value you're using for identification is actually just a mutable value and we'll be chasing valid values forever?
No, this value is NOT mutable.
This radios has a REAL mem span of 0x4000 (you can safely read up to there, and write too) but in the read the OEM software get just up to 0x3200 (we get full span in chirp) and in the write just up to 0x3100.
We respect the write boundary and this driver will never write beyond 0x3100, see _upload() procedure comments
Precisely in the area beyond 0x3200 there is some useful data like the image fingerprint we use to identify each radio model, the ranges for the bands, and other yet unknown data. (all this data with some strange order conform the ident string the radio send at the beginning)
I went ahead and applied this because I know that it's high-demand and that the Jetstream work will likely depend on this for collaboration. I've also put the test images into the tree.
I don't think this should go out to users with the experimental warning the way it is, so I will fix that up myself.
Roger, no problem on that and thanks for pushing it to the release with so many "comments"
If the JetStream is a clone of this (or vice versa) get support for it is as fast as Jim can get it on it's hands to test it and we will work on that direction ASAP.
Sending the radio to me is complicated as custom laws are very strict here (This is Cuba...), the only secure way of enter it is me traveling aboard and carrying it back to Cuba as a Cuban Ham under my ham license privileges. I hope POTUS visit ease the restrictions here... some day.
Also there are some other models that seems to be build on the same base of this: QYT KT-8900, JT-6188, Tokmate 8900, Sainsonic GT-890, Moonraker MT-270, Luiton LT-825U, Zastone MP-300, QYT KT UV-980, etc...
This radio base is apparently becoming in the Baofeng UV-5R for the mobiles. http://chirp.danplanet.com/issues/2673
I hope that there will be some further iteration on the timing stuff, possibly with David's help from the Jetstream side.
In deed, you will see more activity on this, we want to build also the settings tabs for this and possibly for the others alike radios.
Any help will be appreciated with this little beasts.
Thanks!
--Dan
Thanks to you for making Chirp open for us to participate, I'm always open to learn and Chirp is a good one on this.
I wait for your comment about this to improve the code.
73 Pavel CO7WT.
Yes, good to know about the performance issue. Also how can I measure that performance impact to compare for future improvements?
It's just a general thing: making lots of syscalls for very small bits of data is inefficient, and it means you're more likely to be blocked on the kernel during periods of high load (which you're contributing to). It's far more expensive to make a syscall than a regular function call. Also, with the tight timing you've got going on here, it could definitely cause you to miss another deadline you're expecting to make and manifest itself as instability under load and other varying conditions.
We originally wrote the driver that way but we found that the driver was pretty unstable and not because of the driver it self but the radios having and not consistent delays on the answer to PC commands.
I'm not sure why this would be, but I didn't see your other code. In general on a modern multitasking system, doing small reads and doing your own timing like this is a recipe for instability.
Most of the time we got reads with not the amount of data we must have and the driver failing, the other alternative was to use big time.sleep() here and there and we have then a long delayed reads and uploads with occasional resets of the radio.
Right, you can never depend on a read returning the whole thing you're expecting for. That's why you may need a queue which always reads as much as the radio has available, from which your code processes the frames as soon as they are whole.
And that non consistent delays (pauses between write data from radio to PC, mostly) are worst once we got different variants of the *same* radios to test (pre-production, alphas, 1G, 2G) not to mention other models.
The only way we found to manage that was the _rawrecv() procedure that you see now with precise time.sleep() in some places calculated from a timing profile for every radios to found the sweet spot for all of them.
You really should only need to slow yourself down for writes though.
+def _do_magic(radio, status):
- """Try to put the radio in program mode and get the ident string
- it will make multiple tries"""
I think maybe renaming this at some point would be good.
From Aladdin story, this procedure manages the magic phrase to "open" the radios to programming mode.
Any suggestion?
Something like "start_clone_mode" or "initialize_radio" or similar would be good.
But by the way the _rawrec() works we must have the serial timeout to a minimum value, that's why have used a time.sleep() trick instead.
If we use direct serial reads from radio.pipe it can work with the serial timeout, but will require more testing and time.
I think that would be where we should head.
Yikes, five seconds? I guess it seems like we should be able to avoid this with different model subclasses.
Believe it or not it don't work below 3 secs for ones and 5 secs for others, but at this point you don't know about who is who... so 5 secs. (this has a trick, keep reading)
Even so, there is a UV-5001 G2v2 (marked as G22 in the code, and yes, second revision of the second generation) that uses the magic from the Waccom Mini 8900 instead the former one for the other members of the UV-5001 designation. So we have to test both magic words to open it, and the users is unaware of t he version of the radio.
Okay, but we could just separate them out in the model list. A user may not know if they have a v1 or v2 radio, but if you call them out like that, they can try both and then continue to use that one in the future. Making people with radios that aren't first in the list wait many multiples of 5 seconds is not reasonable, IMHO.
It's like the radios has a serial buffer that timeout after 3/5 secs, when you send a wrong magic you have to wait for the serial buffer on the radio to timeout before sending the correct one, otherwise the radio will ignore the correct magic....
Yeah, I'm sure they never expected the radio to be used with something like chirp. Really unfortunate, but I think we can (and must) do better than this long wait.
As a workaround we have arranged the order of the tries from the most used units first to the least used units last (read the comments at the end before the declaration of the specific models) to get a higher chance of get it on the first tries; just for the UV-5001 brand there is 5 radio variants in the market now.
Right, but that's really bad for the poor guy that has a radio late in the list, and over time that list will be unmanageable and people will be waiting around for minutes before they can start clone mode :)
The radios has an uneven (not constant time) delays in the response of our queries starting to count from the end of the sent data, and even on the segments of the replies (ack, pause, header, pause, data...) even on the data stream the radio make pauses!
This has give us some big head aches, if you use serial timeouts then you get from time to time a shorter data block and the driver fails, that's no reliable, for one radio it _/could/_ work, but for 10 variants of the same radio structure the thing get nasty.
No matter how you tweak the serial timeouts or use time.sleep(), to much and the radio resets, to low and the radio give a short block, when you test different generations of the same radios and different models you get an very unstable driver, as all they have different time requirements.
Right, so the way you need to do this is construct a buffer in your code, where you always fill the buffer up to a reasonable serial timeout (like 100ms). From that buffer, you pull frames out when they're ready (i.e. complete). That way, you insulate yourself from the timing characteristics of the radio and your driver on top of this will be much more resilient to changes in platform, USB drivers, etc.
This way and with the few pre-calculated time.sleep() distributed on the _do_magic, _download & _upload (from statistics from real data for all the radio models/variants), we have a very stable driver for all models and with a single tune point for any needed time tweak; the constant in this line on the _rawrecv() procedure:
maxtime = amount * 0.020
Well, first thing this morning there was already a bug about timing issues:
http://chirp.danplanet.com/issues/3507
A resume after reading all this looking for typos: the _rawrecv() procedure implements it's own kind of "software serial timeout" that allow us to be correct and fast and works with all radio models from Btech and the Waccom Mini 8900; but for that to works the real serial timeout must be low enough to pass a single char.
That's just never going to produce reliable results though. Your control of the timeout can be delayed by many seconds on a loaded system. The OS is designed to provide you larger batches of data and expects you to do process them in bulk wherever possible.
- # cleaning the serial buffer, try wrapped
- try:
radio.pipe.flushInput()
- except:
msg = "Error with a serial rx buffer flush at _do_ident"
LOG.error(msg)
This should be a LOG.exception() right? Presumably this means something very strange happened. Also, why do you think this is necessary?
The radio.pipe.flushInput() and the flushOutput() makes the test bed of Chirp fail unless it's try wrapped, note the end of the comment on the start of the block of code. "try wrapped".
Then we should make the tests mock that out, if needed. However:
The radio can send garbage if you wait to long listening on the serial line, it's not the interface but the radio, I have serial logs to prove that it does it with the OEM software, from where I copied the trick of cleaning the serial buffer.
Sure, just do a read() until you get nothing back and then start. Not only does flushInput() imply things about internal buffers, but it also isn't supported in pyserial 3.0 and later. If you want to do this, please don't wrap it so that it passes tests, but make the test stub support it. However, I'd prefer you just read until you get nothing back.
See:
https://github.com/pyserial/pyserial/blob/master/serial/serialposix.py#L565-...
http://linux.die.net/man/3/tcflush
which says:
TCIFLUSH flushes data received but not read.
and
https://msdn.microsoft.com/en-us/library/windows/desktop/aa363428(v=vs.85).a...
Which says:
Terminates all outstanding overlapped read operations and returns immediately, even if the read operations have not been completed. Clears the output buffer (if the device driver has one).
The POSIX example is talking about the serial line discipline in the kernel, and the win32 one appears to be talking about the actual buffer on the device (if any). I don't think you should conflate these two, nor do I think you need to. If you need to quiesce the line, I think you should just read until you're confident it's quiet. However I'm *really* suspecting that you're just not reading everything from the previous block and interpreting it as garbage in front of the next one.
If you don't make a periodic flushInput() before each block of reads you get from time to time garbage in the next read block, and that makes the driver fail.
That doesn't make any sense to me at all. I feel like something else must be going on here, like you're not reading all the data from a previous round. This is probably a big source of instability, actually.
If you're really getting random data, then it's got to be a bad cable or something.
- # basic check for the ident
- if len(ident) != 49:
msg = "Radio send a sort ident block, you need to increase maxtime."
This goes to the UI, right? What is maxtime? Does it refer to the value in _rawrecv()?
Yes it's a debug message for the developer, not the user.
Remember, I code here in Cuba, send the code by email to Jim in USA, Jim test with the radios and fix what he can (help him from explicit debug messages like this), he then pass logs, code & comments to me bye email, I code... (loop)
This was very useful then, must be changed now, will be changed.
Okay, yeah, just want to get it cleaned up for the in-tree stuff. Users will freak out when they seen this :)
Good & sorry, I come from web development (ASP & PHP with auto learned skills) and now into Python, I must learn some tricks yet, thanks for guiding me with details like this. I will correct it.
Thanks :)
- @classmethod
- def get_prompts(cls):
rp = chirp_common.RadioPrompts()
rp.experimental = \
('This driver is experimental and for personal use only.\n'
You can't make this requirement of the user based on the license. Please remove it.
Right, will do it.
I already made the change so that today's build would not have it in it.
No, this value is NOT mutable.
This radios has a REAL mem span of 0x4000 (you can safely read up to there, and write too) but in the read the OEM software get just up to 0x3200 (we get full span in chirp) and in the write just up to 0x3100.
We respect the write boundary and this driver will never write beyond 0x3100, see _upload() procedure comments
Okay, in previous models, there was a reserved region at the end which appeared to be immutable, until we found an extra tool that let a vendor customize what was in this last block...
If the JetStream is a clone of this (or vice versa) get support for it is as fast as Jim can get it on it's hands to test it and we will work on that direction ASAP.
David Fannin has the radio already and will be looking into the changes needed for this. I'm hoping he'll also be able to help with some of the stability issues.
Thanks Pavel and Jim!
--Dan
Hi Dan et al, See below.
El 23/03/16 a las 15:52, Dan Smith via chirp_devel escribió:
Yes, good to know about the performance issue. Also how can I measure that performance impact to compare for future improvements?
It's just a general thing: making lots of syscalls for very small bits of data is inefficient, and it means you're more likely to be blocked on the kernel during periods of high load (which you're contributing to). It's far more expensive to make a syscall than a regular function call. Also, with the tight timing you've got going on here, it could definitely cause you to miss another deadline you're expecting to make and manifest itself as instability under load and other varying conditions.
Make sense.
Most of the time we got reads with not the amount of data we must have and the driver failing, the other alternative was to use big time.sleep() here and there and we have then a long delayed reads and uploads with occasional resets of the radio.
Right, you can never depend on a read returning the whole thing you're expecting for. That's why you may need a queue which always reads as much as the radio has available, from which your code processes the frames as soon as they are whole.
I get the idea, and have a kind of schema in mind that may work even with bad read recovery to protect me from the radio miss behaviors.
Okay, but we could just separate them out in the model list. A user may not know if they have a v1 or v2 radio, but if you call them out like that, they can try both and then continue to use that one in the future. Making people with radios that aren't first in the list wait many multiples of 5 seconds is not reasonable, IMHO.
I was searching the net about this and most sellers have a "(Gen 2)" or something like that in it's advertising but the actual radio have no evident marking of the generation or version (at least in the advertised pictures)
Also the comments on the end of the file before the radio declarations is not accurate and has a lot to do with the former strategy, the only element that has a real impact now in the ident schema is the self._magic list now. This has been corrected in the draft version, sorry for that.
So it makes sense to separate radios from just one family in the listing and tag it as "Gen 2" for example, only if hey have so many magics to try for all members in that family.
Looking the data we have now I realize that we only have one case of this with a family of radios with two magics to try (BTECH UV-5001), with just one UV-5001 Gen 2v2 with a different magic from the other 4 members of the family.
As pre-production and alpha units never got into the market, we only have the 1st, 2nd & 2nd ver 2 generation on the street and being just this last the only that need a different magic there is a chance of just 33% of getining the wait on the radio ident.
That's just about 6-7 seconds of wait on the ident before the clone start if you have that radio (just 33% chance), and also the progress bar will be moving all time to show you progress.
IMHO that's not a big issue, so I vote for keeping it in the actual way as long the count of magics in the list don't climb higher. I have already a note in the driver about this to make the changes if the issue get complicated in the future.
Well, first thing this morning there was already a bug about timing issues:
You are right, the description of the issue has much to do with what you have mentioned about system load.
No, this value is NOT mutable.
This radios has a REAL mem span of 0x4000 (you can safely read up to there, and write too) but in the read the OEM software get just up to 0x3200 (we get full span in chirp) and in the write just up to 0x3100.
We respect the write boundary and this driver will never write beyond 0x3100, see _upload() procedure comments
Okay, in previous models, there was a reserved region at the end which appeared to be immutable, until we found an extra tool that let a vendor customize what was in this last block...
Then this will be a real mess...
I was today inspecting the ident code and it's constructed with data from that region exclusively and the only "non mutable" and unique data for every radio is the one we use now as fingerprint.
Was the fix to the problem you mentioned?
To learn from it.
If the JetStream is a clone of this (or vice versa) get support for it is as fast as Jim can get it on it's hands to test it and we will work on that direction ASAP.
David Fannin has the radio already and will be looking into the changes needed for this. I'm hoping he'll also be able to help with some of the stability issues.
Thanks Pavel and Jim!
Ok, I wait for David to write me an email to give him a welcome to the team and exchange ideas/code/etc.
Thanks for taking the time to coach me on this. I have a lot of "homework" to don now.
73.
Looking the data we have now I realize that we only have one case of this with a family of radios with two magics to try (BTECH UV-5001), with just one UV-5001 Gen 2v2 with a different magic from the other 4 members of the family.
As pre-production and alpha units never got into the market, we only have the 1st, 2nd & 2nd ver 2 generation on the street and being just this last the only that need a different magic there is a chance of just 33% of getining the wait on the radio ident.
That's just about 6-7 seconds of wait on the ident before the clone start if you have that radio (just 33% chance), and also the progress bar will be moving all time to show you progress.
IMHO that's not a big issue, so I vote for keeping it in the actual way as long the count of magics in the list don't climb higher. I have already a note in the driver about this to make the changes if the issue get complicated in the future.
Personally I think it's a big issue. I 6-7 seconds is not really reasonable, IMHO, but definitely not once we have to add even another magic to the list.
So, I can't make you do anything and I don't have a sample of radios in order to test a refactor myself. I would just say that on the list of things I think need to change in this driver, that's in my top five or so.
Maybe some other people can share their opinions.
Okay, in previous models, there was a reserved region at the end which appeared to be immutable, until we found an extra tool that let a vendor customize what was in this last block...
Then this will be a real mess...
I was today inspecting the ident code and it's constructed with data from that region exclusively and the only "non mutable" and unique data for every radio is the one we use now as fingerprint.
Was the fix to the problem you mentioned?
I don't think we were using it for identification, or maybe it was just for firmware records or something. I'm just saying: just because it's not written by the consumer software doesn't mean it's actually immutable, or that you shouldn't ever expect to see it different in the field.
Ok, I wait for David to write me an email to give him a welcome to the team and exchange ideas/code/etc.
In the meantime, I'd surely appreciate you keeping any patches that you have people try as public in the bug trackers. That helps increase collaboration with people that might have other ideas. IMHO, there is no reason to have a patch or other communication be private unless you're concerned that a particular change might be damaging in some way. In the history of CHIRP, I can think of only one such situation that warranted some private communication about a potential fix. In general, we should leverage the community of users and developers and try to keep that communication as open as possible.
Thanks!
--Dan
On Thu, Mar 24, 2016 at 1:21 PM, Dan Smith via chirp_devel chirp_devel@intrepid.danplanet.com wrote:
Looking the data we have now I realize that we only have one case of this with a family of radios with two magics to try (BTECH UV-5001), with just one UV-5001 Gen 2v2 with a different magic from the other 4 members of the family.
As pre-production and alpha units never got into the market, we only have the 1st, 2nd & 2nd ver 2 generation on the street and being just this last the only that need a different magic there is a chance of just 33% of getining the wait on the radio ident.
That's just about 6-7 seconds of wait on the ident before the clone start if you have that radio (just 33% chance), and also the progress bar will be moving all time to show you progress.
IMHO that's not a big issue, so I vote for keeping it in the actual way as long the count of magics in the list don't climb higher. I have already a note in the driver about this to make the changes if the issue get complicated in the future.
Personally I think it's a big issue. I 6-7 seconds is not really reasonable, IMHO, but definitely not once we have to add even another magic to the list.
So, I can't make you do anything and I don't have a sample of radios in order to test a refactor myself. I would just say that on the list of things I think need to change in this driver, that's in my top five or so.
Maybe some other people can share their opinions.
This is to support a single radio. I believe all we have to do is switch back to the way I had it originally. Add the "ident" fingerprint for this one-of-a-kind radio to the WACCOM/MINI-8900 radio class (since that it sends the "magic" that this radio needs and have the user select that vendor/model combination to program that one-of-kind radio.
Or have that radio sent back to the vendor to be used for repair parts so it doesn't need to be supported at all.
We need to start collecting the idents and fingerprints from the other radio variants to determine what the extent of the variations are. We will just have to have a separate class for each magic and do our best on the supported radio models list to let the CHIRP user know which vendor/model selection to use.
Then I will have to figure out what to do with the Baofeng radios that have this issue. I would hoping to use whatever solution was arrived at here to resolve that. I see now I may have to approach it differently.
Jim
This is to support a single radio. I believe all we have to do is switch back to the way I had it originally. Add the "ident" fingerprint for this one-of-a-kind radio to the WACCOM/MINI-8900 radio class (since that it sends the "magic" that this radio needs and have the user select that vendor/model combination to program that one-of-kind radio.
Does the radio send a magic during the exchange but just not part of the image or something? Or does the software have any "identify" or "test communication" function that might cause the radio to barf up an identity?
If you have to, you could always stash something in the image. That would give you something you can key off of later, to tell which radio they used to download with, before you start your upload. If it's purely a detecting-the-radio-model-from-the-file problem, then that will work, assuming there's nothing else to go on. It's not as nice, but better than nothing, and something we should get in place early before people start generating images without that extra data in it.
--Dan
On Thu, Mar 24, 2016 at 2:03 PM, Dan Smith via chirp_devel chirp_devel@intrepid.danplanet.com wrote:
This is to support a single radio. I believe all we have to do is switch back to the way I had it originally. Add the "ident" fingerprint for this one-of-a-kind radio to the WACCOM/MINI-8900 radio class (since that it sends the "magic" that this radio needs and have the user select that vendor/model combination to program that one-of-kind radio.
Does the radio send a magic during the exchange but just not part of the image or something? Or does the software have any "identify" or "test communication" function that might cause the radio to barf up an identity?
If you have to, you could always stash something in the image. That would give you something you can key off of later, to tell which radio they used to download with, before you start your upload. If it's purely a detecting-the-radio-model-from-the-file problem, then that will work, assuming there's nothing else to go on. It's not as nice, but better than nothing, and something we should get in place early before people start generating images without that extra data in it.
This radio identifies itself with a unique (as far as we know) fingerprint. It just needs to receive a different string to initiate cloning. It is apparently the first sample made for BTECH and the manufacturer didn't get around to changing this string yes so it happens to require the same string that initiates cloning of the WACCOM MINI-8900 Plus.
I/we thought that figuring out how to support these 1 or 2 of a kind radios would help us more rapidly support the many variants that there seems to be for these radios.
Jim
This radio identifies itself with a unique (as far as we know) fingerprint. It just needs to receive a different string to initiate cloning. It is apparently the first sample made for BTECH and the manufacturer didn't get around to changing this string yes so it happens to require the same string that initiates cloning of the WACCOM MINI-8900 Plus.
Okay, so if it's a preprod radio, then I would say the few people that have those needing to know they have to choose some random other model is...not the worst thing in the world :)
It would certainly kinda suck to penalize everyone just so that the preprod radios work under their rightful name.
What about another option, which is we include a dummy radio that alone does the probing, and tell people that if they don't know what to use, they can use "GeneriMobile 9000" radio, which will take a lot of time probing all the various magics to find the right one?
We could also consider a generic "probe my radio" command in chirp that people could run from the help menu or something, but that'll require some UI work...
--Dan
What about another option, which is we include a dummy radio that alone does the probing, and tell people that if they don't know what to use, they can use "GeneriMobile 9000" radio, which will take a lot of time probing all the various magics to find the right one?
We could also consider a generic "probe my radio" command in chirp that people could run from the help menu or something, but that'll require some UI work...
I like this idea if it can be implemented. Would this then be something that could also be used on Baofeng radios?
Jim
I like this idea if it can be implemented. Would this then be something that could also be used on Baofeng radios?
The generic tester thing? If so, I think we'd require them to choose between some things to disambiguate. Like, you'd go into the tool and it would say:
here are a list of things I know how to look for:
- Baofeng UV5R Variants - Baofeng UV-5001/5123,WACCOM MINI,Jetstream XXX
and they would choose the one they're trying to do. So, yaesu and icom people wouldn't need to use it because those manufacturers don't do silly things like this.
It's a non-trivial amount of work, but might be the best option here. For now, just having a subclass of your btech driver that does all the probing might be the quickest path out of this particular box.
--Dan
El 24/03/16 a las 14:23, Dan Smith via chirp_devel escribió:
Okay, so if it's a preprod radio, then I would say the few people that have those needing to know they have to choose some random other model is...not the worst thing in the world:)
It would certainly kinda suck to penalize everyone just so that the preprod radios work under their rightful name.
But we can do it right from the beginning!
We can put the magic for the pre-prod/alphas/unique radios *at the end*, and 99.9% of the users will be straight in the first try to the clone mode with ZERO delay.
The users with a pre-prod/alphas/unique radios will have to wait a little more (with some room for improvement) and ... it's not the worst thing in the world ;-)
As a principle this radios must be a very small lot, so affected users will be a very small quantity compared with the other real market users.
And if we look closer we have just ONE radio with this problem so far.
73
It would certainly kinda suck to penalize everyone just so that the preprod radios work under their rightful name.
But we can do it right from the beginning!
We can put the magic for the pre-prod/alphas/unique radios *at the end*, and 99.9% of the users will be straight in the first try to the clone mode with ZERO delay.
The users with a pre-prod/alphas/unique radios will have to wait a little more (with some room for improvement) and ... it's not the worst thing in the world ;-)
Sorry, but I do not think that putting the magics we think are rare at the end and encountering a delay if so is doing it "right from the beginning." Even if it's 0.1% right now, we know the manufacturer(s) are going to continue to release radios based on this from previous experience.
As a principle this radios must be a very small lot, so affected users will be a very small quantity compared with the other real market users.
I don't think this is a good argument. Unless there is a good reason not to, I'd much prefer to have a repeatable process where we go straight to the radio class we need, send the magic string we need, and fail quickly if it's the wrong one.
And if we look closer we have just ONE radio with this problem so far.
If it's literally one preprod sample radio, then we shouldn't put it in CHIRP at all anyway.
--Dan
El 24/03/16 a las 13:59, Jim Unroe via chirp_devel escribió:
since that it sends the "magic" that this radio needs and have the user select that vendor/model combination to program that one-of-kind radio
Jim, can we confirm that this is a "one-of-kind" radio?
Is this a unique radio not in the market and just on John's hands?
If so, we have a storm in a tea cup going on...
73
Hi to all, see at the bootom.
El 24/03/16 a las 13:59, Jim Unroe via chirp_devel escribió:
Looking the data we have now I realize that we only have one case of
this with a family of radios with two magics to try (BTECH UV-5001), with just one UV-5001 Gen 2v2 with a different magic from the other 4 members of the family.
As pre-production and alpha units never got into the market, we only have the 1st, 2nd & 2nd ver 2 generation on the street and being just this last the only that need a different magic there is a chance of just 33% of getining the wait on the radio ident.
That's just about 6-7 seconds of wait on the ident before the clone start if you have that radio (just 33% chance), and also the progress bar will be moving all time to show you progress.
IMHO that's not a big issue, so I vote for keeping it in the actual way as long the count of magics in the list don't climb higher. I have already a note in the driver about this to make the changes if the issue get complicated in the future.
Personally I think it's a big issue. I 6-7 seconds is not really reasonable, IMHO, but definitely not once we have to add even another magic to the list.
So, I can't make you do anything and I don't have a sample of radios in order to test a refactor myself. I would just say that on the list of things I think need to change in this driver, that's in my top five or so.
Maybe some other people can share their opinions.
This is to support a single radio. I believe all we have to do is switch back to the way I had it originally. Add the "ident" fingerprint for this one-of-a-kind radio to the WACCOM/MINI-8900 radio class (since that it sends the "magic" that this radio needs and have the user select that vendor/model combination to program that one-of-kind radio.
Or have that radio sent back to the vendor to be used for repair parts so it doesn't need to be supported at all.
We need to start collecting the idents and fingerprints from the other radio variants to determine what the extent of the variations are. We will just have to have a separate class for each magic and do our best on the supported radio models list to let the CHIRP user know which vendor/model selection to use.
Then I will have to figure out what to do with the Baofeng radios that have this issue. I would hoping to use whatever solution was arrived at here to resolve that. I see now I may have to approach it differently.
Jim
We may have a storm in a tea cup here, we need to get some points clear before going on about that radio.
* *Is this radio unique?* We are working with various pre-production units and alphas that was unique (or in very small lots) and directed to the brand owner to test, so there is a chance that this radio is a unique test unit? If so, then putting it on the "end of the list" is safe as just one or a small count of users will be affected. Jim can you check with Adrew and John about this? * *Can we craft some way of shorten the wait between tries?* If we can make it, and it's short enough (for example, less than a second) then we can safely put a small amount of magics to try in a list as the delay will be negligible. I'm sure this area has a lot of improvement potential as the early tests about it was light and mixed with other issues.
The first is the fast one, and the second will require more work and test but it's doable and worth it.
I'm agree with Jim, I think that getting some portmon serial logs for the other alike radios will reveal some intel about this being a isolated issue or a common practice, and in any case we will gain knowledge about this radios.
Jim, can you post a request of this kind of serial logs in the alike radios issue page?
73
participants (3)
-
Dan Smith
-
Jim Unroe
-
M.Sc. Pavel Milanes Costa