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).