Files
Pluto-SDR/README.md
Maxim 0f207b146d Initial commit: OFDM-радиолиния на двух Pluto+ (PHY/link, код на ARM)
- src: transmitter, receiver, ofdm_bench, common (libiio local: + liquid-dsp)
- scripts: кросс-сборка зависимостей, деплой scp -O, e2e-тест с md5
- docs: бенчмарк CPU (выбор rate 1.92 MSPS), грабли FDD/регуляторика
- все текстовые файлы нормализованы в LF (.gitattributes принудительно):
  CRLF в toolchain.env/makefile ломал source и make на Linux
- .vscode: IntelliSense в режиме linux-gcc-arm, заголовки из .xarm/include

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 11:00:38 +03:00

246 lines
14 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.

# 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). Кросс-тулчейн:
```bash
sudo apt install -y build-essential autoconf automake libtool git wget \
gcc-arm-linux-gnueabihf
```
### 4.2 Зависимости (один раз, ~15 мин)
`scripts/build_deps.sh` собирает в `$HOME/xarm` статические armhf-библиотеки:
1. **FFTW 3.3.10**: `--enable-single --enable-neon --enable-static`
2. **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`
После сборки liquid **обязательно** проверить, что FFTW подхвачен:
`grep HAVE_LIBFFTW3F config.log` → должно быть `1`. Без FFTW liquid молча
падает на встроенный FFT и цифры бюджета CPU невалидны.
### 4.3 Ключевые решения по сборке
- **Только статическая линковка** (`-static`). Причина: не зависим от libc
прошивки Pluto, не таскаем .so, деплой = один файл. Цена +2 МБ — ничто
при 1 ГБ RAM.
- **Флаги обязательны**: `-O3 -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=hard
-ffast-math`. Без NEON бюджет CPU из раздела 2 не выполняется.
- Одной командой:
```bash
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 -lliquid -lfftw3f -lm
```
### 4.4 Деплой
```bash
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 передаются только после явного вызова на обеих сторонах:
```c
ofdmflexframegen_set_header_len(fg, 12); // TX
ofdmflexframesync_set_header_len(fs, 12); // RX
```
Без этого RX читает `hdr[10]` за пределами буфера (маскируется обрезкой
по `original_len`, но это мина).
## 8. Запуск
### 8.1 Передача файла Pluto→Pluto
```bash
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)
```bash
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.*