Refactor radar system

This commit is contained in:
Ayzen
2026-10-05 16:46:28 +03:00
parent 515049d2ed
commit 8f097dfa2e
179 changed files with 8212 additions and 6676 deletions
@@ -54,6 +54,7 @@ class DataProcessor {
void publish_locator(const ipc::ResultCollection& collection, const ProcessingLiveConfig& live_config);
const config::RunConfig& config_;
config::RunConfig processing_config_;
ipc::ShmRing& preprocessed_ring_;
ipc::ShmRing& results_ring_;
ProcessorRegistry processors_{};
@@ -2,9 +2,12 @@
#include <chrono>
#include <cstdint>
#include <optional>
#include <string>
#include <vector>
#include "run_config.hpp"
namespace radar::processing {
enum class HistoryCommand {
@@ -34,12 +37,14 @@ struct ProcessingLiveConfig {
float pass_through_y_max_db = 0.0F;
std::string bscan_axis = "abs";
std::string bscan_channel = "s21";
float bscan_cut_m = 0.824F;
float bscan_max_depth_m = 1.0F;
float bscan_cut_m = 0.0F;
float bscan_max_depth_m = 8.0F;
float bscan_gain = 1.0F;
float bscan_start_freq_mhz = 100.0F;
float bscan_stop_freq_mhz = 8800.0F;
LegacyGprMode legacy_gpr_mode = LegacyGprMode::Point;
// Absent in legacy live files: use the initial run_config geometry and medium.
std::optional<config::GprConfig> gpr{};
std::vector<std::uint32_t> gpr_input_positions{};
std::vector<std::uint32_t> gpr_output_positions{};
float gpr_min_depth_m = 2.0F;
@@ -108,11 +113,12 @@ struct ProcessingLiveConfig {
bool reprocess_current_result = true;
std::uint64_t history_command_seq = 0;
HistoryCommand history_command = HistoryCommand::None;
std::uint64_t settings_revision = 0;
};
class ProcessingLiveConfigLoader {
public:
explicit ProcessingLiveConfigLoader(std::string path);
ProcessingLiveConfigLoader(std::string path, const config::RunConfig& run_config);
[[nodiscard]] auto current() const -> ProcessingLiveConfig;
[[nodiscard]] auto refresh_if_needed() -> ProcessingLiveConfig;
@@ -122,6 +128,7 @@ class ProcessingLiveConfigLoader {
[[nodiscard]] auto read_file_text() const -> std::string;
std::string path_{};
const config::RunConfig& run_config_;
ProcessingLiveConfig current_{};
std::string last_json_text_{};
bool has_last_json_text_ = false;
@@ -1,4 +1,5 @@
#include "data_processor.hpp"
#include "runtime_wait.hpp"
#include <algorithm>
#include <chrono>
@@ -7,7 +8,6 @@
#include <optional>
#include <span>
#include <stdexcept>
#include <thread>
#include <utility>
#include "bscan_processor.hpp"
@@ -27,10 +27,15 @@ constexpr std::size_t kBscanReplayWindow = 50U;
return std::min({preprocessed_capacity, results_capacity, kBscanReplayWindow});
}
void publish_result_collection(const ipc::ResultCollection& collection, ipc::ShmRing& results_ring) {
void publish_result_collection(
const ipc::ResultCollection& collection, ipc::ShmRing& results_ring, std::uint64_t& oversized_results
) {
const auto out_bytes = ipc::serialize_result_collection(collection);
if (!results_ring.push(out_bytes)) {
throw std::runtime_error("Results ring slot is too small for serialized collection");
if (!results_ring.push(out_bytes) && ++oversized_results % 100U == 1U) {
std::cerr << "data_processor: result " << collection.collection_id << " requires " << out_bytes.size()
<< " bytes, results ring slot holds " << results_ring.slot_size_bytes()
<< "; increase rings.results.slot_size_bytes or reduce processing output"
<< " (dropped total=" << oversized_results << ")\n";
}
}
@@ -44,11 +49,12 @@ DataProcessor::DataProcessor(
radar::locator::TcpServer* locator_server
)
: config_(config),
processing_config_(config),
preprocessed_ring_(preprocessed_ring),
results_ring_(results_ring),
processors_(std::move(processors)),
default_processor_mode_(kDefaultProcessorMode),
live_config_loader_(config.runtime.processing_live_config_path),
live_config_loader_(config.runtime.processing_live_config_path, config),
locator_server_(locator_server) {
if (processors_.empty()) {
throw std::runtime_error("DataProcessor requires at least one processor");
@@ -66,6 +72,7 @@ void DataProcessor::run(const std::atomic<bool>& stop_requested) {
std::uint64_t last_applied_history_command_seq = 0;
std::uint64_t error_count = 0;
std::uint64_t consecutive_errors = 0;
std::uint64_t oversized_results = 0;
// track the socket-fed speed used for the last reprocess so a change
// arriving without a live-config revision bump still triggers a reprocess of
// the current result (gated below by reprocess_current_result).
@@ -82,6 +89,9 @@ void DataProcessor::run(const std::atomic<bool>& stop_requested) {
live_config.gpr_speed_m_s = static_cast<float>(*current_socket_speed);
}
const auto live_revision = live_config_loader_.revision();
if (live_revision != last_replayed_revision) {
processing_config_.gpr = live_config.gpr.value_or(config_.gpr);
}
auto& processor = resolve_processor(live_config);
const bool socket_speed_dirty = current_socket_speed != last_reprocessed_socket_speed;
@@ -108,7 +118,7 @@ void DataProcessor::run(const std::atomic<bool>& stop_requested) {
processor,
live_config
);
publish_result_collection(replay_result, results_ring_);
publish_result_collection(replay_result, results_ring_, oversized_results);
publish_locator(replay_result, live_config);
}
} else if (!preprocessed_history.empty()) {
@@ -118,7 +128,7 @@ void DataProcessor::run(const std::atomic<bool>& stop_requested) {
processor,
live_config
);
publish_result_collection(replay_result, results_ring_);
publish_result_collection(replay_result, results_ring_, oversized_results);
publish_locator(replay_result, live_config);
}
last_replayed_revision = live_revision;
@@ -156,7 +166,7 @@ void DataProcessor::run(const std::atomic<bool>& stop_requested) {
processor,
live_config
);
publish_result_collection(result_collection, results_ring_);
publish_result_collection(result_collection, results_ring_, oversized_results);
if (!fed_back) {
publish_locator(result_collection, live_config);
}
@@ -164,14 +174,8 @@ void DataProcessor::run(const std::atomic<bool>& stop_requested) {
continue;
}
std::this_thread::sleep_for(std::chrono::milliseconds(config_.runtime.idle_sleep_ms));
(void)config::wait_or_stop(config_.runtime.idle_sleep_ms, stop_requested);
} catch (const std::exception& exc) {
// A ring-slot-too-small failure is a permanent config error (the slot
// cannot hold a serialized collection): swallowing it would silently
// drop every collection forever, so fail fast and let the operator fix it.
if (std::string(exc.what()).find("slot is too small") != std::string::npos) {
throw;
}
// A single bad collection (torn ring slot, decode/processing error) must
// not kill the long-running processor: drop it and keep going.
++error_count;
@@ -189,7 +193,7 @@ void DataProcessor::run(const std::atomic<bool>& stop_requested) {
}
// Back-off (floored at 1ms even if idle_sleep_ms==0) so a persistent error
// cannot busy-spin a core or flood the log.
std::this_thread::sleep_for(std::chrono::milliseconds(std::max<int>(1, config_.runtime.idle_sleep_ms)));
(void)config::wait_or_stop(std::max(1U, config_.runtime.idle_sleep_ms), stop_requested);
}
}
}
@@ -201,11 +205,20 @@ auto DataProcessor::process_collection(
const ProcessingLiveConfig& live_config
) -> ipc::ResultCollection {
const auto started_at = std::chrono::steady_clock::now();
auto result = processor.process_collection(config_, preprocessed, previous_collections, live_config);
auto result = processor.process_collection(processing_config_, preprocessed, previous_collections, live_config);
const auto finished_at = std::chrono::steady_clock::now();
result.processing_duration_ns = static_cast<std::uint64_t>(
std::chrono::duration_cast<std::chrono::nanoseconds>(finished_at - started_at).count()
);
// Four 16-bit words retain the uint64 exactly in the existing float32 table format.
ipc::ResultPayload revision{};
revision.processing_name = "processing_revision";
revision.kind = ipc::ResultKind::TableF32;
revision.table_columns = 4U;
for (unsigned shift = 0; shift < 64; shift += 16) {
revision.table_values.push_back(static_cast<float>((live_config.settings_revision >> shift) & 0xffffU));
}
result.collection_payloads.push_back(std::move(revision));
return result;
}
@@ -9,6 +9,7 @@
#include <sstream>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
#include <nlohmann/json.hpp>
@@ -92,16 +93,55 @@ void apply_legacy_gpr_algorithm_alias(ProcessingLiveConfig& config, const std::s
throw std::runtime_error(field_name + " must be one of: s21, s11");
}
[[nodiscard]] auto parse_f32(const Json& value, const std::string& field_name) -> float {
if (!value.is_number()) {
throw std::runtime_error(field_name + " must be number");
}
const auto number = value.get<double>();
if (!std::isfinite(number) || std::fabs(number) > std::numeric_limits<float>::max()) {
throw std::runtime_error(field_name + " must fit finite float32");
}
return static_cast<float>(number);
}
template <typename T>
void assign_optional(const Json& root, const char* key, T& target) {
const auto found = root.find(key);
if (found == root.end()) {
return;
}
const auto field_name = std::string("processing.") + key;
if constexpr (std::is_same_v<T, float>) {
target = parse_f32(*found, field_name);
} else {
const bool valid = std::is_same_v<T, bool> ? found->is_boolean() : found->is_string();
if (!valid) {
throw std::runtime_error(field_name + " has invalid type");
}
target = found->get<T>();
}
}
[[nodiscard]] auto parse_u64_number(const Json& value, const std::string& field_name) -> std::uint64_t {
if (!value.is_number()) {
throw std::runtime_error(field_name + " must be number");
}
if (value.is_number_unsigned()) {
return value.get<std::uint64_t>();
}
if (value.is_number_integer()) {
const auto integer = value.get<std::int64_t>();
if (integer < 0) {
throw std::runtime_error(field_name + " is out of uint64 range");
}
return static_cast<std::uint64_t>(integer);
}
const double numeric = value.get<double>();
if (numeric < 0.0 || numeric > static_cast<double>(std::numeric_limits<std::uint64_t>::max())) {
if (!std::isfinite(numeric) || numeric < 0.0 || numeric >= static_cast<double>(std::numeric_limits<std::uint64_t>::max())) {
throw std::runtime_error(field_name + " is out of uint64 range");
}
const double rounded = std::round(numeric);
if (std::fabs(numeric - rounded) > 1e-6) {
if (numeric != rounded) {
throw std::runtime_error(field_name + " must be integer");
}
return static_cast<std::uint64_t>(rounded);
@@ -128,7 +168,31 @@ void apply_legacy_gpr_algorithm_alias(ProcessingLiveConfig& config, const std::s
return result;
}
[[nodiscard]] auto parse_live_config(const std::string& json_text, const std::string& path) -> ProcessingLiveConfig {
template <typename Entry>
auto parse_geometry(const Json& value, const char* position_field, std::uint32_t Entry::*position_member)
-> std::vector<Entry> {
if (!value.is_array()) {
throw std::runtime_error("processing geometry must be an array");
}
std::vector<Entry> entries{};
entries.reserve(value.size());
for (const auto& record : value) {
if (!record.is_object()) {
throw std::runtime_error("processing geometry entry must be an object");
}
Entry entry{};
entry.*position_member = parse_u32_number(record.at(position_field), position_field);
entry.x_m = parse_f32(record.at("x_m"), "processing.x_m");
assign_optional(record, "y_m", entry.y_m);
assign_optional(record, "z_m", entry.z_m);
entries.push_back(entry);
}
return entries;
}
[[nodiscard]] auto parse_live_config(
const std::string& json_text, const std::string& path, const config::RunConfig& run_config
) -> ProcessingLiveConfig {
if (json_text.empty()) {
throw std::runtime_error("Processing live config is empty: " + path);
}
@@ -145,12 +209,23 @@ void apply_legacy_gpr_algorithm_alias(ProcessingLiveConfig& config, const std::s
}
ProcessingLiveConfig config{};
if (const auto found = root.find("processor_mode"); found != root.end()) {
if (!found->is_string()) {
throw std::runtime_error("processing.processor_mode must be string");
}
config.processor_mode = found->get<std::string>();
if (const auto found = root.find("settings_revision"); found != root.end()) {
config.settings_revision = parse_u64_number(*found, "processing.settings_revision");
}
if (root.contains("relative_permittivity") || root.contains("tx_geometry") || root.contains("rx_geometry")) {
config.gpr = run_config.gpr;
if (const auto found = root.find("relative_permittivity"); found != root.end()) {
config.gpr->relative_permittivity = parse_f32(*found, "processing.relative_permittivity");
}
if (const auto found = root.find("tx_geometry"); found != root.end()) {
config.gpr->tx_geometry = parse_geometry(*found, "output_pos", &config::GprTxGeometry::output_pos);
}
if (const auto found = root.find("rx_geometry"); found != root.end()) {
config.gpr->rx_geometry = parse_geometry(*found, "input_pos", &config::GprRxGeometry::input_pos);
}
config::validate_gpr_config(*config.gpr, run_config);
}
assign_optional(root, "processor_mode", config.processor_mode);
if (const auto found = root.find("pass_through_channel"); found != root.end()) {
if (!found->is_string()) {
throw std::runtime_error("processing.pass_through_channel must be string");
@@ -158,66 +233,21 @@ void apply_legacy_gpr_algorithm_alias(ProcessingLiveConfig& config, const std::s
config.pass_through_channel =
parse_s_parameter_channel(found->get<std::string>(), "processing.pass_through_channel");
}
if (const auto found = root.find("pass_through_fixed_y_enabled"); found != root.end()) {
if (!found->is_boolean()) {
throw std::runtime_error("processing.pass_through_fixed_y_enabled must be bool");
}
config.pass_through_fixed_y_enabled = found->get<bool>();
}
if (const auto found = root.find("pass_through_y_min_db"); found != root.end()) {
if (!found->is_number()) {
throw std::runtime_error("processing.pass_through_y_min_db must be number");
}
config.pass_through_y_min_db = static_cast<float>(found->get<double>());
}
if (const auto found = root.find("pass_through_y_max_db"); found != root.end()) {
if (!found->is_number()) {
throw std::runtime_error("processing.pass_through_y_max_db must be number");
}
config.pass_through_y_max_db = static_cast<float>(found->get<double>());
}
if (const auto found = root.find("bscan_axis"); found != root.end()) {
if (!found->is_string()) {
throw std::runtime_error("processing.bscan_axis must be string");
}
config.bscan_axis = found->get<std::string>();
}
assign_optional(root, "pass_through_fixed_y_enabled", config.pass_through_fixed_y_enabled);
assign_optional(root, "pass_through_y_min_db", config.pass_through_y_min_db);
assign_optional(root, "pass_through_y_max_db", config.pass_through_y_max_db);
assign_optional(root, "bscan_axis", config.bscan_axis);
if (const auto found = root.find("bscan_channel"); found != root.end()) {
if (!found->is_string()) {
throw std::runtime_error("processing.bscan_channel must be string");
}
config.bscan_channel = parse_s_parameter_channel(found->get<std::string>(), "processing.bscan_channel");
}
if (const auto found = root.find("bscan_cut_m"); found != root.end()) {
if (!found->is_number()) {
throw std::runtime_error("processing.bscan_cut_m must be number");
}
config.bscan_cut_m = static_cast<float>(found->get<double>());
}
if (const auto found = root.find("bscan_max_depth_m"); found != root.end()) {
if (!found->is_number()) {
throw std::runtime_error("processing.bscan_max_depth_m must be number");
}
config.bscan_max_depth_m = static_cast<float>(found->get<double>());
}
if (const auto found = root.find("bscan_gain"); found != root.end()) {
if (!found->is_number()) {
throw std::runtime_error("processing.bscan_gain must be number");
}
config.bscan_gain = static_cast<float>(found->get<double>());
}
if (const auto found = root.find("bscan_start_freq_mhz"); found != root.end()) {
if (!found->is_number()) {
throw std::runtime_error("processing.bscan_start_freq_mhz must be number");
}
config.bscan_start_freq_mhz = static_cast<float>(found->get<double>());
}
if (const auto found = root.find("bscan_stop_freq_mhz"); found != root.end()) {
if (!found->is_number()) {
throw std::runtime_error("processing.bscan_stop_freq_mhz must be number");
}
config.bscan_stop_freq_mhz = static_cast<float>(found->get<double>());
}
assign_optional(root, "bscan_cut_m", config.bscan_cut_m);
assign_optional(root, "bscan_max_depth_m", config.bscan_max_depth_m);
assign_optional(root, "bscan_gain", config.bscan_gain);
assign_optional(root, "bscan_start_freq_mhz", config.bscan_start_freq_mhz);
assign_optional(root, "bscan_stop_freq_mhz", config.bscan_stop_freq_mhz);
if (const auto found = root.find("legacy_gpr_mode"); found != root.end()) {
if (!found->is_string()) {
throw std::runtime_error("processing.legacy_gpr_mode must be string");
@@ -237,36 +267,11 @@ void apply_legacy_gpr_algorithm_alias(ProcessingLiveConfig& config, const std::s
if (const auto found = root.find("gpr_output_positions"); found != root.end()) {
config.gpr_output_positions = parse_u32_array(*found, "processing.gpr_output_positions");
}
if (const auto found = root.find("gpr_min_depth_m"); found != root.end()) {
if (!found->is_number()) {
throw std::runtime_error("processing.gpr_min_depth_m must be number");
}
config.gpr_min_depth_m = static_cast<float>(found->get<double>());
}
if (const auto found = root.find("gpr_max_depth_m"); found != root.end()) {
if (!found->is_number()) {
throw std::runtime_error("processing.gpr_max_depth_m must be number");
}
config.gpr_max_depth_m = static_cast<float>(found->get<double>());
}
if (const auto found = root.find("gpr_range_comp_power"); found != root.end()) {
if (!found->is_number()) {
throw std::runtime_error("processing.gpr_range_comp_power must be number");
}
config.gpr_range_comp_power = static_cast<float>(found->get<double>());
}
if (const auto found = root.find("gpr_object_min_frac"); found != root.end()) {
if (!found->is_number()) {
throw std::runtime_error("processing.gpr_object_min_frac must be number");
}
config.gpr_object_min_frac = static_cast<float>(found->get<double>());
}
if (const auto found = root.find("gpr_comp_power"); found != root.end()) {
if (!found->is_number()) {
throw std::runtime_error("processing.gpr_comp_power must be number");
}
config.gpr_comp_power = static_cast<float>(found->get<double>());
}
assign_optional(root, "gpr_min_depth_m", config.gpr_min_depth_m);
assign_optional(root, "gpr_max_depth_m", config.gpr_max_depth_m);
assign_optional(root, "gpr_range_comp_power", config.gpr_range_comp_power);
assign_optional(root, "gpr_object_min_frac", config.gpr_object_min_frac);
assign_optional(root, "gpr_comp_power", config.gpr_comp_power);
if (const auto found = root.find("gpr_score_mode"); found != root.end()) {
if (!found->is_string()) {
throw std::runtime_error("processing.gpr_score_mode must be string");
@@ -279,30 +284,10 @@ void apply_legacy_gpr_algorithm_alias(ProcessingLiveConfig& config, const std::s
}
config.gpr_motion_mode = parse_gpr_motion_mode(found->get<std::string>(), "processing.gpr_motion_mode");
}
if (const auto found = root.find("gpr_speed_m_s"); found != root.end()) {
if (!found->is_number()) {
throw std::runtime_error("processing.gpr_speed_m_s must be number");
}
config.gpr_speed_m_s = static_cast<float>(found->get<double>());
}
if (const auto found = root.find("gpr_look_angle_deg"); found != root.end()) {
if (!found->is_number()) {
throw std::runtime_error("processing.gpr_look_angle_deg must be number");
}
config.gpr_look_angle_deg = static_cast<float>(found->get<double>());
}
if (const auto found = root.find("gpr_direction_sign"); found != root.end()) {
if (!found->is_number()) {
throw std::runtime_error("processing.gpr_direction_sign must be number");
}
config.gpr_direction_sign = static_cast<float>(found->get<double>());
}
if (const auto found = root.find("gpr_apply_freq_phase_correction"); found != root.end()) {
if (!found->is_boolean()) {
throw std::runtime_error("processing.gpr_apply_freq_phase_correction must be bool");
}
config.gpr_apply_freq_phase_correction = found->get<bool>();
}
assign_optional(root, "gpr_speed_m_s", config.gpr_speed_m_s);
assign_optional(root, "gpr_look_angle_deg", config.gpr_look_angle_deg);
assign_optional(root, "gpr_direction_sign", config.gpr_direction_sign);
assign_optional(root, "gpr_apply_freq_phase_correction", config.gpr_apply_freq_phase_correction);
if (const auto found = root.find("gpr_reference_mode"); found != root.end()) {
if (!found->is_string()) {
throw std::runtime_error("processing.gpr_reference_mode must be string");
@@ -310,70 +295,25 @@ void apply_legacy_gpr_algorithm_alias(ProcessingLiveConfig& config, const std::s
config.gpr_reference_mode =
parse_legacy_gpr_reference_mode(found->get<std::string>(), "processing.gpr_reference_mode");
}
if (const auto found = root.find("gpr_snr_thresh"); found != root.end()) {
if (!found->is_number()) {
throw std::runtime_error("processing.gpr_snr_thresh must be number");
}
config.gpr_snr_thresh = static_cast<float>(found->get<double>());
}
if (const auto found = root.find("gpr_snr_comp_max"); found != root.end()) {
if (!found->is_number()) {
throw std::runtime_error("processing.gpr_snr_comp_max must be number");
}
config.gpr_snr_comp_max = static_cast<float>(found->get<double>());
}
if (const auto found = root.find("gpr_start_freq_mhz"); found != root.end()) {
if (!found->is_number()) {
throw std::runtime_error("processing.gpr_start_freq_mhz must be number");
}
config.gpr_start_freq_mhz = static_cast<float>(found->get<double>());
}
if (const auto found = root.find("gpr_stop_freq_mhz"); found != root.end()) {
if (!found->is_number()) {
throw std::runtime_error("processing.gpr_stop_freq_mhz must be number");
}
config.gpr_stop_freq_mhz = static_cast<float>(found->get<double>());
}
if (const auto found = root.find("gpr_background_subtract_enabled"); found != root.end()) {
if (!found->is_boolean()) {
throw std::runtime_error("processing.gpr_background_subtract_enabled must be bool");
}
config.gpr_background_subtract_enabled = found->get<bool>();
}
assign_optional(root, "gpr_snr_thresh", config.gpr_snr_thresh);
assign_optional(root, "gpr_snr_comp_max", config.gpr_snr_comp_max);
assign_optional(root, "gpr_start_freq_mhz", config.gpr_start_freq_mhz);
assign_optional(root, "gpr_stop_freq_mhz", config.gpr_stop_freq_mhz);
assign_optional(root, "gpr_background_subtract_enabled", config.gpr_background_subtract_enabled);
if (const auto found = root.find("gpr_background_mean_count"); found != root.end()) {
config.gpr_background_mean_count = parse_u32_number(*found, "processing.gpr_background_mean_count");
}
if (const auto found = root.find("gpr_remove_sidelobe_objects_enabled"); found != root.end()) {
if (!found->is_boolean()) {
throw std::runtime_error("processing.gpr_remove_sidelobe_objects_enabled must be bool");
}
config.gpr_remove_sidelobe_objects_enabled = found->get<bool>();
}
if (const auto found = root.find("gpr_imaging_plane_y_m"); found != root.end()) {
if (!found->is_number()) {
throw std::runtime_error("processing.gpr_imaging_plane_y_m must be number");
}
config.gpr_imaging_plane_y_m = static_cast<float>(found->get<double>());
}
assign_optional(root, "gpr_remove_sidelobe_objects_enabled", config.gpr_remove_sidelobe_objects_enabled);
assign_optional(root, "gpr_imaging_plane_y_m", config.gpr_imaging_plane_y_m);
for (const auto& [key, target] : {
std::pair{"gpr_visible_x_min_m", &config.gpr_visible_x_min_m},
std::pair{"gpr_visible_x_max_m", &config.gpr_visible_x_max_m},
std::pair{"gpr_visible_z_min_m", &config.gpr_visible_z_min_m},
std::pair{"gpr_visible_z_max_m", &config.gpr_visible_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 = static_cast<float>(found->get<double>());
}
}
if (const auto found = root.find("legacy_gpr_min_visible_pair_count"); found != root.end()) {
if (!found->is_number()) {
throw std::runtime_error("processing.legacy_gpr_min_visible_pair_count must be number");
}
config.legacy_gpr_min_visible_pair_count = static_cast<float>(found->get<double>());
assign_optional(root, key, *target);
}
assign_optional(root, "legacy_gpr_min_visible_pair_count", config.legacy_gpr_min_visible_pair_count);
if (const auto found = root.find("gpr_max_detected_objects_to_draw"); found != root.end()) {
config.gpr_max_detected_objects_to_draw =
parse_u32_number(*found, "processing.gpr_max_detected_objects_to_draw");
@@ -394,36 +334,16 @@ void apply_legacy_gpr_algorithm_alias(ProcessingLiveConfig& config, const std::s
std::pair{"sar3d_bp_ground_depth_m", &config.sar3d_bp_ground_depth_m},
std::pair{"sar3d_comp_range_power", &config.sar3d_comp_range_power},
}) {
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 = static_cast<float>(found->get<double>());
}
assign_optional(root, key, *target);
}
for (const auto& [key, target] : {
std::pair{"sar3d_use_compensation", &config.sar3d_use_compensation},
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()) {
throw std::runtime_error(std::string("processing.") + key + " must be bool");
}
*target = found->get<bool>();
}
}
if (const auto found = root.find("ignore_socket_speed"); found != root.end()) {
if (!found->is_boolean()) {
throw std::runtime_error("processing.ignore_socket_speed must be bool");
}
config.ignore_socket_speed = found->get<bool>();
}
if (const auto found = root.find("reprocess_current_result"); found != root.end()) {
if (!found->is_boolean()) {
throw std::runtime_error("processing.reprocess_current_result must be bool");
}
config.reprocess_current_result = found->get<bool>();
assign_optional(root, key, *target);
}
assign_optional(root, "ignore_socket_speed", config.ignore_socket_speed);
assign_optional(root, "reprocess_current_result", config.reprocess_current_result);
if (const auto found = root.find("history_command_seq"); found != root.end()) {
config.history_command_seq = parse_u64_number(*found, "processing.history_command_seq");
}
@@ -438,8 +358,9 @@ void apply_legacy_gpr_algorithm_alias(ProcessingLiveConfig& config, const std::s
} // namespace
ProcessingLiveConfigLoader::ProcessingLiveConfigLoader(std::string path)
ProcessingLiveConfigLoader::ProcessingLiveConfigLoader(std::string path, const config::RunConfig& run_config)
: path_(std::move(path)),
run_config_(run_config),
next_check_at_(std::chrono::steady_clock::now()) {
if (path_.empty()) {
throw std::runtime_error("Processing live config path must not be empty");
@@ -472,7 +393,7 @@ auto ProcessingLiveConfigLoader::refresh_if_needed() -> ProcessingLiveConfig {
return current_;
}
current_ = parse_live_config(json_text, path_);
current_ = parse_live_config(json_text, path_, run_config_);
last_json_text_ = json_text;
has_last_json_text_ = true;
++revision_;