sar3d: port the Multi_antenna_3D_clean_v1 notebook algorithm
The old sar3d core was a port of the superseded Horns_3D notebook and produced wrong volumes. The compute core now follows the reference notebook exactly: - segmented_interleaved frame timing rebuilt from per-trace capture stamps (uniform tx_per_segment split as the no-stamps fallback), with the coarse inter-segment dtau and the intra-sweep dtau applied as one phase ramp before the IFFT; - ground scene coordinates with the ground_depth_m lift (scene Y=0 is the ground), full-region grid instead of a window around a center depth, geo_ref compensation anchored on the ground plane, power 0.3; - notebook finalization in C++: |acc/cnt|, depth mask, normalization without the Y-edge planes, separable Gaussian over (Z, X), level; - CUDA kernel computes geometry in double: float32 loses ~3e-4 of an A-scan sample in tau and drifts ~1e-4 from the float64 reference. Live-config keys follow: the center_depth/half_width/grid_n window knobs are replaced by ground/voxel/y-range/margin/sigma/power/tx_per_segment. Parity against the notebook npz exports (frames 0034-0036 of the 20260922_mine_fast_true dataset): max|diff| <= 1.6e-6 off the depth-mask knife edge, identical argmax, level rel diff <= 7e-7. Verified via make sar3d_parity --dump-dir + scripts/sar3d_volume_compare.py; the executable spec lives in scripts/sar3d_reference_check.py (bit-tight against the npz, including the animation shifted-grid arithmetic). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,23 +1,24 @@
|
||||
# gpr_cpp — C++/CUDA счёт-ядро MIMO GPR 3D SAR
|
||||
|
||||
> Вендорено из репозитория VNA_AlGo (`gpr_cpp/`, коммит `544dc59`); python-биндинги
|
||||
> (`bindings/`, pybind11) при переносе не взяты — сборка здесь только C++
|
||||
> (`GPR_BUILD_PYTHON=OFF`). Собирается из корневого Makefile radar_system
|
||||
> Инфраструктура (сборка, NVRTC, PocketFFT, npy) вендорена из репозитория
|
||||
> VNA_AlGo (`gpr_cpp/`, коммит `544dc59`) без python-биндингов
|
||||
> (`GPR_BUILD_PYTHON=OFF`). АЛГОРИТМ с сентября 2026 переписан: это порт
|
||||
> эталонного ноутбука `Multi_antenna_3D_clean_v1.ipynb` (ячейки 0 и 4), а не
|
||||
> старого `gpr_core`/Horns_3D. Паритет с ноутбуком проверяется инструментом
|
||||
> `sar3d_parity` + `python_app/scripts/sar3d_volume_compare.py` по
|
||||
> референсным `*_volume.npz`. Собирается из корневого 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.
|
||||
**В C++ (`libgpr_core_cpp`):** сегментированный тайминг кадра по per-trace
|
||||
меткам захвата (`compute_pair_timestamps`), препроцессинг A-сканов (bg-subtract,
|
||||
полная фазовая коррекция движения intra+pair до IFFT, k0-вставка + Ханн + IFFT
|
||||
через PocketFFT, нормировка пар), полнообластная ground-сетка
|
||||
(`build_full_region_grid`), 3D SAR апертурный объём (одно CUDA-ядро на всю
|
||||
апертуру; геометрия в double, A-сканы float32) и финализация
|
||||
(`finish_volume`: |acc/cnt| → маска глубины → нормировка без Y-краёв → гаусс
|
||||
по (Z,X) → нормировка; возвращает объём [0,1] + level).
|
||||
|
||||
## Ключевое решение: CUDA без nvcc (NVRTC + Driver API)
|
||||
|
||||
@@ -42,14 +43,14 @@ libnvrtc прописан — бинарники/модуль грузятся
|
||||
## Публичный 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
|
||||
#include "gpr/config.hpp" // GeometryConfig/BPConfig/MotionConfig/SARConfig (дефолты = ноутбук)
|
||||
#include "gpr/ascan.hpp" // compute_pair_timestamps, preprocess_snapshot(...) -> AScanSet
|
||||
#include "gpr/sar3d.hpp" // SarEngine::aperture_volume -> VolumeRaw; finish_volume -> FinishedVolume
|
||||
#include "gpr/geometry.hpp" // frame_offsets, frame_weights, build_full_region_grid
|
||||
|
||||
gpr::SarEngine engine(geom, bp, mot, sar); // компилит ядро под устройство
|
||||
auto vol = engine.aperture_volume(frames, grid); // один запуск ядра на апертуру
|
||||
// vol.acc / vol.cnt — сырые; финализацию (gaussian/normalize) делает вызывающий
|
||||
auto raw = engine.aperture_volume(frames, grid); // один запуск ядра на апертуру
|
||||
auto fin = gpr::finish_volume(raw, grid, mot.pitch_deg, bp, sar); // объём [0,1] + level
|
||||
```
|
||||
Пример без Python: [examples/standalone_bp.cpp](examples/standalone_bp.cpp) — грузит
|
||||
S21 из `.npy`, строит объём, печатает максимум.
|
||||
@@ -67,16 +68,13 @@ a = _gpr_cpp.preprocess_snapshot(s21, freq, background, mot, geom, bp, True) #
|
||||
acc, cnt = eng.aperture_volume([(a, dx, dy, dz, w), ...], gx, gy, gz) # numpy
|
||||
```
|
||||
|
||||
## Паритет (проверено на x86 T600)
|
||||
## Паритет с ноутбуком (проверено на x86 T600, датасет 20260922_mine_fast_true)
|
||||
|
||||
| Этап | Порог | Факт |
|
||||
|---|---|---|
|
||||
| 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).
|
||||
Кадры 0034/0035/0036 против референсных `*_volume.npz` ноутбука
|
||||
(`make sar3d_parity` + `python_app/scripts/sar3d_volume_compare.py`):
|
||||
max|Δ| ≤ 1.6e-6 вне ножевой границы маски глубины, argmax и level совпадают
|
||||
(level rel ≤ 7e-7). Python-эталон той же цепочки:
|
||||
`python_app/scripts/sar3d_reference_check.py`.
|
||||
|
||||
## Статус
|
||||
|
||||
|
||||
Reference in New Issue
Block a user