From 83fcd463f0c613d04dddb997891e289213b69f01 Mon Sep 17 00:00:00 2001
From: Apostolos Kefalas <akef@freemail.gr>
Date: Sat, 5 Nov 2022 17:50:37 +0200
Subject: [PATCH 1/3] Fix registration, invalid syntax of ap510 driver

---
 chirp/drivers/ap510.py          | 19 ++++++++++---------
 tests/Python3_Driver_Testing.md |  7 ++++---
 2 files changed, 14 insertions(+), 12 deletions(-)

--- a/chirp/drivers/ap510.py
+++ b/chirp/drivers/ap510.py
@@ -44,7 +44,7 @@ def decode_base100(u16):
 
 def drain(pipe):
     """Chew up any data waiting on @pipe"""
-    for x in xrange(3):
+    for x in range(3):
         buf = pipe.read(4096)
         if not buf:
             return
@@ -53,7 +53,7 @@ def drain(pipe):
 
 def enter_setup(pipe):
     """Put AP510 in configuration mode."""
-    for x in xrange(30):
+    for x in range(30):
         if x % 2:
             pipe.write("@SETUP")
         else:
@@ -84,7 +84,7 @@ def download(radio):
         radio.pipe.write("@DISP")
     buf = ""
 
-    for status.cur in xrange(status.cur, status.max):
+    for status.cur in range(status.cur, status.max):
         buf += radio.pipe.read(1024)
         if buf.endswith("\r\n"):
             status.cur = status.max
@@ -242,7 +242,7 @@ class AP510Memory(object):
 
 class AP510Memory20141215(AP510Memory):
     """Compatible with firmware version 20141215"""
