Project

General

Profile

Bug #11715 » wouxun.py

Dan Smith, 11/28/2024 05:17 PM

 
# Copyright 2011 Dan Smith <dsmith@danplanet.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 3 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/>.

"""Wouxun radios management module"""

import time
import logging
from chirp import util, chirp_common, bitwise, memmap, errors, directory
from chirp.settings import RadioSetting, RadioSettingGroup, \
RadioSettingValueBoolean, RadioSettingValueList, \
RadioSettingValueInteger, RadioSettingValueString, \
RadioSettingValueFloat, RadioSettings
from chirp.drivers.wouxun_common import wipe_memory, do_download, do_upload

LOG = logging.getLogger(__name__)

FREQ_ENCODE_TABLE = [0x7, 0xa, 0x0, 0x9, 0xb, 0x2, 0xe, 0x1, 0x3, 0xf]


def encode_freq(freq):
"""Convert frequency (4 decimal digits) to Wouxun format (2 bytes)"""
enc = 0
div = 1000
for i in range(0, 4):
enc <<= 4
enc |= FREQ_ENCODE_TABLE[int((freq/div) % 10)]
div /= 10
return enc


def decode_freq(data):
"""Convert from Wouxun format (2 bytes) to frequency (4 decimal digits)"""
freq = 0
shift = 12
for i in range(0, 4):
freq *= 10
freq += FREQ_ENCODE_TABLE.index((data >> shift) & 0xf)
shift -= 4
# LOG.debug("data %04x freq %d shift %d" % (data, freq, shift))
return freq


@directory.register
class KGUVD1PRadio(chirp_common.CloneModeRadio,
chirp_common.ExperimentalRadio):
"""Wouxun KG-UVD1P,UV2,UV3"""
VENDOR = "Wouxun"
MODEL = "KG-UVD1P"
_model = b"KG669V"

_querymodel = (b"HiWOUXUN\x02", b"PROGUV6X\x02")

CHARSET = list("0123456789") + \
[chr(x + ord("A")) for x in range(0, 26)] + list("?+-")

POWER_LEVELS = [chirp_common.PowerLevel("High", watts=5.00),
chirp_common.PowerLevel("Low", watts=1.00)]

valid_freq = [(136000000, 175000000), (216000000, 520000000)]

_MEM_FORMAT = """
#seekto 0x0010;
struct {
lbcd rx_freq[4];
lbcd tx_freq[4];
ul16 rx_tone;
ul16 tx_tone;
u8 _3_unknown_1:4,
bcl:1,
_3_unknown_2:3;
u8 splitdup:1,
skip:1,
power_high:1,
iswide:1,
_2_unknown_2:4;
u8 unknown;
u8 _0_unknown_1:3,
iswidex:1,
_0_unknown_2:4;
} memory[199];

#seekto 0x0842;
u16 fm_presets_0[9];

#seekto 0x0882;
u16 fm_presets_1[9];

#seekto 0x0970;
struct {
u16 vhf_rx_start;
u16 vhf_rx_stop;
u16 uhf_rx_start;
u16 uhf_rx_stop;
u16 vhf_tx_start;
u16 vhf_tx_stop;
u16 uhf_tx_start;
u16 uhf_tx_stop;
} freq_ranges;

#seekto 0x0E00;
struct {
char welcome1[6];
char welcome2[6];
char single_band[6];
} strings;

#seekto 0x0E20;
struct {
u8 unknown_flag_01:6,
vfo_b_ch_disp:2;
u8 unknown_flag_02:5,
vfo_a_fr_step:3;
u8 unknown_flag_03:4,
vfo_a_squelch:4;
u8 unknown_flag_04:7,
power_save:1;
u8 unknown_flag_05:8;
u8 unknown_flag_06:6,
roger_beep:2;
u8 unknown_flag_07:2,
transmit_time_out:6;
u8 unknown_flag_08:4,
vox:4;
u8 unknown_1[4];
u8 unknown_flag_09:6,
voice:2;
u8 unknown_flag_10:7,
beep:1;
u8 unknown_flag_11:7,
ani_id_enable:1;
u8 unknown_2[2];
u8 unknown_flag_12:5,
vfo_b_fr_step:3;
u8 unknown_3[1];
u8 unknown_flag_13:3,
ani_id_tx_delay:5;
u8 unknown_4[1];
u8 unknown_flag_14:6,
ani_id_sidetone:2;
u8 unknown_flag_15:4,
tx_time_out_alert:4;
u8 unknown_flag_16:6,
vfo_a_ch_disp:2;
u8 unknown_flag_27:6,
scan_mode:2;
u8 unknown_flag_28:7,
kbd_lock:1;
u8 unknown_flag_17:6,
ponmsg:2;
u8 unknown_flag_18:5,
pf1_function:3;
u8 unknown_5[1];
u8 unknown_flag_19:7,
auto_backlight:1;
u8 unknown_flag_20:7,
sos_ch:1;
u8 unknown_6;
u8 sd_available;
u8 unknown_flag_21:7,
auto_lock_kbd:1;
u8 unknown_flag_22:4,
vfo_b_squelch:4;
u8 unknown_7[1];
u8 unknown_flag_23:7,
stopwatch:1;
u8 vfo_a_cur_chan;
u8 unknown_flag_24:7,
dual_band_receive:1;
u8 current_vfo:1,
unknown_flag_26:7;
u8 unknown_8[2];
u8 mode_password[6];
u8 reset_password[6];
u8 ani_id_content[6];
u8 unknown_flag_25:7,
menu_available:1;
u8 unknown_9[1];
u8 priority_chan;
u8 vfo_b_cur_chan;
} settings;

#seekto 0x1008;
struct {
u8 unknown[8];
u8 name[6];
u8 pad[2];
} names[199];
"""

@classmethod
def get_prompts(cls):
rp = chirp_common.RadioPrompts()
rp.experimental = \
('This version of the Wouxun driver allows you to modify the '
'frequency range settings of your radio. This has been tested '
'and reports from other users indicate that it is a safe '
'thing to do. However, modifications to this value may have '
'unintended consequences, including damage to your device. '
'You have been warned. Proceed at your own risk!')
rp.pre_download = _(
"1. Turn radio off.\n"
"2. Connect cable to mic/spkr connector.\n"
"3. Make sure connector is firmly connected.\n"
"4. Turn radio on.\n"
"5. Ensure that the radio is tuned to channel with no"
" activity.\n"
"6. Click OK to download image from device.\n")
rp.pre_upload = _(
"1. Turn radio off.\n"
"2. Connect cable to mic/spkr connector.\n"
"3. Make sure connector is firmly connected.\n"
"4. Turn radio on.\n"
"5. Ensure that the radio is tuned to channel with no"
" activity.\n"
"6. Click OK to upload image to device.\n")
return rp

