added GPR

This commit is contained in:
Ayzen
2026-03-19 19:29:08 +03:00
parent bdefe3f581
commit 9581730e41
39 changed files with 3830 additions and 201 deletions
@@ -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));
@@ -2,6 +2,7 @@
#include <atomic>
#include <memory>
#include <span>
#include <string>
#include <unordered_map>
#include <vector>
@@ -29,11 +30,13 @@ class DataProcessor {
private:
[[nodiscard]] auto process_collection(
const ipc::PreprocessedCollection& preprocessed,
std::span<const ipc::PreprocessedCollection> previous_collections,
ProcessorInterface& processor,
const ProcessingLiveConfig& live_config
) -> ipc::ResultCollection;
[[nodiscard]] auto resolve_processor(const ProcessingLiveConfig& live_config) -> ProcessorInterface&;
[[nodiscard]] auto should_replay_entire_history(const ProcessingLiveConfig& live_config) const -> bool;
const config::RunConfig& config_;
ipc::ShmRing& preprocessed_ring_;
@@ -4,6 +4,7 @@
#include <cstdint>
#include <filesystem>
#include <string>
#include <vector>
namespace radar::processing {
@@ -17,12 +18,24 @@ struct ProcessingLiveConfig {
std::string processor_mode = "pass_through";
float gain_db = 0.0F;
float phase_deg = 0.0F;
bool pass_through_fixed_y_enabled = false;
float pass_through_y_min_db = -100.0F;
float pass_through_y_max_db = 0.0F;
std::string bscan_axis = "abs";
float bscan_cut_m = 0.824F;
float bscan_max_depth_m = 1.0F;
float bscan_gain = 1.0F;
float bscan_start_freq_mhz = 100.0F;
float bscan_stop_freq_mhz = 8800.0F;
std::vector<std::uint32_t> gpr_input_positions{};
std::vector<std::uint32_t> gpr_output_positions{};
float gpr_min_depth_m = 2.0F;
float gpr_max_depth_m = 14.0F;
float gpr_comp_power = 0.2F;
float gpr_start_freq_mhz = 3000.0F;
float gpr_stop_freq_mhz = 6000.0F;
bool gpr_background_subtract_enabled = true;
std::uint32_t gpr_background_mean_count = 10U;
std::uint64_t history_command_seq = 0;
HistoryCommand history_command = HistoryCommand::None;
};
@@ -3,12 +3,13 @@
#include <algorithm>
#include <chrono>
#include <cstdint>
#include <deque>
#include <span>
#include <stdexcept>
#include <thread>
#include <utility>
#include "bscan_processor.hpp"
#include "gpr_processor.hpp"
#include "passthrough_processor.hpp"
namespace radar::processing {
@@ -54,7 +55,7 @@ DataProcessor::DataProcessor(
void DataProcessor::run(const std::atomic<bool>& stop_requested) {
std::vector<std::uint8_t> bytes{};
std::deque<ipc::PreprocessedCollection> preprocessed_history{};
std::vector<ipc::PreprocessedCollection> preprocessed_history{};
const std::size_t history_limit = replay_history_limit(config_);
std::uint64_t last_replayed_revision = live_config_loader_.revision();
std::uint64_t last_applied_history_command_seq = 0;
@@ -76,13 +77,23 @@ void DataProcessor::run(const std::atomic<bool>& stop_requested) {
last_applied_history_command_seq = live_config.history_command_seq;
}
if (live_config.processor_mode == "bscan") {
for (const auto& cached : preprocessed_history) {
const auto replay_result = process_collection(cached, processor, live_config);
if (should_replay_entire_history(live_config)) {
for (std::size_t index = 0; index < preprocessed_history.size(); ++index) {
const auto replay_result = process_collection(
preprocessed_history[index],
std::span<const ipc::PreprocessedCollection>(preprocessed_history.data(), index),
processor,
live_config
);
publish_result_collection(replay_result, results_ring_);
}
} else if (!preprocessed_history.empty()) {
const auto replay_result = process_collection(preprocessed_history.back(), processor, live_config);
const auto replay_result = process_collection(
preprocessed_history.back(),
std::span<const ipc::PreprocessedCollection>(preprocessed_history.data(), preprocessed_history.size() - 1U),
processor,
live_config
);
publish_result_collection(replay_result, results_ring_);
}
last_replayed_revision = live_revision;
@@ -92,10 +103,15 @@ void DataProcessor::run(const std::atomic<bool>& stop_requested) {
auto preprocessed = ipc::deserialize_preprocessed_collection(bytes);
preprocessed_history.push_back(std::move(preprocessed));
while (preprocessed_history.size() > history_limit) {
preprocessed_history.pop_front();
preprocessed_history.erase(preprocessed_history.begin());
}
const auto result_collection = process_collection(preprocessed_history.back(), processor, live_config);
const auto result_collection = process_collection(
preprocessed_history.back(),
std::span<const ipc::PreprocessedCollection>(preprocessed_history.data(), preprocessed_history.size() - 1U),
processor,
live_config
);
publish_result_collection(result_collection, results_ring_);
continue;
}
@@ -106,25 +122,11 @@ void DataProcessor::run(const std::atomic<bool>& stop_requested) {
auto DataProcessor::process_collection(
const ipc::PreprocessedCollection& preprocessed,
std::span<const ipc::PreprocessedCollection> previous_collections,
ProcessorInterface& processor,
const ProcessingLiveConfig& live_config
) -> ipc::ResultCollection {
ipc::ResultCollection results{};
results.collection_id = preprocessed.collection_id;
// Keep source monotonic timestamp stable across live-config replays.
results.monotonic_ns = preprocessed.monotonic_ns;
results.blocks.reserve(preprocessed.traces.size());
for (const auto& trace : preprocessed.traces) {
ipc::ResultBlock block{};
block.combo = trace.combo;
block.payloads.reserve(1U);
block.payloads.push_back(processor.process(trace, live_config));
results.blocks.push_back(std::move(block));
}
return results;
return processor.process_collection(config_, preprocessed, previous_collections, live_config);
}
auto DataProcessor::resolve_processor(const ProcessingLiveConfig& live_config) -> ProcessorInterface& {
@@ -140,6 +142,12 @@ auto DataProcessor::resolve_processor(const ProcessingLiveConfig& live_config) -
return *(processors_.begin()->second);
}
auto DataProcessor::should_replay_entire_history(const ProcessingLiveConfig& live_config) const -> bool {
const std::string requested_mode =
live_config.processor_mode.empty() ? default_processor_mode_ : live_config.processor_mode;
return requested_mode == "bscan";
}
auto create_default_processors() -> ProcessorRegistry {
ProcessorRegistry processors{};
{
@@ -150,6 +158,10 @@ auto create_default_processors() -> ProcessorRegistry {
auto processor = std::make_unique<BScanProcessor>();
processors.emplace(processor->name(), std::move(processor));
}
{
auto processor = std::make_unique<GprProcessor>();
processors.emplace(processor->name(), std::move(processor));
}
return processors;
}
@@ -45,6 +45,27 @@ using Json = nlohmann::json;
return static_cast<std::uint64_t>(rounded);
}
[[nodiscard]] auto parse_u32_number(const Json& value, const std::string& field_name) -> std::uint32_t {
const auto parsed = parse_u64_number(value, field_name);
if (parsed > static_cast<std::uint64_t>(std::numeric_limits<std::uint32_t>::max())) {
throw std::runtime_error(field_name + " is out of uint32 range");
}
return static_cast<std::uint32_t>(parsed);
}
[[nodiscard]] auto parse_u32_array(const Json& value, const std::string& field_name) -> std::vector<std::uint32_t> {
if (!value.is_array()) {
throw std::runtime_error(field_name + " must be array");
}
std::vector<std::uint32_t> result{};
result.reserve(value.size());
for (std::size_t index = 0; index < value.size(); ++index) {
result.push_back(parse_u32_number(value[index], field_name + "[" + std::to_string(index) + "]"));
}
return result;
}
[[nodiscard]] auto parse_live_config(const std::string& json_text, const std::string& path) -> ProcessingLiveConfig {
if (json_text.empty()) {
throw std::runtime_error("Processing live config is empty: " + path);
@@ -80,6 +101,24 @@ using Json = nlohmann::json;
}
config.phase_deg = static_cast<float>(found->get<double>());
}
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");
@@ -116,6 +155,51 @@ using Json = nlohmann::json;
}
config.bscan_stop_freq_mhz = static_cast<float>(found->get<double>());
}
if (const auto found = root.find("gpr_input_positions"); found != root.end()) {
config.gpr_input_positions = parse_u32_array(*found, "processing.gpr_input_positions");
}
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_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>());
}
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>();
}
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("history_command_seq"); found != root.end()) {
config.history_command_seq = parse_u64_number(*found, "processing.history_command_seq");
}
@@ -7,10 +7,12 @@ namespace radar::processing {
class BScanProcessor final : public ProcessorInterface {
public:
[[nodiscard]] auto name() const -> std::string override;
[[nodiscard]] auto process(
const ipc::SweepTraceBlock& trace,
[[nodiscard]] auto process_collection(
const config::RunConfig& run_config,
const ipc::PreprocessedCollection& collection,
std::span<const ipc::PreprocessedCollection> previous_collections,
const ProcessingLiveConfig& live_config
) -> ipc::ResultPayload override;
) -> ipc::ResultCollection override;
};
} // namespace radar::processing
@@ -0,0 +1,18 @@
#pragma once
#include "processor_interface.hpp"
namespace radar::processing {
class GprProcessor final : public ProcessorInterface {
public:
[[nodiscard]] auto name() const -> std::string override;
[[nodiscard]] auto process_collection(
const config::RunConfig& run_config,
const ipc::PreprocessedCollection& collection,
std::span<const ipc::PreprocessedCollection> previous_collections,
const ProcessingLiveConfig& live_config
) -> ipc::ResultCollection override;
};
} // namespace radar::processing
@@ -7,10 +7,12 @@ namespace radar::processing {
class PassThroughProcessor final : public ProcessorInterface {
public:
[[nodiscard]] auto name() const -> std::string override;
[[nodiscard]] auto process(
const ipc::SweepTraceBlock& trace,
[[nodiscard]] auto process_collection(
const config::RunConfig& run_config,
const ipc::PreprocessedCollection& collection,
std::span<const ipc::PreprocessedCollection> previous_collections,
const ProcessingLiveConfig& live_config
) -> ipc::ResultPayload override;
) -> ipc::ResultCollection override;
};
} // namespace radar::processing
@@ -1,8 +1,10 @@
#pragma once
#include <span>
#include <string>
#include "processing_live_config.hpp"
#include "run_config.hpp"
#include "shared_types.hpp"
namespace radar::processing {
@@ -12,10 +14,12 @@ class ProcessorInterface {
virtual ~ProcessorInterface() = default;
[[nodiscard]] virtual auto name() const -> std::string = 0;
[[nodiscard]] virtual auto process(
const ipc::SweepTraceBlock& trace,
[[nodiscard]] virtual auto process_collection(
const config::RunConfig& run_config,
const ipc::PreprocessedCollection& collection,
std::span<const ipc::PreprocessedCollection> previous_collections,
const ProcessingLiveConfig& live_config
) -> ipc::ResultPayload = 0;
) -> ipc::ResultCollection = 0;
};
} // namespace radar::processing
@@ -215,23 +215,39 @@ auto BScanProcessor::name() const -> std::string {
return "bscan";
}
auto BScanProcessor::process(const ipc::SweepTraceBlock& trace, const ProcessingLiveConfig& live_config)
-> ipc::ResultPayload {
ipc::ResultPayload payload{};
payload.processing_name = name();
payload.kind = ipc::ResultKind::TraceComplex;
auto BScanProcessor::process_collection(
const config::RunConfig& /*run_config*/,
const ipc::PreprocessedCollection& collection,
std::span<const ipc::PreprocessedCollection> /*previous_collections*/,
const ProcessingLiveConfig& live_config
) -> ipc::ResultCollection {
ipc::ResultCollection results{};
results.collection_id = collection.collection_id;
results.monotonic_ns = collection.monotonic_ns;
results.blocks.reserve(collection.traces.size());
auto profile = compute_bscan_profile(trace, live_config);
payload.frequency_hz = std::move(profile.depth_m);
payload.trace.reserve(profile.response.size());
for (const auto value : profile.response) {
payload.trace.push_back(ipc::Complex32{
.re = value,
.im = 0.0F,
});
for (const auto& trace : collection.traces) {
ipc::ResultPayload payload{};
payload.processing_name = name();
payload.kind = ipc::ResultKind::TraceComplex;
auto profile = compute_bscan_profile(trace, live_config);
payload.frequency_hz = std::move(profile.depth_m);
payload.trace.reserve(profile.response.size());
for (const auto value : profile.response) {
payload.trace.push_back(ipc::Complex32{
.re = value,
.im = 0.0F,
});
}
ipc::ResultBlock block{};
block.combo = trace.combo;
block.payloads.push_back(std::move(payload));
results.blocks.push_back(std::move(block));
}
return payload;
return results;
}
} // namespace radar::processing
File diff suppressed because it is too large Load Diff
@@ -13,27 +13,43 @@ auto PassThroughProcessor::name() const -> std::string {
return "pass_through";
}
auto PassThroughProcessor::process(const ipc::SweepTraceBlock& trace, const ProcessingLiveConfig& live_config)
-> ipc::ResultPayload {
ipc::ResultPayload payload{};
payload.processing_name = name();
payload.kind = ipc::ResultKind::TraceComplex;
payload.frequency_hz = trace.frequency_hz;
payload.trace = trace.s21;
auto PassThroughProcessor::process_collection(
const config::RunConfig& /*run_config*/,
const ipc::PreprocessedCollection& collection,
std::span<const ipc::PreprocessedCollection> /*previous_collections*/,
const ProcessingLiveConfig& live_config
) -> ipc::ResultCollection {
ipc::ResultCollection results{};
results.collection_id = collection.collection_id;
results.monotonic_ns = collection.monotonic_ns;
results.blocks.reserve(collection.traces.size());
const float linear_gain = std::pow(10.0F, live_config.gain_db / 20.0F);
const float phase_rad = live_config.phase_deg * (kPi / 180.0F);
const float cos_phase = std::cos(phase_rad);
const float sin_phase = std::sin(phase_rad);
for (const auto& trace : collection.traces) {
ipc::ResultPayload payload{};
payload.processing_name = name();
payload.kind = ipc::ResultKind::TraceComplex;
payload.frequency_hz = trace.frequency_hz;
payload.trace = trace.s21;
for (auto& sample : payload.trace) {
const float re = sample.re;
const float im = sample.im;
sample.re = linear_gain * ((re * cos_phase) - (im * sin_phase));
sample.im = linear_gain * ((re * sin_phase) + (im * cos_phase));
const float linear_gain = std::pow(10.0F, live_config.gain_db / 20.0F);
const float phase_rad = live_config.phase_deg * (kPi / 180.0F);
const float cos_phase = std::cos(phase_rad);
const float sin_phase = std::sin(phase_rad);
for (auto& sample : payload.trace) {
const float re = sample.re;
const float im = sample.im;
sample.re = linear_gain * ((re * cos_phase) - (im * sin_phase));
sample.im = linear_gain * ((re * sin_phase) + (im * cos_phase));
}
ipc::ResultBlock block{};
block.combo = trace.combo;
block.payloads.push_back(std::move(payload));
results.blocks.push_back(std::move(block));
}
return payload;
return results;
}
} // namespace radar::processing