[chirp_devel] [PATCH] [ft4]rework code for progkeys [#6583]
# HG changeset patch # User DanClemmensen DanClemmensen@gmail.com # Date 1552423723 25200 # Tue Mar 12 13:48:43 2019 -0700 # Node ID 83538aecb6f7f8462f79397950277bf2b91c26bc # Parent 10c0009c697cd9b5f499bcbc59253b5ea47aa552 [ft4]rework code for progkeys [#6583] The code did not support the "func" capability, for which The radio has a separate special mem slot per prog key.
diff -r 10c0009c697c -r 83538aecb6f7 chirp/drivers/ft4.py --- a/chirp/drivers/ft4.py Tue Mar 12 13:41:46 2019 -0700 +++ b/chirp/drivers/ft4.py Tue Mar 12 13:48:43 2019 -0700 @@ -132,14 +132,6 @@ u8 digit[16]; //ASCII (*,#,-,0-9,A-D). (dash-filled) };
-//one block with stuff for the programmable keys -//supports 4 keys. FT-4 has only 2 keys. FT-65 has all 4. -struct progs { - u8 modes[8]; //should be array of 2-byte structs, but bitwise.py refuses - u8 ndx[4]; - u8 unused[8]; -}; - // area to be filled with 0xff, or just ignored struct notused { u8 unused[16]; @@ -148,6 +140,12 @@ struct unknown { u8 notknown[16]; }; + +struct progc { + u8 usage; //P key is (0-2) == unused, use P channel, use parm + u8 submode:2, //if parm!=0 submode 0-3 of mode + parm:6; //if usage == 2: if 0, use m-channel, else mode +}; """ # Actual memory layout. 0x215 blocks, in 20 groups. MEM_FORMAT += """ @@ -170,8 +168,11 @@ struct notused notused4[2]; //2040 struct dtmfset dtmf[9]; //2060 sets 1-9 struct notused notused5; //20f0 -struct progs progkeys; //2100 -struct unknown notused6[3]; //2110 +//struct progs progkeys; //2100 not a struct. bitwise.py refuses +struct progc progctl[4]; //2100 8 bytes. 1 2-byte struct per P key +u8 pmemndx[4]; //2108 4 bytes, 1 per P key +u8 notused6[4]; //210c fill out the block +struct slot prog[4]; //2110 P key "channel" array //---------------- end of FT-4 mem? """ # The remaining mem is (apparently) not available on the FT4 but is @@ -534,11 +535,15 @@
# Three separate arrays of special channel mems. # 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 = [ ("pms", PMSNAMES), ("vfo", ["VFO A UHF", "VFO A VHF", "VFO B UHF", "VFO B UHF", "VFO B FM"]), - ("home", ["HOME UHF", "HOME VHF", "HOME FM"]) + ("home", ["HOME UHF", "HOME VHF", "HOME FM"]), + ("prog", ["P1", "P2"]) ] +FT65_PROGS = ("prog", ["P1", "P2", "P3", "P4"])
# None, and 50 Tones. Use this explicit array because the # one in chirp_common could change and no longer describe our radio @@ -620,7 +625,7 @@ def get_features(self):
rf = chirp_common.RadioFeatures() - specials = [name for s in SPECIALS for name in s[1]] + specials = [name for s in self.CLASS_SPECIALS for name in s[1]] rf.valid_special_chans = specials rf.memory_bounds = (1, self.MAX_MEM_SLOT) rf.valid_duplexes = DUPLEX @@ -704,35 +709,42 @@ dtmf_digits, DTMF_CHARS, "dtmf_%i" % i, "DTMF Autodialer Memory %i" % i, group)
- def apply_P(self, element, pnum): - self.memobj.progkeys.modes[pnum * 2] = [0, 2][element.value] + def apply_P(self, element, pnum, memobj): + memobj.progctl[pnum].usage = element.value + + def apply_Pmode(self, element, pnum, memobj): + memobj.progctl[pnum].parm = element.value
- def apply_Pmode(self, element, pnum): - self.memobj.progkeys.modes[pnum * 2 + 1] = element.value + def apply_Psubmode(self, element, pnum, memobj): + memobj.progctl[pnum].submode = element.value
- def apply_Pmem(self, element, pnum): - self.memobj.progkeys.ndx[pnum] = element.value + def apply_Pmem(self, element, pnum, memobj): + memobj.pmemndx[pnum] = element.value
- MEMLIST = ["%d" % i for i in range(1, MAX_MEM_SLOT)] + PMSNAMES + MEMLIST = ["%d" % i for i in range(1, MAX_MEM_SLOT+1)] + PMSNAMES + USAGE_LIST = ["unused", "P channel", "mode or M channel"]
# called when found in the group_descriptions table # returns the settings for the programmable keys (P1-P4) def get_progs(self, group, parm): - _progkeys = self._memobj.progkeys + _progctl = self._memobj.progctl + _progndx = self._memobj.pmemndx
- def get_prog(i, val_list, valndx, sname, longname, apply): + def get_prog(i, val_list, valndx, sname, longname, f_apply): + k = str(i + 1) val = val_list[valndx] valuelist = RadioSettingValueList(val_list, val) - rs = RadioSetting(sname + str(i), longname + str(i), valuelist) - rs.set_apply_callback(apply, i) + rs = RadioSetting(sname + k, longname + k, valuelist) + rs.set_apply_callback(f_apply, i, self._memobj) group.append(rs) for i in range(0, self.Pkeys): - i1 = i + 1 - get_prog(i1, ["unused", "in use"], _progkeys.modes[i * 2], + get_prog(i, self.USAGE_LIST, _progctl[i].usage, "P", "Programmable key ", self.apply_P) - get_prog(i1, self.SETMODES, _progkeys.modes[i * 2 + 1], "modeP", + get_prog(i, self.SETMODES, _progctl[i].parm, "modeP", "mode for Programmable key ", self.apply_Pmode) - get_prog(i1, self.MEMLIST, _progkeys.ndx[i], "memP", + get_prog(i, ["0", "1", "2", "3"], _progctl[i].submode, "submodeP", + "submode for Programmable key ", self.apply_Psubmode) + get_prog(i, self.MEMLIST, _progndx[i], "memP", "mem for Programmable key ", self.apply_Pmem) # ------------ End of special settings handlers.
@@ -937,7 +949,7 @@ sname = memref if isinstance(memref, str): # named special? num = self.MAX_MEM_SLOT + 1 - for x in SPECIALS: + for x in self.CLASS_SPECIALS: try: ndx = x[1].index(memref) array = x[0] @@ -950,7 +962,7 @@ num += ndx elif memref > self.MAX_MEM_SLOT: # numbered special? ndx = memref - self.MAX_MEM_SLOT - for x in SPECIALS: + for x in self.CLASS_SPECIALS: if ndx < len(x[1]): array = x[0] sname = x[1][ndx] @@ -1071,6 +1083,7 @@ 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 = [ @@ -1088,7 +1101,7 @@
# don't register the FT-65 in the production version until it is tested -# @directory.register +@directory.register class YaesuFT65Radio(YaesuSC35GenericRadio): MODEL = "FT-65R" _basetype = BASETYPE_FT65 @@ -1112,6 +1125,8 @@ class_group_descs = copy.deepcopy(YaesuSC35GenericRadio.group_descriptions) add_paramdesc( class_group_descs, "misc", ("compander", "Compander", ["OFF", "ON"])) + CLASS_SPECIALS = list(SPECIALS) # a shallow copy works here + CLASS_SPECIALS[-1] = FT65_PROGS # replace the last entry (P key names) # names for the setmode function for the programmable keys. Mode zero means # that the key is programmed for a memory not a setmode. SETMODES = [
# don't register the FT-65 in the production version until it is tested -# @directory.register +@directory.register
Looks like you need to remove the comment as well. Assume you will add this to the wiki when it's merged, right? Also, would you mind putting something in the commit message to indicate that a new radio is (effectively) added here? Right now it looks to just be a refactor or bug fix, but it registers a whole new radio, which is a little obscure.
class YaesuFT65Radio(YaesuSC35GenericRadio): MODEL = "FT-65R" _basetype = BASETYPE_FT65 @@ -1112,6 +1125,8 @@ class_group_descs = copy.deepcopy(YaesuSC35GenericRadio.group_descriptions) add_paramdesc( class_group_descs, "misc", ("compander", "Compander", ["OFF", "ON"]))
- CLASS_SPECIALS = list(SPECIALS) # a shallow copy works here
- CLASS_SPECIALS[-1] = FT65_PROGS # replace the last entry (P key names)
I'm surprised this even works, but even if it does, it's kinda icky. When you respin for the comment, can you just define FT65_SPECIALS at module scope, get it set properly, and then just assign it here?
Thanks!
--Dan
My apologies. I did not actually intend to commit the directory.register. It was there for testing and I just did not see it. I don't really know to resubmit a patch starting from where I am now, but I will puzzle it out somehow. Is the .img file acceptable?
About "class scope": it really is an official part of Python: I did not make it up. It's exactly what we wanted to use here: a variable that is only accessible within a class, but not in its superclasses, but is a single variable shared amongst all members of the class. I am new to Python, so I do not know which language constructs are conventionally simply not used by developers. I will change this to conform to norms. Note that many of the scalars (e.g., MAX_MEM_SLOT) are a class scope, not instance scope.
I found out about class scope during testing, when working with group_descriptions. I thought I could simply modify the global from inside the FT-65 class. Nope, that affected the FT4. Then I figured I could just append in FT-65 __init__. Nope, that causes the append to occur multiple times if the main code instantiates multiple FT-65s. That's when I finally figured out the differences between global, instance, and class variables in Python. If the driver were only ever used in code that only works with a single radio during any particular run, I would never have seen this.
On Wed, Mar 13, 2019 at 8:30 AM Dan Smith via chirp_devel < chirp_devel@intrepid.danplanet.com> wrote:
# don't register the FT-65 in the production version until it is tested -# @directory.register +@directory.register
Looks like you need to remove the comment as well. Assume you will add this to the wiki when it's merged, right? Also, would you mind putting something in the commit message to indicate that a new radio is (effectively) added here? Right now it looks to just be a refactor or bug fix, but it registers a whole new radio, which is a little obscure.
class YaesuFT65Radio(YaesuSC35GenericRadio): MODEL = "FT-65R" _basetype = BASETYPE_FT65 @@ -1112,6 +1125,8 @@ class_group_descs =
copy.deepcopy(YaesuSC35GenericRadio.group_descriptions)
add_paramdesc( class_group_descs, "misc", ("compander", "Compander", ["OFF",
"ON"]))
- CLASS_SPECIALS = list(SPECIALS) # a shallow copy works here
- CLASS_SPECIALS[-1] = FT65_PROGS # replace the last entry (P key
names)
I'm surprised this even works, but even if it does, it's kinda icky. When you respin for the comment, can you just define FT65_SPECIALS at module scope, get it set properly, and then just assign it here?
Thanks!
--Dan
chirp_devel mailing list chirp_devel@intrepid.danplanet.com http://intrepid.danplanet.com/mailman/listinfo/chirp_devel Developer docs: http://chirp.danplanet.com/projects/chirp/wiki/Developers
My apologies. I did not actually intend to commit the directory.register. It was there for testing and I just did not see it. I don't really know to resubmit a patch starting from where I am now, but I will puzzle it out somehow.
If you're using mq, just qpop until the one you want is on top and then "hg email tip" If not, just "hg email $rev" where rev is the thing you want to send.
Is the .img file acceptable?
I assume? I'll just throw that in the tree when we do register the thing so it'll get tested too.
About "class scope": it really is an official part of Python: I did not make it up. It's exactly what we wanted to use here: a variable that is only accessible within a class, but not in its superclasses, but is a single variable shared amongst all members of the class. I am new to Python, so I do not know which language constructs are conventionally simply not used by developers. I will change this to conform to norms. Note that many of the scalars (e.g., MAX_MEM_SLOT) are a class scope, not instance scope.
No, I realize it does work, I'm just saying it's so unconventional I wouldn't have been sure without trying it myself. It's really the self-modification of the class variable during the definition of the class that is weird. If you can just move that part to module scope and then assign it to the class during definition that would be appreciated.
Thanks!
--Dan
OK, sorry. But we modify class variables during class definitions all the time. In fact, all of the code in the two radio classes consists of "self-modification of class variables", since all top-level (single-indent) code in a class is class-level code: each such python statement gets executed When the Python interpreter imports the module, as I understand it. (Newbie Python is showing here.)
On Wed, Mar 13, 2019 at 11:26 AM Dan Smith via chirp_devel < chirp_devel@intrepid.danplanet.com> wrote:
My apologies. I did not actually intend to commit the
directory.register. It was there for testing and I just did not see it. I don't really know to resubmit a patch starting from where I am now, but I will puzzle it out somehow.
If you're using mq, just qpop until the one you want is on top and then "hg email tip" If not, just "hg email $rev" where rev is the thing you want to send.
Is the .img file acceptable?
I assume? I'll just throw that in the tree when we do register the thing so it'll get tested too.
About "class scope": it really is an official part of Python: I did not
make it up. It's exactly what we wanted to use here: a variable that is only accessible within a class, but not in its superclasses, but is a single variable shared amongst all members of the class. I am new to Python, so I do not know which language constructs are conventionally simply not used by developers. I will change this to conform to norms. Note that many of the scalars (e.g., MAX_MEM_SLOT) are a class scope, not instance scope.
No, I realize it does work, I'm just saying it's so unconventional I wouldn't have been sure without trying it myself. It's really the self-modification of the class variable during the definition of the class that is weird. If you can just move that part to module scope and then assign it to the class during definition that would be appreciated.
Thanks!
--Dan _______________________________________________ chirp_devel mailing list chirp_devel@intrepid.danplanet.com http://intrepid.danplanet.com/mailman/listinfo/chirp_devel Developer docs: http://chirp.danplanet.com/projects/chirp/wiki/Developers
OK, sorry. But we modify class variables during class definitions all the time. In fact, all of the code in the two radio classes consists of "self-modification of class variables", since all top-level (single-indent) code in a class is class-level code: each such python statement gets executed When the Python interpreter imports the module, as I understand it. (Newbie Python is showing here.)
I think you're misunderstanding what I'm saying and/or we're talking past each other. Would you mind changing it please?
--Dan
I will change it.
On Wed, Mar 13, 2019 at 11:54 AM Dan Smith via chirp_devel < chirp_devel@intrepid.danplanet.com> wrote:
OK, sorry. But we modify class variables during class definitions all
the time. In fact, all of the code in the two radio classes consists of "self-modification of class variables", since all top-level (single-indent) code in a class is class-level code: each such python statement gets executed When the Python interpreter imports the module, as I understand it. (Newbie Python is showing here.)
I think you're misunderstanding what I'm saying and/or we're talking past each other. Would you mind changing it please?
--Dan _______________________________________________ chirp_devel mailing list chirp_devel@intrepid.danplanet.com http://intrepid.danplanet.com/mailman/listinfo/chirp_devel Developer docs: http://chirp.danplanet.com/projects/chirp/wiki/Developers
participants (3)
-
Dan Clemmensen
-
Dan Smith
-
DanClemmensen