@classmethod
def _get_querymodel(cls):
if isinstance(cls._querymodel, str):
while True:
yield cls._querymodel
else:
i = 0
while True:
yield cls._querymodel[i % len(cls._querymodel)]
i += 1

def _identify(self):
"""Do the original Wouxun identification dance"""
query = self._get_querymodel()
for _i in range(0, 10):
self.pipe.write(next(query))
resp = self.pipe.read(9)
if len(resp) != 9:
LOG.debug("Got:\n%s" % util.hexprint(resp))
LOG.info("Retrying identification...")
time.sleep(1)
continue
if resp[2:8] != self._model:
raise Exception("I can't talk to this model (%s)" %
util.hexprint(resp))
return
if len(resp) == 0:
raise Exception("Radio not responding")
else:
raise Exception("Unable to identify radio")

def _start_transfer(self):
"""Tell the radio to go into transfer mode"""
self.pipe.write(b"\x02\x06")
time.sleep(0.05)
ack = self.pipe.read(1)
if ack != b"\x06":
raise Exception("Radio refused transfer mode")

def _download(self):
"""Talk to an original Wouxun and do a download"""
try:
self._identify()
self._start_transfer()
return do_download(self, 0x0000, 0x2000, 0x0040)
except errors.RadioError:
raise
except Exception as e:
raise errors.RadioError("Failed to communicate with radio: %s" % e)

def _upload(self):
"""Talk to an original Wouxun and do an upload"""
try:
self._identify()
self._start_transfer()
return do_upload(self, 0x0000, 0x2000, 0x0010)
except errors.RadioError:
raise
except Exception as e:
raise errors.RadioError("Failed to communicate with radio: %s" % e)

def sync_in(self):
self._mmap = self._download()
self.process_mmap()

def sync_out(self):
self._upload()

def process_mmap(self):
if len(self._mmap.get_packed()) != 8192:
LOG.info("Fixing old-style Wouxun image")
# Originally, CHIRP's Wouxun image had eight bytes of
# static data, followed by the first memory at offset
# 0x0008. Between 0.1.11 and 0.1.12, this was fixed to 16
# bytes of (whatever) followed by the first memory at
# offset 0x0010, like the radio actually stores it. So,
# if we find one of those old ones, convert it to the new
# format, padding 16 bytes of 0xFF in front.
self._mmap = memmap.MemoryMap(
("\xFF" * 16) + self._mmap.get_packed()[8:8184])
self._memobj = bitwise.parse(self._MEM_FORMAT, self._mmap)
# This sets our frequency ranges, so run it once after we process the
# mmap to make sure they're set for later
self.get_settings()

def get_features(self):
rf = chirp_common.RadioFeatures()
rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
rf.valid_cross_modes = [
"Tone->Tone",
"Tone->DTCS",
"DTCS->Tone",
"DTCS->",
"->Tone",
"->DTCS",
"DTCS->DTCS",
]
rf.valid_modes = ["FM", "NFM"]
rf.valid_power_levels = self.POWER_LEVELS
rf.valid_bands = self.valid_freq
rf.valid_characters = "".join(self.CHARSET)
rf.valid_name_length = 6
rf.valid_duplexes = ["", "+", "-", "split", "off"]
rf.valid_tuning_steps = [5.0, 6.25, 10.0, 12.5, 25.0, 50.0, 100.0]
rf.has_ctone = True
rf.has_rx_dtcs = True
rf.has_cross = True
rf.has_tuning_step = False
rf.has_bank = False
rf.has_settings = True
rf.memory_bounds = (1, 128)
rf.can_odd_split = True
return rf

def get_settings(self):
freq_ranges = RadioSettingGroup("freq_ranges", "Freq Ranges")
fm_preset = RadioSettingGroup("fm_preset", "FM Presets")
cfg_s = RadioSettingGroup("cfg_settings", "Configuration Settings")
group = RadioSettings(cfg_s, freq_ranges, fm_preset)

rs = RadioSetting("menu_available", "Menu Available",
RadioSettingValueBoolean(
self._memobj.settings.menu_available))
cfg_s.append(rs)

rs = RadioSetting("vhf_rx_start", "1st band RX Lower Limit (MHz)",
RadioSettingValueInteger(
50, 174, decode_freq(
self._memobj.freq_ranges.vhf_rx_start)))
freq_ranges.append(rs)
rs = RadioSetting("vhf_rx_stop", "1st band RX Upper Limit (MHz)",
RadioSettingValueInteger(
50, 174, decode_freq(
self._memobj.freq_ranges.vhf_rx_stop)))
freq_ranges.append(rs)
rs = RadioSetting("uhf_rx_start", "2nd band RX Lower Limit (MHz)",
RadioSettingValueInteger(
136, 520, decode_freq(
self._memobj.freq_ranges.uhf_rx_start)))
freq_ranges.append(rs)
rs = RadioSetting("uhf_rx_stop", "2nd band RX Upper Limit (MHz)",
RadioSettingValueInteger(
136, 520, decode_freq(
self._memobj.freq_ranges.uhf_rx_stop)))
freq_ranges.append(rs)
rs = RadioSetting("vhf_tx_start", "1st band TX Lower Limit (MHz)",
RadioSettingValueInteger(
50, 174, decode_freq(
self._memobj.freq_ranges.vhf_tx_start)))
freq_ranges.append(rs)
rs = RadioSetting("vhf_tx_stop", "1st TX Upper Limit (MHz)",
RadioSettingValueInteger(
50, 174, decode_freq(
self._memobj.freq_ranges.vhf_tx_stop)))
freq_ranges.append(rs)
rs = RadioSetting("uhf_tx_start", "2nd band TX Lower Limit (MHz)",
RadioSettingValueInteger(
136, 520, decode_freq(
self._memobj.freq_ranges.uhf_tx_start)))
freq_ranges.append(rs)
rs = RadioSetting("uhf_tx_stop", "2nd band TX Upper Limit (MHz)",
RadioSettingValueInteger(
136, 520, decode_freq(
self._memobj.freq_ranges.uhf_tx_stop)))
freq_ranges.append(rs)

# tell the decoded ranges to UI
freq_ranges = self._memobj.freq_ranges
self.valid_freq = \
[(decode_freq(freq_ranges.vhf_rx_start) * 1000000,
(decode_freq(freq_ranges.vhf_rx_stop) + 1) * 1000000),
(decode_freq(freq_ranges.uhf_rx_start) * 1000000,
(decode_freq(freq_ranges.uhf_rx_stop) + 1) * 1000000)]

def _filter(name):
filtered = ""
for char in str(name):
if char in chirp_common.CHARSET_ASCII:
filtered += char
else:
filtered += " "
return filtered

