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

13 KiB
Raw Blame History

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).
  • ⚠ Перед эфиром отлаживай по кабелю с аттенюатором 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; длинные / -G4000 (§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, самозавершается по простою — таймер тикает с запуска процесса, не с первой датаграммы).