Refactor radar system
This commit is contained in:
@@ -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_{};
|
||||
|
||||
+10
-3
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+117
-196
@@ -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_;
|
||||
|
||||
Reference in New Issue
Block a user