# add some radio specific settings
options = ["Off", "Welcome", "V bat"]
rs = RadioSetting("ponmsg", "Poweron message", RadioSettingValueList(
options, current_index=self._memobj.settings.ponmsg))
cfg_s.append(rs)
rs = RadioSetting("strings.welcome1", "Power-On Message 1",
RadioSettingValueString(
0, 6, _filter(self._memobj.strings.welcome1)))
cfg_s.append(rs)
rs = RadioSetting("strings.welcome2", "Power-On Message 2",
RadioSettingValueString(
0, 6, _filter(self._memobj.strings.welcome2)))
cfg_s.append(rs)
rs = RadioSetting("strings.single_band", "Single Band Message",
RadioSettingValueString(
0, 6, _filter(self._memobj.strings.single_band)))
cfg_s.append(rs)
options = ["Channel", "ch/freq", "Name", "VFO"]
rs = RadioSetting(
"vfo_a_ch_disp", "VFO A Channel disp mode",
RadioSettingValueList(
options, current_index=self._memobj.settings.vfo_a_ch_disp))
cfg_s.append(rs)
rs = RadioSetting(
"vfo_b_ch_disp", "VFO B Channel disp mode",
RadioSettingValueList(
options, current_index=self._memobj.settings.vfo_b_ch_disp))
cfg_s.append(rs)
options = ["5.0", "6.25", "10.0", "12.5", "25.0", "50.0", "100.0"]
rs = RadioSetting(
"vfo_a_fr_step", "VFO A Frequency Step",
RadioSettingValueList(
options, current_index=self._memobj.settings.vfo_a_fr_step))
cfg_s.append(rs)
rs = RadioSetting(
"vfo_b_fr_step", "VFO B Frequency Step",
RadioSettingValueList(
options, current_index=self._memobj.settings.vfo_b_fr_step))
cfg_s.append(rs)
rs = RadioSetting("vfo_a_squelch", "VFO A Squelch",
RadioSettingValueInteger(
0, 9, self._memobj.settings.vfo_a_squelch))
cfg_s.append(rs)
rs = RadioSetting("vfo_b_squelch", "VFO B Squelch",
RadioSettingValueInteger(
0, 9, self._memobj.settings.vfo_b_squelch))
cfg_s.append(rs)
rs = RadioSetting("vfo_a_cur_chan", "VFO A current channel",
RadioSettingValueInteger(
1, 128, self._memobj.settings.vfo_a_cur_chan))
cfg_s.append(rs)
rs = RadioSetting("vfo_b_cur_chan", "VFO B current channel",
RadioSettingValueInteger(
1, 128, self._memobj.settings.vfo_b_cur_chan))
cfg_s.append(rs)
rs = RadioSetting("priority_chan", "Priority channel",
RadioSettingValueInteger(
0, 199, self._memobj.settings.priority_chan))
cfg_s.append(rs)
rs = RadioSetting("power_save", "Power save",
RadioSettingValueBoolean(
self._memobj.settings.power_save))
cfg_s.append(rs)
options = ["Off", "Scan", "Lamp", "SOS", "Radio"]
rs = RadioSetting(
"pf1_function", "PF1 Function select",
RadioSettingValueList(
options,
current_index=self._memobj.settings.pf1_function))
cfg_s.append(rs)
options = ["Off", "Begin", "End", "Both"]
rs = RadioSetting("roger_beep", "Roger beep select",
RadioSettingValueList(
options,
current_index=self._memobj.settings.roger_beep))
cfg_s.append(rs)
options = ["%s" % x for x in range(15, 615, 15)]
transmit_time_out = options[self._memobj.settings.transmit_time_out]
rs = RadioSetting("transmit_time_out", "TX Time-out Timer",
RadioSettingValueList(
options, transmit_time_out))
cfg_s.append(rs)
rs = RadioSetting("tx_time_out_alert", "TX Time-out Alert",
RadioSettingValueInteger(
0, 10, self._memobj.settings.tx_time_out_alert))
cfg_s.append(rs)
rs = RadioSetting("vox", "Vox",
RadioSettingValueInteger(
0, 10, self._memobj.settings.vox))
cfg_s.append(rs)
options = ["Off", "Chinese", "English"]
rs = RadioSetting("voice", "Voice", RadioSettingValueList(
options, current_index=self._memobj.settings.voice))
cfg_s.append(rs)
rs = RadioSetting("beep", "Beep",
RadioSettingValueBoolean(
self._memobj.settings.beep))
cfg_s.append(rs)
rs = RadioSetting("ani_id_enable", "ANI id enable",
RadioSettingValueBoolean(
self._memobj.settings.ani_id_enable))
cfg_s.append(rs)
rs = RadioSetting("ani_id_tx_delay", "ANI id tx delay",
RadioSettingValueInteger(
0, 30, self._memobj.settings.ani_id_tx_delay))
cfg_s.append(rs)
options = ["Off", "Key", "ANI", "Key+ANI"]
rs = RadioSetting(
"ani_id_sidetone", "ANI id sidetone",
RadioSettingValueList(
options,
current_index=self._memobj.settings.ani_id_sidetone))
cfg_s.append(rs)
options = ["Time", "Carrier", "Search"]
rs = RadioSetting("scan_mode", "Scan mode",
RadioSettingValueList(
options,
current_index=self._memobj.settings.scan_mode))
cfg_s.append(rs)
rs = RadioSetting("kbd_lock", "Keyboard lock",
RadioSettingValueBoolean(
self._memobj.settings.kbd_lock))
cfg_s.append(rs)
rs = RadioSetting("auto_lock_kbd", "Auto lock keyboard",
RadioSettingValueBoolean(
self._memobj.settings.auto_lock_kbd))
cfg_s.append(rs)
rs = RadioSetting("auto_backlight", "Auto backlight",
RadioSettingValueBoolean(
self._memobj.settings.auto_backlight))
cfg_s.append(rs)
options = ["CH A", "CH B"]
rs = RadioSetting("sos_ch", "SOS CH",
RadioSettingValueList(
options,
current_index=self._memobj.settings.sos_ch))
cfg_s.append(rs)
rs = RadioSetting("stopwatch", "Stopwatch",
RadioSettingValueBoolean(
self._memobj.settings.stopwatch))
cfg_s.append(rs)
rs = RadioSetting("dual_band_receive", "Dual band receive",
RadioSettingValueBoolean(
self._memobj.settings.dual_band_receive))
cfg_s.append(rs)
options = ["VFO A", "VFO B"]
rs = RadioSetting("current_vfo", "Current VFO",
RadioSettingValueList(
options,
current_index=self._memobj.settings.current_vfo))
cfg_s.append(rs)

options = ["Dual", "Single"]
rs = RadioSetting(
"sd_available", "Single/Dual Band",
RadioSettingValueList(
options, current_index=self._memobj.settings.sd_available))
cfg_s.append(rs)

