0DMRMaster (форк)

Частный DMR-мастер-сервер на Python: реализация протокола Homebrew DMR (того же, что используют BrandMeister и HBlink), к которому подключаются хотспоты и репитеры, плюс декодирующий UDP-прокси для анализа трафика и веб-дашборд. Позволяет поднять собственную закрытую DMR-сеть, не завязанную на публичные мастера.

Это форк проекта 0DMRMaster Александра Мокрова (UR6LKW), оригинал — https://github.com/olympy/0DMRMaster, лицензия GPL-3.0 (файл LICENSE). Копия взята для эксплуатации у себя — как мастер-сервер и как инструмент разбора DMR-пакетов рядом с XLX-рефлектором. Документация оригинального проекта (описание протокола, roadmap, список источников ETSI) сохранена в README.upstream.md.

Отличия от оригинала

  • 2025-06-19 — единственный собственный коммит: localmaster_EXAMPLE.py переименован в localmaster.py. Содержимое файла не изменено (similarity index 100%).

Смысл правки: в оригинале localmaster.py — локальный конфиг-файл запуска, он перечислен в .gitignore (/localmaster.py) и в репозитории лежит только пример. В этой копии рабочий файл запуска взят под контроль версий, чтобы разворачивать сервер клонированием без ручного копирования примера. Побочный эффект: любые локальные настройки (в том числе список разрешённых peer ID и паролей в ListPeerAuth) при последующих правках попадут в историю git.

Кода самого проекта (dmrtools/, api/, dashboard/) владелец не менял — он полностью соответствует апстриму по состоянию на 2025-06-01.

Стек

  • Python 3.11+, asyncio (asyncio.DatagramProtocol для UDP).
  • FastAPI + uvicorn — HTTP API и раздача дашборда; bitarray/bitstring — разбор битовых полей DMR; dmr-utils3 (BPTC-декодирование Embedded LC); pydantic. Полный список — requirements.txt.
  • Дашборд — статический HTML + JS без фреймворков.
  • Работает на Linux и Windows. Специального железа (AMBE-чипов) не требуется: голос не транскодируется, кодек AMBE пока не реализован.

Структура

  • localmaster.py — точка входа: наследник DMRMaster, в методе config() задаётся авторизация пиров и регистрируются приложения. Здесь же включается «попугай» (ParrotApp(9990)).
  • udpproxylogger.py — второй исполняемый скрипт: UDP-прокси между хотспотом и чужим мастером с hex-логом и декодированием пакетов (L1 MMDVM + L2 ETSI).
  • relog.py — повторный разбор ранее сохранённого лога (строки HD: <hex>) тем же декодером.
  • dmrtools/dmrmaster.py — каркас сервера: разбор аргументов командной строки, логирование, запуск UDP-сервера и API.
  • dmrtools/dispatcher.py — маршрутизация пакетов между пирами и приложениями.
  • dmrtools/peer.py, peer_controller.py — состояние подключённых репитеров/хотспотов и конечный автомат логина (RPTL/RPTK/RPTC).
  • dmrtools/auth.py — политики доступа: AllowAllPeerAuth (пускать всех), DenyAllPeerAuth, ListPeerAuth({id: 'пароль'}) со сверкой SHA-256 хэша с солью.
  • dmrtools/call.py — сборка голосового вызова из потока пакетов.
  • dmrtools/parrot_app.py — приложение-эхо: записывает вызов на свой ID и проигрывает обратно с задержкой.
  • dmrtools/app.py — базовые интерфейсы приложений (App, IAppCallInterceptor, IAppDispatcher).
  • dmrtools/udpproxy.py — многоклиентский UDP-прокси (сессия на каждый адрес клиента).
  • dmrtools/network.py, asyncnetwork.py — интерфейсы отправки/приёма датаграмм и их asyncio-реализация.
  • dmrtools/dmrproto/mmdvm_l1.py — пакеты Homebrew/MMDVM (DMRD, RPTL, RPTPING, DMRA и т.д.), расчёт хэша пароля.
  • dmrtools/dmrproto/etsi_l2.py, lc_util.py, enums.py, base_fields.py, factory.py — разбор внутренней структуры DMR по ETSI TS 102 361: Link Control, тип голосового кадра, Talker Alias.
  • dmrtools/pphex.py — hexdump для логов.
  • api/dmrapi.py — FastAPI-приложение: /api/dashboard, /api/peers, /api/calls, монтирование статики.
  • dashboard/index.html, dashboard/app.js — веб-дашборд, опрашивающий API.
  • doc/dashboard-screenshot.png — скриншот дашборда для README.

