From 55fabd5da7fa3c9ae59220de31a5f0cf4690a79f Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Sat, 15 Jun 2024 17:13:05 -0700 Subject: [PATCH 1/4] Add some extra logging in bfc._rawrecv() --- chirp/drivers/baofeng_common.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/chirp/drivers/baofeng_common.py b/chirp/drivers/baofeng_common.py index d990c4757..445364c10 100644 --- a/chirp/drivers/baofeng_common.py +++ b/chirp/drivers/baofeng_common.py @@ -51,6 +51,8 @@ def _rawrecv(radio, amount): raise errors.RadioError(msg) if len(data) != amount: + LOG.debug('Wanted %i, got %i: %s', + amount, len(data), util.hexprint(data)) msg = "Error reading data from radio: not the amount of data we want." raise errors.RadioError(msg) From a845e81ff1fe54578813cf901b9125b0049b7d38 Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Sat, 15 Jun 2024 16:04:55 -0700 Subject: [PATCH 2/4] uv17: Refactor memory into definitions and layout Also fix incorrect definition of get_raw_memory() as returning a bitwise structure instead of a string. --- chirp/drivers/baofeng_uv17.py | 87 +++++++++++++++++--------------- chirp/drivers/baofeng_uv17Pro.py | 11 ++-- 2 files changed, 54 insertions(+), 44 deletions(-) diff --git a/chirp/drivers/baofeng_uv17.py b/chirp/drivers/baofeng_uv17.py index 150afbcc3..02b28917d 100644 --- a/chirp/drivers/baofeng_uv17.py +++ b/chirp/drivers/baofeng_uv17.py @@ -189,8 +189,7 @@ class UV17(baofeng_uv17Pro.UV17Pro): LIST_MODE = ["Name", "Frequency"] CHANNELS = 999 - MEM_FORMAT = """ - #seekto 0x0030; + MEM_DEFS = """ struct channel { lbcd rxfreq[4]; lbcd txfreq[4]; @@ -209,28 +208,10 @@ class UV17(baofeng_uv17Pro.UV17Pro): scan:1; u8 unknown4; }; - - struct { - struct channel mem[252]; - } mem1; - - #seek 0x10; - struct { - struct channel mem[255]; - } mem2; - - #seek 0x10; - struct { - struct channel mem[255]; - } mem3; - - #seek 0x10; - struct { - struct channel mem[237]; - } mem4; - - #seekto 0x7000; - struct { + struct channelname { + char name[11]; + }; + struct settings { u8 powerondistype; u8 unknown0[15]; char boottext1[10]; @@ -261,7 +242,39 @@ class UV17(baofeng_uv17Pro.UV17Pro): u8 unknown9:6, chbdistype:1, chadistype:1; - } settings; + }; + struct ani { + u8 unknown[5]; + u8 code[5]; + }; + struct pttid { + u8 code[5]; + }; + """ + + MEM_LAYOUT = """ + #seekto 0x0030; + struct { + struct channel mem[252]; + } mem1; + + #seek 0x10; + struct { + struct channel mem[255]; + } mem2; + + #seek 0x10; + struct { + struct channel mem[255]; + } mem3; + + #seek 0x10; + struct { + struct channel mem[237]; + } mem4; + + #seekto 0x7000; + struct settings settings; struct vfo { lbcd rxfreq[4]; @@ -289,10 +302,6 @@ class UV17(baofeng_uv17Pro.UV17Pro): } vfo; #seekto 0x4000; - struct channelname { - char name[11]; - }; - struct channelname names1[372]; #seek 0x4; struct channelname names2[372]; @@ -300,16 +309,11 @@ class UV17(baofeng_uv17Pro.UV17Pro): struct channelname names3[255]; #seekto 0x8000; - struct { - u8 code[5]; - } pttid[15]; - - struct { - u8 unknown[5]; - u8 code[5]; - } ani; + struct pttid pttid[15]; + struct ani ani; """ + MEM_FORMAT = MEM_DEFS + MEM_LAYOUT def _make_frame(self, cmd, addr, length, data=""): """Pack the info in the header format""" @@ -395,7 +399,7 @@ def decode_tone(self, val): return mode, xval, pol - def get_raw_memory(self, number): + def _get_raw_memory(self, number): # The flash memory contains page_numbers # This is probably to do wear leveling on the memory # These numbers are needed, but make the channel memory @@ -413,6 +417,9 @@ def get_raw_memory(self, number): _mem = self._memobj.mem1.mem[number] return _mem + def get_raw_memory(self, number): + return repr(self._get_raw_memory(number)) + def get_channel_name(self, number): number = number - 1 if number >= 744: @@ -425,7 +432,7 @@ def get_channel_name(self, number): return _name def get_memory(self, number): - _mem = self.get_raw_memory(number) + _mem = self._get_raw_memory(number) _nam = self.get_channel_name(number) mem = chirp_common.Memory() @@ -456,7 +463,7 @@ def encode_tone(self, memtone, mode, tone, pol): memtone.set_value(memtone + 0x4000) def set_memory(self, mem): - _mem = self.get_raw_memory(mem.number) + _mem = self._get_raw_memory(mem.number) _nam = self.get_channel_name(mem.number) if mem.empty: diff --git a/chirp/drivers/baofeng_uv17Pro.py b/chirp/drivers/baofeng_uv17Pro.py index ae2c2ca0b..99e7af1ed 100644 --- a/chirp/drivers/baofeng_uv17Pro.py +++ b/chirp/drivers/baofeng_uv17Pro.py @@ -1191,11 +1191,14 @@ def get_memory_common(self, _mem, name, mem): mem.name = str(name).replace('\xFF', ' ').replace('\x00', ' ').rstrip() - def get_raw_memory(self, number): + def _get_raw_memory(self, number): return self._memobj.memory[number - 1] + def get_raw_memory(self, number): + return repr(self._get_raw_memory(number)) + def get_memory(self, number): - _mem = self.get_raw_memory(number) + _mem = self._get_raw_memory(number) mem = chirp_common.Memory() mem.number = number @@ -1205,7 +1208,7 @@ def get_memory(self, number): return mem def unsplit_txfreq(self, mem): - _mem = self.get_raw_memory(mem.number) + _mem = self._get_raw_memory(mem.number) if mem.duplex == "off": for i in range(0, 4): _mem.txfreq[i].set_raw(b"\xFF") @@ -1251,7 +1254,7 @@ def set_memory_common(self, mem, _mem): _mem.scode = self._scode_offset def set_memory(self, mem): - _mem = self.get_raw_memory(mem.number) + _mem = self._get_raw_memory(mem.number) _mem.set_raw(b"\x00"*16 + b"\xff" * 16) From b14b1daa51806b361305a7553e57ee06dbea0e3d Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Sat, 15 Jun 2024 17:13:51 -0700 Subject: [PATCH 3/4] uv17: Refactor to handle ga510v2 - Allows skipping ident if we are a detected model - Assumes memsize is fixed if the magics are empty - Logs missing blocks with more detail --- chirp/drivers/baofeng_uv17.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/chirp/drivers/baofeng_uv17.py b/chirp/drivers/baofeng_uv17.py index 02b28917d..09b7da0ec 100644 --- a/chirp/drivers/baofeng_uv17.py +++ b/chirp/drivers/baofeng_uv17.py @@ -44,7 +44,10 @@ def _get_memory_size(radio): def _get_memory_map(radio): # Get memory map memory_map = [] - mem_size = _get_memory_size(radio) + if radio._magic_memsize: + mem_size = _get_memory_size(radio) + else: + mem_size = radio._radio_memsize if mem_size != radio._radio_memsize: raise errors.RadioError("Incorrect radio model or model not supported " "(memory size doesn't match)") @@ -60,7 +63,9 @@ def _get_memory_map(radio): def _download(radio): """Get the memory map""" - baofeng_uv17Pro._do_ident(radio) + if not radio._DETECTED_BY: + # The GA510v2 (at least) is detected, and thus has already done ident + baofeng_uv17Pro._do_ident(radio) data = b"" memory_map = _get_memory_map(radio) @@ -73,6 +78,8 @@ def _download(radio): for block_number in radio.BLOCK_ORDER: if block_number not in memory_map: # Memory block not found. + LOG.error('Block %i (0x%x) not in memory map: %s', + block_number, block_number, memory_map) raise errors.RadioError('Radio memory is corrupted. ' + 'Fix this by uploading a backup image ' + 'to the radio.') From de2122e3059acd083beec9119d7ecc286fca6a2f Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Sat, 15 Jun 2024 17:33:10 -0700 Subject: [PATCH 4/4] ga510: Add support for "v2" variant This variant was released in 2024 and looks identical to the original, but is totally different inside and is based on the uv17. This uses the detection framework to basically hide the difference from the user. Fixes: #10600 --- chirp/drivers/ga510.py | 90 +++++++++++++++++++++++++- tests/Python3_Driver_Testing.md | 7 +- tests/images/Radioddity_GA-510_V2.img | Bin 0 -> 20673 bytes 3 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 tests/images/Radioddity_GA-510_V2.img diff --git a/chirp/drivers/ga510.py b/chirp/drivers/ga510.py index dba6609e9..13d426e8f 100644 --- a/chirp/drivers/ga510.py +++ b/chirp/drivers/ga510.py @@ -1,4 +1,4 @@ -# Copyright 2011 Dan Smith +# Copyright 2024 Dan Smith # Portions copyright 2023 Dave Liske # Portions copyright 2020 by Jiauxn Yang # @@ -21,6 +21,8 @@ from chirp import bitwise from chirp import chirp_common from chirp import directory +from chirp.drivers import baofeng_uv17 +from chirp.drivers import baofeng_uv17Pro from chirp import errors from chirp import memmap from chirp.settings import RadioSetting, RadioSettingGroup, RadioSettings @@ -60,7 +62,7 @@ def start_program(radio): def do_download(radio): - ident = start_program(radio) + # No start_program() here because it was done in detect_from_serial() s = chirp_common.Status() s.msg = 'Downloading' @@ -355,6 +357,24 @@ class RadioddityGA510Radio(chirp_common.CloneModeRadio): _gmrs = False + @classmethod + def detect_from_serial(cls, pipe): + for rcls in reversed(cls.detected_models()): + pipe.baudrate = rcls.BAUD_RATE + radio = rcls(pipe) + try: + if isinstance(radio, baofeng_uv17Pro.UV17Pro): + ident = baofeng_uv17Pro._do_ident(radio) + else: + ident = start_program(radio) + except errors.RadioError: + LOG.debug('No response from radio with %s', rcls) + pipe.read(256) + continue + if ident: + return rcls + raise errors.RadioError('No response from radio') + def sync_in(self): try: data = do_download(self) @@ -1074,3 +1094,69 @@ def _set_mem(self, num): def _set_nam(self, number): return self._memobj.names[number - 1] + + +@directory.register +@directory.detected_by(RadioddityGA510Radio) +class RadioddityGA510v2(baofeng_uv17.UV17): + """Baofeng UV-17""" + VENDOR = "Radioddity" + MODEL = "GA-510" + VARIANT = "V2" + + MODES = ["FM", "NFM"] + BLOCK_ORDER = [2, 4, 6, 16, 24] + MEM_TOTAL = 0x6000 + WRITE_MEM_TOTAL = 0x6000 + BLOCK_SIZE = 0x40 + BAUD_RATE = 57600 + + _magic = b"PSEARCH" + _magic_response_length = 8 + _magics = [(b"PASSSTA", 3), (b"SYSINFO", 1), + (b"\x56\x00\x00\x0A\x0D", 13), (b"\x06", 1), + (b"\x56\x00\x10\x0A\x0D", 13), (b"\x06", 1), + (b"\x56\x00\x20\x0A\x0D", 13), (b"\x06", 1), + (b"\x56\x00\x00\x00\x0A", 11), (b"\x06", 1), + (b"\xFF\xFF\xFF\xFF\x0C\x44\x4d\x52\x31\x37\x30\x32", 1), + (b"\02", 8), (b"\x06", 1)] + _magic_memsize = [] + _radio_memsize = 0x10000 + _magics2 = [] + _fingerprint = b"\x06DMR1702" + _scode_offset = 1 + + _tri_band = False + POWER_LEVELS = [chirp_common.PowerLevel("Low", watts=1.00), + chirp_common.PowerLevel("Medium", watts=5.00), + chirp_common.PowerLevel("High", watts=10.00)] + + LENGTH_NAME = 11 + SCODE_LIST = ["%s" % x for x in range(1, 16)] + SQUELCH_LIST = ["Off"] + list("123456789") + LIST_POWERON_DISPLAY_TYPE = ["Full", "Message", "Voltage"] + LIST_TIMEOUT = ["Off"] + ["%s sec" % x for x in range(15, 615, 15)] + LIST_VOICE = ["Chinese", "English"] + LIST_BACKLIGHT_TIMER = ["Always On"] + ["%s sec" % x for x in range(1, 11)] + LIST_MODE = ["Name", "Frequency"] + CHANNELS = 128 + + MEM_LAYOUT = """ + #seekto 0x1000; + struct settings settings; + + #seekto 0x2000; + struct pttid pttid[15]; + struct ani ani; + + #seekto 0x3040; + struct { + struct channel mem[128]; + } mem1; + + #seekto 0x400B; + struct channelname names1[128]; + """ + MEM_FORMAT = baofeng_uv17.UV17.MEM_DEFS + MEM_LAYOUT + + _has_workmode_support = False diff --git a/tests/Python3_Driver_Testing.md b/tests/Python3_Driver_Testing.md index b8d22291f..5a9a1d77b 100644 --- a/tests/Python3_Driver_Testing.md +++ b/tests/Python3_Driver_Testing.md @@ -272,6 +272,7 @@ | Radioddity_DB25-G | [@KC9HI](https://github.com/KC9HI) | 11-Nov-2022 | Yes | 0.17% | | Radioddity_GA-2S | [@KC9HI](https://github.com/KC9HI) | 4-Dec-2022 | Yes | 0.19% | | Radioddity_GA-510 | [@kk7ds](https://github.com/kk7ds) | 21-Oct-2022 | Yes | 0.42% | +| Radioddity_GA-510_V2 | | | Yes | | | Radioddity_GS-5B | [@lunapiercook](https://github.com/lunapiercook) | 5-Mar-2023 | Yes | | | Radioddity_R2 | [@KC9HI](https://github.com/KC9HI) | 17-Dec-2022 | Yes | 0.04% | | Radioddity_UV-5G | [@KC9HI](https://github.com/KC9HI) | 18-Nov-2022 | Yes | 0.47% | @@ -466,11 +467,11 @@ | Zastone_ZT-X6 | [Implied by Retevis_RT22](#user-content-Retevis_RT22) | 9-Dec-2022 | Yes | 0.11% | ## Stats -**Drivers:** 463 +**Drivers:** 464 -**Tested:** 87% (406/57) (93% of usage stats) +**Tested:** 87% (406/58) (93% of usage stats) -**Byte clean:** 91% (424/39) +**Byte clean:** 91% (425/39) ## Meaning of this testing diff --git a/tests/images/Radioddity_GA-510_V2.img b/tests/images/Radioddity_GA-510_V2.img new file mode 100644 index 0000000000000000000000000000000000000000..7695033d1b7291c3e860d3fdee96fd0a889fcafe GIT binary patch literal 20673 zcmeI3!B1LA9LEP)CpD?A7OB!rHbuaC=xz|p8WXpI2ozFN1Odybpx8c@(yiFWUeq>b z&l@khNiV(ix_jDVd+NEjUi%L;ZL+sbmi^AW2RwCeEt~G<`%AuW<~zTcACFH4bRdj- zy<@i6I~o3V_&xaKj@^qs}%C6C8m#uicJQ|*Yy zSVe?Z0D++3eM;C+EW!0#Iy^Lk+r9vx<1T|3h|WA>hih^*t5D>C8` z7sa>`v(Z>;aph_I9z0+BCcX(x&qjqA7ryGdJkTGx8V)UIug+c?&n^Ag-22i>ss{C% zs-88pwNMZaTUvEn{>?;K?7tt?SvB@ov)LRxqwR@HM9h90G}pQ7;%TrTp8r;F&$&2S z7r%dB7lX*13e;qf011!)36KB@kN^pg011!)3HmJF(T!eVR%)MJF~^Kn0bwg`j_G_W&6Wg6fCNZ@1W14cNPq-LfCNZ@ z1W2H31itu$mGybv(c^43t)qXdv48#D8e>ZWBtQZrKmsH{0wh2JBtQZrKmsK2uMv>@ z;=DL^J`ZGK`q9+~19GTW1Tt=$Z-0reJ8}rA$b#09Jd(->LUAp3pSw?b;bZVn)m3%L zN%&=WXxKgMmQnaBJT&2%@W>_j2k_9Sf7CC_@IS&U?FaVd5AZ*u4_!I9av%@jJqfMG zxe!9&hN=b`)+egPiZezE;RzJ$-iL(W&uSMs%5AJNj!|1EqY zrltJ{A$ERAYMFe5$Q-(~wD-b4aBFEl34dusOZzBz9fB{U{Stf;d?D@2r}(O26)oPT zqbCpS1iaq3A4~$=58MxYJ#_#5V7>kizahh4F$M{c011!)36Q{lhQRr|JGDyneo)=; zscxtcIs|= zqmoQKTHQ^3mx`rxH;dVOl~8uGypj!PgYn75<@wN}k=jNsR4RryD^n?~jT-q{VzXSF z*(@cug4LYPH?VH264Q0AE#-n(8#S)SqQ+V+v0E-CcGk>X(W