-    ATTR_MAP = dict(AP510Memory.ATTR_MAP.items() + {
+    ATTR_MAP = dict(list(AP510Memory.ATTR_MAP.items()) + list({
         'tx_volume': '21',  # 1-6
         'rx_volume': '22',  # 1-9
         'tx_power': '23',  # 1: 1 watt,  0: 0.5 watt
@@ -252,7 +252,7 @@ class AP510Memory20141215(AP510Memory):
         'path3': '27',  # like "WIDE1 1" else "0"
         'multiple': '28',
         'auto_on': '29',
-    }.items())
+    }.items()))
 
     def get_multiple(self):
         return dict(zip(
@@ -343,13 +343,14 @@ RP_IMMUTABLE = ["number", "skip", "bank"
                 "offset", "mode", "tuning_step", "bank_index"]
 
 
+@directory.register
 class AP510Radio(chirp_common.CloneModeRadio):
     """Sainsonic AP510"""
     BAUD_RATE = 9600
     VENDOR = "Sainsonic"
     MODEL = "AP510"
 
-    _model = "AVRT5"
+    _model = b"AVRT5"
     mem_upper_limit = 0
 
     def get_features(self):
@@ -380,7 +381,7 @@ class AP510Radio(chirp_common.CloneModeR
             data = download(self)
         except errors.RadioError:
             raise
-        except Exception, e:
+        except Exception as e:
             raise errors.RadioError("Failed to communicate with radio: %s" % e)
 
         # _mmap isn't a Chirp MemoryMap, but since AP510Memory implements
@@ -398,7 +399,7 @@ class AP510Radio(chirp_common.CloneModeR
             upload(self)
         except errors.RadioError:
             raise
-        except Exception, e:
+        except Exception as e:
             raise errors.RadioError("Failed to communicate with radio: %s" % e)
 
     def load_mmap(self, filename):
@@ -804,4 +805,4 @@ class AP510Radio(chirp_common.CloneModeR
 
     @classmethod
     def match_model(cls, filedata, filename):
-        return filedata.startswith('\r\n00=' + cls._model)
+        return filedata.startswith(b'\r\n00=' + cls._model)
--- a/tests/Python3_Driver_Testing.md
+++ b/tests/Python3_Driver_Testing.md
@@ -263,6 +263,7 @@
 | <a name="Retevis_RT95_VOX"></a> Retevis_RT95_VOX |  |  | Yes |
 | <a name="Retevis_RT98"></a> Retevis_RT98 |  |  |  |
 | <a name="Rugged_RH5R-V2"></a> Rugged_RH5R-V2 |  |  |  |
+| <a name="Sainsonic_AP510"></a> Sainsonic_AP510 |  |  |  |
 | <a name="TDXone_TD-Q8A"></a> TDXone_TD-Q8A |  |  |  |
 | <a name="TIDRADIO_TD-H6"></a> TIDRADIO_TD-H6 | [@kk7ds](https://github.com/kk7ds) | 21-Oct-2022 | Yes |
 | <a name="TID_TD-M8"></a> TID_TD-M8 |  |  |  |
@@ -293,10 +294,12 @@
 | <a name="Wouxun_KG-UV9D_Plus"></a> Wouxun_KG-UV9D_Plus |  |  |  |
 | <a name="Yaesu_FT-1500M"></a> Yaesu_FT-1500M |  |  | Yes |
 | <a name="Yaesu_FT-1802M"></a> Yaesu_FT-1802M |  |  | Yes |
+| <a name="Yaesu_FT-1D_R"></a> Yaesu_FT-1D_R |  |  | Yes |
 | <a name="Yaesu_FT-25R"></a> Yaesu_FT-25R |  |  | Yes |
 | <a name="Yaesu_FT-2800M"></a> Yaesu_FT-2800M |  |  | Yes |
 | <a name="Yaesu_FT-2900R_1900R"></a> Yaesu_FT-2900R_1900R |  |  | Yes |
 | <a name="Yaesu_FT-2900R_1900RTXMod_Opened_Xmit"></a> Yaesu_FT-2900R_1900RTXMod_Opened_Xmit |  |  | Yes |
+| <a name="Yaesu_FT-450D"></a> Yaesu_FT-450D |  |  | Yes |
 | <a name="Yaesu_FT-4VR"></a> Yaesu_FT-4VR |  |  | Yes |
 | <a name="Yaesu_FT-4XE"></a> Yaesu_FT-4XE |  |  | Yes |
 | <a name="Yaesu_FT-4XR"></a> Yaesu_FT-4XR |  |  | Yes |
@@ -318,7 +321,12 @@
 | <a name="Yaesu_FT-8800"></a> Yaesu_FT-8800 | [@kk7ds](https://github.com/kk7ds) | 24-Oct-2022 | Yes |
 | <a name="Yaesu_FT-8900"></a> Yaesu_FT-8900 |  |  | Yes |
 | <a name="Yaesu_FT-90"></a> Yaesu_FT-90 |  |  | Yes |
+| <a name="Yaesu_FT2D_R"></a> Yaesu_FT2D_R |  |  | Yes |
+| <a name="Yaesu_FT2D_Rv2"></a> Yaesu_FT2D_Rv2 |  |  | Yes |
+| <a name="Yaesu_FT3D_R"></a> Yaesu_FT3D_R |  |  | Yes |
+| <a name="Yaesu_FTM-3200D_R"></a> Yaesu_FTM-3200D_R |  |  | Yes |
 | <a name="Yaesu_FTM-350"></a> Yaesu_FTM-350 |  |  | Yes |
+| <a name="Yaesu_FTM-7250D_R"></a> Yaesu_FTM-7250D_R |  |  | Yes |
 | <a name="Yaesu_VX-170"></a> Yaesu_VX-170 |  |  | Yes |
 | <a name="Yaesu_VX-2"></a> Yaesu_VX-2 |  |  | Yes |
 | <a name="Yaesu_VX-3"></a> Yaesu_VX-3 | [@kk7ds](https://github.com/kk7ds) | 15-Feb-2019 | Yes |
--- a/chirp/drivers/ft1d.py
+++ b/chirp/drivers/ft1d.py
@@ -558,7 +558,7 @@ class FT1Radio(yaesu_clone.YaesuCloneMod
     MODEL = "FT-1D"
     VARIANT = "R"
 
-    _model = "AH44M"
+    _model = b"AH44M"
     _memsize = 130507
     _block_lengths = [10, 130497]
     _block_size = 32
@@ -698,7 +698,7 @@ class FT1Radio(yaesu_clone.YaesuCloneMod
 
     @staticmethod
     def _add_ff_pad(val, length):
-        return val.ljust(length, "\xFF")[:length]
+        return val.ljust(length, b"\xFF")[:length]
 
     @classmethod
     def _strip_ff_pads(cls, messages):
@@ -740,7 +740,8 @@ class FT1Radio(yaesu_clone.YaesuCloneMod
         return str(mem.label).rstrip("\xFF").translate(charset)
 
     def _encode_label(self, mem):
-        label = "".join([chr(CHARSET.index(x)) for x in mem.name.rstrip()])
+        label = "".join([chr(CHARSET.index(x))
+                         for x in mem.name.rstrip()]).encode()
         return self._add_ff_pad(label, 16)
 
     def _encode_charsetbits(self, mem):
@@ -820,7 +821,7 @@ class FT1Radio(yaesu_clone.YaesuCloneMod
 
     @classmethod
     def _wipe_memory(cls, mem):
-        mem.set_raw("\x00" * (mem.size() / 8))
+        mem.set_raw("\x00" * (mem.size() // 8))
         mem.unknown1 = 0x05
 
     def get_bank_model(self):
@@ -1131,7 +1132,8 @@ class FT1Radio(yaesu_clone.YaesuCloneMod
                 checksum = body[-2:]
                 body = ''.join(s for s in body[:-2]
                                if s in string.printable).translate(
-                                   None, "\x09\x0a\x0b\x0c\x0d")
+                                   str.maketrans(
+                                       "", "", "\x09\x0a\x0b\x0c\x0d"))
                 try:
                     val = RadioSettingValueString(0, 134, body.strip())
                 except Exception as e:
@@ -1890,7 +1892,7 @@ class FT1Radio(yaesu_clone.YaesuCloneMod
                 except AttributeError as e:
                     LOG.error("Setting %s is not in the memory map: %s" %
                               (element.get_name(), e))
-            except Exception, e:
+            except Exception as e:
                 LOG.debug(element.get_name())
                 raise
 
--- a/chirp/drivers/ft2d.py
+++ b/chirp/drivers/ft2d.py
@@ -117,7 +117,7 @@ class FT2D(ft1d.FT1Radio):
         return mem
 
     def _decode_label(self, mem):
-        return str(mem.label).rstrip("\xFF").decode('ascii', 'replace')
+        return str(mem.label).rstrip("\xFF")
 
     def _encode_label(self, mem):
         label = mem.name.rstrip().encode('ascii', 'ignore')
--- a/chirp/drivers/ftm3200d.py
+++ b/chirp/drivers/ftm3200d.py
@@ -124,7 +124,7 @@ class FTM3200Radio(ft1d.FT1Radio):
 
     def _decode_label(self, mem):
         # TODO preserve the unknown \x80-x86 chars?
-        return str(mem.label).rstrip("\xFF").decode('ascii', 'replace')
+        return str(mem.label).rstrip("\xFF")
 
     def _encode_label(self, mem):
         label = mem.name.rstrip().encode('ascii', 'ignore')
@@ -193,7 +193,7 @@ class FTM3200Radio(ft1d.FT1Radio):
 
     @classmethod
     def _wipe_memory(cls, mem):
-        mem.set_raw("\x00" * (mem.size() / 8))
+        mem.set_raw("\x00" * (mem.size() // 8))
 
     def sync_out(self):
         # Need to give enough time for the radio to ACK after writes
--- a/chirp/drivers/ftm7250d.py
+++ b/chirp/drivers/ftm7250d.py
@@ -125,7 +125,7 @@ class FTM7250Radio(ft1d.FT1Radio):
 
     def _decode_label(self, mem):
         # TODO preserve the unknown \x80-x86 chars?
-        return str(mem.label).rstrip("\xFF").decode('ascii', 'replace')
+        return str(mem.label).rstrip("\xFF")
 
     def _encode_label(self, mem):
         label = mem.name.rstrip().encode('ascii', 'ignore')
@@ -194,7 +194,7 @@ class FTM7250Radio(ft1d.FT1Radio):
 
     @classmethod
     def _wipe_memory(cls, mem):
-        mem.set_raw("\x00" * (mem.size() / 8))
+        mem.set_raw("\x00" * (mem.size() // 8))
 
     def sync_out(self):
         # Need to give enough time for the radio to ACK after writes
--- a/tests/py3_remaining.txt
+++ b/tests/py3_remaining.txt
@@ -9,9 +9,3 @@ Wouxun_KG-816.img
 Wouxun_KG-818.img
 Wouxun_KG-UV6.img
 Wouxun_KG-UVD1P.img
-Yaesu_FT-1D_R.img
-Yaesu_FT2D_R.img
-Yaesu_FT3D_R.img
-Yaesu_FT-450D.img
-Yaesu_FTM-3200D_R.img
-Yaesu_FTM-7250D_R.img
--- a/chirp/drivers/ft450d.py
+++ b/chirp/drivers/ft450d.py
@@ -305,8 +305,8 @@ class FT450DRadio(yaesu_clone.YaesuClone
         struct mem_struct current;
 
     """
-    _CALLSIGN_CHARSET = [chr(x) for x in range(ord("0"), ord("9") + 1) +
-                        range(ord("A"), ord("Z") + 1) + [ord(" ")]]
+    _CALLSIGN_CHARSET = [chr(x) for x in list(range(ord("0"), ord("9") + 1)) +
+                        list(range(ord("A"), ord("Z") + 1)) + [ord(" ")]]
     _CALLSIGN_CHARSET_REV = dict(zip(_CALLSIGN_CHARSET,
                                      range(0, len(_CALLSIGN_CHARSET))))
 
@@ -500,7 +500,7 @@ class FT450DRadio(yaesu_clone.YaesuClone
             self._mmap = self._clone_in()
         except errors.RadioError:
             raise
-        except Exception, e:
+        except Exception as e:
             raise errors.RadioError("Failed to communicate with radio: %s"
                                     % e)
         self.process_mmap()
@@ -510,7 +510,7 @@ class FT450DRadio(yaesu_clone.YaesuClone
             self._clone_out()
         except errors.RadioError:
             raise
-        except Exception, e:
+        except Exception as e:
             raise errors.RadioError("Failed to communicate with radio: %s"
                                     % e)
 
@@ -705,18 +705,18 @@ class FT450DRadio(yaesu_clone.YaesuClone
             if mem.number == 1:
                 raise Exception("Sorry, can't delete first memory")
             if wasvalid and not wasused:
-                self._memobj.filled[(mem.number - 1) / 8] &= \
+                self._memobj.filled[(mem.number - 1) // 8] &= \
                     ~(1 << (mem.number - 1) % 8)
-                _mem.set_raw("\xFF" * (_mem.size() / 8))    # clean up
-            self._memobj.visible[(mem.number - 1) / 8] &= \
+                _mem.set_raw("\xFF" * (_mem.size() // 8))    # clean up
+            self._memobj.visible[(mem.number - 1) // 8] &= \
                 ~(1 << (mem.number - 1) % 8)
             return
         if not wasvalid:
-            _mem.set_raw("\x00" * (_mem.size() / 8))    # clean up
+            _mem.set_raw("\x00" * (_mem.size() // 8))    # clean up
 
-        self._memobj.visible[(mem.number - 1) / 8] |= 1 << (mem.number - 1) \
+        self._memobj.visible[(mem.number - 1) // 8] |= 1 << (mem.number - 1) \
                         % 8
-        self._memobj.filled[(mem.number - 1) / 8] |= 1 << (mem.number - 1) \
+        self._memobj.filled[(mem.number - 1) // 8] |= 1 << (mem.number - 1) \
                         % 8
         self._set_memory(mem, _mem)
 
@@ -945,7 +945,7 @@ class FT450DRadio(yaesu_clone.YaesuClone
         """Match the opened/downloaded image to the correct version"""
         if len(filedata) == cls.MEM_SIZE + 7:    # +7 bytes of model name
             rid = filedata[cls.MEM_SIZE:cls.MEM_SIZE + 7]
-            if rid.startswith(cls.MODEL):
+            if rid.startswith(cls.MODEL.encode()):
                 return True
         else:
             return False
@@ -959,8 +959,8 @@ class FT450DRadio(yaesu_clone.YaesuClone
     def _chars2str(self, cary, knt):
         """Convert raw memory char array to a string: NOT a callback."""
         stx = ""
-        for char in cary[:knt]:
-            stx += chr(char)
+        for char in cary[0:knt]:
+            stx += chr(int(char))
         return stx
 
     def _my_str2ary(self, setting, obj, atrba, knt):
@@ -1490,6 +1490,6 @@ class FT450DRadio(yaesu_clone.YaesuClone
                     elif element.value.get_mutable():
                         LOG.debug("Setting %s = %s" % (setting, element.value))
                         setattr(obj, setting, element.value)
-                except Exception, e:
+                except Exception as e:
                     LOG.debug(element.get_name())
                     raise
