Files
radar_system/data_acq_and_processing/gpr_cpp
2026-09-21 13:39:04 +03:00
..
2026-09-21 13:39:04 +03:00
2026-09-21 13:39:04 +03:00
2026-09-21 13:39:04 +03:00
2026-09-21 13:39:04 +03:00
2026-09-21 13:39:04 +03:00
2026-09-21 13:39:04 +03:00
2026-09-21 13:39:04 +03:00

gpr_cpp — C++/CUDA счёт-ядро MIMO GPR 3D SAR

Вендорено из репозитория VNA_AlGo (gpr_cpp/, коммит 544dc59); python-биндинги (bindings/, pybind11) при переносе не взяты — сборка здесь только C++ (GPR_BUILD_PYTHON=OFF). Собирается из корневого Makefile radar_system (цель $(GPR_CPP_LIB) вызывает build_cmake.sh).

Порт вычислительного ядра из Python/CuPy (gpr_core) в самостоятельную C++/CUDA библиотеку для интеграции в C++ проект. Алгоритм — см. ALGORITHM.md в репозитории VNA_AlGo.

Что внутри (граница C++ ↔ Python)

В C++ (libgpr_core_cpp): геометрия, препроцессинг A-сканов (bg-subtract, фазовая коррекция движения, k0-вставка + Ханн + IFFT через PocketFFT, нормировка), 3D SAR апертурный объём (одно CUDA-ядро на всю апертуру). Возвращает сырые acc(complex64)/cnt(float32).

Остаётся в Python: загрузка .npy, финализация объёма (abs+gate+gaussian+ нормировка), CLEAN-детекция, трек, оценка скорости, plotly.

Ключевое решение: CUDA без nvcc (NVRTC + Driver API)

На целевых машинах (x86+T600, Jetson) нет полного CUDA toolkit — только runtime + NVRTC (как у cupy). Поэтому ядро не компилируется nvcc'ом: его исходник встроен строкой (src/kernel_src.hpp), компилируется в рантайме через NVRTC в cubin под точную арх устройства (запрос compute capability → sm_XY, без JIT PTX), грузится и запускается через CUDA Driver API. Хост-часть собирается обычным g++. Плюс: без toolkit и без привязки к SM — один код для x86 (sm_75) и Jetson (sm_87).

Сборка

bash build_cmake.sh            # авто-детект pip CUDA-путей; собирает всё
# артефакты в build/: libgpr_core_cpp.a, _gpr_cpp*.so, standalone_bp

Требуется: g++ ≥13, cmake ≥3.24, pybind11 (pip install pybind11), pip-пакеты nvidia-* (nvrtc+cudart, идут с cupy), системный libcuda. rpath на каталог libnvrtc прописан — бинарники/модуль грузятся без LD_LIBRARY_PATH.

Публичный C++ API (include/gpr/)

#include "gpr/config.hpp"    // GeometryConfig/BPConfig/MotionConfig/SARConfig (дефолты = config.py)
#include "gpr/ascan.hpp"     // preprocess_snapshot(...) -> AScanSet
#include "gpr/sar3d.hpp"     // SarEngine::aperture_volume(frames, grid) -> VolumeRaw{acc,cnt}
#include "gpr/geometry.hpp"  // frame_offsets, frame_weights, build_grid_scene, point_to_scene

gpr::SarEngine engine(geom, bp, mot, sar);          // компилит ядро под устройство
auto vol = engine.aperture_volume(frames, grid);    // один запуск ядра на апертуру
// vol.acc / vol.cnt — сырые; финализацию (gaussian/normalize) делает вызывающий

Пример без Python: examples/standalone_bp.cpp — грузит S21 из .npy, строит объём, печатает максимум.

./build/standalone_bp <preprocessed_dir> 0038_id39_ns11361438137486

Python-модуль

import _gpr_cpp                     # из gpr_cpp/build
eng = _gpr_cpp.SarEngine(_gpr_cpp.GeometryConfig(), _gpr_cpp.BPConfig(),
                         _gpr_cpp.MotionConfig(), _gpr_cpp.SARConfig())
a = _gpr_cpp.preprocess_snapshot(s21, freq, background, mot, geom, bp, True)  # -> AScanSet
acc, cnt = eng.aperture_volume([(a, dx, dy, dz, w), ...], gx, gy, gz)         # numpy

Паритет (проверено на x86 T600)

Этап Порог Факт
config дефолты vs dataclass точно OK
A-сканы raw H vs numpy (float64) <1e-9 1.7e-12
A-сканы норм. H (FFT-ULP через медиану) <1e-4 2.5e-6
3D объём C++ ядро vs cupy (float32) <5e-3 3e-7, argmax идентичен

Тесты: ../tests/test_parity_cpp.py (C++ vs cupy).

Статус

  • x86 (T600, sm_75, CUDA 13): собрано, паритет, standalone работает.
  • Jetson (Orin NX, sm_87, CUDA 12.6): порт тривиален (NVRTC сам берёт sm_87); собрать build_cmake.sh на Jetson с его venv.
  • Опц.: ветка impl='cpp' в gpr_core/sar3d.py (адаптер gpr_cpp_backend.py), кэш device-A-сканов (сейчас каждый вызов грузит стек на GPU).