Files
rt-4d/research/a6tools/a6/rdadebug.py
T
viktorиClaude Opus 4.8 c10ab3c6f1 DMR-чип опознан по схеме: HF6853 = AUCTUS A6 (RDA8809), не SCT3258
Опознание БЕЗ разбора рации, по схеме RT4DDLT01 (Radtel v2.1):
DMR-модуль «FM100B» = один чип U700 HF6853 — одночиповый DMR-SoC
семейства Auctus A6 (внутри RDA8809): CPU + ВЧ-трансивер + DSP +
AMBE-вокодер + аудио-кодек в одном кристалле, прошивка во внешнем
SPI-flash, кварц 26 МГц. Прежняя версия «Sicomm SCT3258TD» неверна
(SCT3258 — baseband-only без ВЧ; и нативный протокол другой).

Скрытый ATE/CPS-интерфейс полностью реверснут (jhart99/a6tools,
вендорен в research/a6tools). Даёт КОНКРЕТНУЮ починку нашей
TX-проблемы: AT+GETFREQERR / AT+DMOFREQERR=N, offset=-2500+10*N,
±2500 Гц — наши ~1100 Гц внутри диапазона. Плюс DMR_ADJTXSYMDEV
(девиация), FGU_AFC, DMOSETPOWER, CPS chanInfo, дамп прошивки модуля.

- docs/hf6853-auctus-a6.md — идентификация + протокол + план TX-fix
- tools/a6_freqfix.py — заготовка тула (probe/read/write, не запускать вслепую)
- research/a6tools/ — вендоренный реверс (MIT), atcommands.md/cpecommands.md
- docs/dmr-tx-recovery.md, README — путь 0 (AT+DMOFREQERR) как лучший
- research/rt4ddlt01-dmr-module-sheet.png — рендер листа схемы DMR-модуля

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NikMGoqQWWk9wy2ww2vJAr
2026-07-24 03:10:24 +09:00

122 строки
3.3 KiB
Python

import functools
import operator
from .escaper import escaper
from .escaper import unescaper
from .eprint import eprint
__author__ = "jhart99"
__license__ = "MIT"
def compute_check(msg):
""" Compute the check value for a message
AUCTUS messages use a check byte which is simply the XOR of all
the values of the message.
"""
if len(msg) == 0:
return int(0).to_bytes(1, 'little')
return functools.reduce(operator.xor, msg).to_bytes(1, 'little')
def rda_debug_frame(flow, cmd, message):
""" Format a raw message into a frame
AUCTUS frames are of the form AD 00 XX FF ...message... YY
where XX is the length of the message and YY is the check byte
Additionally certain bytes in the message are escaped.
"""
header = int(0xad).to_bytes(1, 'big')
msg = flow + cmd + message
msglen = len(msg).to_bytes(2, 'big')
check = compute_check(msg)
return escaper(header + msglen + msg + check)
def read_word(addr, seq = 1):
""" make a frame to read a word at a memory address
this function creates a frame to read the memory from the device
suitable for serial transmission.
"""
flow = bytes([0xff])
command = bytes([0x02])
if isinstance(addr, int):
addr = addr.to_bytes(4, 'little')
msg = addr + seq.to_bytes(1, 'big')
return rda_debug_frame(flow, command, msg)
def write_register_int8(addr, msg):
""" write to a byte to an internal register
this function creates a frame to do some device magic and these
frames are used in the preamble and finalizer commands.
"""
flow = bytes([0xff])
command = bytes([0x84])
msg = addr.to_bytes(4, 'little') + msg.to_bytes(1, 'little')
return rda_debug_frame(flow, command, msg)
def read_register_int8(addr, seq=1):
""" make a frame containing a knock command
this function creates a frame that I assume wakes up the device
for further commands.
"""
flow = bytes([0xff])
command = bytes([0x04])
msg = addr.to_bytes(4, 'little') + seq.to_bytes(1, 'big')
return rda_debug_frame(flow, command, msg)
def write_block(addr, msg):
""" make a frame containing a write command
this function creates a frame to do write a multiple byte content
at a specific memory address. The length need not be a word, but
could be 16 bytes or more.
"""
flow = bytes([0xff])
command = bytes([0x83])
if isinstance(addr, int):
addr = addr.to_bytes(4, 'little')
msg = addr + msg
return rda_debug_frame(flow, command, msg)
class RdaFrame:
""" Received Frame class
This class decodes possible received Frames.
"""
ack = False
check_fail = False
seq = 0
length = 0
content = bytes([])
def __init__(self, msg):
msg = unescaper(msg)
if len(msg) <= 4:
if(msg == b'\x11\x13'):
self.ack = True
else:
# impossibly short frame something is wrong.
self.check_fail = True
return
if (msg[-1].to_bytes(1, 'big') != compute_check(msg[3:-1])):
self.check_fail = True
return
self.seq = msg[4]
self.length = msg[2]
self.content = msg[5:-1]
def __repr__(self):
return 'packet length {} seq {} content {} ack {} check {}'.format(self.length, self.seq, self.content, self.ack, self.check_fail)