Files
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

73 строки
2.7 KiB
Python

#!/usr/bin/env python3
"""Expose the SMS diagnostics through a RAM window that can be read over USB.
Drawing counters on the screen turned out to be unreliable: the notification is
overwritten by the normal screen redraw, so a screenshot usually shows nothing even
when reception is happening. Instead the counters are mirrored into the tail of the
raw audio buffer, which the CPS protocol can read out at any moment (area 7).
Layout at rawBuffer[2376..2399]:
'S' 'M' 'S' 'D' magic, so a stale buffer cannot be mistaken for data
u16 bursts data bursts handed to the assembler
u16 headers data headers parsed
u16 assembled messages assembled completely
u16 lastBlocks block count announced by the last header
u16 collected blocks collected for the message in progress
u16 state 0 = idle, 1 = collecting
u16 textLen length of the last extracted text
"""
W = "/home/viktor/ogd77rel/uv_src/OpenGD77_MDUV380_DM1701_20260130/MDUV380_firmware/application"
p = W + "/source/functions/sms.c"
s = open(p).read()
if "SMS_DIAG_OFFSET" in s:
print("already patched")
raise SystemExit(0)
s = s.replace('#include "functions/ticks.h"\n',
'#include "functions/ticks.h"\n#include "functions/sound.h"\n', 1)
anchor = "void smsInit(void)\n"
assert anchor in s
helper = """// Diagnostics mirror: the tail of the raw audio buffer is readable over USB (CPS area 7),
// which is far more dependable than trying to keep text on the screen.
#define SMS_DIAG_OFFSET 2376
static void smsDiagPublish(void)
{
volatile uint8_t *d = &audioAndHotspotDataBuffer.rawBuffer[SMS_DIAG_OFFSET];
uint16_t values[7];
values[0] = diagBursts;
values[1] = diagHeaders;
values[2] = diagAssembled;
values[3] = diagLastBlocks;
values[4] = blocksCollected;
values[5] = (uint16_t)rxState;
values[6] = diagTextLen;
d[0] = 'S'; d[1] = 'M'; d[2] = 'S'; d[3] = 'D';
for (uint8_t i = 0; i < 7; i++)
{
d[4 + (i * 2)] = (uint8_t)(values[i] & 0xFF);
d[5 + (i * 2)] = (uint8_t)(values[i] >> 8);
}
}
"""
s = s.replace(anchor, helper + anchor, 1)
# track the extracted text length too
s = s.replace("static uint8_t diagLastBlocks = 0;\n",
"static uint8_t diagLastBlocks = 0;\nstatic uint16_t diagTextLen = 0;\n", 1)
s = s.replace("\tlatest.text[t] = '\\0';\n",
"\tlatest.text[t] = '\\0';\n\tdiagTextLen = t;\n", 1)
# publish on every tick, so a read over USB always sees the current state
s = s.replace("void smsTick(void)\n{\n", "void smsTick(void)\n{\n\tsmsDiagPublish();\n\n", 1)
open(p, "w").write(s)
print("sms.c: diagnostics mirrored into the readable RAM window")