_pwd = self._memobj.settings.mode_password
rs = RadioSetting("mode_password", "Mode password (000000 disabled)",
RadioSettingValueInteger(0, 9, _pwd[0]),
RadioSettingValueInteger(0, 9, _pwd[1]),
RadioSettingValueInteger(0, 9, _pwd[2]),
RadioSettingValueInteger(0, 9, _pwd[3]),
RadioSettingValueInteger(0, 9, _pwd[4]),
RadioSettingValueInteger(0, 9, _pwd[5]))
cfg_s.append(rs)
_pwd = self._memobj.settings.reset_password
rs = RadioSetting("reset_password", "Reset password (000000 disabled)",
RadioSettingValueInteger(0, 9, _pwd[0]),
RadioSettingValueInteger(0, 9, _pwd[1]),
RadioSettingValueInteger(0, 9, _pwd[2]),
RadioSettingValueInteger(0, 9, _pwd[3]),
RadioSettingValueInteger(0, 9, _pwd[4]),
RadioSettingValueInteger(0, 9, _pwd[5]))
cfg_s.append(rs)

dtmfchars = "0123456789 *#ABCD"
_codeobj = self._memobj.settings.ani_id_content
_code = "".join([dtmfchars[x] for x in _codeobj if int(x) < 0x1F])
val = RadioSettingValueString(0, 6, _code, False)
val.set_charset(dtmfchars)
rs = RadioSetting("settings.ani_id_content", "PTT-ID Code", val)

def apply_ani_id(setting, obj):
value = []
for j in range(0, 6):
try:
value.append(dtmfchars.index(str(setting.value)[j]))
except IndexError:
value.append(0xFF)
obj.ani_id_content = value
rs.set_apply_callback(apply_ani_id, self._memobj.settings)
cfg_s.append(rs)

for i in range(0, 9):
if self._memobj.fm_presets_0[i] != 0xFFFF:
used = True
preset = self._memobj.fm_presets_0[i] / 10.0 + 76
else:
used = False
preset = 76
rs = RadioSetting("fm_presets_0_%1i" % i,
"Team 1 Location %i" % (i + 1),
RadioSettingValueBoolean(used),
RadioSettingValueFloat(76, 108, preset, 0.1, 1))
fm_preset.append(rs)
for i in range(0, 9):
if self._memobj.fm_presets_1[i] != 0xFFFF:
used = True
preset = self._memobj.fm_presets_1[i] / 10.0 + 76
else:
used = False
preset = 76
rs = RadioSetting("fm_presets_1_%1i" % i,
"Team 2 Location %i" % (i + 1),
RadioSettingValueBoolean(used),
RadioSettingValueFloat(76, 108, preset, 0.1, 1))
fm_preset.append(rs)

return group

def set_settings(self, settings):
for element in settings:
if not isinstance(element, RadioSetting):
if element.get_name() == "freq_ranges":
self._set_freq_settings(element)
elif element.get_name() == "fm_preset":
self._set_fm_preset(element)
else:
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()
else:
LOG.debug("Setting %s = %s" % (setting, element.value))
setattr(obj, setting, element.value)
except Exception:
LOG.debug(element.get_name())
raise

def _set_fm_preset(self, settings):
obj = self._memobj
for element in settings:
try:
(bank, index) = \
(int(a) for a in element.get_name().split("_")[-2:])
val = element.value
if val[0].get_value():
value = int(val[1].get_value()*10-760)
else:
value = 0xffff
LOG.debug("Setting fm_presets_%1i[%1i] = %s" %
(bank, index, value))
if bank == 0:
setting = self._memobj.fm_presets_0
else:
setting = self._memobj.fm_presets_1
setting[index] = value
except Exception:
LOG.debug(element.get_name())
raise

def _set_freq_settings(self, settings):
for element in settings:
try:
setattr(self._memobj.freq_ranges,
element.get_name(),
encode_freq(int(element.value)))
except Exception:
LOG.debug(element.get_name())
raise

def get_raw_memory(self, number):
return repr(self._memobj.memory[number - 1])

def _get_tone(self, _mem, mem):
def _get_dcs(val):
code = int("%03o" % (val & 0x07FF))
pol = (val & 0x8000) and "R" or "N"
return code, pol

tpol = False
if _mem.tx_tone != 0xFFFF and _mem.tx_tone > 0x2800:
tcode, tpol = _get_dcs(_mem.tx_tone)
mem.dtcs = tcode
txmode = "DTCS"
elif _mem.tx_tone != 0xFFFF:
mem.rtone = _mem.tx_tone / 10.0
txmode = "Tone"
else:
txmode = ""

rpol = False
if _mem.rx_tone != 0xFFFF and _mem.rx_tone > 0x2800:
rcode, rpol = _get_dcs(_mem.rx_tone)
mem.rx_dtcs = rcode
rxmode = "DTCS"
elif _mem.rx_tone != 0xFFFF:
mem.ctone = _mem.rx_tone / 10.0
rxmode = "Tone"
else:
rxmode = ""

if txmode == "Tone" and not rxmode:
mem.tmode = "Tone"
elif txmode == rxmode and txmode == "Tone" and mem.rtone == mem.ctone:
mem.tmode = "TSQL"
elif txmode == rxmode and txmode == "DTCS" and mem.dtcs == mem.rx_dtcs:
mem.tmode = "DTCS"
elif rxmode or txmode:
mem.tmode = "Cross"
mem.cross_mode = "%s->%s" % (txmode, rxmode)

# always set it even if no dtcs is used
mem.dtcs_polarity = "%s%s" % (tpol or "N", rpol or "N")

LOG.debug("Got TX %s (%i) RX %s (%i)" %
(txmode, _mem.tx_tone, rxmode, _mem.rx_tone))

def _is_txinh(self, _mem):
return _mem.tx_freq.get_raw() == b"\xFF\xFF\xFF\xFF"

def get_memory(self, number):
_mem = self._memobj.memory[number - 1]
_nam = self._memobj.names[number - 1]

mem = chirp_common.Memory()
mem.number = number

if _mem.get_raw() == (b"\xff" * 16):
mem.empty = True
return mem

mem.freq = int(_mem.rx_freq) * 10
if _mem.splitdup:
mem.duplex = "split"
elif self._is_txinh(_mem):
mem.duplex = "off"
elif int(_mem.rx_freq) < int(_mem.tx_freq):
mem.duplex = "+"
elif int(_mem.rx_freq) > int(_mem.tx_freq):
mem.duplex = "-"

if mem.duplex == "" or mem.duplex == "off":
mem.offset = 0
elif mem.duplex == "split":
mem.offset = int(_mem.tx_freq) * 10
else:
mem.offset = abs(int(_mem.tx_freq) - int(_mem.rx_freq)) * 10

if not _mem.skip:
mem.skip = "S"
if not _mem.iswide:
mem.mode = "NFM"

self._get_tone(_mem, mem)

mem.power = self.POWER_LEVELS[not _mem.power_high]

for i in _nam.name:
if i == 0xFF:
break
mem.name += self.CHARSET[i]

mem.extra = RadioSettingGroup("extra", "Extra")
bcl = RadioSetting("bcl", "BCL",
RadioSettingValueBoolean(bool(_mem.bcl)))
bcl.set_doc("Busy Channel Lockout")
mem.extra.append(bcl)

