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 удалений
+53
Просмотреть файл
@@ -0,0 +1,53 @@
#!/usr/bin/env python3
"""Show a received SMS on a proper message screen instead of a transient notification.
uiNotificationShow() draws over the current screen, but the normal screen redraw wipes
it again - which is why the text never appeared even though the message was received
and decoded correctly. The message box screen is a real menu: it stays until the user
dismisses it.
"""
W = "/home/viktor/ogd77rel/uv_src/OpenGD77_MDUV380_DM1701_20260130/MDUV380_firmware/application"
p = W + "/source/applicationMain.c"
s = open(p).read()
if "MESSAGEBOX_TYPE_INFO" in s and "smsMarkRead" in s and "MessageBox.message, MESSAGEBOX_MESSAGE_LEN_MAX, \"%u:" in s:
print("already patched")
raise SystemExit(0)
# Replace the notification-based display with a message box screen.
old_start = "\t\tif (smsHasUnread())\n\t\t{\n"
assert old_start in s, "unread block not found"
start = s.index(old_start)
# find the end of that block (the closing brace of the if, at the same indent)
end_marker = "\n\t\t}\n"
end = s.index(end_marker, start) + len(end_marker)
old_block = s[start:end]
assert "smsMarkRead" in old_block, "unexpected block contents: %r" % old_block[:200]
new_block = """ if (smsHasUnread())
{
const smsMessage_t *msg = smsGetLatest();
// Only interrupt the user when they are on a normal operating screen.
int currentMenu = menuSystemGetCurrentMenuNumber();
if ((msg != NULL) && ((currentMenu == UI_VFO_MODE) || (currentMenu == UI_CHANNEL_MODE)))
{
snprintf(uiDataGlobal.MessageBox.message, MESSAGEBOX_MESSAGE_LEN_MAX, "%u\\n%s",
(unsigned int)msg->srcId, msg->text);
uiDataGlobal.MessageBox.type = MESSAGEBOX_TYPE_INFO;
uiDataGlobal.MessageBox.decoration = MESSAGEBOX_DECORATION_FRAME;
uiDataGlobal.MessageBox.buttons = MESSAGEBOX_BUTTONS_OK;
uiDataGlobal.MessageBox.validatorCallback = NULL;
menuSystemPushNewMenu(UI_MESSAGE_BOX);
soundSetMelody(MELODY_ACK_BEEP);
smsMarkRead();
}
}
"""
s = s[:start] + new_block + s[end:]
open(p, "w").write(s)
print("applicationMain.c: incoming SMS now opens a message screen")