added GPR
This commit is contained in:
@@ -81,6 +81,26 @@ struct PreprocessConfig {
|
||||
std::string reference_bundle_path{};
|
||||
};
|
||||
|
||||
struct GprTxGeometry {
|
||||
// Transmitter geometry keyed by output switch position.
|
||||
std::uint32_t output_pos = 0;
|
||||
float x_m = 0.0F;
|
||||
};
|
||||
|
||||
struct GprRxGeometry {
|
||||
// Receiver geometry keyed by input switch position.
|
||||
std::uint32_t input_pos = 0;
|
||||
float x_m = 0.0F;
|
||||
};
|
||||
|
||||
struct GprConfig {
|
||||
// Stable collection-level GPR processing settings.
|
||||
std::string mode = "point";
|
||||
float relative_permittivity = 1.0F;
|
||||
std::vector<GprTxGeometry> tx_geometry{};
|
||||
std::vector<GprRxGeometry> rx_geometry{};
|
||||
};
|
||||
|
||||
struct RunConfig {
|
||||
// Single configuration object shared by all C++ processes.
|
||||
RadarConfig radar{};
|
||||
@@ -89,6 +109,7 @@ struct RunConfig {
|
||||
RingsConfig rings{};
|
||||
RuntimeConfig runtime{};
|
||||
PreprocessConfig preprocess{};
|
||||
GprConfig gpr{};
|
||||
std::vector<radar::ipc::ComboKey> run_combos{};
|
||||
};
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -214,6 +216,69 @@ void validate_combo_key(const ipc::ComboKey& key, const RunConfig& config) {
|
||||
}
|
||||
}
|
||||
|
||||
void validate_gpr_config(const GprConfig& config, const RunConfig& run_config) {
|
||||
if (config.mode != "point" && config.mode != "extended") {
|
||||
throw std::runtime_error("gpr.mode must be either 'point' or 'extended'");
|
||||
}
|
||||
if (!(config.relative_permittivity > 0.0F)) {
|
||||
throw std::runtime_error("gpr.relative_permittivity must be > 0");
|
||||
}
|
||||
|
||||
std::unordered_set<std::uint32_t> seen_tx_positions{};
|
||||
for (const auto& entry : config.tx_geometry) {
|
||||
if (entry.output_pos >= run_config.output_switch.positions) {
|
||||
throw std::runtime_error("gpr.tx_geometry output_pos is out of range");
|
||||
}
|
||||
if (!seen_tx_positions.insert(entry.output_pos).second) {
|
||||
throw std::runtime_error("gpr.tx_geometry contains duplicate output_pos");
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_set<std::uint32_t> seen_rx_positions{};
|
||||
for (const auto& entry : config.rx_geometry) {
|
||||
if (entry.input_pos >= run_config.input_switch.positions) {
|
||||
throw std::runtime_error("gpr.rx_geometry input_pos is out of range");
|
||||
}
|
||||
if (!seen_rx_positions.insert(entry.input_pos).second) {
|
||||
throw std::runtime_error("gpr.rx_geometry contains duplicate input_pos");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_gpr_config(const Json& object, const RunConfig& run_config) -> GprConfig {
|
||||
const auto* gpr_obj = as_object(object, "gpr");
|
||||
GprConfig config{};
|
||||
config.mode = optional_string(*gpr_obj, "mode", "point");
|
||||
config.relative_permittivity = optional_f32(*gpr_obj, "relative_permittivity", 1.0F);
|
||||
|
||||
if (const auto* tx_value = optional_field(*gpr_obj, "tx_geometry"); tx_value != nullptr) {
|
||||
const auto* tx_array = as_array(*tx_value, "gpr.tx_geometry");
|
||||
config.tx_geometry.reserve(tx_array->size());
|
||||
for (const auto& entry_value : *tx_array) {
|
||||
const auto* entry_obj = as_object(entry_value, "gpr.tx_geometry[]");
|
||||
GprTxGeometry entry{};
|
||||
entry.output_pos = optional_u32(*entry_obj, "output_pos", 0U);
|
||||
entry.x_m = optional_f32(*entry_obj, "x_m", 0.0F);
|
||||
config.tx_geometry.push_back(std::move(entry));
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto* rx_value = optional_field(*gpr_obj, "rx_geometry"); rx_value != nullptr) {
|
||||
const auto* rx_array = as_array(*rx_value, "gpr.rx_geometry");
|
||||
config.rx_geometry.reserve(rx_array->size());
|
||||
for (const auto& entry_value : *rx_array) {
|
||||
const auto* entry_obj = as_object(entry_value, "gpr.rx_geometry[]");
|
||||
GprRxGeometry entry{};
|
||||
entry.input_pos = optional_u32(*entry_obj, "input_pos", 0U);
|
||||
entry.x_m = optional_f32(*entry_obj, "x_m", 0.0F);
|
||||
config.rx_geometry.push_back(std::move(entry));
|
||||
}
|
||||
}
|
||||
|
||||
validate_gpr_config(config, run_config);
|
||||
return config;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_switch_config(
|
||||
const Json& object,
|
||||
const std::string& default_name,
|
||||
@@ -379,6 +444,12 @@ auto load_run_config(const std::string& path) -> RunConfig {
|
||||
config.preprocess.reference_bundle_path = optional_string(*preprocess_obj, "reference_bundle_path", "");
|
||||
}
|
||||
|
||||
if (const auto* gpr_value = optional_field(*root_obj, "gpr"); gpr_value != nullptr) {
|
||||
config.gpr = parse_gpr_config(*gpr_value, config);
|
||||
} else {
|
||||
validate_gpr_config(config.gpr, config);
|
||||
}
|
||||
|
||||
{
|
||||
const auto* rings_obj = as_object(required_field(*root_obj, "rings"), "rings");
|
||||
config.rings.raw = parse_ring_endpoint(*rings_obj, "raw", config.rings.raw);
|
||||
|
||||
@@ -49,6 +49,8 @@ using PreprocessedCollection = RawSweepCollection;
|
||||
enum class ResultKind : std::uint8_t {
|
||||
TraceComplex = 1,
|
||||
ScalarF32 = 2,
|
||||
ImageF32 = 3,
|
||||
TableF32 = 4,
|
||||
};
|
||||
|
||||
struct ResultPayload {
|
||||
@@ -60,6 +62,13 @@ struct ResultPayload {
|
||||
std::vector<Complex32> trace{};
|
||||
// Valid for ScalarF32 payloads.
|
||||
float scalar_value = 0.0F;
|
||||
// Valid for ImageF32 payloads.
|
||||
std::vector<float> image_x_axis{};
|
||||
std::vector<float> image_y_axis{};
|
||||
std::vector<float> image_values{};
|
||||
// Valid for TableF32 payloads.
|
||||
std::uint32_t table_columns = 0;
|
||||
std::vector<float> table_values{};
|
||||
};
|
||||
|
||||
struct ResultBlock {
|
||||
@@ -70,6 +79,7 @@ struct ResultBlock {
|
||||
struct ResultCollection {
|
||||
std::uint64_t collection_id = 0;
|
||||
std::uint64_t monotonic_ns = 0;
|
||||
std::vector<ResultPayload> collection_payloads{};
|
||||
std::vector<ResultBlock> blocks{};
|
||||
};
|
||||
|
||||
|
||||
@@ -200,6 +200,75 @@ auto read_trace_result_payload(BinaryReader& reader, ResultPayload* payload) ->
|
||||
}
|
||||
}
|
||||
|
||||
void write_image_result_payload(BinaryWriter& writer, const ResultPayload& payload) {
|
||||
const auto x_count = checked_count_to_u32(payload.image_x_axis.size(), "Result image x axis point count");
|
||||
const auto y_count = checked_count_to_u32(payload.image_y_axis.size(), "Result image y axis point count");
|
||||
const std::size_t expected_value_count = static_cast<std::size_t>(x_count) * static_cast<std::size_t>(y_count);
|
||||
if (payload.image_values.size() != expected_value_count) {
|
||||
throw std::runtime_error("Result image payload has inconsistent axis/value sizes");
|
||||
}
|
||||
|
||||
writer.write(x_count);
|
||||
writer.write(y_count);
|
||||
for (const auto value : payload.image_x_axis) {
|
||||
writer.write(value);
|
||||
}
|
||||
for (const auto value : payload.image_y_axis) {
|
||||
writer.write(value);
|
||||
}
|
||||
for (const auto value : payload.image_values) {
|
||||
writer.write(value);
|
||||
}
|
||||
}
|
||||
|
||||
auto read_image_result_payload(BinaryReader& reader, ResultPayload* payload) -> void {
|
||||
const auto x_count = reader.read<std::uint32_t>();
|
||||
const auto y_count = reader.read<std::uint32_t>();
|
||||
|
||||
payload->image_x_axis.reserve(x_count);
|
||||
payload->image_y_axis.reserve(y_count);
|
||||
payload->image_values.reserve(static_cast<std::size_t>(x_count) * static_cast<std::size_t>(y_count));
|
||||
|
||||
for (std::uint32_t index = 0; index < x_count; ++index) {
|
||||
payload->image_x_axis.push_back(reader.read<float>());
|
||||
}
|
||||
for (std::uint32_t index = 0; index < y_count; ++index) {
|
||||
payload->image_y_axis.push_back(reader.read<float>());
|
||||
}
|
||||
for (std::size_t index = 0; index < static_cast<std::size_t>(x_count) * static_cast<std::size_t>(y_count); ++index) {
|
||||
payload->image_values.push_back(reader.read<float>());
|
||||
}
|
||||
}
|
||||
|
||||
void write_table_result_payload(BinaryWriter& writer, const ResultPayload& payload) {
|
||||
const auto column_count = payload.table_columns;
|
||||
if (column_count == 0U && !payload.table_values.empty()) {
|
||||
throw std::runtime_error("Result table payload must declare table_columns when values are present");
|
||||
}
|
||||
if (column_count != 0U && (payload.table_values.size() % column_count) != 0U) {
|
||||
throw std::runtime_error("Result table payload has inconsistent column/value sizes");
|
||||
}
|
||||
|
||||
const std::uint32_t row_count =
|
||||
column_count == 0U ? 0U : checked_count_to_u32(payload.table_values.size() / column_count, "Result table row count");
|
||||
|
||||
writer.write(column_count);
|
||||
writer.write(row_count);
|
||||
for (const auto value : payload.table_values) {
|
||||
writer.write(value);
|
||||
}
|
||||
}
|
||||
|
||||
auto read_table_result_payload(BinaryReader& reader, ResultPayload* payload) -> void {
|
||||
const auto column_count = reader.read<std::uint32_t>();
|
||||
const auto row_count = reader.read<std::uint32_t>();
|
||||
payload->table_columns = column_count;
|
||||
payload->table_values.reserve(static_cast<std::size_t>(column_count) * static_cast<std::size_t>(row_count));
|
||||
for (std::size_t index = 0; index < static_cast<std::size_t>(column_count) * static_cast<std::size_t>(row_count); ++index) {
|
||||
payload->table_values.push_back(reader.read<float>());
|
||||
}
|
||||
}
|
||||
|
||||
void write_result_payload(BinaryWriter& writer, const ResultPayload& payload) {
|
||||
writer.write(static_cast<std::uint8_t>(payload.kind));
|
||||
writer.write_string(payload.processing_name);
|
||||
@@ -211,6 +280,12 @@ void write_result_payload(BinaryWriter& writer, const ResultPayload& payload) {
|
||||
case ResultKind::ScalarF32:
|
||||
writer.write(payload.scalar_value);
|
||||
return;
|
||||
case ResultKind::ImageF32:
|
||||
write_image_result_payload(writer, payload);
|
||||
return;
|
||||
case ResultKind::TableF32:
|
||||
write_table_result_payload(writer, payload);
|
||||
return;
|
||||
default:
|
||||
throw std::runtime_error("Unsupported result payload kind");
|
||||
}
|
||||
@@ -228,6 +303,12 @@ void write_result_payload(BinaryWriter& writer, const ResultPayload& payload) {
|
||||
case ResultKind::ScalarF32:
|
||||
payload.scalar_value = reader.read<float>();
|
||||
return payload;
|
||||
case ResultKind::ImageF32:
|
||||
read_image_result_payload(reader, &payload);
|
||||
return payload;
|
||||
case ResultKind::TableF32:
|
||||
read_table_result_payload(reader, &payload);
|
||||
return payload;
|
||||
default:
|
||||
throw std::runtime_error("Unsupported result payload kind in stream");
|
||||
}
|
||||
@@ -296,8 +377,12 @@ auto serialize_result_collection(const ResultCollection& collection) -> std::vec
|
||||
writer.write(kResultCollectionMagic);
|
||||
writer.write(collection.collection_id);
|
||||
writer.write(collection.monotonic_ns);
|
||||
writer.write(checked_count_to_u32(collection.collection_payloads.size(), "Collection payload count"));
|
||||
writer.write(checked_count_to_u32(collection.blocks.size(), "Result block count"));
|
||||
|
||||
for (const auto& payload : collection.collection_payloads) {
|
||||
write_result_payload(writer, payload);
|
||||
}
|
||||
for (const auto& block : collection.blocks) {
|
||||
write_result_block(writer, block);
|
||||
}
|
||||
@@ -317,7 +402,13 @@ auto deserialize_result_collection(std::span<const std::uint8_t> bytes) -> Resul
|
||||
collection.collection_id = reader.read<std::uint64_t>();
|
||||
collection.monotonic_ns = reader.read<std::uint64_t>();
|
||||
|
||||
const auto collection_payload_count = reader.read<std::uint32_t>();
|
||||
const auto block_count = reader.read<std::uint32_t>();
|
||||
collection.collection_payloads.reserve(collection_payload_count);
|
||||
for (std::uint32_t index = 0; index < collection_payload_count; ++index) {
|
||||
collection.collection_payloads.push_back(read_result_payload(reader));
|
||||
}
|
||||
|
||||
collection.blocks.reserve(block_count);
|
||||
for (std::uint32_t index = 0; index < block_count; ++index) {
|
||||
collection.blocks.push_back(read_result_block(reader));
|
||||
|
||||
Reference in New Issue
Block a user