Files
Pluto-SDR/README.md
2026-07-10 16:39:04 +03:00

16 KiB
Raw Blame History

pluto-link — OFDM-радиолиния между двумя PlutoSDR (весь код на ARM внутри Pluto)

Цифровая радиолиния точка-точка на базе двух ADALM-Pluto+ (клон, Zynq-7020). PHY: OFDM (liquid-dsp) + RS(255,223) FEC. Весь сигнальный тракт исполняется на ARM Cortex-A9 внутри Pluto; хост-ПК нужен только для сборки, заливки бинарей и снятия логов. Прототип в рамках ОКР «R-Link» (двухранговая самоорганизующаяся сеть); данный репозиторий покрывает уровень PHY/линка.


1. Аппаратная платформа (фактическая, проверено)

Параметр Значение Как проверено
Плата Pluto+ (клон, Zynq-7020, AD9361) cat /proc/cpuinfo — 2 ядра
CPU 2 × Cortex-A9 @ 667 МГц, NEON Features: ... neon vfpv3
RAM 1 ГБ (доступно ~1000 МБ) free
Прошивка v0.38-4-g95aad-dirty баннер SSH
Доступ SSH root@192.168.2.1 (пароль analog), USB-Ethernet

⚠️ Стоковый ADALM-Pluto (7010, 512 МБ, 1 ядро) — другая плата; все бюджеты CPU в этом документе рассчитаны для Pluto+. Не путать.

⚠️ В прошивке v0.38 нет sftp-server → заливать файлы только scp -O (legacy-протокол).

2. Бюджет CPU — главное ограничение проекта

Замерено офлайн-бенчмарком src/ofdm_bench.c прямо на ARM Pluto (одно ядро, static, -O3 -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=hard -ffast-math, liquid-dsp 1.8.0 с FFTW/NEON — проверено по HAVE_LIBFFTW3F в config.log):

Режим ofdmflexframesync Пропускная способность Вывод
Поиск преамбулы (шум) 5.32 Msps дежурный режим, не лимитирует
Приём кадров (duty ~57%) 2.34 Msps узкое место

Следствия (зафиксированы как проектные решения):

  • Рабочий sample rate = 1.92 MSPS. Запас 1.22× на ядре синхронизатора; второе ядро — под IIO, RS-декод, ввод-вывод.
  • 3.84 MSPS запрещён: система работает на редких пакетах и лавинообразно разваливается под нагрузкой (0.61× от realtime). Это худший вид отказа — «на демо работало».
  • Полезная скорость ≈ 1.31.5 Мбит/с (~160190 КБ/с). Файл 10 МБ ≈ 1 мин.
  • Параметры замера: M=64, CP=16, taper=4, QPSK, CRC32, payload 1275 Б. Полный протокол — docs/BENCHMARK.md. При любом изменении PHY-параметров бенчмарк перегоняется до изменения кода тракта.

3. Структура репозитория

pluto-link/
├── README.md                  # этот файл
├── CLAUDE.md                  # краткие правила для Claude Code
├── makefile                   # цели: all, tx, rx, bench, deploy, clean
├── toolchain.env              # CROSS=arm-linux-gnueabihf-, SYSROOT=$HOME/xarm
├── src/
│   ├── common.h               # параметры OFDM/RS, дефолты, прототипы
│   ├── common.c               # pluto_init (local:), pluto_configure
│   ├── transmitter.c          # stdin → OFDM → AD9361 TX
│   ├── receiver.c             # AD9361 RX → OFDM sync → stdout
│   └── ofdm_bench.c           # бенчмарк CPU (не удалять!)
├── scripts/
│   ├── build_deps.sh          # кросс-сборка FFTW + liquid-dsp → $HOME/xarm
│   ├── deploy.sh              # scp -O бинарей в /tmp обеих плат
│   └── test_file_link.sh      # e2e: файл → эфир → md5-сверка
└── docs/
    ├── BENCHMARK.md           # методика и результаты замеров CPU
    └── NOTES.md               # FDD, регуляторика, известные грабли

4. Среда разработки и сборка

4.1 Хост

Любой Linux; проверено на Raspberry Pi OS 64-bit (aarch64). Кросс-тулчейн:

sudo apt install -y build-essential autoconf automake libtool git wget \
                    gcc-arm-linux-gnueabihf

4.2 Зависимости (один раз, ~20 мин)