options = ["NFM", "FM"]
iswidex = RadioSetting("iswidex", "Mode TX(KG-UV6X)",
RadioSettingValueList(
options, current_index=_mem.iswidex))
iswidex.set_doc("Mode TX")
mem.extra.append(iswidex)

return mem

def _set_tone(self, mem, _mem):
def _set_dcs(code, pol):
val = int("%i" % code, 8) + 0x2800
if pol == "R":
val += 0x8000
return val

rx_mode = tx_mode = None
rx_tone = tx_tone = 0xFFFF

if mem.tmode == "Tone":
tx_mode = "Tone"
rx_mode = None
tx_tone = int(mem.rtone * 10)
elif mem.tmode == "TSQL":
rx_mode = tx_mode = "Tone"
rx_tone = tx_tone = int(mem.ctone * 10)
elif mem.tmode == "DTCS":
tx_mode = rx_mode = "DTCS"
tx_tone = _set_dcs(mem.dtcs, mem.dtcs_polarity[0])
rx_tone = _set_dcs(mem.dtcs, mem.dtcs_polarity[1])
elif mem.tmode == "Cross":
tx_mode, rx_mode = mem.cross_mode.split("->")
if tx_mode == "DTCS":
tx_tone = _set_dcs(mem.dtcs, mem.dtcs_polarity[0])
elif tx_mode == "Tone":
tx_tone = int(mem.rtone * 10)
if rx_mode == "DTCS":
rx_tone = _set_dcs(mem.rx_dtcs, mem.dtcs_polarity[1])
elif rx_mode == "Tone":
rx_tone = int(mem.ctone * 10)

_mem.rx_tone = rx_tone
_mem.tx_tone = tx_tone

LOG.debug("Set TX %s (%i) RX %s (%i)" %
(tx_mode, _mem.tx_tone, rx_mode, _mem.rx_tone))

def set_memory(self, mem):
_mem = self._memobj.memory[mem.number - 1]
_nam = self._memobj.names[mem.number - 1]

if mem.empty:
wipe_memory(_mem, "\xFF")
return

if _mem.get_raw() == (b"\xFF" * 16):
wipe_memory(_mem, "\x00")

_mem.rx_freq = int(mem.freq / 10)
if mem.duplex == "split":
_mem.tx_freq = int(mem.offset / 10)
elif mem.duplex == "off":
_mem.tx_freq.fill_raw(b"\xFF")
elif mem.duplex == "+":
_mem.tx_freq = int(mem.freq / 10) + int(mem.offset / 10)
elif mem.duplex == "-":
_mem.tx_freq = int(mem.freq / 10) - int(mem.offset / 10)
else:
_mem.tx_freq = int(mem.freq / 10)
_mem.splitdup = mem.duplex == "split"
_mem.skip = mem.skip != "S"
_mem.iswide = mem.mode != "NFM"

self._set_tone(mem, _mem)

if mem.power:
_mem.power_high = not self.POWER_LEVELS.index(mem.power)
else:
_mem.power_high = True

_nam.name = [0xFF] * 6
for i in range(0, len(mem.name)):
try:
_nam.name[i] = self.CHARSET.index(mem.name[i])
except IndexError:
raise Exception("Character `%s' not supported")

for setting in mem.extra:
setattr(_mem, setting.get_name(), setting.value)

@classmethod
def match_model(cls, filedata, filename):
# New-style image (CHIRP 0.1.12)
if len(filedata) == 8192 and \
filedata[0x60:0x64] != b"2009" and \
filedata[0x170:0x173] != b"LX-" and \
filedata[0x1f77:0x1f7d] == b"\xff\xff\xff\xff\xff\xff" and \
filedata[0x0d70:0x0d80] == \
b"\xff\xff\xff\xff\xff\xff\xff\xff" \
b"\xff\xff\xff\xff\xff\xff\xff\xff":
# those areas are (seems to be) unused
return True
# Old-style image (CHIRP 0.1.11)
if len(filedata) == 8200 and \
filedata[0:4] == b"\x01\x00\x00\x00":
return True
return False


@directory.register
class KGUV6DRadio(KGUVD1PRadio):
"""Wouxun KG-UV6 (D and X variants)"""
MODEL = "KG-UV6"

_querymodel = (b"HiWXUVD1\x02", b"HiKGUVD1\x02")

_MEM_FORMAT = """
#seekto 0x0010;
struct {
lbcd rx_freq[4];
lbcd tx_freq[4];
ul16 rx_tone;
ul16 tx_tone;
u8 _3_unknown_1:4,
bcl:1,
_3_unknown_2:3;
u8 splitdup:1,
skip:1,
power_high:1,
iswide:1,
_2_unknown_2:4;
u8 pad;
u8 _0_unknown_1:3,
iswidex:1,
_0_unknown_2:4;
} memory[199];

#seekto 0x0F00;
struct {
char welcome1[6];
char welcome2[6];
char single_band[6];
} strings;

#seekto 0x0F20;
struct {
u8 unknown_flag_01:6,
vfo_b_ch_disp:2;
u8 unknown_flag_02:5,
vfo_a_fr_step:3;
u8 unknown_flag_03:4,
vfo_a_squelch:4;
u8 unknown_flag_04:7,
power_save:1;
u8 unknown_flag_05:5,
pf2_function:3;
u8 unknown_flag_06:6,
roger_beep:2;
u8 unknown_flag_07:2,
transmit_time_out:6;
u8 unknown_flag_08:4,
vox:4;
u8 unknown_1[4];
u8 unknown_flag_09:6,
voice:2;
u8 unknown_flag_10:7,
beep:1;
u8 unknown_flag_11:7,
ani_id_enable:1;
u8 unknown_2[2];
u8 unknown_flag_12:5,
vfo_b_fr_step:3;
u8 unknown_3[1];
u8 unknown_flag_13:3,
ani_id_tx_delay:5;
u8 unknown_4[1];
u8 unknown_flag_14:6,
ani_id_sidetone:2;
u8 unknown_flag_15:4,
tx_time_out_alert:4;
u8 unknown_flag_16:6,
vfo_a_ch_disp:2;
u8 unknown_flag_20:6,
scan_mode:2;
u8 unknown_flag_31:7,
kbd_lock:1;
u8 unknown_flag_17:6,
ponmsg:2;
u8 unknown_flag_18:5,
pf1_function:3;
u8 unknown_5[1];
u8 unknown_flag_19:7,
auto_backlight:1;
u8 unknown_flag_26:7,
sos_ch:1;
u8 unknown_6;
u8 sd_available;
u8 unknown_flag_32:7,
auto_lock_kbd:1;
u8 unknown_flag_22:4,
vfo_b_squelch:4;
u8 unknown_7[1];
u8 unknown_flag_23:7,
stopwatch:1;
u8 vfo_a_cur_chan;
u8 unknown_flag_24:7,
dual_band_receive:1;
u8 current_vfo:1,
unknown_flag_33:7;
u8 unknown_8[2];
u8 mode_password[6];
u8 reset_password[6];
u8 ani_id_content[6];
u8 unknown_flag_25:7,
menu_available:1;
u8 unknown_9[1];
u8 priority_chan;
u8 vfo_b_cur_chan;
} settings;

//#seekto 0x0f60;
struct {
lbcd rx_freq[4];
lbcd tx_freq[4];
ul16 rx_tone;
ul16 tx_tone;
u8 _3_unknown_3:4,
bcl:1,
_3_unknown_4:3;
u8 splitdup:1,
_2_unknown_3:1,
power_high:1,
iswide:1,
_2_unknown_4:4;
u8 pad[2];
} vfo_settings[2];

#seekto 0x0f82;
u16 fm_presets_0[9];

#seekto 0x0ff0;
struct {
u16 vhf_rx_start;
u16 vhf_rx_stop;
u16 uhf_rx_start;
u16 uhf_rx_stop;
u16 vhf_tx_start;
u16 vhf_tx_stop;
u16 uhf_tx_start;
u16 uhf_tx_stop;
} freq_ranges;

#seekto 0x1010;
struct {
u8 name[6];
u8 pad[10];
} names[199];

#seekto 0x1f60;
struct {
u8 unknown_flag_26:6,
tx_offset_dir:2;
u8 tx_offset[6];
u8 pad[9];
} vfo_offset[2];

#seekto 0x1f82;
u16 fm_presets_1[9];
"""

