|
# Copyright 2012 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 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 struct
|
|
import time
|
|
import logging
|
|
|
|
|
|
from chirp import chirp_common, errors, util, directory, memmap
|
|
from chirp import bitwise
|
|
from chirp.settings import InvalidValueError, RadioSetting, \
|
|
RadioSettingGroup, RadioSettingValueFloat, \
|
|
RadioSettingValueList, RadioSettingValueBoolean, \
|
|
RadioSettingValueString, RadioSettings
|
|
from textwrap import dedent
|
|
from chirp import bandplan_na
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
AIRBAND = (108000000, 135999999)
|
|
MEM_FORMAT = """
|
|
#seekto 0x0008;
|
|
struct {
|
|
lbcd rxfreq[4];
|
|
lbcd txfreq[4];
|
|
lbcd rxtone[2];
|
|
lbcd txtone[2];
|
|
u8 unused1;
|
|
u8 pttid:2,
|
|
freqhop:1,
|
|
unused3:1,
|
|
unused4:1,
|
|
bcl:1,
|
|
unused5:1,
|
|
unused2:1;
|
|
u8 unused6:1,
|
|
unused7:1,
|
|
lowpower:2,
|
|
wide:1,
|
|
unused8:1,
|
|
offset:2;
|
|
u8 unused10;
|
|
} memory[200];
|
|
|
|
#seekto 0x0CA8;
|
|
struct {
|
|
u8 txled:1,
|
|
rxled:1,
|
|
unused11:1,
|
|
ham:1,
|
|
gmrs:1,
|
|
unused14:1,
|
|
unused15:1,
|
|
pritx:1;
|
|
u8 scanmode:2,
|
|
unused16:1,
|
|
keyautolock:1,
|
|
unused17:1,
|
|
btnvoice:1,
|
|
unknown18:1,
|
|
voiceprompt:1;
|
|
u8 fmworkmode:1,
|
|
sync:1,
|
|
tonevoice:2,
|
|
fmrec:1,
|
|
mdfa:1,
|
|
aworkmode:2;
|
|
u8 ponmsg:2,
|
|
unused19:1,
|
|
mdfb:1,
|
|
unused20:1,
|
|
dbrx:1,
|
|
bworkmode:2;
|
|
u8 ablock;
|
|
u8 bblock;
|
|
u8 fmroad;
|
|
u8 unused21:1,
|
|
tailclean:1,
|
|
rogerprompt:1,
|
|
unused23:1,
|
|
unused24:1,
|
|
voxgain:3;
|
|
u8 astep:4,
|
|
bstep:4;
|
|
u8 squelch;
|
|
u8 tot;
|
|
u8 lang;
|
|
u8 save;
|
|
u8 ligcon;
|
|
u8 voxdelay;
|
|
u8 onlychmode:1,
|
|
unused:6,
|
|
alarm:1;
|
|
} settings;
|
|
|
|
//#seekto 0x0CB8;
|
|
struct {
|
|
u8 ofseta[4];
|
|
} aoffset;
|
|
|
|
//#seekto 0x0CBC;
|
|
struct {
|
|
u8 ofsetb[4];
|
|
} boffset;
|
|
|
|
#seekto 0x0CD8;
|
|
struct{
|
|
lbcd fmblock[4];
|
|
}fmmode[25];
|
|
|
|
#seekto 0x0D48;
|
|
struct {
|
|
char name[8];
|
|
u8 unknown2[8];
|
|
} names[200];
|
|
|
|
#seekto 0x1A08;
|
|
lbit usedflags[200];
|
|
|
|
#seekto 0x1a28;
|
|
lbit scanadd[200];
|
|
|
|
#seekto 0x1B38;
|
|
lbcd fmvfo[4];
|
|
|
|
#seekto 0x1B58;
|
|
struct {
|
|
lbcd rxfreqa[4];
|
|
lbcd txfreq[4];
|
|
u8 rxtone[2];
|
|
u8 txtone[2];
|
|
u8 unused1;
|
|
u8 pttid:2,
|
|
specialqta:1,
|
|
unused3:1,
|
|
unused4:1,
|
|
bcl:1,
|
|
unused5:1,
|
|
unused2:1;
|
|
u8 unused6:1,
|
|
unused7:1,
|
|
lowpower:2,
|
|
wide:1,
|
|
unused8:1,
|
|
offset:2;
|
|
u8 unused10;
|
|
} vfoa;
|
|
|
|
//#seekto 0x1B68;
|
|
struct {
|
|
lbcd rxfreqb[4];
|
|
lbcd txfreq[4];
|
|
u8 rxtoneb[2];
|
|
u8 txtone[2];
|
|
u8 unused1;
|
|
u8 pttid:2,
|
|
specialqtb:1,
|
|
unused3:1,
|
|
unused4:1,
|
|
bclb:1,
|
|
unused5:1,
|
|
unused2:1;
|
|
u8 unused6:1,
|
|
unused7:1,
|
|
lowpowerb:2,
|
|
wideb:1,
|
|
unused8:1,
|
|
offsetb:2;
|
|
u8 unused10;
|
|
} vfob;
|
|
|
|
//#seekto 0x1B78;
|
|
lbit fmusedflags[32];
|
|
|
|
#seekto 0x1c08;
|
|
struct {
|
|
char msg1[16];
|
|
char msg2[16];
|
|
char msg3[16];
|
|
} poweron_msg;
|
|
|
|
|
|
#seekto 0x1CC8;
|
|
struct{
|
|
u8 stopkey1;
|
|
u8 ssidekey1;
|
|
u8 ssidekey2;
|
|
u8 ltopkey2;
|
|
u8 lsidekey3;
|
|
u8 lsidekey4;
|
|
u8 unused25[10];
|
|
} press;
|
|
|
|
#seekto 0x1E28;
|
|
struct{
|
|
u8 idcode[3];
|
|
}icode;
|
|
|
|
#seekto 0x1E31;
|
|
struct{
|
|
u8 gcode;
|
|
}groupcode;
|
|
|
|
#seekto 0x1E38;
|
|
struct{
|
|
u8 group1[7];
|
|
}group1;
|
|
|
|
#seekto 0x1E48;
|
|
struct{
|
|
u8 group2[7];
|
|
}group2;
|
|
|
|
#seekto 0x1E58;
|
|
struct{
|
|
u8 group3[7];
|
|
}group3;
|
|
|
|
#seekto 0x1E68;
|
|
struct{
|
|
u8 group4[7];
|
|
}group4;
|
|
|
|
#seekto 0x1E78;
|
|
struct{
|
|
u8 group5[7];
|
|
}group5;
|
|
|
|
#seekto 0x1E88;
|
|
struct{
|
|
u8 group6[7];
|
|
}group6;
|
|
|
|
#seekto 0x1E98;
|
|
struct{
|
|
u8 group7[7];
|
|
}group7;
|
|
|
|
#seekto 0x1EA8;
|
|
struct{
|
|
u8 group8[7];
|
|
}group8;
|
|
|
|
#seekto 0x1EC8;
|
|
struct{
|
|
u8 scode[7];
|
|
}startcode;
|
|
|
|
#seekto 0x1ED8;
|
|
struct{
|
|
u8 ecode[7];
|
|
}endcode;
|
|
"""
|
|
|
|
MEM_FORMAT_H3 = """
|
|
#seekto 0x0008;
|
|
struct {
|
|
lbcd rxfreq[4];
|
|
lbcd txfreq[4];
|
|
lbcd rxtone[2];
|
|
lbcd txtone[2];
|
|
u8 unused1;
|
|
u8 pttid:2,
|
|
freqhop:1,
|
|
unused3:1,
|
|
unused4:1,
|
|
bcl:1,
|
|
unused5:1,
|
|
unused2:1;
|
|
u8 unused6:1,
|
|
scramble:1,
|
|
lowpower:2,
|
|
wide:1,
|
|
unused8:1,
|
|
offset:2;
|
|
u8 unused10;
|
|
} memory[200];
|
|
|
|
#seekto 0x0C98;
|
|
struct{
|
|
u8 stopkey1;
|
|
u8 ssidekey1;
|
|
u8 ssidekey2;
|
|
u8 ltopkey2;
|
|
u8 lsidekey3;
|
|
u8 lsidekey4;
|
|
} press;
|
|
|
|
#seekto 0x0CA8;
|
|
struct {
|
|
u8 txled:1,
|
|
rxled:1,
|
|
unused11:1,
|
|
ham:1,
|
|
gmrs:1,
|
|
unused14:1,
|
|
unused15:1,
|
|
pritx:1;
|
|
u8 scanmode:2,
|
|
unused16:1,
|
|
keyautolock:1,
|
|
unused17:1,
|
|
btnvoice:1,
|
|
unknown18:1,
|
|
voiceprompt:1;
|
|
u8 fmworkmode:1,
|
|
sync:1,
|
|
tonevoice:2,
|
|
fmrec:1,
|
|
mdfa:1,
|
|
aworkmode:2;
|
|
u8 ponmsg:2,
|
|
unused19:1,
|
|
mdfb:1,
|
|
unused20:1,
|
|
dbrx:1,
|
|
bworkmode:2;
|
|
u8 ablock;
|
|
u8 bblock;
|
|
u8 fmroad;
|
|
u8 unused21:1,
|
|
tailclean:1,
|
|
rogerprompt_:1,
|
|
kill:1,
|
|
stun:1,
|
|
voxgain:3;
|
|
u8 astep:4,
|
|
bstep:4;
|
|
u8 squelch;
|
|
u8 tot;
|
|
u8 rogerprompt:2,
|
|
unused11_4:4,
|
|
lang:1,
|
|
unused11_1:1;
|
|
u8 save;
|
|
u8 ligcon;
|
|
u8 voxdelay;
|
|
u8 onlychmode:1,
|
|
breathled:3,
|
|
unused:3,
|
|
alarm:1;
|
|
} settings;
|
|
|
|
//#seekto 0x0CB8;
|
|
struct {
|
|
u8 ofseta[4];
|
|
} aoffset;
|
|
|
|
//#seekto 0x0CBC;
|
|
struct {
|
|
u8 ofsetb[4];
|
|
} boffset;
|
|
|
|
#seekto 0x0CD8;
|
|
struct{
|
|
lbcd fmblock[4];
|
|
}fmmode[25];
|
|
|
|
#seekto 0x0D48;
|
|
struct {
|
|
char name[8];
|
|
} names[200];
|
|
|
|
#seekto 0x1808;
|
|
struct{
|
|
u8 stuncode[16];
|
|
u8 killcode[16];
|
|
}skcode;
|
|
|
|
//#seekto 0x1828;
|
|
struct{
|
|
u8 idcode[3];
|
|
}icode;
|
|
|
|
#seekto 0x1837;
|
|
struct{
|
|
u8 gcode;
|
|
}groupcode;
|
|
|
|
//#seekto 0x1838;
|
|
struct{
|
|
u8 group1[7];
|
|
}group1;
|
|
|
|
#seekto 0x1848;
|
|
struct{
|
|
u8 group2[7];
|
|
}group2;
|
|
|
|
#seekto 0x1858;
|
|
struct{
|
|
u8 group3[7];
|
|
}group3;
|
|
|
|
#seekto 0x1868;
|
|
struct{
|
|
u8 group4[7];
|
|
}group4;
|
|
|
|
#seekto 0x1878;
|
|
struct{
|
|
u8 group5[7];
|
|
}group5;
|
|
|
|
#seekto 0x1888;
|
|
struct{
|
|
u8 group6[7];
|
|
}group6;
|
|
|
|
#seekto 0x1898;
|
|
struct{
|
|
u8 group7[7];
|
|
}group7;
|
|
|
|
#seekto 0x18A8;
|
|
struct{
|
|
u8 group8[7];
|
|
}group8;
|
|
|
|
#seekto 0x18C8;
|
|
struct{
|
|
u8 scode[7];
|
|
}startcode;
|
|
|
|
#seekto 0x18D8;
|
|
struct{
|
|
u8 ecode[7];
|
|
}endcode;
|
|
|
|
#seekto 0x1908;
|
|
lbit usedflags[200];
|
|
|
|
#seekto 0x1928;
|
|
lbit scanadd[200];
|
|
|
|
#seekto 0x1948;
|
|
lbit fmusedflags[32];
|
|
|
|
#seekto 0x1958;
|
|
struct {
|
|
lbcd rxfreqa[4];
|
|
lbcd txfreq[4];
|
|
u8 rxtone[2];
|
|
u8 txtone[2];
|
|
u8 unused1;
|
|
u8 pttid:2,
|
|
specialqta:1,
|
|
unused3:1,
|
|
unused4:1,
|
|
bcl:1,
|
|
unused5:1,
|
|
unused2:1;
|
|
u8 unused6:1,
|
|
unused7:1,
|
|
lowpower:2,
|
|
wide:1,
|
|
unused8:1,
|
|
offset:2;
|
|
u8 unused10;
|
|
} vfoa;
|
|
|
|
//#seekto 0x1968;
|
|
struct {
|
|
lbcd rxfreqb[4];
|
|
lbcd txfreq[4];
|
|
u8 rxtoneb[2];
|
|
u8 txtone[2];
|
|
u8 unused1;
|
|
u8 pttid:2,
|
|
specialqtb:1,
|
|
unused3:1,
|
|
unused4:1,
|
|
bclb:1,
|
|
unused5:1,
|
|
unused2:1;
|
|
u8 unused6:1,
|
|
unused7:1,
|
|
lowpowerb:2,
|
|
wideb:1,
|
|
unused8:1,
|
|
offsetb:2;
|
|
u8 unused10;
|
|
} vfob;
|
|
|
|
//#seekto 0x1978;
|
|
lbcd fmvfo[4];
|
|
|
|
#seekto 0x1c08;
|
|
struct {
|
|
char msg1[16];
|
|
char msg2[16];
|
|
char msg3[16];
|
|
} poweron_msg;
|
|
|
|
#seekto 0x1f28;
|
|
struct{
|
|
u8 micgain;
|
|
} mic;
|
|
|
|
"""
|
|
|
|
# basic settings
|
|
SQUELCH = ['%s' % x for x in range(0, 10)]
|
|
LIGHT_LIST = ["CONT", "5s", "10s", "15s", "30s"]
|
|
VOICE_PRMPT_LIST = ["OFF", "ON"]
|
|
AUTOLOCK_LIST = ["OFF", "ON"]
|
|
TIME_OUT_LIST = ["OFF", "60s", "120s", "180s"]
|
|
MDFA_LIST = ["Frequency", "Name"]
|
|
MDFB_LIST = ["Frequency", "Name"]
|
|
SYNC_LIST = ["ON", "OFF"]
|
|
LANG_LIST = ["Chinese", "English"]
|
|
BTV_SAVER_LIST = ["OFF", "1:1", "1:2", "1:3", "1:4"]
|
|
DBRX_LIST = ["OFF", "ON"]
|
|
ASTEP_LIST = ["2.50K", "5.00K", "6.25K",
|
|
"10.00K", "12.00K", "25.00K", "50.00K"]
|
|
BSTEP_LIST = ["2.50K", "5.00K", "6.25K",
|
|
"10.00K", "12.00K", "25.00K", "50.00K"]
|
|
SCAN_MODE_LIST = ["TO", "CO", "SE"]
|
|
PRIO_LIST = ["Edit", "Busy"]
|
|
SHORT_KEY_LIST = ["None", "FM Radio", "Lamp", "Monitor",
|
|
"TONE", "Alarm", "Weather"]
|
|
LONG_KEY_LIST = ["None", "FM Radio", "Lamp",
|
|
"Monitor", "TONE", "Alarm", "Weather"]
|
|
BUSYLOCK_LIST = ["Off", "On"]
|
|
PRESS_NAME = ["stopkey1", "ssidekey1", "ssidekey2",
|
|
"ltopkey2", "lsidekey3", "lsidekey4"]
|
|
|
|
VFOA_NAME = ["rxfreqa",
|
|
"txfreq",
|
|
"rxtone",
|
|
"txtone",
|
|
"pttid",
|
|
"specialqta",
|
|
"bcl",
|
|
"lowpower",
|
|
"wide",
|
|
"offset"]
|
|
|
|
VFOB_NAME = ["rxfreqb",
|
|
"txfreq",
|
|
"rxtoneb",
|
|
"txtone",
|
|
"pttid",
|
|
"specialqtb",
|
|
"bclb",
|
|
"lowpowerb",
|
|
"wideb",
|
|
"offsetb"]
|
|
|
|
# KEY
|
|
VOX_GAIN = ["OFF", "1", "2", "3", "4", "5"]
|
|
VOX_DELAY = ["1.05s", "2.0s", "3.0s"]
|
|
PTTID_VALUES = ["Off", "BOT", "EOT", "BOTH"]
|
|
BCLOCK_VALUES = ["Off", "On"]
|
|
FREQHOP_VALUES = ["Off", "On"]
|
|
SCAN_VALUES = ["Del", "Add"]
|
|
|
|
# AB CHANNEL
|
|
A_OFFSET = ["Off", "-", "+"]
|
|
A_TX_POWER = ["Low", "Mid", "High"]
|
|
A_BAND = ["Wide", "Narrow"]
|
|
A_BUSYLOCK = ["Off", "On"]
|
|
A_SPEC_QTDQT = ["Off", "On"]
|
|
A_WORKMODE = ["VFO", "VFO+CH", "CH Mode"]
|
|
|
|
B_OFFSET = ["Off", "-", "+"]
|
|
B_TX_POWER = ["Low", "Mid", "High"]
|
|
B_BAND = ["Wide", "Narrow"]
|
|
B_BUSYLOCK = ["Off", "On"]
|
|
B_SPEC_QTDQT = ["Off", "On"]
|
|
B_WORKMODE = ["VFO", "VFO+CH", "CH Mode"]
|
|
|
|
# FM
|
|
FM_WORKMODE = ["CH", "VFO"]
|
|
FM_CHANNEL = ['%s' % x for x in range(0, 26)]
|
|
|
|
# DTMF
|
|
GROUPCODE = ["", "Off", "*", "#", "A", "B", "C", "D"]
|
|
|
|
AB_LIST = ["A", "B"]
|
|
ALMOD_LIST = ["Site", "Tone", "Code"]
|
|
BANDWIDTH_LIST = ["Wide", "Narrow"]
|
|
COLOR_LIST = ["Off", "Blue", "Orange", "Purple"]
|
|
DTMFSPEED_LIST = ["%s ms" % x for x in range(50, 2010, 10)]
|
|
DTMFST_LIST = ["OFF", "DT-ST", "ANI-ST", "DT+ANI"]
|
|
MODE_LIST = ["Channel", "Name", "Frequency"]
|
|
PONMSG_LIST = ["Full", "Message", "Icon"]
|
|
PTTID_LIST = ["Off", "BOT", "EOT", "Both"]
|
|
PTTIDCODE_LIST = ["%s" % x for x in range(1, 16)]
|
|
RTONE_LIST = ["1000 Hz", "1450 Hz", "1750 Hz", "2100 Hz"]
|
|
RESUME_LIST = ["TO", "CO", "SE"]
|
|
ROGERRX_LIST = ["Off"] + AB_LIST
|
|
RPSTE_LIST = ["OFF"] + ["%s" % x for x in range(1, 11)]
|
|
SAVE_LIST = ["Off", "1:1", "1:2", "1:3", "1:4"]
|
|
SCODE_LIST = ["%s" % x for x in range(1, 16)]
|
|
SHIFTD_LIST = ["Off", "+", "-"]
|
|
STEDELAY_LIST = ["OFF"] + ["%s ms" % x for x in range(100, 1100, 100)]
|
|
STEPS = [2.5, 5.0, 6.25, 10.0, 12.5, 25.0]
|
|
STEP_LIST = [str(x) for x in STEPS]
|
|
STEPS = [2.5, 5.0, 6.25, 10.0, 12.5, 20.0, 25.0, 50.0]
|
|
STEP291_LIST = [str(x) for x in STEPS]
|
|
TDRAB_LIST = ["Off"] + AB_LIST
|
|
TDRCH_LIST = ["CH%s" % x for x in range(1, 129)]
|
|
TIMEOUT_LIST = ["%s sec" % x for x in range(15, 615, 15)] + \
|
|
["Off (if supported by radio)"]
|
|
VOICE_LIST = ["Off", "English", "Chinese"]
|
|
VOX_LIST = ["OFF"] + ["%s" % x for x in range(1, 11)]
|
|
WORKMODE_LIST = ["Frequency", "Channel"]
|
|
# mic
|
|
MIC_GAIN_LIST = ['%s' % x for x in range(0, 10)]
|
|
MIC_GAIN_LIST_H8 = ['%s' % x for x in range(0, 33)]
|
|
H8_LIST = ["TD-H8", "TD-H8-HAM", "TD-H8-GMRS"]
|
|
|
|
GMRS_FREQS = bandplan_na.GMRS_HIRPT
|
|
|
|
NOAA_FREQS = [162550000, 162400000, 162475000, 162425000, 162450000,
|
|
162500000, 162525000, 161650000, 161775000, 161750000,
|
|
162000000]
|
|
|
|
HAM_GMRS_NAME = ["NOAA 1", "NOAA 2", "NOAA 3", "NOAA 4", "NOAA 5", "NOAA 6",
|
|
"NOAA 7", "NOAA 8", "NOAA 9", "NOAA 10", "NOAA 11"]
|
|
|
|
ALL_MODEL = ["TD-H8", "TD-H8-HAM", "TD-H8-GMRS", "TD-H3",
|
|
"TD-H3-HAM", "TD-H3-GMRS"]
|
|
|
|
TD_H8 = b"\x50\x56\x4F\x4A\x48\x1C\x14"
|
|
TD_H3 = b"\x50\x56\x4F\x4A\x48\x5C\x14"
|
|
|
|
|
|
def in_range(freq, ranges):
|
|
for lo, hi in ranges:
|
|
if lo <= freq <= hi:
|
|
return True
|
|
return False
|
|
|
|
|
|
def _do_status(radio, block):
|
|
status = chirp_common.Status()
|
|
status.msg = "Cloning"
|
|
status.cur = block
|
|
status.max = radio._memsize
|
|
radio.status_fn(status)
|
|
|
|
|
|
def _upper_band_from_data(data):
|
|
return data[0x03:0x04]
|
|
|
|
|
|
def _upper_band_from_image(radio):
|
|
return _upper_band_from_data(radio.get_mmap())
|
|
|
|
|
|
def _firmware_version_from_data(data, version_start, version_stop):
|
|
version_tag = data[version_start:version_stop]
|
|
return version_tag
|
|
|
|
|
|
def _firmware_version_from_image(radio):
|
|
version = _firmware_version_from_data(radio.get_mmap(),
|
|
radio._fw_ver_file_start,
|
|
radio._fw_ver_file_stop)
|
|
# LOG.debug("_firmware_version_from_image: " + util.hexprint(version))
|
|
return version
|
|
|
|
|
|
def _do_ident(radio, magic, secondack=True):
|
|
serial = radio.pipe
|
|
serial.timeout = 1
|
|
|
|
LOG.info("Sending Magic: %s" % util.hexprint(magic))
|
|
serial.write(magic)
|
|
ack = serial.read(1)
|
|
|
|
if ack != b"\x06":
|
|
if ack:
|
|
# LOG.debug(repr(ack))
|
|
pass
|
|
raise errors.RadioError("Radio did not respond")
|
|
|
|
serial.write(b"\x02")
|
|
|
|
response = b""
|
|
for i in range(1, 9):
|
|
byte = serial.read(1)
|
|
response += byte
|
|
if byte == b"\xDD":
|
|
break
|
|
|
|
if len(response) in [8, 12]:
|
|
# DEBUG
|
|
LOG.info("Valid response, got this:")
|
|
LOG.info(util.hexprint(response))
|
|
if len(response) == 12:
|
|
ident = response[0] + response[3] + response[5] + response[7:]
|
|
else:
|
|
ident = response
|
|
else:
|
|
# bad response
|
|
msg = "Unexpected response, got this:"
|
|
msg += util.hexprint(response)
|
|
LOG.debug(msg)
|
|
raise errors.RadioError("Unexpected response from radio.")
|
|
|
|
if secondack:
|
|
serial.write(b"\x06")
|
|
ack = serial.read(1)
|
|
if ack != b"\x06":
|
|
raise errors.RadioError("Radio refused clone")
|
|
|
|
return ident
|
|
|
|
|
|
def response_mode(mode):
|
|
data = mode
|
|
return data
|
|
|
|
|
|
def _read_block(radio, start, size):
|
|
serial = radio.pipe
|
|
|
|
cmd = struct.pack(">cHb", b'R', start, size)
|
|
expectedresponse = b"W" + cmd[1:]
|
|
|
|
try:
|
|
serial.write(cmd)
|
|
response = serial.read(5 + size)
|
|
if response[:4] != expectedresponse:
|
|
raise errors.RadioError("Error reading block %04x." % (start))
|
|
block_data = response[4:-1]
|
|
|
|
except Exception:
|
|
raise errors.RadioError("Failed to read block at %04x" % start)
|
|
|
|
return block_data
|
|
|
|
|
|
def _get_radio_firmware_version(radio):
|
|
if radio.MODEL in ALL_MODEL:
|
|
block = _read_block(radio, 0x1B40, 0x20)
|
|
version = block[0:6]
|
|
return version
|
|
|
|
|
|
IDENT_BLACKLIST = {
|
|
b"\x50\x56\x4F\x4A\x48\x1C\x14": "Radio identifies as TIDRADIO TD-H8",
|
|
}
|
|
|
|
|
|
def _ident_radio(radio):
|
|
for magic in radio._idents:
|
|
error = None
|
|
try:
|
|
data = _do_ident(radio, magic)
|
|
return data
|
|
except errors.RadioError as e:
|
|
error = e
|
|
time.sleep(2)
|
|
|
|
if error:
|
|
raise error
|
|
raise errors.RadioError("Radio did not respond")
|
|
|
|
|
|
def _do_download(radio):
|
|
data = _ident_radio(radio)
|
|
append_model = False
|
|
# HAM OR GMRS
|
|
# Determine the walkie-talkie mode
|
|
# TDH8 have three mode:ham, gmrs and normal
|
|
|
|
LOG.info("Radio mode is " + str(data)[2:8])
|
|
LOG.info("Chirp choose mode is " + str(data)[2:8])
|
|
# The Ham and GMRS modes are subclasses of this model TDH8.
|
|
# We compare the radio identification to the value of that class to
|
|
# make sure the user chose the model that matches
|
|
# the radio we're talking to right now. If they do not match,
|
|
# we refuse to talk to the radio until the user selects the correct model.
|
|
|
|
if radio.ident_mode == data:
|
|
LOG.info("Successful match.")
|
|
else:
|
|
msg = ("Model mismatch!")
|
|
raise errors.RadioError(msg)
|
|
|
|
# Main block
|
|
LOG.info("Downloading...")
|
|
|
|
for i in range(0, radio._memsize, 0x20):
|
|
block = _read_block(radio, i, 0x20)
|
|
data += block
|
|
_do_status(radio, i)
|
|
_do_status(radio, radio._memsize)
|
|
LOG.info("done.")
|
|
|
|
if append_model:
|
|
data += radio.MODEL.ljust(8)
|
|
|
|
return memmap.MemoryMapBytes(data)
|
|
|
|
|
|
def _exit_write_block(radio):
|
|
serial = radio.pipe
|
|
try:
|
|
serial.write(b"E")
|
|
|
|
except Exception:
|
|
raise errors.RadioError("Radio refused to exit programming mode")
|
|
|
|
|
|
def _write_block(radio, addr, data):
|
|
serial = radio.pipe
|
|
cmd = struct.pack(">cHb", b'W', addr, 0x20)
|
|
data = radio.get_mmap()[addr + 8: addr + 40]
|
|
# The checksum needs to be in the last
|
|
check_sum = bytes([sum(data) & 0xFF])
|
|
data += check_sum
|
|
used_data = cmd + data
|
|
serial.write(used_data)
|
|
|
|
ack = radio.pipe.read(1)
|
|
if ack != b"\x06":
|
|
raise errors.RadioError("Radio refused to accept block 0x%04x" % addr)
|
|
|
|
|
|
def _do_upload(radio):
|
|
data = _ident_radio(radio)
|
|
radio_version = _get_radio_firmware_version(radio)
|
|
LOG.info("Radio Version is %s" % repr(radio_version))
|
|
|
|
if radio.ident_mode == data:
|
|
LOG.info("Successful match.")
|
|
else:
|
|
msg = ("Model mismatch!")
|
|
raise errors.RadioError(msg)
|
|
|
|
# Main block
|
|
LOG.debug("Uploading...")
|
|
|
|
for start_addr, end_addr in radio._ranges_main:
|
|
for addr in range(start_addr, end_addr, 0x20):
|
|
_write_block(radio, addr, 0x20)
|
|
_do_status(radio, addr)
|
|
_exit_write_block(radio)
|
|
LOG.debug("Upload all done.")
|
|
|
|
|
|
TDH8_CHARSET = chirp_common.CHARSET_ALPHANUMERIC + \
|
|
"!@#$%^&*()+-=[]:\";'<>?,./"
|
|
|
|
|
|
@directory.register
|
|
class TDH8(chirp_common.CloneModeRadio):
|
|
"""TIDRADIO TD-H8"""
|
|
VENDOR = "TIDRADIO"
|
|
MODEL = "TD-H8"
|
|
ident_mode = b'P31183\xff\xff'
|
|
BAUD_RATE = 38400
|
|
NEEDS_COMPAT_SERIAL = False
|
|
_memsize = 0x1eef
|
|
_ranges_main = [(0x0000, 0x1eef)]
|
|
_idents = [TD_H8]
|
|
_txbands = [(136000000, 175000000), (400000000, 521000000)]
|
|
_rxbands = []
|
|
_aux_block = True
|
|
_tri_power = True
|
|
_gmrs = False
|
|
_ham = False
|
|
_mem_params = (0x1F2F)
|
|
|
|
# offset of fw version in image file
|
|
_fw_ver_file_start = 0x1838
|
|
_fw_ver_file_stop = 0x1846
|
|
_valid_chars = TDH8_CHARSET
|
|
_tx_power = [chirp_common.PowerLevel("Low", watts=1.00),
|
|
chirp_common.PowerLevel("Mid", watts=4.00),
|
|
chirp_common.PowerLevel("High", watts=8.00)]
|
|
|
|
@classmethod
|
|
def get_prompts(cls):
|
|
rp = chirp_common.RadioPrompts()
|
|
rp.pre_download = (dedent("""\
|
|
1. Turn radio off.
|
|
2. Connect cable to mic/spkr connector.
|
|
3. Make sure connector is firmly connected.
|
|
4. Turn radio on (volume may need to be set at 100%).
|
|
5. Ensure that the radio is tuned to channel with no activity.
|
|
6. Click OK to download image from device."""))
|
|
rp.pre_upload = (dedent("""\
|
|
1. Turn radio off.
|
|
2. Connect cable to mic/spkr connector.
|
|
3. Make sure connector is firmly connected.
|
|
4. Turn radio on (volume may need to be set at 100%).
|
|
5. Ensure that the radio is tuned to channel with no activity.
|
|
6. Click OK to upload image to device."""))
|
|
return rp
|
|
|
|
def get_features(self):
|
|
rf = chirp_common.RadioFeatures()
|
|
rf.has_settings = True
|
|
rf.has_bank = False
|
|
rf.has_cross = True
|
|
rf.has_ctone = True
|
|
rf.has_rx_dtcs = True
|
|
rf.has_tuning_step = False
|
|
rf.has_ctone = True
|
|
rf.can_odd_split = True
|
|
rf.valid_name_length = 8
|
|
rf.valid_characters = self._valid_chars
|
|
rf.valid_skips = ["", "S"]
|
|
rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
|
|
rf.valid_cross_modes = [
|
|
"Tone->Tone",
|
|
"DTCS->",
|
|
"->DTCS",
|
|
"Tone->DTCS",
|
|
"DTCS->Tone",
|
|
"->Tone",
|
|
"DTCS->DTCS"]
|
|
rf.valid_power_levels = [x for x in self._tx_power if x]
|
|
rf.valid_duplexes = ["", "-", "+", "split", "off"]
|
|
rf.valid_modes = ["FM", "NFM"]
|
|
rf.valid_tuning_steps = STEPS
|
|
|
|
rf.valid_bands = self._txbands + self._rxbands
|
|
rf.valid_bands.sort()
|
|
rf.memory_bounds = (1, 199)
|
|
return rf
|
|
|
|
def process_mmap(self):
|
|
self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
|
|
|
|
def sync_in(self):
|
|
try:
|
|
self._mmap = _do_download(self)
|
|
self.process_mmap()
|
|
except Exception as e:
|
|
raise errors.RadioError("Failed to communicate with radio: %s" % e)
|
|
|
|
def sync_out(self):
|
|
try:
|
|
_do_upload(self)
|
|
except errors.RadioError:
|
|
raise
|
|
except Exception as e:
|
|
raise errors.RadioError("Failed to communicate with radio: %s" % e)
|
|
|
|
def get_raw_memory(self, number):
|
|
return repr(self._memobj.memory[number])
|
|
|
|
# Encoding processing
|
|
def _decode_tone(self, val):
|
|
if val == 16665 or val == 0:
|
|
return '', None, None
|
|
elif val >= 12000:
|
|
return 'DTCS', val - 12000, 'R'
|
|
elif val >= 8000:
|
|
return 'DTCS', val - 8000, 'N'
|
|
else:
|
|
return 'Tone', val / 10.0, None
|
|
|
|
# Decoding processing
|
|
def _encode_tone(self, memval, mode, value, pol):
|
|
if mode == "":
|
|
memval[0].set_raw(0xFF)
|
|
memval[1].set_raw(0xFF)
|
|
elif mode == 'Tone':
|
|
memval.set_value(int(value * 10))
|
|
|
|
elif mode == 'DTCS':
|
|
flag = 0x80 if pol == 'N' else 0xC0
|
|
memval.set_value(value)
|
|
memval[1].set_bits(flag)
|
|
else:
|
|
raise Exception("Internal error: invalid mode `%s'" % mode)
|
|
|
|
def _get_mem(self, number):
|
|
return self._memobj.memory[number]
|
|
|
|
def _get_nam(self, number):
|
|
return self._memobj.names[number - 1]
|
|
|
|
def _get_fm(self, number):
|
|
return self._memobj.fmmode[number]
|
|
|
|
def _get_get_scanvfo(self, number):
|
|
return self._memobj.fmvfo[number]
|
|
|
|
def get_memory(self, number):
|
|
_mem = self._get_mem(number)
|
|
_nam = self._get_nam(number)
|
|
mem = chirp_common.Memory()
|
|
mem.number = number
|
|
|
|
if _mem.get_raw()[0] == 0xff:
|
|
mem.empty = True
|
|
return mem
|
|
|
|
# narrow and wide
|
|
mem.mode = _mem.wide and "NFM" or "FM"
|
|
|
|
# power
|
|
try:
|
|
mem.power = self._tx_power[_mem.lowpower]
|
|
if mem.power is None:
|
|
# Gaps are basically missing power levels
|
|
raise IndexError()
|
|
except IndexError:
|
|
LOG.error("Radio reported invalid power level %s (in %s)" %
|
|
(_mem.lowpower, self._tx_power))
|
|
mem.power = self._tx_power[0]
|
|
|
|
# Channel name
|
|
for char in _nam.name:
|
|
if "\x00" in str(char) or "\xFF" in str(char):
|
|
char = ""
|
|
mem.name += str(char)
|
|
|
|
mem.name = mem.name.rstrip()
|
|
if self.ident_mode != b'P31183\xff\xff' and \
|
|
(mem.number >= 189 and mem.number <= 199):
|
|
mem.name = HAM_GMRS_NAME[mem.number - 200]
|
|
|
|
# tmode
|
|
lin2 = int(_mem.rxtone)
|
|
rxtone = self._decode_tone(lin2)
|
|
|
|
lin = int(_mem.txtone)
|
|
txtone = self._decode_tone(lin)
|
|
|
|
if txtone[0] == "Tone" and not rxtone[0]:
|
|
mem.tmode = "Tone"
|
|
elif txtone[0] == rxtone[0] and txtone[0] == "Tone" \
|
|
and mem.rtone == mem.ctone:
|
|
mem.tmode = "TSQL"
|
|
elif txtone[0] == rxtone[0] and txtone[0] == "DTCS" \
|
|
and mem.dtcs == mem.rx_dtcs:
|
|
mem.tmode = "DTCS"
|
|
elif rxtone[0] or txtone[0]:
|
|
mem.tmode = "Cross"
|
|
mem.cross_mode = "%s->%s" % (txtone[0], rxtone[0])
|
|
|
|
chirp_common.split_tone_decode(mem, txtone, rxtone)
|
|
|
|
mem.skip = '' if self._memobj.scanadd[mem.number - 1] else 'S'
|
|
|
|
mem.freq = int(_mem.rxfreq) * 10
|
|
if _mem.txfreq.get_raw() == b'\xff\xff\xff\xff':
|
|
mem.offset = 0
|
|
mem.duplex = 'off'
|
|
else:
|
|
chirp_common.split_to_offset(mem,
|
|
int(_mem.rxfreq) * 10,
|
|
int(_mem.txfreq) * 10)
|
|
|
|
if self._gmrs:
|
|
# mem.duplex = ""
|
|
# mem.offset = 0
|
|
if mem.number >= 1 and mem.number <= 30:
|
|
mem.immutable.append('freq')
|
|
if mem.number >= 8 and mem.number <= 14:
|
|
mem.mode = 'NFM'
|
|
mem.power = self._tx_power[0]
|
|
mem.immutable = ['freq', 'mode', 'power',
|
|
'duplex', 'offset']
|
|
elif mem.number >= 31 and mem.number <= 54:
|
|
# mem.immutable = ['duplex', 'offset']
|
|
mem.duplex = '+'
|
|
mem.offset = 5000000
|
|
elif mem.number >= 189 and mem.number <= 199:
|
|
ham_freqs = NOAA_FREQS[mem.number - 189]
|
|
mem.freq = ham_freqs
|
|
mem.immutable = ['name', 'power', 'duplex', 'freq',
|
|
'rx_dtcs', 'vfo', 'tmode', 'empty',
|
|
'offset', 'rtone', 'ctone', 'dtcs',
|
|
'dtcs_polarity', 'cross_mode']
|
|
elif self._ham:
|
|
if mem.number >= 189 and mem.number <= 199:
|
|
ham_freqs = NOAA_FREQS[mem.number - 189]
|
|
mem.freq = ham_freqs
|
|
mem.immutable = ['name', 'power', 'freq', 'rx_dtcs', 'vfo',
|
|
'tmode', 'empty', 'offset', 'rtone', 'ctone',
|
|
'dtcs', 'dtcs_polarity', 'cross_mode']
|
|
|
|
# other function
|
|
# pttid
|
|
mem.extra = RadioSettingGroup("Extra", "extra")
|
|
|
|
rs = RadioSetting("pttid", "PTT ID",
|
|
RadioSettingValueList(PTTID_VALUES,
|
|
PTTID_VALUES[_mem.pttid]))
|
|
mem.extra.append(rs)
|
|
|
|
# Busylock
|
|
rs = RadioSetting("bcl", "Busy Lock",
|
|
RadioSettingValueList(BCLOCK_VALUES,
|
|
BCLOCK_VALUES[_mem.bcl]))
|
|
mem.extra.append(rs)
|
|
|
|
rs = RadioSetting(
|
|
"freqhop", "Frequency Hop", RadioSettingValueList(
|
|
FREQHOP_VALUES, FREQHOP_VALUES[_mem.freqhop]))
|
|
mem.extra.append(rs)
|
|
|
|
if in_range(mem.freq, self._rxbands):
|
|
mem.duplex = 'off'
|
|
mem.immutable.append('duplex')
|
|
if in_range(mem.freq, [AIRBAND]):
|
|
mem.mode = 'AM'
|
|
mem.immutable.append('mode')
|
|
|
|
return mem
|
|
|
|
def _set_mem(self, number):
|
|
return self._memobj.memory[number]
|
|
|
|
def _set_nam(self, number):
|
|
return self._memobj.names[number - 1]
|
|
|
|
def _get_scan_list(self, scan_data):
|
|
# scan_val_list - Get all scans Add data 1-200 digits
|
|
scan_val_list = []
|
|
for x in range(25):
|
|
a = self._get_scan(x)
|
|
for i in range(0, 8):
|
|
scan_val = (getattr(a, 'scan%i' % (i+1)))
|
|
used_scan_val = str(scan_val)[3]
|
|
scan_val_list.append(used_scan_val)
|
|
|
|
# used_scan_list - 25 structures, split the scan added
|
|
# data into 25 groups of 8 bits each
|
|
used_scan_list = []
|
|
count_num = 1
|
|
for i in range(0, len(scan_val_list), 8):
|
|
used_scan_list.append(scan_val_list[i:i + 8])
|
|
count_num += 1
|
|
# Determine whether it is a standard number that can be divisible
|
|
# Which group is the scan addition located in the modified channel
|
|
if scan_data % 8 != 0:
|
|
x_list = scan_data / 8
|
|
y_list = scan_data % 8
|
|
|
|
else:
|
|
x_list = (scan_data / 8) - 1
|
|
y_list = 8
|
|
|
|
return ([x_list, y_list])
|
|
|
|
def set_memory(self, mem):
|
|
_mem = self._get_mem(mem.number)
|
|
_nam = self._get_nam(mem.number)
|
|
|
|
# When the channel is empty, you need to set "usedflags" to 0,
|
|
# When the channel is used , you need to set "usedflags" to 1.
|
|
self._memobj.usedflags[mem.number - 1] = int(not mem.empty)
|
|
|
|
if mem.empty:
|
|
_mem.fill_raw(b'\xFF')
|
|
return
|
|
|
|
_mem.fill_raw(b'\x00')
|
|
|
|
if mem.duplex == "":
|
|
_mem.rxfreq = _mem.txfreq = mem.freq / 10
|
|
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
|
|
elif mem.duplex == 'off':
|
|
_mem.txfreq.fill_raw(b'\xFF')
|
|
else:
|
|
_mem.txfreq = mem.freq / 10
|
|
|
|
if in_range(mem.freq, self._rxbands):
|
|
_mem.txfreq.fill_raw(b'\xFF')
|
|
|
|
_mem.rxfreq = mem.freq / 10
|
|
_namelength = self.get_features().valid_name_length
|
|
|
|
for i in range(_namelength):
|
|
try:
|
|
_nam.name[i] = mem.name[i]
|
|
except IndexError:
|
|
_nam.name[i] = "\xFF"
|
|
|
|
txtone, rxtone = chirp_common.split_tone_encode(mem)
|
|
|
|
self._encode_tone(_mem.txtone, *txtone)
|
|
self._encode_tone(_mem.rxtone, *rxtone)
|
|
|
|
if mem.mode == "FM":
|
|
_mem.wide = 0
|
|
else:
|
|
_mem.wide = 1
|
|
|
|
try:
|
|
_mem.lowpower = self._tx_power.index(mem.power or
|
|
self._tx_power[-1])
|
|
except ValueError:
|
|
_mem.lowpower = 0
|
|
LOG.warning('Unsupported power %r', mem.power)
|
|
|
|
# Skip/Scanadd Setting
|
|
self._memobj.scanadd[mem.number - 1] = mem.skip != 'S'
|
|
|
|
for setting in mem.extra:
|
|
if (self.ident_mode == b'P31185\xff\xff' or
|
|
self.ident_mode == b'P31184\xff\xff') and \
|
|
mem.number >= 189 and mem.number <= 199:
|
|
if setting.get_name() == 'pttid':
|
|
setting.value = 'Off'
|
|
setattr(_mem, setting.get_name(), setting.value)
|
|
elif setting.get_name() == 'bcl':
|
|
setting.value = 'Off'
|
|
setattr(_mem, setting.get_name(), setting.value)
|
|
elif setting.get_name() == 'freqhop':
|
|
setting.value = 'Off'
|
|
setattr(_mem, setting.get_name(), setting.value)
|
|
else:
|
|
setattr(_mem, setting.get_name(), setting.value)
|
|
|
|
def _is_orig(self):
|
|
version_tag = _firmware_version_from_image(self)
|
|
try:
|
|
if b'BFB' in version_tag:
|
|
idx = version_tag.index(b"BFB") + 3
|
|
version = int(version_tag[idx:idx + 3])
|
|
return version < 291
|
|
return False
|
|
except Exception:
|
|
pass
|
|
raise errors.RadioError("Unable to parse version string %s" %
|
|
version_tag)
|
|
|
|
def _my_upper_band(self):
|
|
band_tag = _upper_band_from_image(self)
|
|
return band_tag
|
|
|
|
def _get_settings(self):
|
|
_settings = self._memobj.settings
|
|
_press = self._memobj.press
|
|
_aoffset = self._memobj.aoffset
|
|
_boffset = self._memobj.boffset
|
|
_vfoa = self._memobj.vfoa
|
|
_vfob = self._memobj.vfob
|
|
_gcode = self._memobj.groupcode
|
|
_msg = self._memobj.poweron_msg
|
|
basic = RadioSettingGroup("basic", "Basic Settings")
|
|
abblock = RadioSettingGroup("abblock", "A/B Channel")
|
|
fmmode = RadioSettingGroup("fmmode", "FM")
|
|
dtmf = RadioSettingGroup("dtmf", "DTMF")
|
|
|
|
# group = RadioSettings(fmmode, dtmf)
|
|
group = RadioSettings(basic, abblock, fmmode, dtmf)
|
|
|
|
rs = RadioSetting("squelch", "Squelch Level",
|
|
RadioSettingValueList(
|
|
SQUELCH, SQUELCH[_settings.squelch]))
|
|
basic.append(rs)
|
|
|
|
rs = RadioSetting("ligcon", "Light Control",
|
|
RadioSettingValueList(
|
|
LIGHT_LIST, LIGHT_LIST[_settings.ligcon]))
|
|
basic.append(rs)
|
|
|
|
rs = RadioSetting("voiceprompt", "Voice Prompt",
|
|
RadioSettingValueList(
|
|
VOICE_PRMPT_LIST, VOICE_PRMPT_LIST[
|
|
_settings.voiceprompt]))
|
|
basic.append(rs)
|
|
|
|
rs = RadioSetting("keyautolock", "Auto Lock",
|
|
RadioSettingValueList(
|
|
AUTOLOCK_LIST, AUTOLOCK_LIST[
|
|
_settings.keyautolock]))
|
|
basic.append(rs)
|
|
|
|
rs = RadioSetting("mdfa", "MDF-A",
|
|
RadioSettingValueList(
|
|
MDFA_LIST, MDFA_LIST[_settings.mdfa]))
|
|
basic.append(rs)
|
|
|
|
rs = RadioSetting("mdfb", "MDF-B",
|
|
RadioSettingValueList(
|
|
MDFB_LIST, MDFB_LIST[_settings.mdfb]))
|
|
basic.append(rs)
|
|
|
|
rs = RadioSetting("sync", "SYNC",
|
|
RadioSettingValueList(
|
|
SYNC_LIST, SYNC_LIST[_settings.sync]))
|
|
basic.append(rs)
|
|
|
|
rs = RadioSetting("save", "Battery Save",
|
|
RadioSettingValueList(
|
|
BTV_SAVER_LIST, BTV_SAVER_LIST[_settings.save]))
|
|
basic.append(rs)
|
|
|
|
rs = RadioSetting("dbrx", "Double Rx",
|
|
RadioSettingValueList(
|
|
DBRX_LIST, DBRX_LIST[_settings.dbrx]))
|
|
basic.append(rs)
|
|
|
|
rs = RadioSetting("astep", "A Step",
|
|
RadioSettingValueList(
|
|
ASTEP_LIST, ASTEP_LIST[_settings.astep]))
|
|
basic.append(rs)
|
|
|
|
rs = RadioSetting("bstep", "B Step",
|
|
RadioSettingValueList(
|
|
BSTEP_LIST, BSTEP_LIST[_settings.bstep]))
|
|
basic.append(rs)
|
|
|
|
rs = RadioSetting("scanmode", "Scan Mode",
|
|
RadioSettingValueList(
|
|
SCAN_MODE_LIST, SCAN_MODE_LIST[
|
|
_settings.scanmode]))
|
|
basic.append(rs)
|
|
|
|
rs = RadioSetting("pritx", "Priority TX",
|
|
RadioSettingValueList(
|
|
PRIO_LIST, PRIO_LIST[_settings.pritx]))
|
|
basic.append(rs)
|
|
|
|
rs = RadioSetting("btnvoice", "Beep",
|
|
RadioSettingValueBoolean(_settings.btnvoice))
|
|
basic.append(rs)
|
|
|
|
rs = RadioSetting("rogerprompt", "Roger",
|
|
RadioSettingValueBoolean(_settings.rogerprompt))
|
|
basic.append(rs)
|
|
|
|
rs = RadioSetting("txled", "Disp Lcd(TX)",
|
|
|
|
RadioSettingValueBoolean(_settings.txled))
|
|
basic.append(rs)
|
|
|
|
rs = RadioSetting("rxled", "Disp Lcd(RX)",
|
|
RadioSettingValueBoolean(_settings.rxled))
|
|
basic.append(rs)
|
|
|
|
rs = RadioSetting("onlychmode", "Only CH Mode",
|
|
RadioSettingValueBoolean(_settings.onlychmode))
|
|
basic.append(rs)
|
|
rs = RadioSetting("ssidekey1", "SHORT_KEY_PF1",
|
|
RadioSettingValueList(
|
|
SHORT_KEY_LIST, SHORT_KEY_LIST[
|
|
_press.ssidekey1]))
|
|
basic.append(rs)
|
|
rs = RadioSetting("lsidekey3", "LONG_KEY_PF1",
|
|
RadioSettingValueList(
|
|
LONG_KEY_LIST,
|
|
LONG_KEY_LIST[_press.lsidekey3]))
|
|
basic.append(rs)
|
|
if self.MODEL in H8_LIST:
|
|
rs = RadioSetting("stopkey1", "SHORT_KEY_TOP",
|
|
RadioSettingValueList(SHORT_KEY_LIST,
|
|
SHORT_KEY_LIST[0]))
|
|
basic.append(rs)
|
|
|
|
rs = RadioSetting("ltopkey2", "LONG_KEY_TOP",
|
|
RadioSettingValueList(
|
|
LONG_KEY_LIST,
|
|
LONG_KEY_LIST[_press.ltopkey2]))
|
|
basic.append(rs)
|
|
|
|
rs = RadioSetting("ssidekey2", "SHORT_KEY_PF2",
|
|
RadioSettingValueList(
|
|
SHORT_KEY_LIST,
|
|
SHORT_KEY_LIST[_press.ssidekey2]))
|
|
basic.append(rs)
|
|
|
|
rs = RadioSetting("lsidekey4", "LONG_KEY_PF2",
|
|
RadioSettingValueList(
|
|
LONG_KEY_LIST,
|
|
LONG_KEY_LIST[_press.lsidekey4]))
|
|
basic.append(rs)
|
|
|
|
rs = RadioSetting("voxgain", "VOX Gain",
|
|
RadioSettingValueList(
|
|
VOX_GAIN, VOX_GAIN[_settings.voxgain]))
|
|
basic.append(rs)
|
|
|
|
rs = RadioSetting("voxdelay", "VOX Delay",
|
|
RadioSettingValueList(
|
|
VOX_DELAY, VOX_DELAY[_settings.voxdelay]))
|
|
basic.append(rs)
|
|
|
|
rs = RadioSetting("ponmsg", "Power-On Message",
|
|
RadioSettingValueList(
|
|
PONMSG_LIST, PONMSG_LIST[_settings.ponmsg]))
|
|
basic.append(rs)
|
|
|
|
# mic gain
|
|
if self.MODEL not in H8_LIST:
|
|
_mic = self._memobj.mic
|
|
rs = RadioSetting("micgain", "MIC GAIN",
|
|
RadioSettingValueList(
|
|
MIC_GAIN_LIST, current_index=_mic.micgain))
|
|
basic.append(rs)
|
|
|
|
if self.MODEL not in H8_LIST:
|
|
rs = RadioSetting("kill", "Kill",
|
|
RadioSettingValueBoolean(_settings.kill))
|
|
basic.append(rs)
|
|
rs = RadioSetting("stun", "Stun",
|
|
RadioSettingValueBoolean(_settings.stun))
|
|
basic.append(rs)
|
|
|
|
def _filter(name):
|
|
filtered = ""
|
|
for char in str(name):
|
|
if char in chirp_common.CHARSET_ASCII:
|
|
filtered += char
|
|
else:
|
|
filtered += " "
|
|
return filtered
|
|
|
|
rs = RadioSetting("poweron_msg.msg1", "Power-On Message 1",
|
|
RadioSettingValueString(0, 16, _filter(_msg.msg1)))
|
|
basic.append(rs)
|
|
rs = RadioSetting("poweron_msg.msg2", "Power-On Message 2",
|
|
RadioSettingValueString(0, 16, _filter(_msg.msg2)))
|
|
basic.append(rs)
|
|
rs = RadioSetting("poweron_msg.msg3", "Power-On Message 3",
|
|
RadioSettingValueString(0, 16, _filter(_msg.msg3)))
|
|
basic.append(rs)
|
|
|
|
# A channel
|
|
a_freq = int(_vfoa.rxfreqa)
|
|
freqa = "%i.%05i" % (a_freq / 100000, a_freq % 100000)
|
|
if freqa == "0.00000":
|
|
val1a = RadioSettingValueString(0, 7, '0.00000')
|
|
else:
|
|
val1a = RadioSettingValueFloat(
|
|
136, 520, float(freqa), 0.00001, 5)
|
|
rs = RadioSetting("rxfreqa", "A Channel - Frequency", val1a)
|
|
abblock.append(rs)
|
|
|
|
# Offset
|
|
# If the offset is 12.345
|
|
# Then the data obtained is [0x45, 0x23, 0x01, 0x00]
|
|
a_set_val = _aoffset.ofseta
|
|
a_set_list = len(_aoffset.ofseta) - 1
|
|
real_val = ''
|
|
for i in range(a_set_list, -1, -1):
|
|
real_val += str(a_set_val[i])[2:]
|
|
if real_val == "FFFFFFFF":
|
|
rs = RadioSetting("ofseta", "A Offset Frequency",
|
|
RadioSettingValueString(0, 7, ""))
|
|
|
|
else:
|
|
real_val = int(real_val)
|
|
real_val = "%i.%05i" % (real_val / 100000, real_val % 100000)
|
|
rs = RadioSetting("ofseta", "A Offset Frequency",
|
|
RadioSettingValueFloat(
|
|
0.00000, 59.99750, real_val, 0.00001, 5))
|
|
abblock.append(rs)
|
|
|
|
rs = RadioSetting("offset", "A Offset",
|
|
RadioSettingValueList(
|
|
A_OFFSET, A_OFFSET[_vfoa.offset]))
|
|
abblock.append(rs)
|
|
|
|
rs = RadioSetting("lowpower", "A TX Power",
|
|
RadioSettingValueList(
|
|
A_TX_POWER, A_TX_POWER[_vfoa.lowpower]))
|
|
abblock.append(rs)
|
|
|
|
rs = RadioSetting("wide", "A Band",
|
|
RadioSettingValueList(
|
|
A_BAND, A_BAND[_vfoa.wide]))
|
|
abblock.append(rs)
|
|
|
|
rs = RadioSetting("bcl", "A Busy Lock",
|
|
RadioSettingValueList(
|
|
A_BUSYLOCK, A_BUSYLOCK[_vfoa.bcl]))
|
|
abblock.append(rs)
|
|
|
|
rs = RadioSetting("specialqta", "A Special QT/DQT",
|
|
RadioSettingValueList(
|
|
A_SPEC_QTDQT, A_SPEC_QTDQT[_vfoa.specialqta]))
|
|
abblock.append(rs)
|
|
|
|
rs = RadioSetting("aworkmode", "A Work Mode",
|
|
RadioSettingValueList(
|
|
A_WORKMODE, A_WORKMODE[_settings.aworkmode]))
|
|
abblock.append(rs)
|
|
|
|
# B channel
|
|
b_freq = int(str(int(_vfob.rxfreqb)).ljust(8, '0'))
|
|
freqb = "%i.%05i" % (b_freq / 100000, b_freq % 100000)
|
|
if freqb == "0.00000":
|
|
val1a = RadioSettingValueString(0, 7, '0.00000')
|
|
else:
|
|
val1a = RadioSettingValueFloat(
|
|
136, 520, float(freqb), 0.00001, 5)
|
|
rs = RadioSetting("rxfreqb", "B Channel - Frequency", val1a)
|
|
abblock.append(rs)
|
|
|
|
# Offset frequency
|
|
# If the offset is 12.345
|
|
# Then the data obtained is [0x45, 0x23, 0x01, 0x00]
|
|
# Need to use the following anonymous function to process data
|
|
b_set_val = _boffset.ofsetb
|
|
b_set_list = len(_boffset.ofsetb) - 1
|
|
real_val = ''
|
|
for i in range(b_set_list, -1, -1):
|
|
real_val += str(b_set_val[i])[2:]
|
|
if real_val == "FFFFFFFF":
|
|
rs = RadioSetting("ofsetb", "B Offset Frequency",
|
|
RadioSettingValueString(0, 7, " "))
|
|
else:
|
|
real_val = int(real_val)
|
|
real_val = "%i.%05i" % (real_val / 100000, real_val % 100000)
|
|
rs = RadioSetting("ofsetb", "B Offset Frequency",
|
|
RadioSettingValueFloat(
|
|
0.00000, 59.99750, real_val, 0.00001, 5))
|
|
abblock.append(rs)
|
|
|
|
rs = RadioSetting("offsetb", "B Offset",
|
|
RadioSettingValueList(
|
|
B_OFFSET, B_OFFSET[_vfob.offsetb]))
|
|
abblock.append(rs)
|
|
|
|
rs = RadioSetting("lowpowerb", "B TX Power",
|
|
RadioSettingValueList(
|
|
B_TX_POWER, B_TX_POWER[_vfob.lowpowerb]))
|
|
abblock.append(rs)
|
|
|
|
rs = RadioSetting("wideb", "B Band",
|
|
RadioSettingValueList(
|
|
B_BAND, B_BAND[_vfob.wideb]))
|
|
abblock.append(rs)
|
|
|
|
rs = RadioSetting("bclb", "B Busy Lock",
|
|
RadioSettingValueList(
|
|
B_BUSYLOCK, B_BUSYLOCK[_vfob.bclb]))
|
|
abblock.append(rs)
|
|
|
|
rs = RadioSetting("specialqtb", "B Special QT/DQT",
|
|
RadioSettingValueList(
|
|
B_SPEC_QTDQT, B_SPEC_QTDQT[_vfob.specialqtb]))
|
|
abblock.append(rs)
|
|
|
|
rs = RadioSetting("bworkmode", "B Work Mode",
|
|
RadioSettingValueList(
|
|
B_WORKMODE, B_WORKMODE[_settings.bworkmode]))
|
|
abblock.append(rs)
|
|
|
|
rs = RadioSetting("fmworkmode", "Work Mode",
|
|
RadioSettingValueList(
|
|
FM_WORKMODE, FM_WORKMODE[_settings.fmworkmode]))
|
|
fmmode.append(rs)
|
|
|
|
rs = RadioSetting("fmroad", "Channel",
|
|
RadioSettingValueList(
|
|
FM_CHANNEL, FM_CHANNEL[_settings.fmroad]))
|
|
fmmode.append(rs)
|
|
|
|
rs = RadioSetting("fmrec", "Forbid Receive",
|
|
RadioSettingValueBoolean(_settings.fmrec))
|
|
fmmode.append(rs)
|
|
|
|
# FM
|
|
numeric = '0123456789.'
|
|
for i in range(25):
|
|
if self._memobj.fmusedflags[i]:
|
|
_fm = self._get_fm(i).fmblock
|
|
try:
|
|
if not (760 < int(_fm) < 1080):
|
|
raise ValueError()
|
|
val = '%.1f' % (int(_fm) / 10)
|
|
except ValueError:
|
|
LOG.warning('FM channel index %i is invalid', i)
|
|
val = ''
|
|
else:
|
|
val = ''
|
|
rs = RadioSetting('block%02i' % i, "Channel %i" % (i + 1),
|
|
RadioSettingValueString(0, 5,
|
|
val,
|
|
False, charset=numeric))
|
|
fmmode.append(rs)
|
|
|
|
try:
|
|
_fmv = int(self._memobj.fmvfo) / 10
|
|
except ValueError:
|
|
LOG.warning('FM VFO is invalid')
|
|
_fmv = 0
|
|
|
|
rs = RadioSetting(
|
|
"fmvfo", "VFO", RadioSettingValueFloat(
|
|
76.0, 108.0, _fmv, 0.1, 1))
|
|
fmmode.append(rs)
|
|
|
|
# DTMF
|
|
gcode_val = str(_gcode.gcode)[2:]
|
|
if gcode_val == "FF":
|
|
gcode_val = "Off"
|
|
elif gcode_val == "0F":
|
|
gcode_val = "#"
|
|
elif gcode_val == "0E":
|
|
gcode_val = "*"
|
|
elif gcode_val == '00':
|
|
gcode_val = ""
|
|
else:
|
|
gcode_val = gcode_val[1]
|
|
rs = RadioSetting("gcode", "Group Code",
|
|
RadioSettingValueList(GROUPCODE,
|
|
gcode_val))
|
|
dtmf.append(rs)
|
|
|
|
icode_list = self._memobj.icode.idcode
|
|
used_icode = ''
|
|
for i in icode_list:
|
|
if i == 0xFF:
|
|
continue
|
|
used_icode += str(i)[3]
|
|
dtmfcharsani = "0123456789ABCD "
|
|
i_val = RadioSettingValueString(0, 3, used_icode)
|
|
rs = RadioSetting("icode", "ID Code", i_val)
|
|
i_val.set_charset(dtmfcharsani)
|
|
dtmf.append(rs)
|
|
|
|
gcode_list_1 = self._memobj.group1.group1
|
|
used_group1 = ''
|
|
for i in gcode_list_1:
|
|
if i == 0xFF:
|
|
continue
|
|
used_group1 += str(i)[3]
|
|
group1_val = RadioSettingValueString(0, 7, used_group1)
|
|
rs = RadioSetting("group1", "1", group1_val)
|
|
group1_val.set_charset(dtmfcharsani)
|
|
dtmf.append(rs)
|
|
|
|
gcode_list_2 = self._memobj.group2.group2
|
|
used_group2 = ''
|
|
for i in gcode_list_2:
|
|
if i == 0xFF:
|
|
continue
|
|
used_group2 += str(i)[3]
|
|
group2_val = RadioSettingValueString(0, 7, used_group2)
|
|
rs = RadioSetting("group2", "2", group2_val)
|
|
group2_val.set_charset(dtmfcharsani)
|
|
dtmf.append(rs)
|
|
|
|
gcode_list_3 = self._memobj.group3.group3
|
|
used_group3 = ''
|
|
for i in gcode_list_3:
|
|
if i == 0xFF:
|
|
continue
|
|
used_group3 += str(i)[3]
|
|
group3_val = RadioSettingValueString(0, 7, used_group3)
|
|
rs = RadioSetting("group3", "3", group3_val)
|
|
group3_val.set_charset(dtmfcharsani)
|
|
dtmf.append(rs)
|
|
|
|
gcode_list_4 = self._memobj.group4.group4
|
|
used_group4 = ''
|
|
for i in gcode_list_4:
|
|
if i == 0xFF:
|
|
continue
|
|
used_group4 += str(i)[3]
|
|
group4_val = RadioSettingValueString(0, 7, used_group4)
|
|
rs = RadioSetting("group4", "4", group4_val)
|
|
group4_val.set_charset(dtmfcharsani)
|
|
dtmf.append(rs)
|
|
|
|
gcode_list_5 = self._memobj.group5.group5
|
|
used_group5 = ''
|
|
for i in gcode_list_5:
|
|
if i == 0xFF:
|
|
continue
|
|
used_group5 += str(i)[3]
|
|
group5_val = RadioSettingValueString(0, 7, used_group5)
|
|
rs = RadioSetting("group5", "5", group5_val)
|
|
group5_val.set_charset(dtmfcharsani)
|
|
dtmf.append(rs)
|
|
|
|
gcode_list_6 = self._memobj.group6.group6
|
|
used_group6 = ''
|
|
for i in gcode_list_6:
|
|
if i == 0xFF:
|
|
continue
|
|
used_group6 += str(i)[3]
|
|
group6_val = RadioSettingValueString(0, 7, used_group6)
|
|
rs = RadioSetting("group6", "6", group6_val)
|
|
group6_val.set_charset(dtmfcharsani)
|
|
dtmf.append(rs)
|
|
|
|
gcode_list_7 = self._memobj.group7.group7
|
|
used_group7 = ''
|
|
for i in gcode_list_7:
|
|
if i == 0xFF:
|
|
continue
|
|
used_group7 += str(i)[3]
|
|
group7_val = RadioSettingValueString(0, 7, used_group7)
|
|
rs = RadioSetting("group7", "7", group7_val)
|
|
group7_val.set_charset(dtmfcharsani)
|
|
dtmf.append(rs)
|
|
|
|
gcode_list_8 = self._memobj.group8.group8
|
|
used_group8 = ''
|
|
for i in gcode_list_8:
|
|
if i == 0xFF:
|
|
continue
|
|
used_group8 += str(i)[3]
|
|
group8_val = RadioSettingValueString(0, 7, used_group8)
|
|
rs = RadioSetting("group8", "8", group7_val)
|
|
group8_val.set_charset(dtmfcharsani)
|
|
dtmf.append(rs)
|
|
|
|
scode_list = self._memobj.startcode.scode
|
|
used_scode = ''
|
|
for i in scode_list:
|
|
if i == 0xFF:
|
|
continue
|
|
used_scode += str(i)[3]
|
|
scode_val = RadioSettingValueString(0, 7, used_scode)
|
|
rs = RadioSetting("scode", "PTT ID Starting(BOT)", scode_val)
|
|
scode_val.set_charset(dtmfcharsani)
|
|
dtmf.append(rs)
|
|
|
|
ecode_list = self._memobj.endcode.ecode
|
|
used_ecode = ''
|
|
for i in ecode_list:
|
|
if i == 0xFF:
|
|
continue
|
|
used_ecode += str(i)[3]
|
|
ecode_val = RadioSettingValueString(0, 7, used_ecode)
|
|
rs = RadioSetting("ecode", "PTT ID Ending(BOT)", ecode_val)
|
|
dtmf.append(rs)
|
|
# H3
|
|
if self.MODEL not in H8_LIST:
|
|
# stuncode
|
|
ecode_list = self._memobj.skcode.stuncode
|
|
used_ecode = ''
|
|
for i in ecode_list:
|
|
if i == 0xFF:
|
|
continue
|
|
used_ecode += str(i)[3]
|
|
ecode_val = RadioSettingValueString(0, 16, used_ecode)
|
|
rs = RadioSetting("stuncode", "Stun Code", ecode_val)
|
|
dtmf.append(rs)
|
|
# killcode
|
|
ecode_list = self._memobj.skcode.killcode
|
|
used_ecode = ''
|
|
for i in ecode_list:
|
|
if i == 0xFF:
|
|
continue
|
|
used_ecode += str(i)[3]
|
|
ecode_val = RadioSettingValueString(0, 16, used_ecode)
|
|
rs = RadioSetting("killcode", "Kill Code", ecode_val)
|
|
dtmf.append(rs)
|
|
|
|
return group
|
|
|
|
def get_settings(self):
|
|
try:
|
|
return self._get_settings()
|
|
except Exception:
|
|
raise InvalidValueError("Setting Failed!")
|
|
|
|
def set_settings(self, settings):
|
|
|
|
def fm_validate(value):
|
|
if 760 > value or value > 1080:
|
|
msg = ("FM Channel must be between 76.0-108.0")
|
|
raise InvalidValueError(msg)
|
|
|
|
_settings = self._memobj.settings
|
|
_press = self._memobj.press
|
|
_aoffset = self._memobj.aoffset
|
|
_boffset = self._memobj.boffset
|
|
_vfoa = self._memobj.vfoa
|
|
_vfob = self._memobj.vfob
|
|
_fmmode = self._memobj.fmmode
|
|
|
|
for element in settings:
|
|
if not isinstance(element, RadioSetting):
|
|
if element.get_name() == "fm_preset":
|
|
self._set_fm_preset(element)
|
|
else:
|
|
self.set_settings(element)
|
|
continue
|
|
else:
|
|
try:
|
|
name = element.get_name()
|
|
if "." in name:
|
|
bits = name.split(".")
|
|
obj = self._memobj
|
|
for bit in bits[:-1]:
|
|
if "/" in bit:
|
|
bit, index = bit.split("/", 1)
|
|
index = int(index)
|
|
obj = getattr(obj, bit)[index]
|
|
else:
|
|
obj = getattr(obj, bit)
|
|
setting = bits[-1]
|
|
elif name in PRESS_NAME:
|
|
obj = _press
|
|
setting = element.get_name()
|
|
|
|
elif name in VFOA_NAME:
|
|
obj = _vfoa
|
|
setting = element.get_name()
|
|
elif name == "ofseta":
|
|
obj = _aoffset
|
|
setting = element.get_name()
|
|
elif name in VFOB_NAME:
|
|
obj = _vfob
|
|
setting = element.get_name()
|
|
elif name == "ofsetb":
|
|
obj = _boffset
|
|
setting = element.get_name()
|
|
elif "block" in name:
|
|
obj = _fmmode
|
|
setting = element.get_name()
|
|
elif "fmvfo" in name:
|
|
obj = self._memobj.fmvfo
|
|
setting = element.get_name()
|
|
elif "gcode" in name:
|
|
obj = self._memobj.groupcode.gcode
|
|
setting = element.get_name()
|
|
elif "idcode" in name:
|
|
obj = self._memobj.icode.idcode
|
|
setting = element.get_name()
|
|
elif "scode" in name:
|
|
obj = self._memobj.startcode.scode
|
|
setting = element.get_name()
|
|
elif "ecode" in name:
|
|
obj = self._memobj.endcode.ecode
|
|
setting = element.get_name()
|
|
elif "group1" in name:
|
|
obj = self._memobj.group1.group1
|
|
setting = element.get_name()
|
|
elif "group2" in name:
|
|
obj = self._memobj.group2.group2
|
|
setting = element.get_name()
|
|
elif "group3" in name:
|
|
obj = self._memobj.group3.group3
|
|
setting = element.get_name()
|
|
elif "group4" in name:
|
|
obj = self._memobj.group4.group4
|
|
setting = element.get_name()
|
|
elif "group5" in name:
|
|
obj = self._memobj.group5.group5
|
|
setting = element.get_name()
|
|
elif "group6" in name:
|
|
obj = self._memobj.group6.group6
|
|
setting = element.get_name()
|
|
elif "group7" in name:
|
|
obj = self._memobj.group7.group7
|
|
setting = element.get_name()
|
|
elif "group8" in name:
|
|
obj = self._memobj.group8.group8
|
|
setting = element.get_name()
|
|
elif "micgain" in name:
|
|
obj = self._memobj.mic.micgain
|
|
setting = element.get_name()
|
|
elif "killcode" in name:
|
|
obj = self._memobj.skcode.killcode
|
|
setting = element.get_name()
|
|
elif "stuncode" in name:
|
|
obj = self._memobj.skcode.stuncode
|
|
setting = element.get_name()
|
|
else:
|
|
obj = _settings
|
|
setting = element.get_name()
|
|
if element.has_apply_callback():
|
|
LOG.debug("Using apply callback")
|
|
element.run_apply_callback()
|
|
|
|
# Channel A
|
|
elif setting == "rxfreqa" and element.value.get_mutable():
|
|
val = int(str(element.value).replace(
|
|
'.', '').ljust(8, '0'))
|
|
if (val >= 13600000 and val <= 17400000) or \
|
|
(val >= 40000000 and val <= 52000000):
|
|
setattr(obj, setting, val)
|
|
else:
|
|
msg = (
|
|
"Frequency must be between "
|
|
"136.00000-174.00000 or 400.00000-520.00000")
|
|
raise InvalidValueError(msg)
|
|
|
|
elif setting == "ofseta" and element.value.get_mutable():
|
|
if '.' in str(element.value):
|
|
val = str(element.value).replace(' ', '')
|
|
if len(
|
|
val[val.index(".") + 1:]
|
|
) >= 1 and int(val[val.index(".") + 1:]
|
|
) != 0:
|
|
val = '00' + val.replace('.', '')
|
|
else:
|
|
val = '0' + val.replace('.', '')
|
|
val = val.ljust(8, '0')
|
|
lenth_val = 0
|
|
list_val = []
|
|
else:
|
|
val = '0' + str(element.value).replace(' ', '')
|
|
val = val.ljust(8, '0')
|
|
lenth_val = 0
|
|
list_val = []
|
|
if (int(val) >= 0 and int(val) <= 5999750):
|
|
if int(val) == 0:
|
|
_aoffset.ofseta = [0xFF, 0xFF, 0xFF, 0xFF]
|
|
else:
|
|
while lenth_val < (len(val)):
|
|
list_val.insert(
|
|
0, val[lenth_val:lenth_val + 2])
|
|
lenth_val += 2
|
|
for i in range(len(list_val)):
|
|
list_val[i] = int(list_val[i], 16)
|
|
_aoffset.ofseta = list_val
|
|
else:
|
|
msg = ("Offset must be between 0.00000-59.99750")
|
|
raise InvalidValueError(msg)
|
|
|
|
# B channel
|
|
elif setting == "rxfreqb" and element.value.get_mutable():
|
|
val = 0
|
|
val = int(str(element.value).replace(
|
|
'.', '').ljust(8, '0'))
|
|
if (val >= 13600000 and val <= 17400000) or \
|
|
(val >= 40000000 and val <= 52000000):
|
|
setattr(obj, setting, val)
|
|
else:
|
|
msg = (
|
|
"Frequency must be between "
|
|
"136.00000-174.00000 or 400.00000-520.00000")
|
|
raise InvalidValueError(msg)
|
|
# setattr(obj, setting, val)
|
|
|
|
elif setting == "ofsetb" and element.value.get_mutable():
|
|
if '.' in str(element.value):
|
|
val = str(element.value).replace(' ', '')
|
|
if len(val[val.index(".") + 1:]
|
|
) >= 1 and int(val[val.index(".") + 1:]
|
|
) != 0:
|
|
val = '00' + \
|
|
str(element.value).replace('.', '')
|
|
else:
|
|
val = '0' + str(element.value).replace('.', '')
|
|
val = val.ljust(8, '0')
|
|
lenth_val = 0
|
|
list_val = []
|
|
else:
|
|
val = '0' + str(element.value).replace(' ', '')
|
|
val = val.ljust(8, '0')
|
|
lenth_val = 0
|
|
list_val = []
|
|
if (int(val) >= 0 and int(val) <= 5999750):
|
|
if int(val) == 0:
|
|
_boffset.ofsetb = [0xFF, 0xFF, 0xFF, 0xFF]
|
|
else:
|
|
while lenth_val < (len(val)):
|
|
list_val.insert(
|
|
0, val[lenth_val:lenth_val + 2])
|
|
lenth_val += 2
|
|
for i in range(len(list_val)):
|
|
list_val[i] = int(list_val[i], 16)
|
|
_boffset.ofsetb = list_val
|
|
else:
|
|
msg = ("Offset must be between 0.00000-59.99750")
|
|
raise InvalidValueError(msg)
|
|
|
|
# FM
|
|
elif "block" in name:
|
|
num = int(name[-2:], 10)
|
|
val = str(element.value)
|
|
if val.strip():
|
|
try:
|
|
val = int(float(val) * 10)
|
|
except ValueError:
|
|
raise InvalidValueError(
|
|
'Value must be between 76.0-108.0')
|
|
fm_validate(val)
|
|
else:
|
|
val = 0
|
|
self._memobj.fmmode[num].fmblock = val
|
|
self._memobj.fmusedflags[num] = bool(val)
|
|
|
|
elif setting == 'fmvfo' and element.value.get_mutable():
|
|
self._memobj.fmvfo = int(element.value * 10)
|
|
|
|
elif setting == 'gcode' and element.value.get_mutable():
|
|
val = str(element.value)
|
|
if val == 'Off':
|
|
gcode_used = 0xFF
|
|
elif val == 'A':
|
|
gcode_used = 0x0A
|
|
elif val == 'B':
|
|
gcode_used = 0x0B
|
|
elif val == 'C':
|
|
gcode_used = 0x0C
|
|
elif val == 'D':
|
|
gcode_used = 0x0D
|
|
elif val == '#':
|
|
gcode_used = 0x0F
|
|
elif val == '*':
|
|
gcode_used = 0x0E
|
|
elif val == '':
|
|
gcode_used = 0x00
|
|
self._memobj.groupcode.gcode = gcode_used
|
|
|
|
elif setting == 'icode' and element.value.get_mutable():
|
|
val = str(element.value)
|
|
list_val = []
|
|
lenth_val = 0
|
|
while lenth_val < (len(val)):
|
|
if val[lenth_val] != ' ':
|
|
list_val.append(int(val[lenth_val], 16))
|
|
lenth_val += 1
|
|
else:
|
|
list_val.append(0xFF)
|
|
lenth_val += 1
|
|
self._memobj.icode.idcode = list_val
|
|
|
|
elif setting == 'scode' and element.value.get_mutable():
|
|
val = str(element.value)
|
|
list_val = []
|
|
lenth_val = 0
|
|
while lenth_val < (len(val)):
|
|
if val[lenth_val] != ' ':
|
|
list_val.append(int(val[lenth_val], 16))
|
|
lenth_val += 1
|
|
else:
|
|
list_val.append(0xFF)
|
|
lenth_val += 1
|
|
self._memobj.startcode.scode = list_val
|
|
|
|
elif setting == 'ecode' and element.value.get_mutable():
|
|
val = str(element.value)
|
|
list_val = []
|
|
lenth_val = 0
|
|
while lenth_val < (len(val)):
|
|
if val[lenth_val] != ' ':
|
|
list_val.append(int(val[lenth_val], 16))
|
|
lenth_val += 1
|
|
else:
|
|
list_val.append(0xFF)
|
|
lenth_val += 1
|
|
self._memobj.endcode.ecode = list_val
|
|
|
|
elif setting == 'group1' and element.value.get_mutable():
|
|
val = str(element.value)
|
|
list_val = []
|
|
lenth_val = 0
|
|
while lenth_val < (len(val)):
|
|
if val[lenth_val] != ' ':
|
|
list_val.append(int(val[lenth_val], 16))
|
|
lenth_val += 1
|
|
else:
|
|
list_val.append(0xFF)
|
|
lenth_val += 1
|
|
self._memobj.group1.group1 = list_val
|
|
|
|
elif setting == 'group2' and element.value.get_mutable():
|
|
val = str(element.value)
|
|
list_val = []
|
|
lenth_val = 0
|
|
while lenth_val < (len(val)):
|
|
if val[lenth_val] != ' ':
|
|
list_val.append(int(val[lenth_val], 16))
|
|
lenth_val += 1
|
|
else:
|
|
list_val.append(0xFF)
|
|
lenth_val += 1
|
|
self._memobj.group2.group2 = list_val
|
|
|
|
elif setting == 'group3' and element.value.get_mutable():
|
|
val = str(element.value)
|
|
list_val = []
|
|
lenth_val = 0
|
|
while lenth_val < (len(val)):
|
|
if val[lenth_val] != ' ':
|
|
list_val.append(int(val[lenth_val], 16))
|
|
lenth_val += 1
|
|
else:
|
|
list_val.append(0xFF)
|
|
lenth_val += 1
|
|
self._memobj.group3.group3 = list_val
|
|
|
|
elif setting == 'group4' and element.value.get_mutable():
|
|
val = str(element.value)
|
|
list_val = []
|
|
lenth_val = 0
|
|
while lenth_val < (len(val)):
|
|
if val[lenth_val] != ' ':
|
|
list_val.append(int(val[lenth_val], 16))
|
|
lenth_val += 1
|
|
else:
|
|
list_val.append(0xFF)
|
|
lenth_val += 1
|
|
self._memobj.group4.group4 = list_val
|
|
|
|
elif setting == 'group5' and element.value.get_mutable():
|
|
val = str(element.value)
|
|
list_val = []
|
|
lenth_val = 0
|
|
while lenth_val < (len(val)):
|
|
if val[lenth_val] != ' ':
|
|
list_val.append(int(val[lenth_val], 16))
|
|
lenth_val += 1
|
|
else:
|
|
list_val.append(0xFF)
|
|
lenth_val += 1
|
|
self._memobj.group5.group5 = list_val
|
|
|
|
elif setting == 'group6' and element.value.get_mutable():
|
|
val = str(element.value)
|
|
list_val = []
|
|
lenth_val = 0
|
|
while lenth_val < (len(val)):
|
|
if val[lenth_val] != ' ':
|
|
list_val.append(int(val[lenth_val], 16))
|
|
lenth_val += 1
|
|
else:
|
|
list_val.append(0xFF)
|
|
lenth_val += 1
|
|
self._memobj.group6.group6 = list_val
|
|
|
|
elif setting == 'group7' and element.value.get_mutable():
|
|
val = str(element.value)
|
|
list_val = []
|
|
lenth_val = 0
|
|
while lenth_val < (len(val)):
|
|
if val[lenth_val] != ' ':
|
|
list_val.append(int(val[lenth_val], 16))
|
|
lenth_val += 1
|
|
else:
|
|
list_val.append(0xFF)
|
|
lenth_val += 1
|
|
self._memobj.group7.group7 = list_val
|
|
|
|
elif setting == 'group8' and element.value.get_mutable():
|
|
val = str(element.value)
|
|
list_val = []
|
|
lenth_val = 0
|
|
while lenth_val < (len(val)):
|
|
if val[lenth_val] != ' ':
|
|
list_val.append(int(val[lenth_val], 16))
|
|
lenth_val += 1
|
|
else:
|
|
list_val.append(0xFF)
|
|
lenth_val += 1
|
|
self._memobj.group8.group8 = list_val
|
|
elif setting == 'micgain':
|
|
self._memobj.mic.micgain = (
|
|
str(element.value))
|
|
elif setting == 'stuncode' and element.value.get_mutable():
|
|
val = str(element.value)
|
|
list_val = []
|
|
lenth_val = 0
|
|
while lenth_val < (len(val)):
|
|
if val[lenth_val] != ' ':
|
|
list_val.append(int(val[lenth_val], 16))
|
|
lenth_val += 1
|
|
else:
|
|
list_val.append(0xFF)
|
|
lenth_val += 1
|
|
self._memobj.skcode.stuncode = list_val
|
|
elif setting == 'killcode' and element.value.get_mutable():
|
|
val = str(element.value)
|
|
list_val = []
|
|
lenth_val = 0
|
|
while lenth_val < (len(val)):
|
|
if val[lenth_val] != ' ':
|
|
list_val.append(int(val[lenth_val], 16))
|
|
lenth_val += 1
|
|
else:
|
|
list_val.append(0xFF)
|
|
lenth_val += 1
|
|
self._memobj.skcode.killcode = list_val
|
|
elif element.value.get_mutable():
|
|
setattr(obj, setting, element.value)
|
|
except Exception:
|
|
LOG.debug(element.get_name())
|
|
raise
|
|
|
|
def _set_fm_preset(self, settings):
|
|
for element in settings:
|
|
try:
|
|
val = element.value
|
|
if self._memobj.fm_presets <= 108.0 * 10 - 650:
|
|
value = int(val.get_value() * 10 - 650)
|
|
else:
|
|
value = int(val.get_value() * 10)
|
|
LOG.debug("Setting fm_presets = %s" % (value))
|
|
self._memobj.fm_presets = value
|
|
except Exception:
|
|
LOG.debug(element.get_name())
|
|
raise
|
|
|
|
|
|
@directory.register
|
|
class TDH8_HAM(TDH8):
|
|
VENDOR = "TIDRADIO"
|
|
MODEL = "TD-H8-HAM"
|
|
ident_mode = b'P31185\xff\xff'
|
|
_ham = True
|
|
_txbands = [(144000000, 149000000), (420000000, 451000000)]
|
|
|
|
|
|
@directory.register
|
|
class TDH8_GMRS(TDH8):
|
|
VENDOR = "TIDRADIO"
|
|
MODEL = "TD-H8-GMRS"
|
|
ident_mode = b'P31184\xff\xff'
|
|
_gmrs = True
|
|
_txbands = [(136000000, 175000000), (400000000, 521000000)]
|
|
_tx_power = [chirp_common.PowerLevel("Low", watts=1.00),
|
|
chirp_common.PowerLevel("High", watts=8.00)]
|
|
|
|
def validate_memory(self, mem):
|
|
msgs = super().validate_memory(mem)
|
|
if 31 <= mem.number <= 54 and mem.freq not in GMRS_FREQS:
|
|
msgs.append(chirp_common.ValidationError(
|
|
"The frequency in channels 31-54 must be between"
|
|
"462.55000-462.72500 in 0.025 increments."))
|
|
return msgs
|
|
|
|
|
|
@directory.register
|
|
class UV68(TDH8):
|
|
VENDOR = "TID"
|
|
MODEL = "TD-UV68"
|
|
|
|
|
|
@directory.register
|
|
class TDH3(TDH8):
|
|
VENDOR = "TIDRADIO"
|
|
MODEL = "TD-H3"
|
|
ident_mode = b'P31185\xff\xff'
|
|
_memsize = 0x1fef
|
|
_ranges_main = [(0x0000, 0x1fef)]
|
|
_idents = [TD_H3]
|
|
_txbands = [(136000000, 600000000)]
|
|
_rxbands = [(50000000, 107999000), (108000000, 136000000)]
|
|
_aux_block = True
|
|
_tri_power = True
|
|
_gmrs = False
|
|
_ham = False
|
|
_mem_params = (0x1F2F)
|
|
_tx_power = [chirp_common.PowerLevel("Low", watts=1.00),
|
|
chirp_common.PowerLevel("High", watts=4.00)]
|
|
|
|
def process_mmap(self):
|
|
self._memobj = bitwise.parse(MEM_FORMAT_H3, self._mmap)
|
|
|
|
|
|
@directory.register
|
|
class TDH3_HAM(TDH3):
|
|
VENDOR = "TIDRADIO"
|
|
MODEL = "TD-H3-HAM"
|
|
ident_mode = b'P31185\xff\xff'
|
|
_ham = True
|
|
_txbands = [(144000000, 149000000), (420000000, 451000000)]
|
|
|
|
|
|
@directory.register
|
|
class TDH3_GMRS(TDH3):
|
|
VENDOR = "TIDRADIO"
|
|
MODEL = "TD-H3-GMRS"
|
|
ident_mode = b'P31184\xff\xff'
|
|
_gmrs = True
|
|
_txbands = [(136000000, 175000000), (400000000, 521000000)]
|
|
|
|
def validate_memory(self, mem):
|
|
msgs = super().validate_memory(mem)
|
|
if 31 <= mem.number <= 54 and mem.freq not in GMRS_FREQS:
|
|
msgs.append(chirp_common.ValidationError(
|
|
"The frequency in channels 31-54 must be between"
|
|
"462.55000-462.72500 in 0.025 increments."))
|
|
return msgs
|