Files
rt-4d/tools/a6_probe.py
T
viktorиClaude Opus 4.8 dfae06acfc Реверс: RDA-debug в мосте недоступен; тюнер калибрует по 0x68 (cmd 0xB0-0xBD)
- tools/a6_probe.py: проверка достижимости RDA-debug через мост.
  РЕЗУЛЬТАТ: 0x68-мост жив, но RDA knock молчит на 115200/921600 (XON/XOFF и без) —
  прошивка модуля держит UART, ROM-debug недоступен. Значит a6tools/AT+DMOFREQERR
  напрямую не пробить; калибровку писать через 0x68.
- docs/tuning-software-protocol.md: декомпиляция Radio Tuning Software 241219.exe
  (dnfile). Сборка UV5118_4819_Tune. Калибровка идёт ТЕМ ЖЕ 0x68 (не 0xAAAA),
  команды 0xB0-0xBD извлечены из статических шаблонов: 0xBD connect, 0xB1 границы
  диапазонов (UHF 400/435/470, VHF 136/150/174 МГц), 0xB2/B4 девиация, 0xB6/B7/B8
  мощность/частотные точки, num_U_DigFreq = смещение DMR-частоты. Достижимо через
  наш мост -> путь к правильной калибровке TX без прибора (фидбек по MMDVM BER).

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

87 строки
3.6 KiB
Python

#!/usr/bin/env python3
"""Проверка: достижим ли RDA-debug/ATE-интерфейс модуля HF6853 через мост RT-4D.
Рация в режиме «Обновление DMR чипа» (вкл. с зажатой MENU). Под WSL COM идёт
через powershell.exe. Отправляем сырые байты, печатаем сырой ответ.
Тесты:
1) 0x68 ping — жив ли мост (база).
2) RDA knock (reg3) — отвечает ли чип на низкоуровневый debug (ждём content=0x80
либо ack 11 13). Пробуем 115200 и 921600, с XON/XOFF и без.
Ничего НЕ пишет в калибровку — только читающие кадры.
"""
import argparse, subprocess, sys
PS = r'''
$p = New-Object System.IO.Ports.SerialPort "{port}",{baud},"None",8,"one"
$p.ReadTimeout = 150
$p.Handshake = [System.IO.Ports.Handshake]::{hs}
try {{ $p.Open() }} catch {{ Write-Output "ERR:$($_.Exception.Message)"; exit }}
$p.DiscardInBuffer()
$tx = [byte[]]@({txlist})
if ($tx.Length -gt 0) {{ $p.Write($tx,0,$tx.Length) }}
$buf = New-Object System.Collections.Generic.List[byte]
$sw = [Diagnostics.Stopwatch]::StartNew()
$last = 0
while ($sw.ElapsedMilliseconds -lt {tmo}) {{
try {{ $buf.Add($p.ReadByte()); $last = $sw.ElapsedMilliseconds }} catch {{}}
if ($buf.Count -gt 0 -and ($sw.ElapsedMilliseconds - $last) -gt {idle}) {{ break }}
}}
$p.Close()
Write-Output ("OK:" + (($buf | ForEach-Object {{ "{{0:x2}}" -f $_ }}) -join ""))
'''
def find_port():
out = subprocess.run(["powershell.exe","-NoProfile","-Command",
"[System.IO.Ports.SerialPort]::GetPortNames() -join ','"],
capture_output=True, text=True).stdout.strip().replace("\r","")
ports = [p for p in out.split(",") if p]
if not ports: sys.exit("COM не найден. Рация подключена?")
return ports[0]
def xfer(port, tx, baud=115200, hs="None", tmo=1500, idle=300):
txlist = ",".join(str(x) for x in tx) if tx else ""
script = PS.format(port=port, baud=baud, hs=hs, txlist=txlist, tmo=tmo, idle=idle)
r = subprocess.run(["powershell.exe","-NoProfile","-Command",script],
capture_output=True, text=True)
line = [l for l in r.stdout.replace("\r","").split("\n") if l.startswith(("OK:","ERR:"))]
if not line: return b""
if line[0].startswith("ERR:"): sys.exit(line[0])
return bytes.fromhex(line[0][3:])
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--port")
a = ap.parse_args()
port = a.port or find_port()
print(f"[порт {port}]\n")
ping = bytes([0x68,0x84,0x01,0x01,0x00,0x00,0x00,0x01,0x00,0x10])
print("1) 0x68 ping @115200:")
rx = xfer(port, ping, 115200, "None")
print(" TX:", ping.hex(' '))
print(" RX:", rx.hex(' ') if rx else "(нет ответа)")
alive = bool(rx and rx[0]==0x68)
print(" ->", "МОСТ ЖИВ" if alive else "нет 0x68-ответа")
knock = bytes.fromhex("ad0007ff0403000000 01 f9".replace(" ",""))
print("\n2) RDA knock (read reg3), ждём content=0x80 или ack 11 13:")
for baud in (115200, 921600):
for hs in ("None","XOnXOff"):
rx = xfer(port, knock, baud, hs, tmo=800, idle=250)
tag = f" {baud} {hs:8}"
if not rx:
print(tag, "-> (нет ответа)")
else:
hexs = rx.hex(' ')
hit = " <== ПОХОЖЕ НА RDA!" if (b'\x80' in rx or rx[:2]==b'\x11\x13' or rx[:1]==b'\xad') else ""
print(tag, "->", hexs, hit)
if __name__ == "__main__":
main()