sar3d: GPU port of the notebook with four views
The sar3d processor is rewritten as a checked port of Multi_antenna_3D_clean_v1.ipynb: NVRTC kernels, timing from the trace stamps, notebook parameter names and one cubic SAR_VOXEL_M. It adds front, side, top and 3D views, drops gpr_cpp and the parity tools, and the GUI speed now applies only while the socket speed is ignored.
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
@@ -40,11 +41,10 @@ class DataProcessor {
|
||||
[[nodiscard]] auto resolve_processor(const ProcessingLiveConfig& live_config) -> ProcessorInterface&;
|
||||
[[nodiscard]] auto should_replay_entire_history(const ProcessingLiveConfig& live_config) const -> bool;
|
||||
|
||||
// Merge live config with the latest socket-supplied speed (if any and if
|
||||
// not suppressed by `ignore_socket_speed`). The returned config is what
|
||||
// actually drives processing for this tick.
|
||||
[[nodiscard]] auto resolve_effective_live_config(const ProcessingLiveConfig& live_config) const
|
||||
-> ProcessingLiveConfig;
|
||||
// Speed streamed by a locator client (`vlc`), which then replaces
|
||||
// `gpr_speed_m_s`; nullopt when `ignore_socket_speed` is set or no fresh
|
||||
// value exists, so the speed from the live config stays in force.
|
||||
[[nodiscard]] auto socket_speed(const ProcessingLiveConfig& live_config) const -> std::optional<double>;
|
||||
|
||||
// Convert one processed collection into a locator filter spec, derived
|
||||
// from the live config and current processor mode.
|
||||
|
||||
+8
-42
@@ -90,51 +90,17 @@ struct ProcessingLiveConfig {
|
||||
float gpr_visible_x_max_m = 2.0F;
|
||||
float gpr_visible_z_min_m = 0.0F;
|
||||
float gpr_visible_z_max_m = 14.0F;
|
||||
// 3D SAR (sar3d processor) parameters; defaults mirror the reference
|
||||
// notebook Multi_antenna_3D_clean_v1.ipynb (cells 0/4). Frequency band,
|
||||
// depth gate, antenna selection and motion (speed, look angle, direction,
|
||||
// phase correction) are shared with the gpr_* fields.
|
||||
// 3D SAR (sar3d processor) parameters, named after the reference notebook
|
||||
// Multi_antenna_3D_clean_v1.ipynb (SAR_FRAME_RADIUS, BP_GROUND_DEPTH_M, ...).
|
||||
// The band, depth gate, antenna selection, speed and look angle are the
|
||||
// shared gpr_* fields above.
|
||||
std::uint32_t sar3d_frame_radius = 0U;
|
||||
float sar3d_edge_frame_weight = 0.8F;
|
||||
// Frame pitch for ground coordinates and inter-frame offsets. Override
|
||||
// disabled -> the pitch follows gpr_look_angle_deg (notebook
|
||||
// BP_GROUND_PITCH_DEG = None).
|
||||
bool sar3d_pitch_override_enabled = false;
|
||||
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;
|
||||
// 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;
|
||||
// Volume finalization: normalization ignores this much of the grid's Y
|
||||
// edges (constant-range ridge), then Gaussian smoothing over (Z, X).
|
||||
float sar3d_norm_edge_margin_m = 0.3F;
|
||||
float sar3d_smooth_sigma = 1.5F;
|
||||
// Geometric spreading compensation 1/(Rtx*Rrx)^power, capped.
|
||||
// Edge of the cubic voxel: the notebook's SAR_VOXEL_M with one value for X, Y, Z.
|
||||
float sar3d_voxel_m = 0.02F;
|
||||
float sar3d_bp_ground_depth_m = 1.25F;
|
||||
float sar3d_comp_range_power = 0.3F;
|
||||
// Fallback frame segmentation when per-trace capture stamps are absent.
|
||||
std::uint32_t sar3d_tx_per_segment = 2U;
|
||||
bool sar3d_use_compensation = true;
|
||||
bool sar3d_normalize_pairs = true;
|
||||
// Debug: when non-empty, the processor dumps every finished volume to this
|
||||
// directory (raw float32 + json header). Used by the parity tool only.
|
||||
std::string sar3d_volume_dump_dir{};
|
||||
// Bottom-view projection angle around the vertical Y axis, degrees:
|
||||
// 0 = front view (X horizontal), 90 = side view (Z horizontal),
|
||||
// intermediate values project along the rotated horizontal direction.
|
||||
float sar3d_bottom_view_deg = 0.0F;
|
||||
bool sar3d_normalize_each_frame = true;
|
||||
// When true, the data_processor ignores socket-supplied `vlc` updates and
|
||||
// keeps using `gpr_speed_m_s` from this file. Mirrored from the GUI's
|
||||
// "ignore socket speed" checkbox.
|
||||
|
||||
@@ -73,19 +73,16 @@ void DataProcessor::run(const std::atomic<bool>& stop_requested) {
|
||||
|
||||
while (!stop_requested.load(std::memory_order_relaxed)) {
|
||||
try {
|
||||
const auto live_config_raw = live_config_loader_.refresh_if_needed();
|
||||
const auto live_config = resolve_effective_live_config(live_config_raw);
|
||||
// The socket speed is read once per tick: it both drives this tick's
|
||||
// processing and, when it changed, triggers a reprocess on its own,
|
||||
// even when the live revision is unchanged.
|
||||
auto live_config = live_config_loader_.refresh_if_needed();
|
||||
const std::optional<double> current_socket_speed = socket_speed(live_config);
|
||||
if (current_socket_speed.has_value()) {
|
||||
live_config.gpr_speed_m_s = static_cast<float>(*current_socket_speed);
|
||||
}
|
||||
const auto live_revision = live_config_loader_.revision();
|
||||
auto& processor = resolve_processor(live_config);
|
||||
|
||||
// The effective config folds in latest_socket_speed(); a change there is
|
||||
// a reprocess trigger on its own, even when the live revision is unchanged.
|
||||
// Mirror the gating in resolve_effective_live_config(): only consider the
|
||||
// socket speed when it is actually applied to the effective config.
|
||||
const std::optional<double> current_socket_speed =
|
||||
(live_config_raw.ignore_socket_speed || locator_server_ == nullptr)
|
||||
? std::nullopt
|
||||
: locator_server_->latest_socket_speed();
|
||||
const bool socket_speed_dirty = current_socket_speed != last_reprocessed_socket_speed;
|
||||
|
||||
if (live_revision != last_replayed_revision || socket_speed_dirty) {
|
||||
@@ -213,18 +210,11 @@ auto DataProcessor::should_replay_entire_history(const ProcessingLiveConfig& liv
|
||||
return requested_mode == "bscan";
|
||||
}
|
||||
|
||||
auto DataProcessor::resolve_effective_live_config(const ProcessingLiveConfig& live_config) const
|
||||
-> ProcessingLiveConfig {
|
||||
auto DataProcessor::socket_speed(const ProcessingLiveConfig& live_config) const -> std::optional<double> {
|
||||
if (live_config.ignore_socket_speed || locator_server_ == nullptr) {
|
||||
return live_config;
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto socket_speed = locator_server_->latest_socket_speed();
|
||||
if (!socket_speed.has_value()) {
|
||||
return live_config;
|
||||
}
|
||||
ProcessingLiveConfig effective = live_config;
|
||||
effective.gpr_speed_m_s = static_cast<float>(*socket_speed);
|
||||
return effective;
|
||||
return locator_server_->latest_socket_speed();
|
||||
}
|
||||
|
||||
auto DataProcessor::build_locator_filter(const ProcessingLiveConfig& live_config) const
|
||||
|
||||
@@ -390,16 +390,9 @@ void apply_legacy_gpr_algorithm_alias(ProcessingLiveConfig& config, const std::s
|
||||
config.sar3d_frame_radius = parse_u32_number(*found, "processing.sar3d_frame_radius");
|
||||
}
|
||||
for (const auto& [key, target] : {
|
||||
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_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},
|
||||
std::pair{"sar3d_norm_edge_margin_m", &config.sar3d_norm_edge_margin_m},
|
||||
std::pair{"sar3d_smooth_sigma", &config.sar3d_smooth_sigma},
|
||||
std::pair{"sar3d_voxel_m", &config.sar3d_voxel_m},
|
||||
std::pair{"sar3d_bp_ground_depth_m", &config.sar3d_bp_ground_depth_m},
|
||||
std::pair{"sar3d_comp_range_power", &config.sar3d_comp_range_power},
|
||||
std::pair{"sar3d_bottom_view_deg", &config.sar3d_bottom_view_deg},
|
||||
}) {
|
||||
if (const auto found = root.find(key); found != root.end()) {
|
||||
if (!found->is_number()) {
|
||||
@@ -409,30 +402,8 @@ void apply_legacy_gpr_algorithm_alias(ProcessingLiveConfig& config, const std::s
|
||||
}
|
||||
}
|
||||
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) {
|
||||
throw std::runtime_error("processing.sar3d_tx_per_segment must be >= 1");
|
||||
}
|
||||
}
|
||||
for (const auto& [key, target] : {
|
||||
std::pair{"sar3d_pitch_override_enabled", &config.sar3d_pitch_override_enabled},
|
||||
std::pair{"sar3d_use_compensation", &config.sar3d_use_compensation},
|
||||
std::pair{"sar3d_normalize_pairs", &config.sar3d_normalize_pairs},
|
||||
std::pair{"sar3d_normalize_each_frame", &config.sar3d_normalize_each_frame},
|
||||
}) {
|
||||
if (const auto found = root.find(key); found != root.end()) {
|
||||
if (!found->is_boolean()) {
|
||||
|
||||
Reference in New Issue
Block a user