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
Коммит b5a8e92e45
32 изменённых файлов: 3812 добавлений и 0 удалений
+63
Просмотреть файл
@@ -0,0 +1,63 @@
#!/usr/bin/env python3
"""Stop emitting voice LC headers in front of a packet data transmission.
TX_START_1/3/5 always sent three Voice LC Headers built from the radio's own DMR ID,
which is correct for a voice call but pollutes the air before an SMS: the receiving
end logs them as "RF user <id> rejected for using TG 0". When the queued frame is a
data burst we send standard Idle data bursts instead - a legitimate filler that
receivers discard silently.
"""
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 "DT_IDLE << 4" in s:
print("already patched")
raise SystemExit(0)
# The three start steps share the same two lines; patch each occurrence.
old_variants = [
# TX_START_1 (preceded by the LED write and header call)
(""" hrc6000SendPcOrTgLCHeader();
SPI0WritePageRegByte(0x04, 0x41, 0x80); // Transmit during next Timeslot
SPI0WritePageRegByte(0x04, 0x50, 0x10); // Set Data Type to 0001 (Voice LC Header), Data, LCSS=00
""",
""" if (hrc.txDataType != HOTSPOT_TX_TYPE_VOICE)
{
// A data burst is queued: don't announce a voice call in front of it.
SPI0WritePageRegByte(0x04, 0x41, 0x80); // Transmit during next Timeslot
SPI0WritePageRegByte(0x04, 0x50, (DT_IDLE << 4)); // Idle data burst as filler
}
else
{
hrc6000SendPcOrTgLCHeader();
SPI0WritePageRegByte(0x04, 0x41, 0x80); // Transmit during next Timeslot
SPI0WritePageRegByte(0x04, 0x50, 0x10); // Set Data Type to 0001 (Voice LC Header), Data, LCSS=00
}
"""),
# TX_START_3 and TX_START_5 (no header call, just the two register writes)
(""" SPI0WritePageRegByte(0x04, 0x41, 0x80); // Transmit during Next Timeslot
SPI0WritePageRegByte(0x04, 0x50, 0x10); // Set Data Type to 0001 (Voice LC Header), Data, LCSS=00
""",
""" SPI0WritePageRegByte(0x04, 0x41, 0x80); // Transmit during Next Timeslot
SPI0WritePageRegByte(0x04, 0x50, ((hrc.txDataType != HOTSPOT_TX_TYPE_VOICE) ? (DT_IDLE << 4) : 0x10));// Voice LC Header, or Idle filler ahead of data
"""),
(""" SPI0WritePageRegByte(0x04, 0x41, 0x80); // Transmit during next Timeslot
SPI0WritePageRegByte(0x04, 0x50, 0x10); // Set Data Type to 0001 (Voice LC Header), Data, LCSS=00
""",
""" SPI0WritePageRegByte(0x04, 0x41, 0x80); // Transmit during next Timeslot
SPI0WritePageRegByte(0x04, 0x50, ((hrc.txDataType != HOTSPOT_TX_TYPE_VOICE) ? (DT_IDLE << 4) : 0x10));// Voice LC Header, or Idle filler ahead of data
"""),
]
count = 0
for old, new in old_variants:
if old in s:
s = s.replace(old, new, 1)
count += 1
if count == 0:
raise SystemExit("no TX_START patterns matched - aborting")
open(p, "w").write(s)
print("patched %d of 3 TX start steps" % count)