def get_features(self):
rf = KGUVD1PRadio.get_features(self)
rf.memory_bounds = (1, 199)
rf.valid_tuning_steps = [2.5, 5.0, 6.25, 10.0, 12.5, 25.0, 50.0,
100.0]
return rf

def get_settings(self):
freq_ranges = RadioSettingGroup("freq_ranges", "Freq Ranges")
fm_preset = RadioSettingGroup("fm_preset", "FM Presets")
cfg_s = RadioSettingGroup("cfg_settings", "Configuration Settings")
group = RadioSettings(cfg_s, freq_ranges, fm_preset)

rs = RadioSetting("menu_available", "Menu Available",
RadioSettingValueBoolean(
self._memobj.settings.menu_available))
cfg_s.append(rs)

rs = RadioSetting("vhf_rx_start", "VHF RX Lower Limit (MHz)",
RadioSettingValueInteger(
1, 1000, decode_freq(
self._memobj.freq_ranges.vhf_rx_start)))
freq_ranges.append(rs)
rs = RadioSetting("vhf_rx_stop", "VHF RX Upper Limit (MHz)",
RadioSettingValueInteger(
1, 1000, decode_freq(
self._memobj.freq_ranges.vhf_rx_stop)))
freq_ranges.append(rs)
rs = RadioSetting("uhf_rx_start", "UHF RX Lower Limit (MHz)",
RadioSettingValueInteger(
1, 1000, decode_freq(
self._memobj.freq_ranges.uhf_rx_start)))
freq_ranges.append(rs)
rs = RadioSetting("uhf_rx_stop", "UHF RX Upper Limit (MHz)",
RadioSettingValueInteger(
1, 1000, decode_freq(
self._memobj.freq_ranges.uhf_rx_stop)))
freq_ranges.append(rs)
rs = RadioSetting("vhf_tx_start", "VHF TX Lower Limit (MHz)",
RadioSettingValueInteger(
1, 1000, decode_freq(
self._memobj.freq_ranges.vhf_tx_start)))
freq_ranges.append(rs)
rs = RadioSetting("vhf_tx_stop", "VHF TX Upper Limit (MHz)",
RadioSettingValueInteger(
1, 1000, decode_freq(
self._memobj.freq_ranges.vhf_tx_stop)))
freq_ranges.append(rs)
rs = RadioSetting("uhf_tx_start", "UHF TX Lower Limit (MHz)",
RadioSettingValueInteger(
1, 1000, decode_freq(
self._memobj.freq_ranges.uhf_tx_start)))
freq_ranges.append(rs)
rs = RadioSetting("uhf_tx_stop", "UHF TX Upper Limit (MHz)",
RadioSettingValueInteger(
1, 1000, decode_freq(
self._memobj.freq_ranges.uhf_tx_stop)))
freq_ranges.append(rs)

# tell the decoded ranges to UI
freq_ranges = self._memobj.freq_ranges
self.valid_freq = \
[(decode_freq(freq_ranges.vhf_rx_start) * 1000000,
(decode_freq(freq_ranges.vhf_rx_stop) + 1) * 1000000),
(decode_freq(freq_ranges.uhf_rx_start) * 1000000,
(decode_freq(freq_ranges.uhf_rx_stop) + 1) * 1000000)]

def _filter(name):
filtered = ""
for char in str(name):
if char in chirp_common.CHARSET_ASCII:
filtered += char
else:
filtered += " "
return filtered

