[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