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 удалений
+67
Просмотреть файл
@@ -0,0 +1,67 @@
#!/usr/bin/env python3
"""Patch 3 of RX support: dispatch in the hotspot state machine, and read the
payload out of the HR-C6000 when a data burst arrives off air."""
W = "/home/viktor/ogd77rel/uv_src/OpenGD77_MDUV380_DM1701_20260130/MDUV380_firmware/application"
# ---------------------------------------------------------------- hotspot.c
p = W + "/source/functions/hotspot.c"
s = open(p).read()
if "case HOTSPOT_RX_DATA_FRAME:" in s:
print("hotspot.c: dispatch already present")
else:
a = "\t\t\t\t\t\tcase HOTSPOT_RX_STOP:\n"
assert a in s, "RX_STOP case not found"
case = """\t\t\t\t\t\tcase HOTSPOT_RX_DATA_FRAME:
\t\t\t\t\t\t\t// Packet data burst (SMS, position report...) received off air.
\t\t\t\t\t\t\t// The data type the HR-C6000 reported is stored right after the frame.
\t\t\t\t\t\t\tsendDataFrameToHost(audioAndHotspotDataBuffer.hotspotBuffer[rfFrameBufReadIdx],
\t\t\t\t\t\t\t\t\taudioAndHotspotDataBuffer.hotspotBuffer[rfFrameBufReadIdx][AMBE_AUDIO_LENGTH + LC_DATA_LENGTH + 1]);
\t\t\t\t\t\t\tlastRxState = HOTSPOT_RX_DATA_FRAME;
\t\t\t\t\t\t\trxFrameTime = ticksGetMillis();
\t\t\t\t\t\t\tbreak;
"""
s = s.replace(a, case + a, 1)
open(p, "w").write(s)
print("hotspot.c: RX_DATA_FRAME dispatch added")
# ------------------------------------------------------------- HR-C6000.c
p = W + "/source/hardware/HR-C6000.c"
s = open(p).read()
if "HOTSPOT_RX_DATA_FRAME" in s:
print("HR-C6000.c: already patched")
raise SystemExit(0)
# Hook in right after the terminator handling, which is the existing example of
# "this is a data sync burst" handling in this interrupt.
a = """ if (hrc6000CrcIsValid() && (slotState != DMR_STATE_IDLE) && (hrc.skipCount > 0) && (rxSyncClass != SYNC_CLASS_DATA) && ((rxDataType & 0x07) == 0x01))
{
hrc.skipCount--;
}
"""
assert a in s, "skipCount anchor missing"
block = """ // Packet data bursts (SMS, position reports, CSBK). Stock firmware reads nothing off the
// chip for these, which is why data never reached the host. The HR-C6000 has already
// removed the FEC, so the 96 bit payload can be read from the same register page that
// carries LC data, and handed to the hotspot code to be rebuilt into a full burst.
if ((settingsUsbMode == USB_MODE_HOTSPOT) && hrc6000CrcIsValid() && (rxSyncClass == SYNC_CLASS_DATA) &&
((rxDataType == DT_CSBK) || (rxDataType == DT_DATA_HEADER) || (rxDataType == DT_RATE_12_DATA)) &&
hrc6000CheckColourCodeFilter())
{
if (SPI0ReadPageRegByteArray(0x02, 0x00, (uint8_t *)DMR_frame_buffer, LC_DATA_LENGTH) == kStatus_Success)
{
DMR_frame_buffer[AMBE_AUDIO_LENGTH + LC_DATA_LENGTH] = HOTSPOT_RX_DATA_FRAME;
DMR_frame_buffer[AMBE_AUDIO_LENGTH + LC_DATA_LENGTH + 1] = rxDataType;
hrc.hotspotDMRRxFrameBufferAvailable = true;
}
return;
}
"""
s = s.replace(a, block + a, 1)
open(p, "w").write(s)
print("HR-C6000.c: data burst capture added")