# HG changeset patch # User Bernhard Hailer ham73tux@gmail.com # Date 1580597959 28800 # Sat Feb 01 14:59:19 2020 -0800 # Node ID 04404b4e386b95acfe95b590af75d9f26619d0ba # Parent 53cd045bbf8253d32b43a528b245caff243e4c02 While implementing two more radios of the Yaesu FT-4 family (the FT-25, see #7543, and the FT-4V, see #7387), I found that with some moderate reorganization, implementation of new radios will become easier. The proposed reorganization implements one more interstitial layer of inheritance to support the sub families of FT-4 (containing the FT-4X and FT-4V) and FT-65 (containing FT-65 and FT-25). Also, some variable assignments have been moved from the individual radio classes to the SCU-35 base class and to the interstitial classes named above.
This change also adds the infrastructure for adding European or Asian models, and as such prepare for addressing a number of currently open issues (6.25kHz tune step issues on EU models, frequency limitations; see #6619, #6651, #6677, #6761, #6869). It will also make it easier for a few additional fixes for issues which were found during my work on this driver (#7601, #7603, #7605). I will submit these fixes in additional patches.
Fixes: #7615
diff --git a/chirp/drivers/ft4.py b/chirp/drivers/ft4.py --- a/chirp/drivers/ft4.py +++ b/chirp/drivers/ft4.py @@ -2,6 +2,7 @@ # Derives loosely from two sources released under GPLv2: # ./template.py, Copyright 2012 Dan Smith dsmith@danplanet.com # ./ft60.py, Copyright 2011 Dan Smith dsmith@danplanet.com +# Edited 2020 Bernhard Hailer AE6YN ham73tux@gmail.com # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -18,8 +19,8 @@
""" CHIRP driver for Yaesu radios that use the SCU-35 cable. This includes at -least the FT-4, FT-25, FT-35, and FT-65. This driver will not work with -older Yaesu models. +least the FT-4X, FT-4V, FT-65, and FT-25. This driver will not work with older +Yaesu models. """ import logging import struct @@ -261,7 +262,8 @@ msg = "Bad echo. Sent:" + util.hexprint(cmd) + ", " msg += "Received:" + util.hexprint(echo) LOG.debug(msg) - raise errors.RadioError("Incorrect echo on serial port. Bad cable?") + raise errors.RadioError( + "Incorrect echo on serial port. Radio off? Bad cable?") if response_len is None: return variable_len_resp(pipe) if response_len > 0: @@ -474,8 +476,6 @@
SKIPS = ["", "S"]
-BASETYPE_FT4 = ["FT-4XR", "FT-4XE"] -BASETYPE_FT65 = ["FT-65R"] POWER_LEVELS = [chirp_common.PowerLevel("Low", watts=0.5), chirp_common.PowerLevel("Mid", watts=2.5), chirp_common.PowerLevel("High", watts=5.0)] @@ -560,16 +560,53 @@ # Each special has unique constrants: band, name yes/no, and pms L/U # The FT-65 class replaces the "prog" entry in this list. # The name field must be the name of a slot array in MEM_FORMAT -SPECIALS = [ +SPECIALS_FT4 = [ ("pms", PMSNAMES), ("vfo", ["VFO A UHF", "VFO A VHF", "VFO B FM", "VFO B VHF", "VFO B UHF"]), ("home", ["HOME FM", "HOME VHF", "HOME UHF"]), ("prog", ["P1", "P2"]) ] -BAND_ASSIGNMENTS = [2, 1, 0, 1, 2, 0, 1, 2] # bands for the vfos and homes +SPECIALS_FT65 = SPECIALS_FT4 FT65_PROGS = ("prog", ["P1", "P2", "P3", "P4"]) -FT65_SPECIALS = list(SPECIALS) # a shallow copy works here -FT65_SPECIALS[-1] = FT65_PROGS # replace the last entry (P key names) +SPECIALS_FT65[-1] = FT65_PROGS # replace the last entry (P key names) + +# I wonder whether we should simply open the bands to what the radios allow +# for RX? The radios do take care of allowing or prohibiting TX on their own. +# In that case, the ASIA settings can be used for any region model. +# To be discussed with Dan Clemmensen. [AE6YN] + +VALID_BANDS_US_DUAL = [ + (65000000, 108000000), # broadcast FM, receive only + (144000000, 148000000), # VHF, US version, TX and RX + (430000000, 450000000) # UHF, US version, TX and RX + ] +VALID_BANDS_EU_DUAL = [ + (65000000, 108000000), # broadcast FM, receive only + (144000000, 146000000), # VHF, EU version, TX and RX + (430000000, 440000000) # UHF, EU version, TX and RX + ] +VALID_BANDS_ASIA_DUAL = [ + (65000000, 108000000), # broadcast FM, receive only + (136000000, 174000000), # VHF, Asia version, TX and RX + (400000000, 480000000) # UHF, Asia version, TX and RX + ] + +VALID_BANDS_US_VHF = [ + (65000000, 108000000), # broadcast FM, receive only + (144000000, 148000000), # VHF, US version, TX and RX + ] +VALID_BANDS_EU_VHF = [ + (65000000, 108000000), # broadcast FM, receive only + (144000000, 146000000), # VHF, EU version, TX and RX + ] +VALID_BANDS_ASIA_VHF = [ + (65000000, 108000000), # broadcast FM, receive only + (136000000, 174000000), # VHF, Asia version, TX and RX + ] + +# bands for the five VFO and three home channel memories +BAND_ASSIGNMENTS_DUALBAND = [2, 1, 0, 1, 2, 0, 1, 2] # all locations used +BAND_ASSIGNMENTS_MONO_VHF = [1, 1, 0, 1, 1, 0, 1, 1] # UHF locations unused
# None, and 50 Tones. Use this explicit array because the @@ -620,8 +657,8 @@ chirp_common.ExperimentalRadio): """ Base class for all Yaesu radios using the SCU-35 programming cable - and its protocol. Classes for specific radios extend this class and - are found at the end of this file. + and its protocol. Classes for sub families extend this class and + are found towards the end of this file. """ VENDOR = "Yaesu" MODEL = "SCU-35Generic" # No radio directly uses the base class @@ -629,6 +666,12 @@ MAX_MEM_SLOT = 200 NEEDS_COMPAT_SERIAL = False
+ # These settings are common to all radios in this family. + _valid_chars = chirp_common.CHARSET_ASCII + numblocks = 0x215 # number of 16-byte blocks in the radio + _memsize = 16 * numblocks # used by CHIRP file loader to guess radio type + MAX_MEM_SLOT = 200 + @classmethod def get_prompts(cls): rp = chirp_common.RadioPrompts() @@ -669,7 +712,6 @@ rf.has_dtcs_polarity = False # REV TN reverses the tone, not the dcs rf.has_cross = True rf.has_settings = True - rf.valid_tuning_steps = self.legal_steps
return rf
@@ -1027,14 +1069,11 @@ mem = chirp_common.Memory() _mem, ndx, num, regtype, sname = self.slotloc(memref) mem.number = num - mem.freq = int(_mem.freq) * 10 - mem.offset = int(_mem.offset) * self.freq_offset_scale - mem.duplex = DUPLEX[_mem.duplex]
- self.decode_sql(mem, _mem) - mem.power = POWER_LEVELS[_mem.tx_pwr] - mem.mode = ["FM", "NFM"][_mem.tx_width] - mem.tuning_step = STEP_CODE[_mem.step] + # First, we need to know whether a channel is enabled, + # then we can process any channel parameters. + # It was found (at least on an FT-25) that channels might be + # uninitialized and memory is just completely filled with 0xFF.
if regtype == "pms": mem.extd_number = sname @@ -1043,15 +1082,34 @@ mem.name = clean_name(self._memobj.names[ndx].chrs) mem.empty = not retrieve_bit(self._memobj.enable, ndx) mem.skip = SKIPS[retrieve_bit(self._memobj.scan, ndx)] - txfreq = int(self._memobj.txfreqs[ndx].freq) * 10 - if (txfreq != 0) and (txfreq != mem.freq): - mem.duplex = "split" - mem.offset = txfreq else: mem.empty = False mem.extd_number = sname mem.immutable = ["number", "extd_number", "name", "skip"]
+ # So, now if channel is not empty, we can do the evaluation of + # all parameters. Otherwise we set them to defaults. + + if mem.empty: + mem.freq = 0 + mem.offset = 0 + mem.duplex = "off" + mem.power = POWER_LEVELS[0] # "High" + mem.mode = "FM" + mem.tuning_step = 0 + else: + mem.freq = int(_mem.freq) * 10 + txfreq = int(self._memobj.txfreqs[ndx].freq) * 10 + if (txfreq != 0) and (txfreq != mem.freq): + mem.duplex = "split" + mem.offset = txfreq + else: + mem.offset = int(_mem.offset) * self.freq_offset_scale + mem.duplex = DUPLEX[_mem.duplex] + self.decode_sql(mem, _mem) + mem.power = POWER_LEVELS[2 - _mem.tx_pwr] + mem.mode = ["FM", "NFM"][_mem.tx_width] + mem.tuning_step = STEP_CODE[_mem.step] return mem
def enforce_band(self, memloc, freq, mem_num, sname): @@ -1105,28 +1163,16 @@ return
-@directory.register -class YaesuFT4Radio(YaesuSC35GenericRadio): - MODEL = "FT-4XR" - _basetype = BASETYPE_FT4 - valid_bands = [ - (65000000, 108000000), # broadcast FM, receive only - (144000000, 148000000), # VHF, US version, TX and RX - (430000000, 450000000) # UHF, US version, TX and RX - # VHF, RX (136000000, 174000000) - # UHF, RX (400000000, 480000000) - ] - _valid_chars = chirp_common.CHARSET_ASCII - numblocks = 0x215 # number of 16-byte blocks in the radio - _memsize = 16 * numblocks # used by CHIRP file loader to guess radio type - MAX_MEM_SLOT = 200 - Pkeys = 2 # number of programmable keys on the FT-4 - namelen = 6 # length of the mem name display on the FT-4 front-panel - id_str = b'IFT-35R\x00\x00V100\x00\x00' +class YaesuFT4GenericRadio(YaesuSC35GenericRadio): + """ + FT-4 sub family class. Classes for individual radios extend + these classes and are found at the end of this file. + """ + class_specials = SPECIALS_FT4 + Pkeys = 2 # number of programmable keys + namelen = 6 # length of the mem name display on the front-panel freq_offset_scale = 25000 - legal_steps = US_LEGAL_STEPS class_group_descs = YaesuSC35GenericRadio.group_descriptions - class_specials = SPECIALS # names for the setmode function for the programmable keys. Mode zero means # that the key is programmed for a memory not a setmode. SETMODES = [ @@ -1143,31 +1189,19 @@ ]
-@directory.register -class YaesuFT65Radio(YaesuSC35GenericRadio): - MODEL = "FT-65R" - _basetype = BASETYPE_FT65 - valid_bands = [ - (65000000, 108000000), # broadcast FM, receive only - (144000000, 148000000), # VHF, US version, TX and RX - (430000000, 450000000) # UHF, US version, TX and RX - # VHF, RX (136000000, 174000000) - # UHF, RX (400000000, 480000000) - ] - _valid_chars = chirp_common.CHARSET_ASCII - numblocks = 0x215 # number of 16-byte blocks in the radio - _memsize = 16 * numblocks # used by CHIRP file loader to guess radio type - 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' +class YaesuFT65GenericRadio(YaesuSC35GenericRadio): + """ + FT-65 sub family class. Classes for individual radios extend + these classes and are found at the end of this file. + """ + class_specials = SPECIALS_FT65 + Pkeys = 4 # number of programmable keys + namelen = 8 # length of the mem name display on the front-panel freq_offset_scale = 50000 - legal_steps = US_LEGAL_STEPS # we need a deep copy here because we are adding deeper than the top level. class_group_descs = copy.deepcopy(YaesuSC35GenericRadio.group_descriptions) add_paramdesc( class_group_descs, "misc", ("compander", "Compander", ["OFF", "ON"])) - class_specials = FT65_SPECIALS # names for the setmode function for the programmable keys. Mode zero means # that the key is programmed for a memory not a setmode. SETMODES = [ @@ -1180,3 +1214,31 @@ "step", "tot", "tx pwr", "tx save", "vfo.spl", # 30-34 "vox", "wfm.rcv", "wide/nar", "wx alert", "scramble" # 35-39 ] + + +# Classes for each individual radio. + + +@directory.register +class YaesuFT4XRRadio(YaesuFT4GenericRadio): + """ + FT-4X dual band, US version + """ + MODEL = "FT-4XR" + id_str = b'IFT-35R\x00\x00V100\x00\x00' + valid_bands = VALID_BANDS_US_DUAL + legal_steps = US_LEGAL_STEPS + BAND_ASSIGNMENTS = BAND_ASSIGNMENTS_DUALBAND + + +@directory.register +class YaesuFT65RRadio(YaesuFT65GenericRadio): + """ + FT-65 dual band, US version + """ + MODEL = "FT-65R" + id_str = b'IH-420\x00\x00\x00V100\x00\x00' + valid_bands = VALID_BANDS_US_DUAL + legal_steps = US_LEGAL_STEPS + BAND_ASSIGNMENTS = BAND_ASSIGNMENTS_DUALBAND +