[chirp_devel] Fwd: [PATCH] [BF-T8] add support for the Baofeng BF-T8

Jim Unroe
Wed May 19 18:07:32 PDT 2021


Image attached for testing: Baofeng_BF-T8.img

---------- Forwarded message ---------
From: Jim Unroe <kc9hi at comcast.net>
Date: Wed, May 19, 2021 at 9:00 PM
Subject: [PATCH] [BF-T8] add support for the Baofeng BF-T8
To: <Rock.Unroe at gmail.com>


# HG changeset patch
# User Jim Unroe <rock.unroe at gmail.com>
# Date 1621472333 14400
#      Wed May 19 20:58:53 2021 -0400
# Node ID ccf8f96034cd9dbb6f3462bcf10d68526c3e0572
# Parent  b04ba05b7b646e144af4f074663941ca122dc4ed
[BF-T8] add support for the Baofeng BF-T8

This patch adds support for the Baofeng BF-T8 (and it variants)

related to #8263

diff -r b04ba05b7b64 -r ccf8f96034cd chirp/drivers/bf-t8.py
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/chirp/drivers/bf-t8.py    Wed May 19 20:58:53 2021 -0400
@@ -0,0 +1,702 @@
+# Copyright 2021 Jim Unroe <rock.unroe at gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import time
+import os
+import struct
+import logging
+
+from chirp import (
+    bitwise,
+    chirp_common,
+    directory,
+    errors,
+    memmap,
+    util,
+)
+from chirp.settings import (
+    RadioSetting,
+    RadioSettingGroup,
+    RadioSettings,
+    RadioSettingValueBoolean,
+    RadioSettingValueFloat,
+    RadioSettingValueInteger,
+    RadioSettingValueList,
+    RadioSettingValueString,
+)
+
+LOG = logging.getLogger(__name__)
+
+MEM_FORMAT = """
+#seekto 0x0000;
+struct {
+  lbcd rxfreq[4];       // RX Frequency
+  lbcd txfreq[4];       // TX Frequency
+  u8 rx_tmode;          // RX Tone Mode
+  u8 rx_tone;           // PL/DPL Decode
+  u8 tx_tmode;          // TX Tone Mode
+  u8 tx_tone;           // PL/DPL Encode
+  u8 unknown1:3,        //
+     skip:1,            // Scan Add: 1 = Skip, 0 = Scan
+     unknown2:2,
+     isnarrow:1,        // W/N: 1 = Narrow, 0 = Wide
+     lowpower:1;        // TX Power: 1 = Low, 0 = High
+  u8 unknown3[3];       //
+} memory[99];
+
+#seekto 0x0630;
+struct {
+  u8 squelch;           // SQL
+  u8 vox;               // Vox Lv
+  u8 tot;               // TOT
+  u8 unk1:3,            //
+     ste:1,             // Tail Clear
+     bcl:1,             // BCL
+     save:1,            // Save
+     tdr:1,             // TDR
+     beep:1;            // Beep
+  u8 voice;             // Voice
+  u8 abr;               // Back Light
+  u8 ring;              // Ring
+  u8 unknown;           //
+  u8 mra;               // MR Channel A
+  u8 mrb;               // MR Channel B
+  u8 disp_ab;           // Display A/B Selected
+  ul16 fmcur;           // Broadcast FM station
+  u8 workmode;          // Work Mode
+  u8 wx;                // NOAA WX ch#
+  u8 area;              // Area Selected
+} settings;
+"""
+
+CMD_ACK = "\x06"
+
+TONES = chirp_common.TONES
+TMODES = ["", "Tone", "DTCS", "DTCS"]
+
+AB_LIST = ["A", "B"]
+ABR_LIST = ["OFF", "ON", "Key"]
+AREA_LIST = ["China", "Japan", "Korea", "Malaysia", "American",
+             "Australia", "Iran", "Taiwan", "Europe", "Russia"]
+MDF_LIST = ["Frequency", "Channel #", "Name"]
+RING_LIST = ["OFF"] + ["%s" % x for x in range(1, 11)]
+TOT_LIST = ["OFF"] + ["%s seconds" % x for x in range(30, 210, 30)]
+VOICE_LIST = ["Off", "Chinese", "English"]
+VOX_LIST = ["OFF"] + ["%s" % x for x in range(1, 6)]
+WORKMODE_LIST = ["General", "PMR"]
+WX_LIST = ["CH01 - 162.550",
+           "CH02 - 162.400",
+           "CH03 - 162.475",
+           "CH04 - 162.425",
+           "CH05 - 162.450",
+           "CH06 - 162.500",
+           "CH07 - 162.525"
+           ]
+
+SETTING_LISTS = {
+    "ab": AB_LIST,
+    "abr": ABR_LIST,
+    "area": AREA_LIST,
+    "mdf": MDF_LIST,
+    "ring": RING_LIST,
+    "tot": TOT_LIST,
+    "voice": VOICE_LIST,
+    "vox": VOX_LIST,
+    "workmode": WORKMODE_LIST,
+    "wx": WX_LIST,
+    }
+
+FRS_FREQS1 = [462.5625, 462.5875, 462.6125, 462.6375, 462.6625,
+              462.6875, 462.7125]
+FRS_FREQS2 = [467.5625, 467.5875, 467.6125, 467.6375, 467.6625,
+              467.6875, 467.7125]
+FRS_FREQS3 = [462.5500, 462.5750, 462.6000, 462.6250, 462.6500,
+              462.6750, 462.7000, 462.7250]
+FRS_FREQS = FRS_FREQS1 + FRS_FREQS2 + FRS_FREQS3
+
+
+def _enter_programming_mode(radio):
+    serial = radio.pipe
+
+    exito = False
+    for i in range(0, 5):
+        serial.write(radio._magic)
+        ack = serial.read(1)
+
+        try:
+            if ack == CMD_ACK:
+                exito = True
+                break
+        except:
+            LOG.debug("Attempt #%s, failed, trying again" % i)
+            pass
+
+    # check if we had EXITO
+    if exito is False:
+        _exit_programming_mode(radio)
+        msg = "The radio did not accept program mode after five tries.\n"
+        msg += "Check you interface cable and power cycle your radio."
+        raise errors.RadioError(msg)
+
+    try:
+        serial.write("\x02")
+        ident = serial.read(len(radio._fingerprint))
+    except:
+        _exit_programming_mode(radio)
+        raise errors.RadioError("Error communicating with radio")
+
+    if not ident == radio._fingerprint:
+        _exit_programming_mode(radio)
+        LOG.debug(util.hexprint(ident))
+        raise errors.RadioError("Radio returned unknown identification string")
+
+    try:
+        serial.write(CMD_ACK)
+        ack = serial.read(1)
+    except:
+        _exit_programming_mode(radio)
+        raise errors.RadioError("Error communicating with radio")
+
+    if ack != CMD_ACK:
+        _exit_programming_mode(radio)
+        raise errors.RadioError("Radio refused to enter programming mode")
+
+
+def _exit_programming_mode(radio):
+    serial = radio.pipe
+    try:
+        serial.write("E")
+    except:
+        raise errors.RadioError("Radio refused to exit programming mode")
+
+
+def _read_block(radio, block_addr, block_size):
+    serial = radio.pipe
+
+    cmd = struct.pack(">cHb", 'R', block_addr, block_size)
+    expectedresponse = "W" + cmd[1:]
+    LOG.debug("Reading block %04x..." % (block_addr))
+
+    try:
+        serial.write(cmd)
+        response = serial.read(4 + block_size)
+        if response[:4] != expectedresponse:
+            raise Exception("Error reading block %04x." % (block_addr))
+
+        block_data = response[4:]
+
+        serial.write(CMD_ACK)
+        ack = serial.read(1)
+    except:
+        _exit_programming_mode(radio)
+        raise errors.RadioError("Failed to read block at %04x" % block_addr)
+
+    if ack != CMD_ACK:
+        _exit_programming_mode(radio)
+        raise Exception("No ACK reading block %04x." % (block_addr))
+
+    return block_data
+
+
+def _write_block(radio, block_addr, block_size):
+    serial = radio.pipe
+
+    cmd = struct.pack(">cHb", 'W', block_addr, block_size)
+    data = radio.get_mmap()[block_addr:block_addr + block_size]
+
+    LOG.debug("Writing Data:")
+    LOG.debug(util.hexprint(cmd + data))
+
+    try:
+        serial.write(cmd + data)
+        if serial.read(1) != CMD_ACK:
+            raise Exception("No ACK")
+    except:
+        _exit_programming_mode(radio)
+        raise errors.RadioError("Failed to send block "
+                                "to radio at %04x" % block_addr)
+
+
+def do_download(radio):
+    LOG.debug("download")
+    _enter_programming_mode(radio)
+
+    data = ""
+
+    status = chirp_common.Status()
+    status.msg = "Cloning from radio"
+
+    status.cur = 0
+    status.max = radio._memsize
+
+    for addr in range(0, radio._memsize, radio.BLOCK_SIZE):
+        status.cur = addr + radio.BLOCK_SIZE
+        radio.status_fn(status)
+
+        block = _read_block(radio, addr, radio.BLOCK_SIZE)
+        data += block
+
+        LOG.debug("Address: %04x" % addr)
+        LOG.debug(util.hexprint(block))
+
+    _exit_programming_mode(radio)
+
+    return memmap.MemoryMap(data)
+
+
+def do_upload(radio):
+    status = chirp_common.Status()
+    status.msg = "Uploading to radio"
+
+    _enter_programming_mode(radio)
+
+    status.cur = 0
+    status.max = radio._memsize
+
+    for start_addr, end_addr in radio._ranges:
+        for addr in range(start_addr, end_addr, radio.BLOCK_SIZE_UP):
+            status.cur = addr + radio.BLOCK_SIZE_UP
+            radio.status_fn(status)
+            _write_block(radio, addr, radio.BLOCK_SIZE_UP)
+
+    _exit_programming_mode(radio)
+
+
+class BFT8Radio(chirp_common.CloneModeRadio):
+    """Baofeng BF-T8"""
+    VENDOR = "Baofeng"
+    MODEL = "BF-T8"
+    BAUD_RATE = 9600
+    BLOCK_SIZE = BLOCK_SIZE_UP = 0x10
+    ODD_SPLIT = True
+    HAS_NAMES = False
+    SKIP_VALUES = []
+    DTCS_CODES = sorted(chirp_common.DTCS_CODES)
+
+    POWER_LEVELS = [chirp_common.PowerLevel("High", watts=2.00),
+                    chirp_common.PowerLevel("Low", watts=0.50)]
+
+    _magic = "\x02" + "PROGRAM"
+    _fingerprint = "\x2E" + "BF-T6" + "\x2E"
+    _upper = 99
+    _frs = _upper == 22
+
+    _ranges = [
+               (0x0000, 0x0B60),
+              ]
+    _memsize = 0x0B60
+
+    def get_features(self):
+        rf = chirp_common.RadioFeatures()
+        rf.has_settings = True
+        rf.has_bank = False
+        rf.has_ctone = True
+        rf.has_cross = True
+        rf.has_rx_dtcs = True
+        rf.has_tuning_step = False
+        rf.can_odd_split = self.ODD_SPLIT
+        rf.has_name = self.HAS_NAMES
+        rf.valid_skips = self.SKIP_VALUES
+        rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
+        rf.valid_cross_modes = ["Tone->Tone", "Tone->DTCS", "DTCS->Tone",
+                                "->Tone", "->DTCS", "DTCS->", "DTCS->DTCS"]
+        rf.valid_dtcs_codes = self.DTCS_CODES
+        rf.valid_power_levels = self.POWER_LEVELS
+        rf.valid_duplexes = ["", "-", "+", "split", "off"]
+        rf.valid_modes = ["FM", "NFM"]  # 25 kHz, 12.5 KHz.
+        rf.memory_bounds = (1, self._upper)
+        rf.valid_tuning_steps = [2.5, 5., 6.25, 10., 12.5, 25.]
+        rf.valid_bands = [(400000000, 470000000)]
+
+        return rf
+
+    def process_mmap(self):
+        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
+
+    def validate_memory(self, mem):
+        msgs = ""
+        msgs = chirp_common.CloneModeRadio.validate_memory(self, mem)
+
+        _msg_freq = 'Memory location cannot change frequency'
+        _msg_simplex = 'Memory location only supports Duplex:(None)'
+        _msg_nfm = 'Memory location only supports Mode: NFM'
+        _msg_txp = 'Memory location only supports Power: Low'
+
+        # FRS only models
+        if self._frs:
+            # range of memories with values set by FCC rules
+            if mem.freq != int(FRS_FREQS[mem.number - 1] * 1000000):
+                # warn user can't change frequency
+                msgs.append(chirp_common.ValidationError(_msg_freq))
+
+            # channels 1 - 22 are simplex only
+            if str(mem.duplex) != "":
+                # warn user can't change duplex
+                msgs.append(chirp_common.ValidationError(_msg_simplex))
+
+            # channels 1 - 22 are NFM only
+            if str(mem.mode) != "NFM":
+                # warn user can't change mode
+                msgs.append(chirp_common.ValidationError(_msg_nfm))
+
+            # channels 8 - 14 are low power NFM only
+            if mem.number >= 8 and mem.number <= 14:
+                if str(mem.power) != "Low":
+                    # warn user can't change power
+                    msgs.append(chirp_common.ValidationError(_msg_txp))
+
+        return msgs
+
+    def sync_in(self):
+        """Download from radio"""
+        try:
+            data = do_download(self)
+        except errors.RadioError:
+            # Pass through any real errors we raise
+            raise
+        except:
+            # If anything unexpected happens, make sure we raise
+            # a RadioError and log the problem
+            LOG.exception('Unexpected error during download')
+            raise errors.RadioError('Unexpected error communicating '
+                                    'with the radio')
+        self._mmap = data
+        self.process_mmap()
+
+    def sync_out(self):
+        """Upload to radio"""
+        try:
+            do_upload(self)
+        except:
+            # If anything unexpected happens, make sure we raise
+            # a RadioError and log the problem
+            LOG.exception('Unexpected error during upload')
+            raise errors.RadioError('Unexpected error communicating '
+                                    'with the radio')
+
+    def get_raw_memory(self, number):
+        return repr(self._memobj.memory[number - 1])
+
+    def _get_tone(self, mem, _mem):
+        rx_tone = tx_tone = None
+
+        tx_tmode = TMODES[_mem.tx_tmode]
+        rx_tmode = TMODES[_mem.rx_tmode]
+
+        if tx_tmode == "Tone":
+            tx_tone = TONES[_mem.tx_tone]
+        elif tx_tmode == "DTCS":
+            tx_tone = self.DTCS_CODES[_mem.tx_tone]
+
+        if rx_tmode == "Tone":
+            rx_tone = TONES[_mem.rx_tone]
+        elif rx_tmode == "DTCS":
+            rx_tone = self.DTCS_CODES[_mem.rx_tone]
+
+        tx_pol = _mem.tx_tmode == 0x03 and "R" or "N"
+        rx_pol = _mem.rx_tmode == 0x03 and "R" or "N"
+
+        chirp_common.split_tone_decode(mem, (tx_tmode, tx_tone, tx_pol),
+                                       (rx_tmode, rx_tone, rx_pol))
+
+    def _get_mem(self, number):
+        return self._memobj.memory[number]
+
+    def get_memory(self, number):
+        _mem = self._get_mem(number - 1)
+
+        mem = chirp_common.Memory()
+
+        mem.number = number
+        mem.freq = int(_mem.rxfreq) * 10
+
+        # We'll consider any blank (i.e. 0MHz frequency) to be empty
+        if mem.freq == 0:
+            mem.empty = True
+            return mem
+
+        if _mem.rxfreq.get_raw() == "\xFF\xFF\xFF\xFF":
+            mem.freq = 0
+            mem.empty = True
+            mem.mode = "NFM"
+            if mem.number <= 22 and not self._frs:
+                LOG.debug("Initializing empty memory")
+                _mem.set_raw("\x00" * 13 + "\xFF" * 3)
+                FRS_FREQ = FRS_FREQS[mem.number - 1] * 1000000
+                mem.freq = FRS_FREQ
+                if mem.number >= 8 and mem.number <= 14:
+                    mem.power = "Low"
+                else:
+                    mem.power = "High"
+
+            return mem
+
+        if _mem.get_raw() == ("\xFF" * 16):
+            LOG.debug("Initializing empty memory")
+            _mem.set_raw("\x00" * 13 + "\xFF" * 3)
+
+        if int(_mem.rxfreq) == int(_mem.txfreq):
+            mem.duplex = ""
+            mem.offset = 0
+        else:
+            mem.duplex = int(_mem.rxfreq) > int(_mem.txfreq) and "-" or "+"
+            mem.offset = abs(int(_mem.rxfreq) - int(_mem.txfreq)) * 10
+
+        # wide/narrow
+        mem.mode = _mem.isnarrow and "NFM" or "FM"
+
+        # tone data
+        self._get_tone(mem, _mem)
+
+        # tx power
+        levels = self.POWER_LEVELS
+        try:
+            mem.power = levels[_mem.lowpower]
+        except IndexError:
+            LOG.error("Radio reported invalid power level %s (in %s)" %
+                      (_mem.power, levels))
+            mem.power = levels[0]
+
+        if mem.number <= 22 and self._frs:
+            FRS_IMMUTABLE = ["freq", "duplex", "offset", "mode"]
+            if mem.number >= 8 and mem.number <= 14:
+                mem.immutable = FRS_IMMUTABLE + ["power"]
+            else:
+                mem.immutable = FRS_IMMUTABLE
+
+        return mem
+
+    def _set_tone(self, mem, _mem):
+        ((txmode, txtone, txpol),
+         (rxmode, rxtone, rxpol)) = chirp_common.split_tone_encode(mem)
+
+        _mem.tx_tmode = TMODES.index(txmode)
+        _mem.rx_tmode = TMODES.index(rxmode)
+        if txmode == "Tone":
+            _mem.tx_tone = TONES.index(txtone)
+        elif txmode == "DTCS":
+            _mem.tx_tmode = txpol == "R" and 0x03 or 0x02
+            _mem.tx_tone = self.DTCS_CODES.index(txtone)
+        if rxmode == "Tone":
+            _mem.rx_tone = TONES.index(rxtone)
+        elif rxmode == "DTCS":
+            _mem.rx_tmode = rxpol == "R" and 0x03 or 0x02
+            _mem.rx_tone = self.DTCS_CODES.index(rxtone)
+
+    def set_memory(self, mem):
+        _mem = self._get_mem(mem.number - 1)
+
+        # if empty memmory
+        if mem.empty:
+            if mem.number <= 22 and self._frs:
+                _mem.set_raw("\xFF" * 8 + "\x00" * 5 + "\xFF" * 3)
+                FRS_FREQ = int(FRS_FREQS[mem.number - 1] * 100000)
+                _mem.rxfreq = _mem.txfreq = FRS_FREQ
+                _mem.isnarrow = True
+                if mem.number >= 8 and mem.number <= 14:
+                    _mem.lowpower = True
+                else:
+                    _mem.lowpower = False
+            else:
+                _mem.set_raw("\xFF" * 8 + "\x00" * 4 + "\x03" + "\xFF" * 3)
+
+            return mem
+
+        _mem.set_raw("\x00" * 13 + "\xFF" * 3)
+
+        # frequency
+        _mem.rxfreq = mem.freq / 10
+
+        if mem.duplex == "off":
+            for i in range(0, 4):
+                _mem.txfreq[i].set_raw("\xFF")
+        elif mem.duplex == "split":
+            _mem.txfreq = mem.offset / 10
+        elif mem.duplex == "+":
+            _mem.txfreq = (mem.freq + mem.offset) / 10
+        elif mem.duplex == "-":
+            _mem.txfreq = (mem.freq - mem.offset) / 10
+        else:
+            _mem.txfreq = mem.freq / 10
+
+        # wide/narrow
+        _mem.isnarrow = mem.mode == "NFM"
+
+        # tone data
+        self._set_tone(mem, _mem)
+
+        # tx power
+        if mem.power:
+            _mem.lowpower = self.POWER_LEVELS.index(mem.power)
+        else:
+            _mem.lowpower = 0
+
+        return mem
+
+    def get_settings(self):
+        _settings = self._memobj.settings
+        basic = RadioSettingGroup("basic", "Basic Settings")
+        top = RadioSettings(basic)
+
+        # Menu 03
+        rs = RadioSettingValueInteger(0, 9, _settings.squelch)
+        rset = RadioSetting("squelch", "Squelch Level", rs)
+        basic.append(rset)
+
+        # Menu 11
+        rs = RadioSettingValueList(TOT_LIST, TOT_LIST[_settings.tot])
+        rset = RadioSetting("tot", "Time-out timer", rs)
+        basic.append(rset)
+
+        # Menu 06
+        rs = RadioSettingValueList(VOX_LIST, VOX_LIST[_settings.vox])
+        rset = RadioSetting("vox", "VOX Level", rs)
+        basic.append(rset)
+
+        # Menu 15 (BF-T8)
+        rs = RadioSettingValueList(VOICE_LIST, VOICE_LIST[_settings.voice])
+        rset = RadioSetting("voice", "Voice", rs)
+        basic.append(rset)
+
+        # Menu 12
+        rs = RadioSettingValueBoolean(_settings.bcl)
+        rset = RadioSetting("bcl", "Busy Channel Lockout", rs)
+        basic.append(rset)
+
+        # Menu 10
+        rs = RadioSettingValueBoolean(_settings.save)
+        rset = RadioSetting("save", "Battery Saver", rs)
+        basic.append(rset)
+
+        # Menu 08
+        rs = RadioSettingValueBoolean(_settings.tdr)
+        rset = RadioSetting("tdr", "Dual Watch", rs)
+        basic.append(rset)
+
+        # Menu 05
+        rs = RadioSettingValueBoolean(_settings.beep)
+        rset = RadioSetting("beep", "Beep", rs)
+        basic.append(rset)
+
+        # Menu 04
+        rs = RadioSettingValueList(ABR_LIST, ABR_LIST[_settings.abr])
+        rset = RadioSetting("abr", "Back Light", rs)
+        basic.append(rset)
+
+        # Menu 13
+        rs = RadioSettingValueList(RING_LIST, RING_LIST[_settings.ring])
+        rset = RadioSetting("ring", "Ring", rs)
+        basic.append(rset)
+
+        rs = RadioSettingValueBoolean(not _settings.ste)
+        rset = RadioSetting("ste", "Squelch Tail Eliminate", rs)
+        basic.append(rset)
+
+        #
+
+        rs = RadioSettingValueInteger(1, self._upper, _settings.mra)
+        rset = RadioSetting("mra", "MR A Channel #", rs)
+        basic.append(rset)
+
+        rs = RadioSettingValueInteger(1, self._upper, _settings.mrb)
+        rset = RadioSetting("mrb", "MR B Channel #", rs)
+        basic.append(rset)
+
+        rs = RadioSettingValueList(AB_LIST, AB_LIST[_settings.disp_ab])
+        rset = RadioSetting("disp_ab", "Selected Display Line", rs)
+        basic.append(rset)
+
+        rs = RadioSettingValueList(WX_LIST, WX_LIST[_settings.wx])
+        rset = RadioSetting("wx", "NOAA WX Radio", rs)
+        basic.append(rset)
+
+        def myset_freq(setting, obj, atrb, mult):
+            """ Callback to set frequency by applying multiplier"""
+            value = int(float(str(setting.value)) * mult)
+            setattr(obj, atrb, value)
+            return
+
+        # FM Broadcast Settings
+        val = _settings.fmcur
+        val = val / 10.0
+        val_low = 76.0
+        if val < val_low or val > 108.0:
+            val = 90.4
+        rx = RadioSettingValueFloat(val_low, 108.0, val, 0.1, 1)
+        rset = RadioSetting("settings.fmcur", "Broadcast FM Radio (MHz)", rx)
+        rset.set_apply_callback(myset_freq, _settings, "fmcur", 10)
+        basic.append(rset)
+
+        rs = RadioSettingValueList(WORKMODE_LIST,
+                                   WORKMODE_LIST[_settings.workmode])
+        rset = RadioSetting("workmode", "Work Mode", rs)
+        basic.append(rset)
+
+        rs = RadioSettingValueList(AREA_LIST, AREA_LIST[_settings.area])
+        rs.set_mutable(False)
+        rset = RadioSetting("area", "Area", rs)
+        basic.append(rset)
+
+        return top
+
+    def set_settings(self, settings):
+        for element in settings:
+            if not isinstance(element, RadioSetting):
+                self.set_settings(element)
+                continue
+            else:
+                try:
+                    if "." in element.get_name():
+                        bits = element.get_name().split(".")
+                        obj = self._memobj
+                        for bit in bits[:-1]:
+                            obj = getattr(obj, bit)
+                        setting = bits[-1]
+                    else:
+                        obj = self._memobj.settings
+                        setting = element.get_name()
+
+                    if element.has_apply_callback():
+                        LOG.debug("Using apply callback")
+                        element.run_apply_callback()
+                    elif setting == "ste":
+                        setattr(obj, setting, not int(element.value))
+                    elif element.value.get_mutable():
+                        LOG.debug("Setting %s = %s" % (setting, element.value))
+                        setattr(obj, setting, element.value)
+                except Exception, e:
+                    LOG.debug(element.get_name())
+                    raise
+
+    @classmethod
+    def match_model(cls, filedata, filename):
+        # This radio has always been post-metadata, so never do
+        # old-school detection
+        return False
+
+
+class BFU9Alias(chirp_common.Alias):
+    VENDOR = "Baofeng"
+    MODEL = "BF-U9"
+
+
+class AR8Alias(chirp_common.Alias):
+    VENDOR = "Arcshell"
+    MODEL = "AR-8"
+
+
+ at directory.register
+class BaofengBFT8Generic(BFT8Radio):
+    ALIASES = [BFU9Alias, AR8Alias, ]
diff -r b04ba05b7b64 -r ccf8f96034cd tools/cpep8.manifest
--- a/tools/cpep8.manifest      Wed Apr 28 07:50:34 2021 -0700
+++ b/tools/cpep8.manifest      Wed May 19 20:58:53 2021 -0400
@@ -19,6 +19,7 @@
 ./chirp/drivers/baofeng_common.py
 ./chirp/drivers/baofeng_uv3r.py
 ./chirp/drivers/baofeng_wp970i.py
+./chirp/drivers/bf-t8.py
 ./chirp/drivers/bjuv55.py
 ./chirp/drivers/btech.py
 ./chirp/drivers/ft1500m.py
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Baofeng_BF-T8.img
Type: application/octet-stream
Size: 3081 bytes
Desc: not available
Url : http://intrepid.danplanet.com/pipermail/chirp_devel/attachments/20210519/a3564068/attachment-0001.img 


More information about the chirp_devel mailing list