#include "sar3d_processor.hpp" #include "gpr_processor.hpp" #include "../processing/processors/src/sar3d_clean.hpp" #include #include #include #include #include #include using Json = nlohmann::json; using namespace radar; namespace { auto complex_samples(const Json& samples) -> std::vector> { std::vector> values; for (const auto& sample : samples) { values.emplace_back(sample.at(0).get(), sample.at(1).get()); } return values; } auto collection(const Json& source) -> ipc::PreprocessedCollection { ipc::PreprocessedCollection result; result.collection_id = source.at("id"); result.monotonic_ns = source.at("timestamp"); for (const auto& entry : source.at("traces")) { ipc::SweepTraceBlock trace; trace.combo = {entry.at("input"), entry.at("output")}; trace.capture_start_ns = entry.at("start"); trace.capture_end_ns = entry.at("end"); trace.frequency_hz = entry.at("frequency").get>(); for (const auto& sample : complex_samples(entry.at("s21"))) { trace.s21.push_back({sample.real(), sample.imag()}); } if (result.traces.empty() || trace.capture_start_ns < result.capture_start_ns) { result.capture_start_ns = trace.capture_start_ns; } result.capture_end_ns = std::max(result.capture_end_ns, trace.capture_end_ns); result.traces.push_back(std::move(trace)); } return result; } // Boxes around given CLEAN objects, rows [x, y, z, y_low, y_high, level], in a scene with the // given X and Y bounds and radar placement (sar3d::marks). auto marks(const Json& input) -> Json { std::vector objects; for (const auto& row : input.at("objects")) { objects.push_back({.index = {}, .position = {row.at(0), row.at(1), row.at(2)}, .y_low = row.at(3), .y_high = row.at(4), .level = row.at(5)}); } const auto x = input.at("x").get>(); const auto y = input.at("y").get>(); const processing::sar3d::VolumeRequest frame{ .grid = {x, y, {}}, .pitch_rad = input.at("pitch").get() * (std::numbers::pi / 180.0), .ground_depth_m = input.at("ground"), .min_depth_m = input.at("min_depth"), .max_depth_m = input.at("max_depth"), }; Json rows = Json::array(); for (const auto& mark : processing::sar3d::marks(objects, frame)) { Json row = Json::array({static_cast(mark.kind)}); for (const double edge : mark.box) { row.push_back(edge); } rows.push_back(std::move(row)); } return Json{{"marks", rows}}; } } // Runs GPR or sar3d on one request or a batch, retaining processor caches. Each request holds the antenna // geometry, the live settings and the latest and previous collections; the // published payloads go to stdout as JSON. A request with mode "marks" boxes given objects instead. int main() { try { const Json source = Json::parse(std::cin); const bool batch = source.is_array(); const Json requests = batch ? source : Json::array({source}); Json results = Json::array(); processing::Sar3dProcessor sar; processing::GprProcessor gpr; for (const auto& input : requests) { if (input.value("mode", "sar3d") == "marks") { results.push_back(marks(input)); continue; } config::RunConfig config; config.rings.results.slot_size_bytes = input.value("slot", config.rings.results.slot_size_bytes); for (const auto& row : input.at("tx")) { config.gpr.tx_geometry.push_back({row.at(0), row.at(1), row.at(2), row.at(3)}); } for (const auto& row : input.at("rx")) { config.gpr.rx_geometry.push_back({row.at(0), row.at(1), row.at(2), row.at(3)}); } const bool gpr_mode = input.value("mode", "sar3d") == "gpr"; processing::ProcessingLiveConfig live; live.gpr_start_freq_mhz = input.at("f_start").get() / 1e6; live.gpr_stop_freq_mhz = input.at("f_stop").get() / 1e6; live.gpr_min_depth_m = input.at("min_depth"); live.gpr_max_depth_m = input.at("max_depth"); live.gpr_speed_m_s = input.at("speed"); live.gpr_look_angle_deg = input.at("pitch"); live.pair_depth_equalize = input.value("equalize", true); live.gpr_background_subtract_enabled = input.value("subtract_mean", false); live.gpr_background_mean_count = input.value("mean_count", 10U); if (gpr_mode) { live.gpr_bp_ground_depth_m = input.at("ground"); live.gpr_range_comp_power = input.at("power"); live.gpr_motion_mode = input.value("motion", "int_minus"); live.gpr_object_approach_min_frames = 1; live.gpr_remove_sidelobe_objects_enabled = false; } else { const auto& voxel = input.at("voxel"); live.sar3d_voxel_x_m = voxel.at(0); live.sar3d_voxel_y_m = voxel.at(1); live.sar3d_voxel_z_m = voxel.at(2); live.sar3d_bp_ground_depth_m = input.at("ground"); live.sar3d_frame_radius = input.at("radius"); live.sar3d_comp_range_power = input.at("power"); live.sar3d_use_compensation = input.at("compensation"); live.sar3d_normalize_each_frame = input.at("normalize"); live.sar3d_x_size_m = input.value("x_size", 0.0F); live.sar3d_y_min_m = input.value("y_min", -0.5F); live.sar3d_y_max_m = input.value("y_max", 2.0F); live.sar3d_z_size_m = input.value("z_size", 0.0F); live.sar3d_bg_gate_enabled = input.value("bg_gate", live.sar3d_bg_gate_enabled); live.sar3d_clean_enabled = input.value("clean", live.sar3d_clean_enabled); } std::vector previous; for (const auto& member : input.at("previous")) { previous.push_back(collection(member)); } const auto latest = collection(input.at("latest")); for (const auto& trace : latest.traces) { config.run_combos.push_back(trace.combo); } processing::ProcessorInterface& processor = gpr_mode ? static_cast(gpr) : static_cast(sar); const auto result = processor.process_collection(config, latest, previous, live); Json payloads = Json::array(); for (const auto& payload : result.collection_payloads) { payloads.push_back({{"name", payload.processing_name}, {"x", payload.image_x_axis}, {"y", payload.image_y_axis}, {"image", payload.image_values}, {"table", payload.table_values}, {"columns", payload.table_columns}}); } results.push_back(Json{{"payloads", payloads}}); } std::cout << (batch ? results : results.at(0)) << '\n'; } catch (const std::exception& error) { std::cout << Json{{"error", error.what()}} << '\n'; return 1; } }