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:
@@ -95,7 +95,20 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
};
|
||||
|
||||
auto ts = compute_pair_timestamps(mot, geom);
|
||||
// Демо без чтения meta.json: равномерные сегменты по интервалу между
|
||||
// соседними снапшотами (реальные per-trace окна читает sar3d_parity).
|
||||
double acq_s = 0.0;
|
||||
{
|
||||
std::vector<double> stamps;
|
||||
for (auto& s : snaps) {
|
||||
auto pos = s.rfind("_ns");
|
||||
if (pos != std::string::npos) stamps.push_back(std::stod(s.substr(pos + 3)) * 1e-9);
|
||||
}
|
||||
std::sort(stamps.begin(), stamps.end());
|
||||
if (stamps.size() >= 2) acq_s = stamps[1] - stamps[0];
|
||||
}
|
||||
auto windows = uniform_tx_windows(geom.output_idx, acq_s, mot.tx_per_segment);
|
||||
auto ts = compute_pair_timestamps(windows, acq_s, geom.output_idx, geom, mot);
|
||||
SarEngine engine(geom, bp, mot, sar);
|
||||
|
||||
// держим AScanSet живыми
|
||||
@@ -122,22 +135,20 @@ int main(int argc, char** argv) {
|
||||
frames.push_back(ApertureFrame{a.get(), offs[f], wts[f]});
|
||||
}
|
||||
|
||||
// центр сетки: под центральным снапшотом на глубине 5.6 м (сцена)
|
||||
auto center_scene = point_to_scene(0.0, 0.0, 5.6, mot, sar);
|
||||
Grid grid = build_grid_scene(center_scene, sar);
|
||||
// полнообластная ground-сетка (как одиночный 3D в ноутбуке)
|
||||
Grid grid = build_full_region_grid(geom, bp, sar, mot.pitch_deg);
|
||||
printf("сетка %dx%dx%d, счёт объёма...\n", grid.NX(), grid.NY(), grid.NZ());
|
||||
|
||||
VolumeRaw vol = engine.aperture_volume(frames, grid);
|
||||
FinishedVolume fin = finish_volume(vol, grid, mot.pitch_deg, bp, sar);
|
||||
|
||||
// финализация: |acc/cnt|, максимум
|
||||
double vmax = 0; size_t amax = 0;
|
||||
for (size_t i = 0; i < vol.acc.size(); ++i) {
|
||||
double v = std::abs(vol.acc[i]) / (vol.cnt[i] + 1e-12);
|
||||
if (v > vmax) { vmax = v; amax = i; }
|
||||
}
|
||||
for (size_t i = 0; i < fin.volume.size(); ++i)
|
||||
if (fin.volume[i] > vmax) { vmax = fin.volume[i]; amax = i; }
|
||||
int NX = vol.NX, NZ = vol.NZ;
|
||||
int ix = amax % NX, iz = (amax / NX) % NZ, iy = amax / ((size_t)NX * NZ);
|
||||
printf("МАКСИМУМ объёма: |acc/cnt|=%.4e в вокселе (ix=%d,iy=%d,iz=%d)\n", vmax, ix, iy, iz);
|
||||
printf("МАКСИМУМ объёма: %.4f (level %.4e) в вокселе (ix=%d,iy=%d,iz=%d)\n",
|
||||
vmax, fin.level, ix, iy, iz);
|
||||
printf(" X=%.1f см, Y=%.1f см, Z=%.1f см\n",
|
||||
grid.x[ix] * 100, grid.y[iy] * 100, grid.z[iz] * 100);
|
||||
printf("OK: автономный C++ путь отработал без Python.\n");
|
||||
|
||||
Reference in New Issue
Block a user