# 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](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: `) тем же декодером. - `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. ## Как запустить/установить ```sh 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-прокси с логом: ```sh python udpproxylogger.py [-l log.txt] ``` Повторный разбор лога: ```sh 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.py` → `DMRMasterLocal.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:141` — `return None # Not supported; !!TODO: decode 7-bit` (не декодируется 7-битный Talker Alias). - По roadmap апстрима (см. `README.upstream.md`) не реализованы: файл конфигурации и конфигурация пользователей, полноценная маршрутизация (по слотам, подписки на группы), кодирование внутренних структур DMR, датапакеты/сообщения, кодек AMBE, запись голосовых вызовов в файлы, TA внутри DMRD, регистрация абонентов и резолв ID в позывные. - Собственных доработок форка нет — расхождение с оригиналом сводится к одному переименованию, поэтому обновление апстрима должно проходить без конфликтов, кроме файла `localmaster.py`. - `localmaster.py` числится в `.gitignore`, но при этом отслеживается — при правке локальных настроек их легко закоммитить случайно.