[chirp_devel] [PATCH 5 of 5] [ft4] rework tones [#4787]
DanClemmensen
Tue Feb 26 13:34:04 PST 2019
# HG changeset patch
# User DanClemmensen <DanClemmensen at gmail.com>
# Date 1551210139 28800
# Tue Feb 26 11:42:19 2019 -0800
# Node ID b0ed770692e8e00ea9d3cec5bf9cf58c3f7367da
# Parent 607b8b18a90e46231a2818304107f3f8d8a7173a
[ft4] rework tones [#4787]
cleanup last tone bugs and slightly refactor to
remove redundant lists. There is some minor
cosmetic stuff also
diff -r 607b8b18a90e -r b0ed770692e8 chirp/drivers/ft4.py
--- a/chirp/drivers/ft4.py Mon Feb 25 17:43:10 2019 -0800
+++ b/chirp/drivers/ft4.py Tue Feb 26 11:42:19 2019 -0800
@@ -192,7 +192,6 @@
# the serial port. The code runs on either Python 2 or Python3, so some
# constructs could be better optimized for one or the other, but not both.
-
def get_mmap_data(radio):
"""
horrible kludge needed until we convert entirely to Python 3 OR we add a
@@ -217,7 +216,7 @@
return sum(x for x in bytearray(data)) & 0xFF
-def variable_len_resp(pipe):
+def variable_len_resp(pipe, cmd):
"""
when length of expected reply is not known, read byte at a time
until the ack character is found.
@@ -233,8 +232,11 @@
response += b
i += 1
if i > toolong:
- LOG.debug("Response too long. got" + util.hexprint(response))
- raise errors.RadioError("Response too long.")
+ msg = "received too many bytes from radio before ACK. "
+ msg += "Sent " + util.hexprint(cmd)
+ msg += ". Got " + util.hexprint(response)
+ LOG.debug(msg)
+ raise errors.RadioError(msg)
return(response)
@@ -253,19 +255,19 @@
pipe.write(cmd)
echo = pipe.read(len(cmd))
if echo != cmd:
- msg = "Bad echo. Sent:" + util.hexprint(cmd) + ", "
+ msg = "Bad echo on serial port. Sent:" + util.hexprint(cmd) + ", "
msg += "Received:" + util.hexprint(echo)
LOG.debug(msg)
- raise errors.RadioError("Incorrect echo on serial port.")
+ raise errors.RadioError(msg)
if response_len is None:
- return variable_len_resp(pipe)
+ return variable_len_resp(pipe, cmd)
if response_len > 0:
response = pipe.read(response_len)
else:
response = b""
ack = pipe.read(1)
if ack != b'\x06':
- LOG.debug("missing ack: expected 0x06, got" + util.hexprint(ack))
+ LOG.debug("missing ACK: expected 0x06, got" + util.hexprint(ack))
raise errors.RadioError("Incorrect ACK on serial port.")
return response
@@ -282,12 +284,13 @@
raise errors.RadioError("expected QX from radio.")
id_response = sendcmd(radio.pipe, b'\x02', None)
if id_response != radio.id_str:
- substr0=radio.id_str[:radio.id_str.find('\x00')]
+ substr0 = radio.id_str[:radio.id_str.find('\x00')]
if id_response[:id_response.find('\x00')] != substr0:
- msg = "ID mismatch. Expected" + util.hexprint(radio.id_str)
+ msg = "Unknown ID string received from radio"
+ msg += "Expected:" + util.hexprint(radio.id_str)
msg += ", Received:" + util.hexprint(id_response)
LOG.warning(msg)
- raise errors.RadioError("Incorrect ID.")
+ raise errors.RadioError(msg)
else:
msg = "ID suspect. Expected" + util.hexprint(radio.id_str)
msg += ", Received:" + util.hexprint(id_response)
@@ -447,8 +450,59 @@
# on the US versions (FT-4XR)
STEP_CODE = [0, 5.0, 6.25, 10.0, 12.5, 15.0, 20.0, 25.0, 50.0, 100.0]
-TONE_MODES = ["", "Tone", "TSQL", "DTCS", "DTCS-R", "TSQL-R", "Cross"]
-CROSS_MODES = ["DTCS->", "DTCS->DTCS"] # only the extras we need
+# Yaesu sql_type field codes
+SQL_TYPE = ["off", "R-TONE", "T-TONE", "TSQL", "REV-TN", "DCS", "PAGER"]
+
+# map a CHIRP tone mode to a FT-4 sql and which if any code to set to 0.
+# The keys are provided to RadioFeatures as a list, so these are the
+# only values we expect to see in mem.tmode. This map as the identical
+# structure as the one below to simplify the code. It permits stricter
+# zeroing of the unused fields later if we find that it is needed,
+# but the CHIRP unit tests want us to leave them alone.
+MODES_TONE = {
+ "": ("off", None),
+ "Tone": ("T-TONE", None), # chirp means "xmit, not rcv"
+ "TSQL": ("TSQL", None), # chirp means "xmit and rcv, tx==rx"
+ "DTCS": ("DCS", None), # chirp means "xmt and rcv, tx==rx"
+ "TSQL-R": ("REV-TN", None), # chirp means reverse R-Tone
+ "Cross": () # not used in lookup, needed in list
+ }
+
+# Map a CHIRP Cross type if the CHIRP mem.tmode is "Cross". The keys
+# are provided to RadioFeatures as a list, so these are the
+# only values we expect to see in mem.cross.
+MODES_CROSS = {
+ "DTCS->": ("DCS", "rx_dcs"),
+ "->DTCS": ("DCS", "tx_dcs"),
+ "DTCS->DTCS": ("DCS", None),
+ "->Tone": ("R-TONE", None),
+ "Tone->Tone": ("TSQL", None)
+ }
+
+# Map the radio image sql_type (0-6) back to the CHIRP mem values
+# Types "TSQL" and "DCS" each map to different CHIRP values depending
+# on the radio values on the tx and rx tone codes.
+RADIO_TMODES = [
+ ("(None)", ["", ""]), # sql_type= 0. off
+ ("(None)", ["Cross", "->Tone"]), # sql_type= 1. R-TONE
+ ("(None)", ["Tone", ""]), # sql_type= 2. T-TONE
+ ("(None)", None, "tx_ctcss", "rx_ctcss", [ # sql_type= 3. TSQL
+ ["", None], # tx==0, rx==0 : not valid
+ ["TSQL", ""], # tx==0
+ ["Tone", ""], # rx==0
+ ["Cross", "Tone->Tone"], # tx!=rx
+ ["TSQL", ""] # tx==rx
+ ]),
+ ("(None)", ["TSQL-R", ""]), # sql_type= 4. REV TN
+ ("(None)", None, "tx_dcs", "rx_dcs", [ # sql_type= 5.DCS
+ ["", None], # tx==0, rx==0 : not valid
+ ["Cross", "->DTCS"], # tx==0
+ ["Cross", "DTCS->"], # rx==0
+ ["Cross", "DTCS->DTCS"], # tx!=rx
+ ["DTCS", ""] # tx==rx
+ ]),
+ ("PAGER", ["", None]) # sql_type= 6. handled as a CHIRP "extra"
+ ]
DTMF_CHARS = "0123456789ABCD*#- "
CW_ID_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ "
@@ -491,6 +545,9 @@
612, 624, 627, 631, 632, 654, 662, 664, 703, 712, 723,
731, 732, 734, 743, 754
]
+
+# The legal PAGER codes are exactly the same as the CTCSS codes,
+# but we pass them to the UI as a lsit of strings to display.
EPCS_CODES = [format(flt) for flt in [0] + TONE_MAP[1:]]
@@ -534,8 +591,8 @@
rf.valid_special_chans = specials
rf.memory_bounds = (1, self.MAX_MEM_SLOT)
rf.valid_duplexes = DUPLEX
- rf.valid_tmodes = TONE_MODES
- rf.valid_cross_modes = CROSS_MODES
+ rf.valid_tmodes = list(MODES_TONE.keys())
+ rf.valid_cross_modes = list(MODES_CROSS.keys())
rf.valid_power_levels = POWER_LEVELS
rf.valid_tuning_steps = self.legal_steps
rf.valid_skips = SKIPS
@@ -559,6 +616,8 @@
def sync_in(self):
try:
self._mmap = do_download(self)
+ except errors.RadioError:
+ raise
except Exception as e:
raise errors.RadioError("Failed to communicate with radio: %s" % e)
self.process_mmap()
@@ -567,6 +626,8 @@
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)
@@ -707,7 +768,7 @@
# ----------------end of group_descriptions
# allow a child class to add a param.
- def add_paramdesc(self,group, param):
+ def add_paramdesc(self, group, param):
for description in self.group_descriptions:
groupname, title, parms = description
if group == groupname:
@@ -770,35 +831,14 @@
LOG.debug(element.get_name())
raise
- RADIO_TMODES = [
- ("(None)", ["", ""]), # off
- ("(None)", ["TSQL-R", ""]), # R-TONE
- ("(None)", ["Tone", ""]), # T-TONE
- ("(None)", None, "tx_ctcss", "rx_ctcss", [ # TSQL
- ["", None], # x==0, r==0 : not valid
- ["TSQL-R", ""], # x==0
- ["Tone", ""], # r==0
- ["TSQL", ""], # x!=r
- ["TSQL", ""] # x==r
- ]),
- ("REV-TN", ["TSQL-R", ""]),
- ("(None)", None, "tx_dcs", "rx_dcs", [ # DCS
- ["", None], # x==0, r==0 : not valid
- ["DTCS-R", ""], # x==0
- ["Cross", "DTCS->"], # r==0
- ["Cross", "DTCS->DTCS"], # x!=r
- ["DTCS", ""] # x==r
- ]),
- ("PAGER", ["", None]) # handled as a CHIRP "extra"
- ]
LOOKUP = [[True, True], [True, False], [False, True], [False, False]]
def decode_sql(self, mem, chan):
"""
examine the radio channel fields and determine the correct
- CHIRP CSV values for tmode, cross_mode, and dcts_polarity
+ CHIRP CSV values for tmode, cross_mode, and sql_override
"""
- mode = self.RADIO_TMODES[chan.sql_type]
+ mode = RADIO_TMODES[chan.sql_type]
chirpvals = mode[1]
if not chirpvals:
x = getattr(chan, mode[2])
@@ -820,32 +860,10 @@
mem.rx_dtcs = DTCS_MAP[chan.rx_dcs]
LOG.debug(" setting sql_override to <%s>" % mode[0])
mem.extra = RadioSettingGroup("Extra", "extra")
- extra_modes = ["(None)", "REV-TN", "PAGER"]
+ extra_modes = ["(None)", "PAGER"]
valuelist = RadioSettingValueList(extra_modes, mode[0])
rs = RadioSetting("sql_override", "Squelch override", valuelist)
mem.extra.append(rs)
- # Yaesu sql_type field codes
- SQL_TYPE = ["off", "R-TONE", "T-TONE", "TSQL", "REV-TN", "DCS", "PAGER"]
- # map a CHIRP tone mode to a FT-4 sql and which if any code to set to 0.
- MODE_TONE = {
- "": ("off", None),
- "Tone": ("T-TONE", "rx_ctcss"),
- "TSQL": ("TSQL", None),
- "DTCS": ("DCS", None), # must set rx_dcs to tx_dcs?
- "DTCS-R": ("DCS", "tx_dcs"),
- "TSQL-R": ("R-TONE", "tx_ctcss"), # not documented on wiki
- "Cross": () # not used in lookup
- }
-
- # map a CHIRP Cross type if the CHIRP sql type is "cross"
- MODE_CROSS = {
- "DTCS->": ("DCS", "rx_dcs"),
- "DTCS->DTCS": ("DCS", None)
- # "Tone->Tone": ("TSQL", None),
- # "->DTCS": ("DCS", "tx_dcs"),
- # "->Tone": ("R-TONE", None),
- # "Tone->": ("T-Tone", None)
- }
def encode_sql(self, mem, chan):
"""
@@ -858,22 +876,23 @@
chan.tx_dcs = DTCS_MAP.index(mem.dtcs)
chan.rx_ctcss = TONE_MAP.index(mem.ctone)
chan.rx_dcs = DTCS_MAP.index(mem.rx_dtcs)
+ if mem.tmode == "TSQL":
+ chan.tx_ctcss = chan.rx_ctcss # CHIRP uses ctone for TSQL
+ if mem.tmode == "DTCS":
+ chan.tx_dcs = chan.rx_dcs # CHIRP uses rx_dtcs for DTCS
tbl, ndx = [
- (self.MODE_TONE, mem.tmode),
- (self.MODE_CROSS, mem.cross_mode)
+ (MODES_TONE, mem.tmode),
+ (MODES_CROSS, mem.cross_mode)
][mem.tmode == "Cross"]
- row = tbl[ndx]
- if ndx == "DTCS":
- chan.rx_dcs = chan.tx_dcs
- chan.sql_type = self.SQL_TYPE.index(row[0])
- if row[1]:
- setattr(chan, row[1], 0)
+ sql_type, suppress = tbl[ndx]
+ chan.sql_type = SQL_TYPE.index(sql_type)
+ if suppress:
+ setattr(chan, suppress, 0)
for setting in mem.extra:
if (setting.get_name() == 'sql_override'):
value = str(setting.value)
if value != "(None)":
- chan.sql_type = self.SQL_TYPE.index(value)
-
+ chan.sql_type = SQL_TYPE.index(value)
return
# given a CHIRP memory ref, get the radio memobj for it.
@@ -962,7 +981,6 @@
mem.empty = False
mem.extd_number = sname
mem.immutable = ["number", "extd_number", "name", "skip"]
-
return mem
# modify a radio channel in memobj based on info in CHIRP canonical form
@@ -995,7 +1013,6 @@
duplex = "off" # radio ignores when tx != rx
self._memobj.txfreqs[num-1].freq = txfreq
_mem.duplex = DUPLEX.index(duplex)
-
return
@@ -1018,22 +1035,23 @@
namelen = 6 # length of the mem name display on the FT-4 front-panel
id_str = b'IFT-35R\x00\x00V100\x00\x00'
legal_steps = list(STEP_CODE)
- legal_steps.remove(6.25) #should not remove if euro version
+ legal_steps.remove(6.25) # should not remove if euro version
# names for the setmode function for the programmable keys. Mode zero means
# that the key is programmed for a memory not a setmode.
SETMODES = [
- "mem", "apo", "ar bep", "ar int", "beclo", #00-04
- "beep", "bell", "cw id", "cw wrt", "dc vlt", #05-09
- "dcs cod", "dt dly", "dt set", "dtc spd", "edg.bep", #10-14
- "lamp", "led.bsy", "led.tx", "lock", "m/t-cl", #15-19
- "mem.del", "mem.tag", "pag.abk", "pag.cdr", "pag.cdt", #20-24
- "pri.rvt", "pswd", "pswdwt", "rf sql", "rpt.ars", #25-29
- "rpt.frq", "rpt.sft", "rxsave", "scn.lmp", "scn.rsm", #30-34
- "skip", "sql.typ", "step", "tn frq", "tot", #35-39
- "tx pwr", "tx save", "vfo.spl", "vox", "wfm.rcv", #40-44
- "w/n.dev", "wx.alert" #45-46
+ "mem", "apo", "ar bep", "ar int", "beclo", # 00-04
+ "beep", "bell", "cw id", "cw wrt", "dc vlt", # 05-09
+ "dcs cod", "dt dly", "dt set", "dtc spd", "edg.bep", # 10-14
+ "lamp", "led.bsy", "led.tx", "lock", "m/t-cl", # 15-19
+ "mem.del", "mem.tag", "pag.abk", "pag.cdr", "pag.cdt", # 20-24
+ "pri.rvt", "pswd", "pswdwt", "rf sql", "rpt.ars", # 25-29
+ "rpt.frq", "rpt.sft", "rxsave", "scn.lmp", "scn.rsm", # 30-34
+ "skip", "sql.typ", "step", "tn frq", "tot", # 35-39
+ "tx pwr", "tx save", "vfo.spl", "vox", "wfm.rcv", # 40-44
+ "w/n.dev", "wx.alert" # 45-46
]
+
# don't register the FT-65 in the production version until it is tested
# @directory.register
class YaesuFT65Radio(YaesuSC35GenericRadio):
@@ -1052,9 +1070,9 @@
MAX_MEM_SLOT = 200
Pkeys = 4 # number of programmable keys on the FT-65
namelen = 8 # length of the mem name display on the FT-65 front panel
- id_str=b'IH-420\x00\x00\x00V100\x00\x00'
+ id_str = b'IH-420\x00\x00\x00V100\x00\x00'
legal_steps = list(STEP_CODE)
- legal_steps.remove(6.25) #should not remove if euro version
+ legal_steps.remove(6.25) # should not remove if euro version
# names for the setmode function for the programmable keys. Mode zero means
# that the key is programmed for a memory not a setmode.
SETMODES = [
@@ -1063,10 +1081,10 @@
"dc volt", "dcs code", "dtmf set", "dtmf wrt", "edg bep", # 10-14
"key lock", "lamp", "ledbsy", "mem del", "mon/t-cl", # 15-19
"name tag", "pager", "password", "pri.rvt", "repeater", # 20-24
- "resume", "rf.sql", "scn.lamp", "skip", "sql type", # 25-29
+ "resume", "rf.sql", "scn.lamp", "skip", "sql type", # 25-29
"step", "tot", "tx pwr", "tx save", "vfo.spl", # 30-34
"vox", "wfm.rcv", "wide/nar", "wx alert", "scramble" # 35-39
]
+
def __init__(self):
self.add_paramdesc("misc", ("compander", "Compander", ["ON", "OFF"]))
-
More information about the chirp_devel
mailing list