sar3d: fixed default scene window X [-2,2], Y [-0.5,2], Z [0,8] m
The grid bounds are now explicit config (sar3d_x/y/z_min/max_m) instead of being derived from the antenna span and the depth gate. The depth mask still zeroes voxels outside [min_depth, max_depth], so the wider Z window just shows the masked wedge. The bound fields are double: the parity tool recomputes the notebook-derived bounds (notebook_grid_bounds) and passes them through the live config, and a float roundtrip shifted the axes by ~1e-7 m, flipping depth-mask knife-edge voxels against the reference npz. Parity on frames 0034-0036 unchanged (1.3e-6, axes bit-match). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+12
-6
@@ -103,12 +103,18 @@ struct ProcessingLiveConfig {
|
||||
float sar3d_pitch_deg = 17.5F;
|
||||
// Height of the LOWER antenna row above the ground; scene Y=0 is the ground.
|
||||
float sar3d_ground_depth_m = 1.25F;
|
||||
// Full-region grid: X spans the antennas +- x_margin, Y is height above the
|
||||
// ground, Z covers radar depths [gpr_min_depth, gpr_max_depth] over the Y
|
||||
// range. Voxel pitch per axis.
|
||||
float sar3d_x_margin_m = 2.0F;
|
||||
float sar3d_y_min_m = -0.5F;
|
||||
float sar3d_y_max_m = 2.0F;
|
||||
// Scene grid bounds (metres): X across, Y height above the ground, Z along
|
||||
// the ground away from the radar. Voxels whose radar depth falls outside
|
||||
// [gpr_min_depth, gpr_max_depth] are zeroed by the mask regardless.
|
||||
// Doubles, not floats: the parity tool feeds the notebook-derived bounds
|
||||
// through here and a float roundtrip shifts the grid by ~1e-7 m, flipping
|
||||
// knife-edge voxels of the depth mask against the reference volumes.
|
||||
double sar3d_x_min_m = -2.0;
|
||||
double sar3d_x_max_m = 2.0;
|
||||
double sar3d_y_min_m = -0.5;
|
||||
double sar3d_y_max_m = 2.0;
|
||||
double sar3d_z_min_m = 0.0;
|
||||
double sar3d_z_max_m = 8.0;
|
||||
float sar3d_voxel_x_m = 0.025F;
|
||||
float sar3d_voxel_y_m = 0.02F;
|
||||
float sar3d_voxel_z_m = 0.02F;
|
||||
|
||||
@@ -393,9 +393,6 @@ void apply_legacy_gpr_algorithm_alias(ProcessingLiveConfig& config, const std::s
|
||||
std::pair{"sar3d_edge_frame_weight", &config.sar3d_edge_frame_weight},
|
||||
std::pair{"sar3d_pitch_deg", &config.sar3d_pitch_deg},
|
||||
std::pair{"sar3d_ground_depth_m", &config.sar3d_ground_depth_m},
|
||||
std::pair{"sar3d_x_margin_m", &config.sar3d_x_margin_m},
|
||||
std::pair{"sar3d_y_min_m", &config.sar3d_y_min_m},
|
||||
std::pair{"sar3d_y_max_m", &config.sar3d_y_max_m},
|
||||
std::pair{"sar3d_voxel_x_m", &config.sar3d_voxel_x_m},
|
||||
std::pair{"sar3d_voxel_y_m", &config.sar3d_voxel_y_m},
|
||||
std::pair{"sar3d_voxel_z_m", &config.sar3d_voxel_z_m},
|
||||
@@ -411,6 +408,21 @@ void apply_legacy_gpr_algorithm_alias(ProcessingLiveConfig& config, const std::s
|
||||
*target = static_cast<float>(found->get<double>());
|
||||
}
|
||||
}
|
||||
for (const auto& [key, target] : {
|
||||
std::pair{"sar3d_x_min_m", &config.sar3d_x_min_m},
|
||||
std::pair{"sar3d_x_max_m", &config.sar3d_x_max_m},
|
||||
std::pair{"sar3d_y_min_m", &config.sar3d_y_min_m},
|
||||
std::pair{"sar3d_y_max_m", &config.sar3d_y_max_m},
|
||||
std::pair{"sar3d_z_min_m", &config.sar3d_z_min_m},
|
||||
std::pair{"sar3d_z_max_m", &config.sar3d_z_max_m},
|
||||
}) {
|
||||
if (const auto found = root.find(key); found != root.end()) {
|
||||
if (!found->is_number()) {
|
||||
throw std::runtime_error(std::string("processing.") + key + " must be number");
|
||||
}
|
||||
*target = found->get<double>();
|
||||
}
|
||||
}
|
||||
if (const auto found = root.find("sar3d_tx_per_segment"); found != root.end()) {
|
||||
config.sar3d_tx_per_segment = parse_u32_number(*found, "processing.sar3d_tx_per_segment");
|
||||
if (config.sar3d_tx_per_segment == 0U) {
|
||||
|
||||
@@ -155,9 +155,12 @@ struct Selection {
|
||||
sar.frame_radius = static_cast<int>(std::max<std::uint32_t>(0U, live_config.sar3d_frame_radius));
|
||||
sar.edge_frame_weight = static_cast<double>(live_config.sar3d_edge_frame_weight);
|
||||
sar.ground_depth_m = static_cast<double>(live_config.sar3d_ground_depth_m);
|
||||
sar.x_margin_m = static_cast<double>(live_config.sar3d_x_margin_m);
|
||||
sar.x_min_m = static_cast<double>(live_config.sar3d_x_min_m);
|
||||
sar.x_max_m = static_cast<double>(live_config.sar3d_x_max_m);
|
||||
sar.y_min_m = static_cast<double>(live_config.sar3d_y_min_m);
|
||||
sar.y_max_m = static_cast<double>(live_config.sar3d_y_max_m);
|
||||
sar.z_min_m = static_cast<double>(live_config.sar3d_z_min_m);
|
||||
sar.z_max_m = static_cast<double>(live_config.sar3d_z_max_m);
|
||||
sar.voxel_m = {
|
||||
static_cast<double>(live_config.sar3d_voxel_x_m),
|
||||
static_cast<double>(live_config.sar3d_voxel_y_m),
|
||||
@@ -570,7 +573,7 @@ auto Sar3dProcessor::process_collection(
|
||||
state_->engine_fp = engine_fp;
|
||||
}
|
||||
|
||||
const gpr::Grid grid = gpr::build_full_region_grid(geom, bp, sar, motion.pitch_deg);
|
||||
const gpr::Grid grid = gpr::build_full_region_grid(sar);
|
||||
const gpr::VolumeRaw raw = state_->engine->aperture_volume(frames, grid);
|
||||
// Full notebook finalization: |acc/cnt|, depth gate, normalization
|
||||
// without the Y-edge planes, Gaussian smoothing over (Z, X), clip.
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "gpr/config.hpp"
|
||||
#include "gpr/geometry.hpp"
|
||||
#include "processing_live_config.hpp"
|
||||
#include "run_config.hpp"
|
||||
#include "sar3d_processor.hpp"
|
||||
@@ -85,7 +87,11 @@ NpyArray load_npy(const std::string& path) {
|
||||
}
|
||||
|
||||
// Live config matching the reference notebook run (npz meta parameters).
|
||||
auto build_live_config() -> radar::processing::ProcessingLiveConfig {
|
||||
// The default program grid is a fixed window (X -2..2, Z 0..8), while the
|
||||
// notebook derives its bounds from the antenna span and the depth gate — so
|
||||
// the notebook bounds are recomputed here and passed explicitly.
|
||||
auto build_live_config(const radar::config::RunConfig& run_config)
|
||||
-> radar::processing::ProcessingLiveConfig {
|
||||
radar::processing::ProcessingLiveConfig live{};
|
||||
live.processor_mode = "sar3d";
|
||||
live.gpr_start_freq_mhz = 2000.0F;
|
||||
@@ -96,6 +102,32 @@ auto build_live_config() -> radar::processing::ProcessingLiveConfig {
|
||||
live.gpr_look_angle_deg = 17.5F;
|
||||
live.gpr_direction_sign = 1.0F;
|
||||
live.gpr_apply_freq_phase_correction = true;
|
||||
|
||||
gpr::GeometryConfig geom{};
|
||||
geom.input_idx.clear();
|
||||
geom.output_idx.clear();
|
||||
geom.rx_positions.clear();
|
||||
geom.tx_positions.clear();
|
||||
for (const auto& entry : run_config.gpr.rx_geometry) {
|
||||
geom.input_idx.push_back(static_cast<int>(entry.input_pos));
|
||||
geom.rx_positions.push_back({entry.x_m, entry.y_m, entry.z_m});
|
||||
}
|
||||
for (const auto& entry : run_config.gpr.tx_geometry) {
|
||||
geom.output_idx.push_back(static_cast<int>(entry.output_pos));
|
||||
geom.tx_positions.push_back({entry.x_m, entry.y_m, entry.z_m});
|
||||
}
|
||||
geom.finalize();
|
||||
gpr::BPConfig bp{};
|
||||
bp.min_depth = live.gpr_min_depth_m;
|
||||
bp.max_depth = live.gpr_max_depth_m;
|
||||
gpr::SARConfig sar{};
|
||||
double x_min = 0.0, x_max = 0.0, z_min = 0.0, z_max = 0.0;
|
||||
gpr::notebook_grid_bounds(geom, bp, sar, live.gpr_look_angle_deg, /*x_margin_m=*/2.0,
|
||||
x_min, x_max, z_min, z_max);
|
||||
live.sar3d_x_min_m = x_min;
|
||||
live.sar3d_x_max_m = x_max;
|
||||
live.sar3d_z_min_m = z_min;
|
||||
live.sar3d_z_max_m = z_max;
|
||||
return live;
|
||||
}
|
||||
|
||||
@@ -240,7 +272,13 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
const auto center_index = static_cast<std::size_t>(center_it - snapshots.begin());
|
||||
|
||||
auto live = build_live_config();
|
||||
const auto run_config = radar::config::load_run_config(root + "/config_profile.json");
|
||||
if (run_config.gpr.tx_geometry.empty() || run_config.gpr.rx_geometry.empty()) {
|
||||
std::printf("FAIL: config_profile.json has no gpr geometry\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto live = build_live_config(run_config);
|
||||
live.sar3d_volume_dump_dir = dump_dir;
|
||||
const auto radius = static_cast<std::size_t>(live.sar3d_frame_radius);
|
||||
if (center_index < radius || center_index + radius >= snapshots.size()) {
|
||||
@@ -248,12 +286,6 @@ int main(int argc, char** argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const auto run_config = radar::config::load_run_config(root + "/config_profile.json");
|
||||
if (run_config.gpr.tx_geometry.empty() || run_config.gpr.rx_geometry.empty()) {
|
||||
std::printf("FAIL: config_profile.json has no gpr geometry\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// The processor centers the aperture `radius` frames before the newest
|
||||
// collection, so feeding [center-r .. center+r] in order reproduces the
|
||||
// symmetric aperture around `center`.
|
||||
|
||||
Reference in New Issue
Block a user