Как запустить/установить

python -m venv venv
. venv/bin/activate
python -m pip install --upgrade pip
pip install -r requirements.txt
python localmaster.py

Остановка — Ctrl+C.

По умолчанию сервер слушает 62031/udp (DMR) и 8000/tcp (API и дашборд):

  • API: http://<сервер>:8000/api/dashboard
  • Дашборд: http://<сервер>:8000/dashboard/index.html

UDP-прокси с логом:

python udpproxylogger.py <listenip> <listenport> <serverip> <serverport> [-l log.txt]

Повторный разбор лога:

python relog.py <входной-лог> [-l log.txt]

Конфигурация

Аргументы localmaster.py (обрабатываются в dmrtools/dmrmaster.py):

  • -i, --interface — интерфейс для DMR (по умолчанию 0.0.0.0);
  • -p, --port — UDP-порт DMR (по умолчанию 62031);
  • --web-interface — интерфейс API (по умолчанию 0.0.0.0);
  • --web-port — TCP-порт API (по умолчанию 8000);
  • -l, --log-file — файл лога;
  • -d, --ll-debug — уровень логирования DEBUG вместо INFO.

Настройки в коде localmaster.pyDMRMasterLocal.config():

  • политика доступа: сейчас активна AllowAllPeerAuth()подключиться может любой peer ID без пароля; закомментирована строка с ListPeerAuth({1: 'pass1', 2: ''}) — вариант со списком разрешённых ID и паролей;
  • self.register_app(ParrotApp(9990)) — ID «попугая»; у ParrotApp также есть параметры repeat_delay (5 с) и прочие, задаваемые при создании.

Паролей и токенов в репозитории нет: pass1 в закомментированной строке — заглушка из примера. Реальные пароли пиров при вводе в эксплуатацию следует держать вне репозитория (см. .env/локальный незакоммиченный конфиг), тем более что localmaster.py здесь отслеживается git.

Состояние

Рабочий прототип (сам автор оригинала определяет статус как public alpha, версия 0.5, «не использовать в проде, интерфейсы могут меняться»). Последний коммит 2025-06-19 (собственный, переименование файла), последний коммит апстрима — 2025-06-01. Всего 17 коммитов.

Что не доделано

  • Явный TODO в коде один: dmrtools/dmrproto/lc_util.py:141return None # Not supported; !!TODO: decode 7-bit (не декодируется 7-битный Talker Alias).
  • По roadmap апстрима (см. README.upstream.md) не реализованы: файл конфигурации и конфигурация пользователей, полноценная маршрутизация (по слотам, подписки на группы), кодирование внутренних структур DMR, датапакеты/сообщения, кодек AMBE, запись голосовых вызовов в файлы, TA внутри DMRD, регистрация абонентов и резолв ID в позывные.
  • Собственных доработок форка нет — расхождение с оригиналом сводится к одному переименованию, поэтому обновление апстрима должно проходить без конфликтов, кроме файла localmaster.py.
  • localmaster.py числится в .gitignore, но при этом отслеживается — при правке локальных настроек их легко закоммитить случайно.
S
Описание
Private DMR master server
Readme GPL-3.0
233 KiB
Languages
Python 92.1%
JavaScript 6.3%
HTML 1.6%