# 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](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 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/)) ```cpp #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](examples/standalone_bp.cpp) — грузит S21 из `.npy`, строит объём, печатает максимум. ```bash ./build/standalone_bp 0038_id39_ns11361438137486 ``` ## Python-модуль ```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](../tests/test_parity_cpp.py) (C++ vs cupy). ## Статус - [x] 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).