Доработка документации
All checks were successful
01-smoke / smoke (push) Successful in 0s
02-guardrails / guardrails (push) Successful in 0s
03-gftest / gftest (push) Successful in 6s
04-cross-build / cross-build (push) Successful in 3s

This commit is contained in:
Maxim
2026-07-17 10:16:10 +03:00
parent d0a82bd92d
commit 7f508b6fd7
3 changed files with 156 additions and 3 deletions

View File

@@ -143,7 +143,11 @@ group-FEC слоя, а не переписывание протокола с н
group-FEC-ом, ARQ там принципиально недоступен — так и задумано.
2. Адаптивная модуляция (QPSK↔16QAM) — измеримый шаг к паритету по
«auto link adaptation» с минимальными изменениями кода.
3. **§10 п.8** (UDP-туннель) — переводит pluto-link из «демо передачи файла»
в «сетевой линк», ближе к тому, как позиционируются оба конкурента.
3. **§10 п.8** (UDP-туннель) — **выполнено 2026-07-17** (README §12.10).
Переводит pluto-link из «демо передачи файла» в «сетевой линк», ближе к
тому, как позиционируются оба конкурента. `udp_gw` — record-слой поверх
существующего байтового потока, ядро тракта не изменено ни строкой.
Радио-смоук на антеннах (два прогона): md5 == байт-в-байт, 0 дропов/
ресинков/дыр. Web-настройка (хвост п.8) осталась нереализованной.
4. Решение о горизонте 2 (перенос PHY в FPGA) принимать по факту завершения
пп. 79 roadmap — раньше данных для оценки трудоёмкости недостаточно.

View File

@@ -85,3 +85,105 @@ A → эфир → B и сверяет md5.
Более глубокая диагностика линка — README §11; журнал этапов и рабочие точки —
README §12; бюджет CPU и модель паузы — [BENCHMARK.md](BENCHMARK.md).
## 6. UDP-туннель (`udp_gw`)
Прозрачный UDP-шлюз поверх линка (README §10 п.8, §12.10): вместо файла через
`cat`/`>` в тракт можно завести живой UDP-трафик. Ядро (transmitter/receiver)
не меняется — `udp_gw` инкапсулирует датаграммы в самосинхронизирующиеся
рекорды (8 Б оверхеда) поверх того же байтового потока.
### Топология: кто где слушает
RPi-хост в этой схеме НЕ часть тракта данных — он только запускает команды по
ssh и (в тесте) заливает/забирает файл по scp. Порты 6000/6001 — `127.0.0.1`
**на каждой плате Pluto своя, отдельная**, а не адрес хоста: пакет от
`udp_gw -l 6000` на плате A никогда не покидает саму плату A, пока не выйдет
в эфир через `transmitter`; так же на B.
```
================== Pluto A (TX) — 192.168.2.1 ===================
внешний источник UDP на плате A
(в scripts/test_udp_tunnel.sh — это /tmp/probe_in.bin через
udp_probe send, запущенный по ssh НА ЭТОЙ ЖЕ плате)
|
| UDP -> 127.0.0.1:6000
v (localhost ПЛАТЫ A, не RPi-хоста!)
udp_gw -l 6000
| stdout — рекорды (magic D5 5D + len/id/hcrc8)
v
transmitter
-f 915e6 -r 1920000 -b 1500000
-g -20 -p 3000 -c rs8 -G -u local:
|
v
===================================================================
ЭФИР 915 МГц, симплекс
===================================================================
|
v
receiver
-f 915e6 -r 1920000 -b 1500000
-c rs8 -u local:
| stdout — рекорды
v
udp_gw -d 127.0.0.1:6001
| UDP -> 127.0.0.1:6001
v (localhost ПЛАТЫ B, не RPi-хоста!)
внешний приёмник UDP на плате B
(в scripts/test_udp_tunnel.sh — это udp_probe recv, запущенный по
ssh НА ЭТОЙ ЖЕ плате, пишет в /tmp/probe_out.bin)
================== Pluto B (RX) — 192.168.3.1 ===================
```
Если источник/приёмник UDP — не тестовый `udp_probe`, а реальный внешний
прибор, порт `-l`/`-d` слушает не только `127.0.0.1`, а нужный интерфейс
платы (или `0.0.0.0`) — тогда трафик действительно приходит извне платы A
и уходит наружу с платы B, а не варится в loopback, как в тесте.
Пример (симплекс, антенны, те же флаги линка, что и в §3):
```bash
# B — приёмник: расшифровать поток в UDP на 127.0.0.1:6001
ssh root@192.168.3.1 \
'/tmp/receiver -f 915000000 -r 1920000 -b 1500000 -c rs8 -u local: \
| /tmp/udp_gw -d 127.0.0.1:6001'
# A — передатчик: слушать UDP на :6000, завернуть в линк
ssh root@192.168.2.1 \
'/tmp/udp_gw -l 6000 \
| /tmp/transmitter -f 915000000 -r 1920000 -b 1500000 -g -20 -p 3000 -c rs8 -G -u local:'
```
Автотест по этой схеме (генерирует файл, гоняет через оба шлюза, сверяет md5):
**`TXGAIN=-20 scripts/test_udp_tunnel.sh`** — на антеннах правило №7
(кабель+аттенюатор) не применяется: шлюз не трогает PHY, его логика проверена
`make gwtest` + loopback (README §12.10).
**Потолок и семантика.** Вход быстрее линка (~107 КБ/с на `-G -p 3000`) —
`udp_gw -l` дропает датаграммы с ростом счётчика в stderr (не блокирует
приём — так и задумано для UDP). У шлюза нет EOF-протокола: завершение —
`kill`/`SIGTERM`, не END-хендшейк (тот остаётся фичей файловых передач).
| Что нужно | Профиль | Свойства |
|---|---|---|
| Надёжность, задержка не критична | FDD (`-F`) + `-G` | 0 потерь (ARQ добирает то, что не закрыл паритет), HOL-задержка на ретрансмите |
| Ровная задержка, редкие потери ок | симплекс + `-G` | GF 16+2 закрывает ≤2 стёртых кадра/группу без обратного канала; сверх этого — дыра в id, поток не рвётся |
Диагностика по счётчикам `udp_gw` (печатаются в stderr раз в секунду):
| Счётчик | Где | Значит |
|---|---|---|
| `дропов` | `-l` (вход) | вход быстрее линка — контрактный дроп, подними паузу между датаграммами у источника |
| `негабарит` | `-l` (вход) | датаграмма > 1472 Б — уменьшить MTU источника |
| `ресинк` | `-d` (выход) | дыра в потоке (радиопотеря сверх FEC) — парсер откатился к следующему валидному magic+hcrc8 |
| `bad_crc` | `-d` (выход) | ложный magic в данных, отсеян по hcrc8 — не баг, штатная защита рескана |
| `дыр(id)` | `-d` (выход) | сколько id пропущено — оценка объёма потерь на симплексе |
`udp_probe send/recv` — тестовый генератор/приёмник UDP для бринг-апа
(busybox-прошивка без `nc`/`socat`, Python на хосте запрещён правилом №1):
`udp_probe send ip:port РАЗМЕРАНКААУЗА_МС]` (stdin → UDP),
`udp_probe recv ПОРТ ТАЙМАУТРОСТОЯ_С` (UDP → stdout, самозавершается по
простою — таймер тикает с запуска процесса, не с первой датаграммы).