scripts/build_deps.sh собирает в $HOME/xarm статические armhf-библиотеки. Порядок важен: liquid зависит от FFTW и libfec, поэтому они собираются до него.

  1. FFTW 3.3.10: --enable-single --enable-neon --enable-static
  2. libfec (jgaeddert): Reed-Solomon для liquid. liquid не реализует RS сам — оборачивает libfec. Без неё fec_create(LIQUID_FEC_RS_M8) вернёт NULL и -c rs8 молча пойдёт БЕЗ FEC. Собирать строго ДО liquid.
  3. liquid-dsp 1.8.0: с CPPFLAGS=-I$HOME/xarm/include LDFLAGS=-L$HOME/xarm/lib и обходом autoconf-кросс-проблемы: ac_cv_func_malloc_0_nonnull=yes ac_cv_func_realloc_0_nonnull=yes
  4. libiio v0.25: только local backend (network/usb/xml/iiod отключены).
  5. libad9361-iio: FIR-фильтр для sample rate < 2.083 MSPS (ad9361_set_bb_rate). Без него AD9361 отвергает 1.92 MSPS (EINVAL).

После сборки liquid обязательно проверить, что подхвачены и FFTW, и libfec: grep -E "HAVE_LIBFFTW3F 1|HAVE_LIBFEC 1" config.logобе строки. Без FFTW liquid молча падает на встроенный FFT (бюджет CPU невалиден); без libfec нет RS.

⚠️ При обновлении зависимостей liquid надо пересобирать через make clean: его makefile не отслеживает зависимость от config.h, и configure+make без очистки лишь переустановят старую libliquid.a (без FEC). Проверка перед деплоем: arm-linux-gnueabihf-nm $XARM/lib/libliquid.a | grep -c init_rs_char → ненулевое = RS-символы libfec действительно связаны.

4.3 Ключевые решения по сборке

  • Только статическая линковка (-static). Причина: не зависим от libc прошивки Pluto, не таскаем .so, деплой = один файл. Цена +2 МБ — ничто при 1 ГБ RAM.
  • Флаги обязательны: -O3 -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=hard -ffast-math. Без NEON бюджет CPU из раздела 2 не выполняется.
  • Одной командой:
arm-linux-gnueabihf-gcc -O3 -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=hard \
  -ffast-math -static src/receiver.c src/common.c -o receiver \
  -I$HOME/xarm/include -L$HOME/xarm/lib \
  -lad9361 -liio -lliquid -lfec -lfftw3f -lm

Порядок библиотек критичен при статической линковке: зависимый идёт раньше (-lad9361 перед -liio, -lliquid перед -lfec).

4.4 Деплой

scp -O transmitter receiver root@192.168.2.1:/tmp/   # Pluto A
scp -O transmitter receiver root@<pluto_B>:/tmp/     # Pluto B

rootfs Pluto живёт в RAM: /tmp очищается при ребуте — это нормально, deploy.sh заливает заново.

5. Правила кода (конституция)

  1. Язык — C. Python/GNU Radio в тракте данных запрещены.
  2. Железо — только libiio, контекст local: (код исполняется на Pluto). Сетевые контексты ip: — только для отладочных утилит с хоста.
  3. Бюджет: считай, что у тебя одно ядро 667 МГц на DSP; второе занято IIO/FEC/IO. Любая новая нагрузка в тракте — сначала через ofdm_bench.
  4. Тяжёлые зависимости (FFmpeg, cJSON и т.п.) — запрещены. JSON руками, видео — raw UDP.
  5. Порядок отладки: кабель+аттенюатор → антенны на столе → дистанция. Никогда не отлаживать новый код сразу «в воздухе».
  6. Изменил PHY-параметры (M, CP, модуляция, rate) — перегони бенчмарк и обнови docs/BENCHMARK.md в том же коммите.

6. Параметры радиотракта

Параметр Значение Примечание
Sample rate 1 920 000 см. раздел 2; 3.84 MSPS запрещён
Полоса 1 500 000 ~0.78 × rate
Частота (симплекс-тест) 915 МГц обе платы на одной частоте
FDD (этап 2) A: TX 915/RX 868; B: TX 868/RX 915 разнос 47 МГц
TX gain (кабель) 40 дБ + аттенюатор 3040 дБ обязателен
TX gain (антенны) 20…0 дБ начинать с минимума
RX gain 50 дБ, manual фиксированный
OFDM M=64, CP=16, taper=4, QPSK liquid defaults + CRC32
FEC RS(255,223), payload ≤ 1024 Б 5 блоков на кадр

⚠️ FDD без дуплексеров: собственный TX 915 МГц глушит свой RX 868 МГц широкополосным шумом. Симптом: loopback работает, дуплекс в эфире — нет. Меры: max tx_attenuation, разнос/кросс-поляризация антенн, SAW-фильтр 868 МГц на RX. Подробнее — docs/NOTES.md.

