Files
Pluto-SDR/docs/USAGE.md
Maxim 7f508b6fd7
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
Доработка документации
2026-07-17 10:16:10 +03:00

190 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# USAGE.md — руководство пользователя pluto-link
Практический быстрый старт к [README.md](../README.md). README — инженерный
журнал (бюджет CPU, история отладки, §12-этапы, техдолг); этот файл — короткий
путь «склонировал → собрал → залил → поднял приём/передачу». Детали и обоснования
цифр — в README §12 и [BENCHMARK.md](BENCHMARK.md).
## 1. Что это
Радиолиния точка-точка на двух ADALM-Pluto+ (клон, Zynq-7020, 2 × Cortex-A9).
Весь сигнальный тракт (OFDM + RS(255,223) FEC) исполняется на ARM **внутри**
Pluto; хост (Raspberry Pi, aarch64) нужен только для кросс-сборки, заливки
бинарей и снятия логов. Рабочая точка: симплекс 915 МГц, ~120 КБ/с (legacy) или
~95 КБ/с с групповым FEC 16+2 (`-G`), передача файлов байт-в-байт.
## 2. Быстрый старт (5 команд)
На свежем клоне репозитория (на хосте — Raspberry Pi):
```bash
./scripts/build_deps.sh # 1. один раз: кросс-либы в ~/xarm
make # 2. собрать tx/rx/bench → build/
make deploy PLUTO_A=192.168.2.1 PLUTO_B=192.168.3.1 # 3. scp -O на обе платы в /tmp
ssh root@192.168.3.1 '/tmp/receiver -f 915000000 -r 1920000 -b 1500000 -c rs8 -u local: > /tmp/out.bin' # 4. приём (B)
ssh root@192.168.2.1 'cat /tmp/file.bin | /tmp/transmitter -f 915000000 -r 1920000 -b 1500000 -g -30 -p 4000 -c rs8 -G -u local:' # 5. передача (A)
```
Или всё сразу: **`scripts/test_file_link.sh`** — генерит тестовый файл, гоняет
A → эфир → B и сверяет md5.
- Пароль root на Pluto — `analog`. `apt install sshpass` убирает ручной ввод
пароля (скрипты подхватывают его автоматически).
- rootfs платы в RAM: после перезагрузки Pluto бинари из `/tmp` пропадают —
залить заново (`make deploy`).
- ⚠ Перед эфиром отлаживай по кабелю с аттенюатором 3040 дБ и TX gain 40
(`-g -40`), только потом антенны и дистанция (правило №7 CLAUDE.md).
## 3. Типовые команды
| Сценарий | Команда |
|---|---|
| Собрать всё | `make` |
| Залить на обе платы | `make deploy` (или `scripts/deploy.sh <ipA> <ipB>`) |
| E2E-тест с md5-сверкой | `scripts/test_file_link.sh` |
| Короткий файл ≤1 МБ | TX `-p 3000` (без `-G`) |
| Длинная / соук-передача | TX `-p 4000 -G` (групповой FEC 16+2, README §12.7) |
| Первый эфир по кабелю | `TXGAIN=-40 scripts/test_file_link.sh` |
| OTA на антенны | `TXGAIN=-20 scripts/test_file_link.sh` |
| Соук 10 МБ | `GROUPFEC=1 PAUSE=4000 scripts/test_file_link.sh <ipA> <ipB> 10240` |
| Бенчмарк PHY на плате | `make bench && scripts/deploy.sh bench <ip> && ssh root@<ip> '/tmp/ofdm_bench 1.92'` |
| Без FEC (отладка PHY) | `-c none` на обеих сторонах |
## 4. Параметры
Флаги передатчика: `-f -r -b -g -a -p -u -c -G`. Приёмник понимает только общие:
`-f -r -b -u -c`. Колонка «Сторона» отмечает передатчик-специфичные (`-g/-a/-p/-G`).
| Флаг | Значение | По умолч. | Сторона | Примечание |
|---|---|---|---|---|
| `-f` | несущая, Гц | 915000000 | TX + RX | одинаково на обеих |
| `-r` | sample rate, Гц | 1920000 | TX + RX | **не поднимать** — бюджет CPU (правило №5, BENCHMARK.md) |
| `-b` | полоса, Гц | 1500000 | TX + RX | — |
| `-g` | TX gain, дБ | 30 | TX | кабель 40, антенны от 20 |
| `-a` | амплитуда сигнала | 0.20 | TX | обычно не трогать |
| `-p` | пауза между кадрами, мкс | 2000 | TX | ≤1 МБ → 3000; длинные / `-G`**4000** (§12.7) |
| `-c` | FEC: `rs8` \| `none` | rs8 | TX + RX | должно совпадать на обеих сторонах |
| `-G` | групповой FEC 16+2 | выкл | TX | RX автодетект по флагам в hdr[11] |
| `-u` | URI libiio | — | TX + RX | в тракте всегда `local:` (правило №2) |
| `-h` | справка | — | TX + RX | — |
## 5. Troubleshooting
| Симптом | Вероятная причина | Что делать |
|---|---|---|
| `out.bin` = 0 байт | RX ничего не поймал: частота / усиление / кабель | сверь `-f`, TX gain, аттенюатор или антенны |
| размер меньше исходного | потерянные кадры (симплекс, ARQ нет) | включи `-G`; смотри разрывы seq в `rx.log` |
| размер равен, md5 не сходится | header_len ≠ 12 (техдолг #1) или fec_decode (#3) | 12-байт заголовок на обеих сторонах; проверь `-c` |
| много `Overrun` в `rx.log` | ядро 0 срывается под нагрузкой | подними `-p` (3000 → 4000), см. §12.7 |
| 10 МБ-соук падает на `-p 3000` | overrun-пачки рвут стирающий FEC | только `-p 4000` — проверенная точка |
| `Invalid argument (22)` на rate | AD9361 без FIR не берёт 1.92 Msps | нужен FIR через `ad9361_set_bb_rate` (уже в коде) |
| `Reed-Solomon codes unavailable` | liquid собран без libfec | пересобрать liquid (`rm libliquid.a`, затем `make`) |
| `scp` виснет / `subsystem request failed` | в прошивке v0.38 нет sftp-server | только `scp -O` (deploy.sh уже так делает) |
| после ребута Pluto бинарей нет | rootfs в RAM, `/tmp` очищается | `make deploy` заново |
| ssh каждый раз просит пароль | не установлен sshpass | `apt install sshpass` (пароль `analog`) |
Более глубокая диагностика линка — 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, самозавершается по
простою — таймер тикает с запуска процесса, не с первой датаграммы).