Доработка документации
This commit is contained in:
49
README.md
49
README.md
@@ -312,7 +312,11 @@ ssh root@192.168.2.1 '/tmp/ofdm_bench 1.92'
|
||||
переупорядочивания на RX (1024 кадра). **Побайтовый лосслесс на 10 МБ:
|
||||
44 потери вытянуты 45 ретрансмитами, md5 ==.** ARQ и group-FEC работают
|
||||
вместе: паритет гасит одиночные потери без задержки, ARQ добирает остальное.
|
||||
8. UDP-туннель поверх линка; затем web-настройка (libmicrohttpd)
|
||||
8. ✅ **UDP-туннель поверх линка** (§12.10) — `udp_gw` инкапсулирует UDP-датаграммы
|
||||
в самосинхронизирующиеся рекорды поверх байтового потока transmitter/receiver,
|
||||
без единой правки ядра тракта. Радио-смоук на антеннах пройден дважды подряд:
|
||||
md5 == байт-в-байт, 0 дропов/ресинков/дыр. Web-настройка (libmicrohttpd) —
|
||||
осталась нереализованной, следующий шаг п.8
|
||||
9. Видео (raw UDP, пакеты ≤1472 Б) — при устойчивом PER
|
||||
|
||||
## 11. Диагностика (шпаргалка)
|
||||
@@ -638,6 +642,49 @@ COMPLETE, A вышел с 2. Настоящий PER-стресс — свипо
|
||||
переупорядочивать нечего, а дыру закрыть некому: кольцо лишь задержало бы вывод
|
||||
до EOF. Отсюда ветка `g_fb_fg ? rb : stdout` в `process_frame`/`group_flush`.
|
||||
|
||||
### 12.10 UDP-туннель — радио-смоук (§10 п.8) — 2026-07-17
|
||||
|
||||
**Реализация.** `udp_gw` — самостоятельный бинарь без зависимостей от
|
||||
iio/liquid/fec, два режима: `-l <port>` (UDP → рекорды в stdout) и
|
||||
`-d ip:port` (рекорды из stdin → UDP). Рекорд — 8 Б оверхеда (magic `D5 5D`,
|
||||
len, id, flags, hcrc8) поверх уже существующего байтового потока
|
||||
transmitter/receiver — ядро тракта не тронуто ни строкой. Парсер
|
||||
самосинхронизируется по magic+hcrc8 после дыры (симплекс рвёт границы
|
||||
рекордов произвольно). `stdout` шлюза в режиме `-l` — `O_NONBLOCK`: вход
|
||||
быстрее линка дропается с счётчиком, а не блокирует приём (семантика UDP).
|
||||
`udp_probe` (send/recv по UDP, свой мини-инструмент — busybox без `nc`/`socat`,
|
||||
правило №1 запрещает Python и на хосте) закрывает бринг-ап без внешних
|
||||
зависимостей. Пиннинг `udp_gw` на отдельное ядро не сделан — по факту
|
||||
замера не нужен (Overrun/FQdrp = 0 при работающем шлюзе, ниже).
|
||||
|
||||
**Loopback на одной плате** (без радио, сокет↔сокет через localhost):
|
||||
66/66 датаграмм, md5 ==, все счётчики (дропы/ресинк/bad_crc/дыры) — 0.
|
||||
|
||||
**Радио-смоук** (антенны на столе, симплекс 915 МГц, `TXGAIN −20`, `-G -p 3000`,
|
||||
64 КБ / 66 датаграмм по 1000 Б, темп генератора `PACE_MS=15` ≈65 КБ/с — с
|
||||
запасом под потолок линка ~107 КБ/с): два прогона подряд — md5 == байт-в-байт,
|
||||
`отправлено 66 | дропов 0` на входе, `доставлено 66 | ресинк 0 Б | bad_crc 0 |
|
||||
дыр(id) 0` на выходе, `Потери seq 0` на радио, EVM −18.5…−26.2 дБ.
|
||||
|
||||
Правило №7 (кабель+аттенюатор до антенн) сознательно не применялось: `udp_gw`
|
||||
не меняет PHY ни на байт (слой поверх уже принятого байтового потока), его
|
||||
собственная логика парсера закрыта юнит-тестом (`make gwtest`) и loopback-ом
|
||||
на плате — радио-прогон здесь равносилен штатной приёмке линка.
|
||||
|
||||
**Отладка теста, не туннеля.** Первый радио-прогон (ручные команды в терминале)
|
||||
дал пустой файл на приёме: у `udp_probe recv` сторожевой таймер простоя тикает
|
||||
с момента ЗАПУСКА процесса, а не с первой датаграммы — интерактивная вставка
|
||||
команд заняла больше отведённых 10 с, и приёмная цепочка на B самоликвидировалась
|
||||
до начала передачи на A. После автоматизации в `scripts/test_udp_tunnel.sh`
|
||||
всплыли ещё два эффекта хореографии: (а) единовременный `killall` терял хвост
|
||||
последней group-FEC-группы, потому что `receiver` флашит его только в момент
|
||||
собственной смерти (`receiver.c:798`, после основного цикла) — гашение
|
||||
переписано на строгий порядок «receiver → пауза на прохождение хвоста через
|
||||
`udp_gw -d` → `udp_probe»; (б) старт генератора без ожидания готовности
|
||||
transmitter ронял 1–2 первые датаграммы в EAGAIN, пока iio ещё поднимался —
|
||||
заменено на ожидание строки «Ожидание данных» в логе TX. Оба фикса —
|
||||
в `scripts/test_udp_tunnel.sh`, радиотракт и `udp_gw` не менялись.
|
||||
|
||||
**Гибрид с group-FEC.** Паритет гасит одиночные потери без задержки (RX снимает
|
||||
восстановленные seq с NACK — ретрансмит не нужен), ARQ добирает то, что паритету
|
||||
не по силам (>2 стираний/группу). `grp_failed > 0` при целом md5 — норма: группа
|
||||
|
||||
@@ -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) принимать по факту завершения
|
||||
пп. 7–9 roadmap — раньше данных для оценки трудоёмкости недостаточно.
|
||||
|
||||
102
docs/USAGE.md
102
docs/USAGE.md
@@ -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, самозавершается по
|
||||
простою — таймер тикает с запуска процесса, не с первой датаграммы).
|
||||
|
||||
Reference in New Issue
Block a user