# add some radio specific settings
options = ["Off", "Welcome", "V bat", "N/A(KG-UV6X)"]
rs = RadioSetting("ponmsg", "Poweron message", RadioSettingValueList(
options, current_index=self._memobj.settings.ponmsg))
cfg_s.append(rs)
rs = RadioSetting("strings.welcome1", "Power-On Message 1",
RadioSettingValueString(
0, 6, _filter(self._memobj.strings.welcome1)))
cfg_s.append(rs)
rs = RadioSetting("strings.welcome2", "Power-On Message 2",
RadioSettingValueString(
0, 6, _filter(self._memobj.strings.welcome2)))
cfg_s.append(rs)
rs = RadioSetting("strings.single_band", "Single Band Message",
RadioSettingValueString(
0, 6, _filter(self._memobj.strings.single_band)))
cfg_s.append(rs)
options = ["Channel", "ch/freq", "Name", "VFO"]
rs = RadioSetting(
"vfo_a_ch_disp", "VFO A Channel disp mode",
RadioSettingValueList(
options, current_index=self._memobj.settings.vfo_a_ch_disp))
cfg_s.append(rs)
rs = RadioSetting(
"vfo_b_ch_disp", "VFO B Channel disp mode",
RadioSettingValueList(
options, current_index=self._memobj.settings.vfo_b_ch_disp))
cfg_s.append(rs)
options = \
["2.5", "5.0", "6.25", "10.0", "12.5", "25.0", "50.0", "100.0"]
rs = RadioSetting(
"vfo_a_fr_step", "VFO A Frequency Step",
RadioSettingValueList(
options, current_index=self._memobj.settings.vfo_a_fr_step))
cfg_s.append(rs)
rs = RadioSetting(
"vfo_b_fr_step", "VFO B Frequency Step",
RadioSettingValueList(
options, current_index=self._memobj.settings.vfo_b_fr_step))
cfg_s.append(rs)
rs = RadioSetting("vfo_a_squelch", "VFO A Squelch",
RadioSettingValueInteger(
0, 9, self._memobj.settings.vfo_a_squelch))
cfg_s.append(rs)
rs = RadioSetting("vfo_b_squelch", "VFO B Squelch",
RadioSettingValueInteger(
0, 9, self._memobj.settings.vfo_b_squelch))
cfg_s.append(rs)
rs = RadioSetting("vfo_a_cur_chan", "VFO A current channel",
RadioSettingValueInteger(
1, 199, self._memobj.settings.vfo_a_cur_chan))
cfg_s.append(rs)
rs = RadioSetting("vfo_b_cur_chan", "VFO B current channel",
RadioSettingValueInteger(
1, 199, self._memobj.settings.vfo_b_cur_chan))
cfg_s.append(rs)
rs = RadioSetting("priority_chan", "Priority channel",
RadioSettingValueInteger(
0, 199, self._memobj.settings.priority_chan))
cfg_s.append(rs)
rs = RadioSetting("power_save", "Power save",
RadioSettingValueBoolean(
self._memobj.settings.power_save))
cfg_s.append(rs)
options = ["Off", "Scan", "Lamp", "SOS", "Radio"]
rs = RadioSetting(
"pf1_function", "PF1 Function select",
RadioSettingValueList(
options,
current_index=self._memobj.settings.pf1_function))
cfg_s.append(rs)
options = ["Off", "Radio", "fr/ch", "Rpt", "Stopwatch", "Lamp", "SOS"]
rs = RadioSetting(
"pf2_function", "PF2 Function select",
RadioSettingValueList(
options,
current_index=self._memobj.settings.pf2_function))
cfg_s.append(rs)
options = ["Off", "Begin", "End", "Both"]
rs = RadioSetting("roger_beep", "Roger beep select",
RadioSettingValueList(
options,
current_index=self._memobj.settings.roger_beep))
cfg_s.append(rs)
options = ["%s" % x for x in range(15, 615, 15)]
transmit_time_out = options[self._memobj.settings.transmit_time_out]
rs = RadioSetting("transmit_time_out", "TX Time-out Timer",
RadioSettingValueList(
options, transmit_time_out))
cfg_s.append(rs)
rs = RadioSetting("tx_time_out_alert", "TX Time-out Alert",
RadioSettingValueInteger(
0, 10, self._memobj.settings.tx_time_out_alert))
cfg_s.append(rs)
rs = RadioSetting("vox", "Vox",
RadioSettingValueInteger(
0, 10, self._memobj.settings.vox))
cfg_s.append(rs)
options = ["Off", "Chinese", "English"]
rs = RadioSetting("voice", "Voice", RadioSettingValueList(
options, current_index=self._memobj.settings.voice))
cfg_s.append(rs)
rs = RadioSetting("beep", "Beep",
RadioSettingValueBoolean(
self._memobj.settings.beep))
cfg_s.append(rs)
rs = RadioSetting("ani_id_enable", "ANI id enable",
RadioSettingValueBoolean(
self._memobj.settings.ani_id_enable))
cfg_s.append(rs)
rs = RadioSetting("ani_id_tx_delay", "ANI id tx delay",
RadioSettingValueInteger(
0, 30, self._memobj.settings.ani_id_tx_delay))
cfg_s.append(rs)
options = ["Off", "Key", "ANI", "Key+ANI"]
rs = RadioSetting(
"ani_id_sidetone", "ANI id sidetone",
RadioSettingValueList(
options,
current_index=self._memobj.settings.ani_id_sidetone))
cfg_s.append(rs)
options = ["Time", "Carrier", "Search"]
rs = RadioSetting("scan_mode", "Scan mode",
RadioSettingValueList(
options,
current_index=self._memobj.settings.scan_mode))
cfg_s.append(rs)
rs = RadioSetting("kbd_lock", "Keyboard lock",
RadioSettingValueBoolean(
self._memobj.settings.kbd_lock))
cfg_s.append(rs)
rs = RadioSetting("auto_lock_kbd", "Auto lock keyboard",
RadioSettingValueBoolean(
self._memobj.settings.auto_lock_kbd))
cfg_s.append(rs)
rs = RadioSetting("auto_backlight", "Auto backlight",
RadioSettingValueBoolean(
self._memobj.settings.auto_backlight))
cfg_s.append(rs)
options = ["CH A", "CH B"]
rs = RadioSetting("sos_ch", "SOS CH", RadioSettingValueList(
options, current_index=self._memobj.settings.sos_ch))
cfg_s.append(rs)
rs = RadioSetting("stopwatch", "Stopwatch",
RadioSettingValueBoolean(
self._memobj.settings.stopwatch))
cfg_s.append(rs)
rs = RadioSetting("dual_band_receive", "Dual band receive",
RadioSettingValueBoolean(
self._memobj.settings.dual_band_receive))
cfg_s.append(rs)
options = ["VFO A", "VFO B"]
rs = RadioSetting("current_vfo", "Current VFO",
RadioSettingValueList(
options,
current_index=self._memobj.settings.current_vfo))
cfg_s.append(rs)

options = ["Dual", "Single"]
rs = RadioSetting(
"sd_available", "Single/Dual Band",
RadioSettingValueList(
options, current_index=self._memobj.settings.sd_available))
cfg_s.append(rs)

_pwd = self._memobj.settings.mode_password
rs = RadioSetting("mode_password", "Mode password (000000 disabled)",
RadioSettingValueInteger(0, 9, _pwd[0]),
RadioSettingValueInteger(0, 9, _pwd[1]),
RadioSettingValueInteger(0, 9, _pwd[2]),
RadioSettingValueInteger(0, 9, _pwd[3]),
RadioSettingValueInteger(0, 9, _pwd[4]),
RadioSettingValueInteger(0, 9, _pwd[5]))
cfg_s.append(rs)
_pwd = self._memobj.settings.reset_password
rs = RadioSetting("reset_password", "Reset password (000000 disabled)",
RadioSettingValueInteger(0, 9, _pwd[0]),
RadioSettingValueInteger(0, 9, _pwd[1]),
RadioSettingValueInteger(0, 9, _pwd[2]),
RadioSettingValueInteger(0, 9, _pwd[3]),
RadioSettingValueInteger(0, 9, _pwd[4]),
RadioSettingValueInteger(0, 9, _pwd[5]))
cfg_s.append(rs)

dtmfchars = "0123456789 *#ABCD"
_codeobj = self._memobj.settings.ani_id_content
_code = "".join([dtmfchars[x] for x in _codeobj if int(x) < 0x1F])
val = RadioSettingValueString(0, 6, _code, False)
val.set_charset(dtmfchars)
rs = RadioSetting("settings.ani_id_content", "ANI Code", val)

