DMR-данные (SMS) на TYT MD-UV380 под OpenGD77
Восстановлено из транскрипта сессии после потери рабочего дерева на сервере сборки. Что внутри: - четыре новых модуля прошивки (приём, сборка исходящего, меню Messages); - правки существующих файлов в виде скриптов, с порядком применения; - инструменты стенда: снятие экрана рации по USB, дамп кадров, эталонная сборка пакета; - NOTES.md: что проверено в эфире, что не доделано, где грабли. Работало в эфире в августе 2026: данные ходят в обе стороны в режиме хотспота, рация принимает SMS и показывает на экране. Отправка собрана и сверена с эталоном сервера побайтово, но в эфире не проверялась. Claude-Session: https://claude.ai/code/session_01GJtvG5WwqYNe1b7s9LLtgy
Этот коммит содержится в:
@@ -0,0 +1,72 @@
|
||||
#!/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")
|
||||
Ссылка в новой задаче
Block a user