Files
Pluto-SDR/docs/BENCHMARK.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

4.8 KiB
Raw Blame History

BENCHMARK.md — протокол замеров CPU (ofdmflexframesync на ARM Pluto)

Этот документ — основание для выбора sample rate. При любом изменении PHY-параметров (M, CP, модуляция, payload) бенчмарк перегоняется и таблица обновляется в том же коммите (правило №6 CLAUDE.md).

Зачем

ofdmflexframesync (liquid-dsp) — самый дорогой блок тракта. Если его пропускная способность на одном ядре ниже sample rate, приёмник отстаёт от эфира: IIO-буферы какое-то время амортизируют, затем переполняются и кадры теряются лавинообразно. Опасность в том, что на редких пакетах система выглядит работающей — отказ проявляется только под нагрузкой.

Методика

Инструмент: src/ofdm_bench.c. Генерирует эталонный буфер (8 кадров + гэпы, duty cycle кадров ~57%), делает int16-версии «сигнал+шум» (SNR ~28 дБ) и «чистый шум», прогоняет каждый через синхронизатор ≥5 с. Конвертация int16→float входит в замер (как в receiver.c). Процесс пинится на ядро 0 (sched_setaffinity).

Меряются два режима, лимитирует худший:

  • шум — стоимость дежурного поиска преамбулы (>90% времени в линии);
  • сигнал+кадры — полный цикл демодуляции под нагрузкой.

Валидация замера: CRC ok должен равняться 8 × число проходов (все сгенерированные кадры пойманы и декодированы). Иначе сборка кривая и цифрам верить нельзя.

Окружение эталонного замера

Параметр Значение
Дата 2026-07-10
Плата Pluto+ (Zynq-7020), 2 × Cortex-A9 @ 667 МГц, NEON, 1 ГБ
Прошивка v0.38-4-g95aad-dirty
liquid-dsp 1.8.0, с FFTW 3.3.10 (NEON)HAVE_LIBFFTW3F 1 в config.log
Сборка -O3 -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=hard -ffast-math -static
Кадр M=64, CP=16, taper=4, QPSK, CRC32, FEC none, payload 1275 Б, header 8 Б
Ядро одно (affinity CPU0)

Результаты

буфер: 143360 сэмплов, 8 кадров на проход

шум (поиск):     5.3185.324 Msps   (186 проходов; кадров 0 — норма)
сигнал+кадры:    2.3382.339 Msps   (82 прохода; 656/656 CRC ok — валидно)

Разброс между тремя запусками < 0.1% — замер чистый.

Выводы (проектные решения)

Sample rate Запас (кадры) Вердикт
3.84 MSPS 0.61× ЗАПРЕЩЁН: не realtime, отказ под нагрузкой
1.92 MSPS 1.22× рабочий: второе ядро свободно под IIO/RS/IO
1.00 MSPS 2.34× резерв, если 1.92 окажется впритык в реальном тракте
  • Полезная скорость при 1.92 MSPS: ~1.31.5 Мбит/с (≈160190 КБ/с) после RS(255,223), преамбул и служебки.
  • Поиск на шуме (5.32 Msps) не лимитирует ни один из режимов.
  • RS-декод в замер не входит: его стоимость мала относительно синхронизатора и планируется на второе ядро.

Неисследованные резервы (если понадобится > 1.92 MSPS)

  1. M=32/CP=8 — дешевле FFT и эквалайзер; цена: выше доля пилотов, чувствительнее к CFO. Прогнать бенчмарк с этими параметрами.
  2. Двухпоточный конвейер — IIO-refill + int16→float на ядре 1, синхронизатор на ядре 0: ~+1015% и убирает джиттер refill.
  3. Сравнение NEON vs VFP — пересборка без -mfpu=neon (с -mfpu=vfpv3) даёт цифру вклада векторизации для отчёта.

Как воспроизвести

make bench
scripts/deploy.sh bench <ip_платы>
ssh root@<ip_платы> '/tmp/ofdm_bench 1.92'
# критерий приёмки: "сигнал+кадры" ≥ 1.2× цели на одном ядре