def apply_ani_id(setting, obj):
value = []
for j in range(0, 6):
try:
value.append(dtmfchars.index(str(setting.value)[j]))
except IndexError:
value.append(0xFF)
obj.ani_id_content = value
rs.set_apply_callback(apply_ani_id, self._memobj.settings)
cfg_s.append(rs)

for i in range(0, 9):
if self._memobj.fm_presets_0[i] != 0xFFFF:
used = True
preset = self._memobj.fm_presets_0[i]/10.0+76
else:
used = False
preset = 76
rs = RadioSetting("fm_presets_0_%1i" % i,
"Team 1 Location %i" % (i+1),
RadioSettingValueBoolean(used),
RadioSettingValueFloat(76, 108, preset, 0.1, 1))
fm_preset.append(rs)
for i in range(0, 9):
if self._memobj.fm_presets_1[i] != 0xFFFF:
used = True
preset = self._memobj.fm_presets_1[i]/10.0+76
else:
used = False
preset = 76
rs = RadioSetting("fm_presets_1_%1i" % i,
"Team 2 Location %i" % (i+1),
RadioSettingValueBoolean(used),
RadioSettingValueFloat(76, 108, preset, 0.1, 1))
fm_preset.append(rs)

return group

def set_settings(self, settings):
for element in settings:
if not isinstance(element, RadioSetting):
if element.get_name() == "freq_ranges":
self._set_freq_settings(element)
elif element.get_name() == "fm_preset":
self._set_fm_preset(element)
else:
self.set_settings(element)
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()
else:
LOG.debug("Setting %s = %s" % (setting, element.value))
setattr(obj, setting, element.value)
except Exception:
LOG.debug(element.get_name())
raise

def _set_fm_preset(self, settings):
obj = self._memobj
for element in settings:
try:
(bank, index) = \
(int(a) for a in element.get_name().split("_")[-2:])
val = element.value
if val[0].get_value():
value = int(val[1].get_value()*10-760)
else:
value = 0xffff
LOG.debug("Setting fm_presets_%1i[%1i] = %s" %
(bank, index, value))
if bank == 0:
setting = self._memobj.fm_presets_0
else:
setting = self._memobj.fm_presets_1
setting[index] = value
except Exception:
LOG.debug(element.get_name())
raise

@classmethod
def match_model(cls, filedata, filename):
if len(filedata) == 8192 and \
filedata[0x1f77:0x1f7d] == b"WELCOM":
return True
return False


@directory.register
class KG816Radio(KGUVD1PRadio, chirp_common.ExperimentalRadio):
"""Wouxun KG-816"""
MODEL = "KG-816"

_querymodel = b"HiWOUXUN\x02"

_MEM_FORMAT = """
#seekto 0x0010;
struct {
lbcd rx_freq[4];
lbcd tx_freq[4];
ul16 rx_tone;
ul16 tx_tone;
u8 _3_unknown_1:4,
bcl:1,
_3_unknown_2:3;
u8 splitdup:1,
skip:1,
power_high:1,
iswide:1,
_2_unknown_2:4;
u8 unknown;
u8 _0_unknown_1:3,
iswidex:1,
_0_unknown_2:4;
} memory[199];

#seekto 0x0d70;
struct {
u16 vhf_rx_start;
u16 vhf_rx_stop;
u16 uhf_rx_start;
u16 uhf_rx_stop;
u16 vhf_tx_start;
u16 vhf_tx_stop;
u16 uhf_tx_start;
u16 uhf_tx_stop;
} freq_ranges;

#seekto 0x1010;
struct {
u8 name[6];
u8 pad[10];
} names[199];
"""

@classmethod
def get_experimental_warning(cls):
return ('We have not that much information on this model '
'up to now we only know it has the same memory '
'organization of KGUVD1 but uses 199 memories. '
'it has been reported to work but '
'proceed at your own risk!')

def get_features(self):
rf = KGUVD1PRadio.get_features(self)
rf.memory_bounds = (1, 199) # this is the only known difference
return rf

def get_settings(self):
freq_ranges = RadioSettingGroup("freq_ranges",
"Freq Ranges (read only)")
group = RadioSettings(freq_ranges)

rs = RadioSetting("vhf_rx_start", "vhf rx start",
RadioSettingValueInteger(
66, 520, decode_freq(
self._memobj.freq_ranges.vhf_rx_start)))
freq_ranges.append(rs)
rs = RadioSetting("vhf_rx_stop", "vhf rx stop",
RadioSettingValueInteger(
66, 520, decode_freq(
self._memobj.freq_ranges.vhf_rx_stop)))
freq_ranges.append(rs)
rs = RadioSetting("uhf_rx_start", "uhf rx start",
RadioSettingValueInteger(
66, 520, decode_freq(
self._memobj.freq_ranges.uhf_rx_start)))
freq_ranges.append(rs)
rs = RadioSetting("uhf_rx_stop", "uhf rx stop",
RadioSettingValueInteger(
66, 520, decode_freq(
self._memobj.freq_ranges.uhf_rx_stop)))
freq_ranges.append(rs)
rs = RadioSetting("vhf_tx_start", "vhf tx start",
RadioSettingValueInteger(
66, 520, decode_freq(
self._memobj.freq_ranges.vhf_tx_start)))
freq_ranges.append(rs)
rs = RadioSetting("vhf_tx_stop", "vhf tx stop",
RadioSettingValueInteger(
66, 520, decode_freq(
self._memobj.freq_ranges.vhf_tx_stop)))
freq_ranges.append(rs)
rs = RadioSetting("uhf_tx_start", "uhf tx start",
RadioSettingValueInteger(
66, 520, decode_freq(
self._memobj.freq_ranges.uhf_tx_start)))
freq_ranges.append(rs)
rs = RadioSetting("uhf_tx_stop", "uhf tx stop",
RadioSettingValueInteger(
66, 520, decode_freq(
self._memobj.freq_ranges.uhf_tx_stop)))
freq_ranges.append(rs)

# tell the decoded ranges to UI
self.valid_freq = \
[(decode_freq(self._memobj.freq_ranges.vhf_rx_start) * 1000000,
(decode_freq(self._memobj.freq_ranges.vhf_rx_stop)+1) * 1000000)]

return group

@classmethod
def match_model(cls, filedata, filename):
if len(filedata) == 8192 and \
filedata[0x60:0x64] != b"2009" and \
filedata[0x170:0x173] != b"LX-" and \
filedata[0xF7E:0xF80] != b"\x01\xE2" and \
filedata[0x1f77:0x1f7d] == b"\xff\xff\xff\xff\xff\xff" and \
filedata[0x0d70:0x0d80] != (
b"\xff\xff\xff\xff\xff\xff\xff\xff"
b"\xff\xff\xff\xff\xff\xff\xff\xff"):
return True
return False


@directory.register
class KG818Radio(KG816Radio):
"""Wouxun KG-818"""
MODEL = "KG-818"

@classmethod
def match_model(cls, filedata, filename):
return False
(5-5/8)