⚠️ Регуляторика: 868 МГц — SRD-диапазон (ограничения полосы/мощности/ duty cycle), 915 МГц в регионе ETSI занят GSM-900 uplink. Работа — кабель или минимальная мощность на столе, без внешних усилителей.

7. Формат кадра

OFDM-кадр liquid: [преамбула][заголовок 12 Б][payload ≤ 1275 Б][CRC32]

Заголовок (12 байт):
 0-1   0xF0 0xAA        сигнатура
 2-5   seq (uint32 BE)  порядковый номер кадра
 6-7   len (uint16 BE)  длина исходных данных до FEC
 8-9   nblocks          число RS-блоков
 10    last_block_bytes хвост последнего блока (0 = полный)
 11    резерв

⚠️ Критично: у liquid заголовок пользователя по умолчанию 8 байт. Байты 811 передаются только после явного вызова на обеих сторонах:

ofdmflexframegen_set_header_len(fg, 12);   // TX
ofdmflexframesync_set_header_len(fs, 12);  // RX

Без этого RX читает hdr[10] за пределами буфера (маскируется обрезкой по original_len, но это мина).

8. Запуск

8.1 Передача файла Pluto→Pluto

scp -O test.bin root@192.168.2.1:/tmp/

# Pluto B — приёмник
ssh root@<pluto_B> '/tmp/receiver -f 915000000 -r 1920000 -b 1500000 -c rs8 \
    > /tmp/out.bin'

# Pluto A — передатчик (кабель: -g -40 + аттенюатор!)
ssh root@192.168.2.1 'cat /tmp/test.bin | /tmp/transmitter -f 915000000 \
    -r 1920000 -b 1500000 -g -40 -p 0 -c rs8'

# сверка
md5sum test.bin && ssh root@<pluto_B> 'md5sum /tmp/out.bin'

8.2 Бенчмарк CPU (перед любым изменением PHY)

scp -O ofdm_bench root@192.168.2.1:/tmp/
ssh root@192.168.2.1 '/tmp/ofdm_bench 1.92'
# критерий: "сигнал+кадры" ≥ 1.2× цели на одном ядре

9. Известные проблемы и техдолг

# Проблема Статус
1 Заголовок 12 Б vs 8 Б по умолчанию у liquid (раздел 7) чинить до передачи файлов
2 recovered[4096] в receiver при лимите nblocks≤100 (22 КБ) → переполнение стека на битом заголовке. Лимит должен быть 4096/RS_DATA = 18 чинить
3 fec_decode() liquid не сообщает о неисправимых RS-блоках → счётчик rs_saved фиктивен. Достоверный критерий — только CRC/md5 поверх данных знать; добавить CRC после декода
4 Внешний NCO-CFO в receiver: set_phase(0) на границах буферов рвёт фазу; ofdmflexframesync и так компенсирует CFO сам убрать или обосновать замером
5 TX пушит весь буфер 16384 сэмпла при кадре ~10300 → ~35% эфира впустую + паузы -p оптимизация: push ровно idx сэмплов
6 Нет ARQ: потерянный кадр = молчаливая дыра в файле. seq в заголовке есть, но RX его игнорирует минимум: детектор пропусков seq с логом
7 Файл reciever.c → переименовать в receiver.c (make его ждёт) тривиально

10. Дорожная карта

  1. Бенчмарк CPU на ARM Pluto → выбран rate 1.92 MSPS
  2. Перенос TX/RX на Pluto (local:), фиксы #1, #2, #7
  3. Файл через кабель + аттенюатор, md5 на 1 и 10 МБ
  4. Антенны на столе: замер скорости, PER, счётчик потерянных seq (#6)
  5. FDD 868/915 двусторонняя + меры из docs/NOTES.md
  6. UDP-туннель поверх линка; затем web-настройка (libmicrohttpd)
  7. Видео (raw UDP, пакеты ≤1472 Б) — при устойчивом PER

11. Диагностика (шпаргалка)

Симптом Первое, что проверить
scp: sftp-server not found использовать scp -O
RX молчит частоты TX/RX, -g TX, антенны/кабель, RX gain
CRC бьётся, кадры находятся SNR: снизить полосу, амплитуду TX (-a 0.15)
md5 не сходится, CRC ок потерянные кадры → смотреть разрывы seq (#6)
Работает на малом трафике, падает под нагрузкой CPU-лимит: rate > 1.92 MSPS? второй процесс на ядре 0?
Loopback ок, дуплекс в эфире нет самоглушение FDD (раздел 6)

Замеры и решения актуальны на 2026-07-10, прошивка v0.38, liquid-dsp 1.8.0.