Files
opengd77-uv380-sms/patches/patch_txfix.py
T
viktor b5a8e92e45 DMR-данные (SMS) на TYT MD-UV380 под OpenGD77
Восстановлено из транскрипта сессии после потери рабочего дерева на сервере сборки.

Что внутри:
- четыре новых модуля прошивки (приём, сборка исходящего, меню Messages);
- правки существующих файлов в виде скриптов, с порядком применения;
- инструменты стенда: снятие экрана рации по USB, дамп кадров, эталонная сборка пакета;
- NOTES.md: что проверено в эфире, что не доделано, где грабли.

Работало в эфире в августе 2026: данные ходят в обе стороны в режиме хотспота,
рация принимает SMS и показывает на экране. Отправка собрана и сверена с эталоном
сервера побайтово, но в эфире не проверялась.

Claude-Session: https://claude.ai/code/session_01GJtvG5WwqYNe1b7s9LLtgy
2026-09-11 02:39:24 +09:00

75 строки
3.1 KiB
Python

#!/usr/bin/env python3
"""Fix: a stale "this frame is packet data" marker could suppress the Voice LC Header
of an ordinary voice transmission.
hrc.txDataType is only ever set on the hotspot path, and was only cleared after a data
burst was actually sent. If a data transmission was cut short before that, the marker
stayed set - and the next PTT from the radio itself started without a Voice LC Header,
so receivers never recognised the call.
Two guards:
- the start-of-transmission checks now only apply while acting as a hotspot modem;
- the marker is cleared whenever a normal (non-hotspot) transmission begins.
"""
W = "/home/viktor/ogd77rel/uv_src/OpenGD77_MDUV380_DM1701_20260130/MDUV380_firmware/application"
p = W + "/source/hardware/HR-C6000.c"
s = open(p).read()
if "isHotspotDataTx" in s:
print("already patched")
raise SystemExit(0)
# 1. helper right after the struct instance, so all three call sites can use it
anchor = "\t\t.txDataType = HOTSPOT_TX_TYPE_VOICE,\n"
assert anchor in s, "hrc initialiser not found"
# find the end of the initialiser block to place the helper after it
marker = "static void hrc6000SendPcOrTgLCHeader(void);"
assert marker in s, "forward declaration anchor not found"
helper = """// True only when the frame about to be sent is a packet data burst queued by the host.
// Outside hotspot mode there is no such thing, so ordinary voice transmissions are never
// affected by a leftover marker.
static inline bool isHotspotDataTx(void)
{
return ((settingsUsbMode == USB_MODE_HOTSPOT) && (hrc.txDataType != HOTSPOT_TX_TYPE_VOICE));
}
"""
s = s.replace(marker, marker + "\n" + helper, 1)
# 2. use it in the three start-of-transmission checks
a1 = "\t\t\tif (hrc.txDataType != HOTSPOT_TX_TYPE_VOICE)\n\t\t\t{\n\t\t\t\t// A data burst is queued: don't announce a voice call in front of it."
assert a1 in s, "TX_START_1 check not found"
s = s.replace(a1, "\t\t\tif (isHotspotDataTx())\n\t\t\t{\n\t\t\t\t// A data burst is queued: don't announce a voice call in front of it.", 1)
a2 = "((hrc.txDataType != HOTSPOT_TX_TYPE_VOICE) ? (DT_IDLE << 4) : 0x10)"
n2 = s.count(a2)
assert n2 == 2, "expected 2 shared checks, found %d" % n2
s = s.replace(a2, "(isHotspotDataTx() ? (DT_IDLE << 4) : 0x10)")
# 3. and in the ongoing-TX branch, so a stale marker cannot divert voice frames either
a3 = "\t\t\t\t\tif (hrc.txDataType != HOTSPOT_TX_TYPE_VOICE)\n"
assert a3 in s, "TX_2 data branch not found"
s = s.replace(a3, "\t\t\t\t\tif (isHotspotDataTx())\n", 1)
# 4. belt and braces: clear the marker when a non-hotspot transmission starts
a4 = """ if (settingsUsbMode != USB_MODE_HOTSPOT)
{
codecInit(false);
}
else
{
hrc.hotspotPostponedFrameHandling = (HS_NUM_OF_SILENCE_SEQ_ON_STARTUP * 6);"""
assert a4 in s, "TX start branch not found"
s = s.replace(a4, """ if (settingsUsbMode != USB_MODE_HOTSPOT)
{
hrc.txDataType = HOTSPOT_TX_TYPE_VOICE;// plain voice call: never a data burst
codecInit(false);
}
else
{
hrc.hotspotPostponedFrameHandling = (HS_NUM_OF_SILENCE_SEQ_ON_STARTUP * 6);""", 1)
open(p, "w").write(s)
print("HR-C6000.c: voice transmissions can no longer be suppressed by a stale data marker")