13 KiB
USAGE.md — руководство пользователя pluto-link
Практический быстрый старт к README.md. README — инженерный журнал (бюджет CPU, история отладки, §12-этапы, техдолг); этот файл — короткий путь «склонировал → собрал → залил → поднял приём/передачу». Детали и обоснования цифр — в README §12 и 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):
./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). - ⚠ Перед эфиром отлаживай по кабелю с аттенюатором 30–40 дБ и 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.
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):
# 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, самозавершается по
простою — таймер тикает с запуска процесса, не с первой датаграммы).