311 lines
19 KiB
Markdown
311 lines
19 KiB
Markdown
# HTTP-канал управления RF-001
|
||
|
||
Текущее состояние: **HTTP-КАНАЛ РАБОТАЕТ; ВЕРСИОННЫЙ МЕХАНИЗМ ОБНОВЛЕНИЯ
|
||
И НЕБЛОКИРУЮЩИЙ `STAIRCASE_SEARCH` ПРОВЕРЕНЫ БЕЗ НОВОГО ДОСТУПА К РАДИО**.
|
||
|
||
Канал заменяет Git как транспорт между работающими компьютерами. Git остаётся
|
||
для исходного кода и истории. Старые watcher-ы сохранены как резервный вариант.
|
||
|
||
## Граница этапа 1
|
||
|
||
Реализованы:
|
||
|
||
- JSON-сервер на основном ПК;
|
||
- исходящее подключение RX-агента с ноутбука к основному ПК;
|
||
- подпись каждого запроса HMAC-SHA256;
|
||
- защита от повторного использования запроса по времени и nonce;
|
||
- ограничение адреса RX-агента на сервере;
|
||
- фиксированные задачи `INVENTORY`, `OFFLINE_VERIFY`, `RX_CAPTURE_START`,
|
||
`RX_CAPTURE_STATUS`, `CAPTURE_DECODE`, `CAPTURE_DIAGNOSTICS` и
|
||
`STAIRCASE_SEARCH` и `UPDATE_AGENT`;
|
||
- чтение `hostname`, `whoami`, состояния Git и `hackrf_info`;
|
||
- сохранение результата на основном ПК;
|
||
- обязательное поле `rf_tx = 0` во всех сообщениях.
|
||
|
||
На RX-ноутбуке 23 сентября 2026 года автономная проверка завершилась так:
|
||
|
||
- ветка `video-development`;
|
||
- HEAD `a94f2db4ee8377201a97263166cb09431df5f5d6`;
|
||
- 100 CRC-корректных пакетов из 100;
|
||
- 100 точных пакетов из 100;
|
||
- `hardware_accessed = false`;
|
||
- ошибок нет;
|
||
- `rf_tx = 0`.
|
||
|
||
В сеансе `RF001A_20260925T125829Z_227EADB4` уже были выполнены:
|
||
|
||
- один конечный RX-захват с фиксированным профилем;
|
||
- проверка READY по фактическому PID, обнаруженному CLKIN и растущему файлу;
|
||
- чтение итогового состояния захвата;
|
||
- потоковое декодирование принятого IQ;
|
||
- полный потоковый поиск эталонной лестницы без обращения к SDR.
|
||
|
||
Новые захваты и передачи не входят в текущее состояние и требуют отдельного
|
||
явного разрешения. Завершённый поиск лестницы сохраняет исходный результат
|
||
`NOT_DETECTED`; последующая диагностика должна записываться отдельно и не
|
||
подменять этот результат.
|
||
|
||
Не входят в управляющий канал:
|
||
|
||
- запуск `hackrf_transfer` в режиме TX;
|
||
- команда или сетевой метод RF TX;
|
||
- изменение firmware, CPLD, USB-драйвера или Zadig;
|
||
- commit и push.
|
||
|
||
Для сервера и RX-агента подготовлен пользовательский автозапуск через
|
||
Планировщик заданий Windows. Службы Windows и права администратора для этого не
|
||
нужны. Сервер и агент работают постоянно после входа соответствующего
|
||
пользователя в Windows. Агент не содержит произвольной shell-команды.
|
||
|
||
## Схема
|
||
|
||
```text
|
||
ОСНОВНОЙ ПК 192.168.255.185:8765
|
||
HTTP JSON server
|
||
очередь только фиксированных типов заданий
|
||
^
|
||
| исходящее соединение с подписью запроса
|
||
|
|
||
RX-НОУТБУК 192.168.255.56
|
||
polling agent
|
||
hostname / whoami / Git / hackrf_info / offline 100/100
|
||
RF TX = 0
|
||
```
|
||
|
||
Сервер не раздаёт каталоги и произвольные пути. Он может выдать только заранее
|
||
зарегистрированный архив агента с точным `release_id` через ограниченный маршрут
|
||
обновлений. Произвольной shell-команды в протоколе нет. Неизвестный тип задания
|
||
и любое значение `rf_tx`, отличное от нуля, отклоняются.
|
||
|
||
## Файлы
|
||
|
||
- `tools/rf001_http_control.py`: сервер, локальная очередь и клиент управления;
|
||
- `tools/rf001_rx_http_agent.py`: RX-агент с фиксированной инвентаризацией;
|
||
- `tools/rf001_offline_decode.py`: фиксированный программный `legacy`-декодер;
|
||
- `tools/rf001_staircase_search.py`: потоковый поиск эталонной лестницы без
|
||
обращения к SDR;
|
||
- `tools/rf001_staircase_job.py`: фиксированный дочерний процесс длительного
|
||
поиска и сохранение результата до сетевого подтверждения;
|
||
- `tools/rf001_agent_update.py`: строгая проверка состава и распаковка
|
||
версионного архива;
|
||
- `tools/rf001_agent_launcher.py`: стабильный запускатель, переключение версии
|
||
и локальный откат при неудачном запуске;
|
||
- `tools/rf001_build_agent_packages.py`: воспроизводимая сборка выпусков и
|
||
первоначального пакета без включения секрета;
|
||
- `tools/rf001_rx_capture.py`: жёсткие RX-параметры и проверки READY/COMPLETE;
|
||
- `tools/rf001_capture_runner.py`: один конечный фоновый RX-процесс и журнал;
|
||
- `.local/rf001_http_token.txt`: общий секрет вне Git;
|
||
- `.local/rf001_http_server_state.json`: локальное состояние вне Git.
|
||
|
||
## Подготовка общего секрета
|
||
|
||
На основном ПК из корня проекта один раз выполнить:
|
||
|
||
```powershell
|
||
python -B tools\rf001_http_control.py init-token
|
||
```
|
||
|
||
Команда не перезаписывает существующий секрет. Файл
|
||
`.local\rf001_http_token.txt` надо скопировать на RX-ноутбук по доверенному
|
||
каналу и не добавлять в Git. Сервер и агент должны читать одинаковое содержимое.
|
||
|
||
## Запуск сервера на основном ПК
|
||
|
||
```powershell
|
||
python -B tools\rf001_http_control.py serve `
|
||
--bind 192.168.255.185 `
|
||
--port 8765 `
|
||
--allowed-agent-ip 192.168.255.56
|
||
```
|
||
|
||
Ожидаемый стартовый вывод:
|
||
|
||
```text
|
||
SERVER = http://192.168.255.185:8765
|
||
ALLOWED AGENT IP = 192.168.255.56
|
||
TASK KINDS = INVENTORY, OFFLINE_VERIFY, RX_CAPTURE_START, RX_CAPTURE_STATUS, CAPTURE_DECODE, CAPTURE_DIAGNOSTICS, STAIRCASE_SEARCH, UPDATE_AGENT
|
||
RF TX = 0
|
||
```
|
||
|
||
Действующее правило Windows Firewall должно оставаться ограниченным программой
|
||
Python, TCP-портом `8765`, локальным адресом `192.168.255.185`, удалённым
|
||
адресом `192.168.255.56` и беспроводным интерфейсом.
|
||
|
||
## Постановка задания на основном ПК
|
||
|
||
```powershell
|
||
python -B tools\rf001_http_control.py enqueue-inventory --agent-id user-pc
|
||
```
|
||
|
||
Это локальная операция. Сервер принимает её только от собственного адреса и с
|
||
правильной подписью. Задание не содержит произвольной команды.
|
||
|
||
## Автозапуск агента на RX-ноутбуке
|
||
|
||
Первоначальный пакет содержит `INSTALL_RX_AGENT.cmd`. После распаковки его надо
|
||
запустить один раз. Установщик проверяет HMAC-манифест существующим секретом,
|
||
размеры и SHA256 всех файлов, сохраняет прежнюю установку и XML задания, а
|
||
затем меняет только действие существующего пользовательского задания
|
||
`SDR Rover RX Agent`. Пользователь, триггеры, параметры и уровень прав задания
|
||
сохраняются. Ожидаемый итог:
|
||
|
||
```text
|
||
BOOTSTRAP_INSTALL=COMPLETE
|
||
TASK_STATE=Running
|
||
AGENT_VERSION=2026.09.29.1
|
||
ARBITRARY_SHELL=DISABLED
|
||
RF_TX=0
|
||
```
|
||
|
||
После этого ручной запуск на ноутбуке для штатных обновлений не требуется.
|
||
Запускатель хранит выпуски в отдельных каталогах, проверяет запуск по PID,
|
||
версии и одноразовой метке и при сбое возвращается к предыдущему локальному
|
||
выпуску. Очередь MAIN, IQ, эталоны, результаты, конфигурация и секрет находятся
|
||
вне каталога конкретной версии.
|
||
|
||
## Модель доверия обновлений
|
||
|
||
Манифест каждого выпуска подписывается `HMAC-SHA256` из стандартной библиотеки
|
||
Python тем же общим секретом, которым уже подписываются HTTP-запросы. Подпись
|
||
охватывает отдельный домен `RF001-UPDATE-MANIFEST-V1`, `release_id`, версию
|
||
агента, совместимую версию протокола, размер и SHA256 архива и хеш внутреннего
|
||
манифеста. Разрешение MAIN дополнительно привязано к `agent_id` и сроку
|
||
действия. Сам секрет в ZIP не включается и при установке не заменяется.
|
||
|
||
Ограничение модели: общий симметричный секрет находится и на MAIN, и на
|
||
RX-ноутбуке. Компрометация одного из этих узлов позволяет создавать корректные
|
||
HMAC-подписи. Аппаратного корня доверия и независимой издательской подписи у
|
||
этого контура нет.
|
||
|
||
Перед переключением агент скачивает архив не более двух раз за одну
|
||
сохранённую попытку обновления, проверяет подпись, размер, SHA256, точный список
|
||
файлов, хеш каждого файла, допустимость путей, предел распакованного объёма,
|
||
совместимость и свободное место. Затем выполняется программная самопроверка без
|
||
SDR. Если агент занят локальной задачей, обновление остаётся в состоянии
|
||
`WAITING_FOR_IDLE`. Наличие файлов само по себе не считается установкой:
|
||
фактическую версию MAIN получает из следующего сообщения работающего агента.
|
||
|
||
## Поиск эталонной лестницы
|
||
|
||
`STAIRCASE_SEARCH` принимает только идентификатор существующего сеанса и
|
||
ожидаемые SHA256 RX-файла и эталона. Агент сам строит пути внутри
|
||
`%USERPROFILE%\Documents\SDR_Rover_Runtime`: запись берётся из каталога
|
||
`rx_capture`, а эталон — из отдельного каталога `references`. Сетевой запрос не
|
||
может передать путь или команду.
|
||
|
||
Перед запуском агент проверяет оба SHA256. Отчёт сохраняется рядом с записью
|
||
под именем `staircase_search_report.json`. Если подходящий отчёт уже существует,
|
||
он проверяется и возвращается без повторного анализа и без перезаписи. Для
|
||
задачи всегда заданы `hardware_accessed = false`, `rf_tx = 0` и
|
||
`automatic_retry = false`.
|
||
|
||
Поиск выполняется отдельным фиксированным процессом. Агент продолжает опрос
|
||
сервера и сообщает фактическую фазу, число обработанных комплексных отсчётов и
|
||
общий объём. Эти значения берутся из потокового анализатора, а не оцениваются
|
||
по времени. Активная аренда продлевается даже после истечения обычного короткого
|
||
тайм-аута. Указатель задачи записывается до запуска процесса, поэтому перезапуск
|
||
агента не создаёт второй анализатор. Готовый результат остаётся на диске до
|
||
подтверждения MAIN и после обрыва сети отправляется без повторного вычисления.
|
||
|
||
## Диагностический однократный запуск агента
|
||
|
||
Команду выполнять из фактического клона `SDR_Rover_Video` на ноутбуке. Пути к
|
||
репозиторию, секрету и `hackrf_info.exe` надо подставить по результатам
|
||
инвентаризации ноутбука:
|
||
|
||
```powershell
|
||
python -B tools\rf001_rx_http_agent.py `
|
||
--server http://192.168.255.185:8765 `
|
||
--token-file <путь-к-rf001_http_token.txt> `
|
||
--agent-id user-pc `
|
||
--repository <путь-к-SDR_Rover_Video> `
|
||
--hackrf-info <путь-к-hackrf_info.exe> `
|
||
--hackrf-clock <путь-к-hackrf_clock.exe> `
|
||
--hackrf-transfer <путь-к-hackrf_transfer.exe> `
|
||
--once
|
||
```
|
||
|
||
Без `--once` агент работает постоянно. Этот ручной режим нужен только для
|
||
диагностики; при установленном автозапуске его не используют.
|
||
|
||
## Просмотр результата на основном ПК
|
||
|
||
```powershell
|
||
python -B tools\rf001_http_control.py status
|
||
```
|
||
|
||
Для завершённой инвентаризации сервер показывает состояние задания
|
||
`COMPLETE` или `FAILED`, фактические результаты команд и `rf_tx = 0`.
|
||
|
||
## Автономная проверка 100/100
|
||
|
||
После подтверждения одинакового HEAD на обоих компьютерах основной ПК ставит
|
||
фиксированное программное задание:
|
||
|
||
```powershell
|
||
python -B tools\rf001_http_control.py enqueue-offline-verify `
|
||
--agent-id user-pc `
|
||
--expected-head a94f2db4ee8377201a97263166cb09431df5f5d6
|
||
```
|
||
|
||
Агент сверяет ветку и HEAD чтением `.git`, поэтому `git.exe` для этой проверки
|
||
не нужен. Затем он запускает только два заранее заданных проектных сценария:
|
||
создание детерминированной RF-001A волны и её программное декодирование через
|
||
встроенный `legacy`-декодер, не требующий Numba. Рабочие
|
||
файлы сохраняются вне Git в `Documents\SDR_Rover_Runtime\offline_verify`.
|
||
Успех требует ровно 100 CRC-корректных и 100 точных пакетов. HackRF при этом не
|
||
открывается, `hardware_accessed = false`, `rf_tx = 0`.
|
||
|
||
## Подготовленный RX-захват
|
||
|
||
Сервер не примет постановку RX-захвата без точной локальной фразы
|
||
`AUTHORIZE RF RX`. До отдельного разрешения эту команду не выполнять:
|
||
|
||
```powershell
|
||
python -B tools\rf001_http_control.py enqueue-rx-capture `
|
||
--agent-id user-pc `
|
||
--expected-head a94f2db4ee8377201a97263166cb09431df5f5d6 `
|
||
--authorize-rx "AUTHORIZE RF RX"
|
||
```
|
||
|
||
Агент перед захватом проверяет:
|
||
|
||
- ветку и HEAD без обязательного наличия `git.exe`;
|
||
- отсутствие конфликтующих SDR-процессов;
|
||
- серийный номер RX `000000000000000097c063dc2e5327d3`;
|
||
- строку `clock signal detected` от `hackrf_clock.exe`;
|
||
- не менее `6000000000` свободных байт;
|
||
- неизменность частоты `1400000000` Гц, частоты дискретизации `8000000`
|
||
отсчётов/с, LNA/VGA `24/24` дБ, выключенных RF amp и antenna bias.
|
||
|
||
Для ручного барьера TX захват длится 300 секунд: `2400000000` комплексных
|
||
отсчётов, ожидаемый файл `4800000000` байт. Значение 20 секунд в исходном
|
||
профиле проверяется как часть исходных данных, но управляющий контур расширяет
|
||
окно до 300 секунд. Это зафиксировано в журнале сеанса. Автоматического повтора
|
||
нет.
|
||
|
||
Фоновый процесс запускает фиксированный Python-runner. Старый
|
||
`capture_process_runner.ps1` и Git watcher-ы не изменены и остаются резервом.
|
||
|
||
Состояние конкретного сеанса запрашивается отдельной фиксированной задачей:
|
||
|
||
```powershell
|
||
python -B tools\rf001_http_control.py enqueue-rx-status `
|
||
--agent-id user-pc `
|
||
--session-id <SESSION_ID>
|
||
```
|
||
|
||
TX разрешён только при свежем результате `READY`, открытом окне захвата и после
|
||
точной отдельной фразы `AUTHORIZE RF TX`. В сетевом протоколе операции TX нет.
|
||
|
||
После завершения захвата сервер может поставить потоковое декодирование с тремя
|
||
фактическими временами. Отчёт и IQ остаются на RX-ноутбуке вне Git.
|
||
|
||
## Текущее продолжение
|
||
|
||
На RX-ноутбуке работает версия агента `2026.09.29.2`; штатное обновление через
|
||
подписанный HTTP-канал и полный `STAIRCASE_SEARCH` завершены. Дальнейшая работа
|
||
с существующим сеансом является только потоковой офлайн-диагностикой сохранённой
|
||
IQ-записи. Новый RX-захват и любой RF TX требуют нового отдельного разрешения;
|
||
разрешения, использованные в завершённом сеансе, повторно не применяются.
|