added s11!
This commit is contained in:
@@ -73,16 +73,31 @@ struct RuntimeConfig {
|
||||
std::string processing_live_config_path = "python_app/runtime/processing_live.json";
|
||||
};
|
||||
|
||||
struct PreprocessAssetConfig {
|
||||
std::string set_name{};
|
||||
std::string bundle_path{};
|
||||
};
|
||||
|
||||
struct S21PreprocessConfig {
|
||||
PreprocessAssetConfig calibration{};
|
||||
PreprocessAssetConfig reference{};
|
||||
};
|
||||
|
||||
struct S11CalibrationPreprocessConfig {
|
||||
PreprocessAssetConfig open{};
|
||||
PreprocessAssetConfig short_standard{};
|
||||
PreprocessAssetConfig load{};
|
||||
};
|
||||
|
||||
struct S11PreprocessConfig {
|
||||
S11CalibrationPreprocessConfig calibration{};
|
||||
PreprocessAssetConfig reference{};
|
||||
};
|
||||
|
||||
struct PreprocessConfig {
|
||||
// Names and bundle paths selected by Python GUI layer.
|
||||
std::string s21_calibration_set{};
|
||||
std::string s21_reference_set{};
|
||||
std::string s21_calibration_bundle_path{};
|
||||
std::string s21_reference_bundle_path{};
|
||||
std::string s11_open_calibration_bundle_path{};
|
||||
std::string s11_short_calibration_bundle_path{};
|
||||
std::string s11_load_calibration_bundle_path{};
|
||||
std::string s11_reference_bundle_path{};
|
||||
// Channel-specific preprocessing assets selected by Python GUI layer.
|
||||
S21PreprocessConfig s21{};
|
||||
S11PreprocessConfig s11{};
|
||||
};
|
||||
|
||||
struct GprTxGeometry {
|
||||
|
||||
@@ -140,6 +140,20 @@ using Json = nlohmann::json;
|
||||
return fallback;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_preprocess_asset(
|
||||
const Json& object,
|
||||
const std::string& key,
|
||||
const std::string& context
|
||||
) -> PreprocessAssetConfig {
|
||||
const auto asset_context = context + "." + key;
|
||||
const auto* asset_obj = as_object(required_field(object, key), asset_context);
|
||||
|
||||
PreprocessAssetConfig asset{};
|
||||
asset.set_name = optional_string(*asset_obj, "set_name", "");
|
||||
asset.bundle_path = optional_string(*asset_obj, "bundle_path", "");
|
||||
return asset;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_driver_mode(const std::string& value) -> DriverMode {
|
||||
if (value == "mock") {
|
||||
return DriverMode::Mock;
|
||||
@@ -438,18 +452,20 @@ auto load_run_config(const std::string& path) -> RunConfig {
|
||||
|
||||
{
|
||||
const auto* preprocess_obj = as_object(required_field(*root_obj, "preprocess"), "preprocess");
|
||||
config.preprocess.s21_calibration_set = optional_string(*preprocess_obj, "s21_calibration_set", "");
|
||||
config.preprocess.s21_reference_set = optional_string(*preprocess_obj, "s21_reference_set", "");
|
||||
config.preprocess.s21_calibration_bundle_path =
|
||||
optional_string(*preprocess_obj, "s21_calibration_bundle_path", "");
|
||||
config.preprocess.s21_reference_bundle_path = optional_string(*preprocess_obj, "s21_reference_bundle_path", "");
|
||||
config.preprocess.s11_open_calibration_bundle_path =
|
||||
optional_string(*preprocess_obj, "s11_open_calibration_bundle_path", "");
|
||||
config.preprocess.s11_short_calibration_bundle_path =
|
||||
optional_string(*preprocess_obj, "s11_short_calibration_bundle_path", "");
|
||||
config.preprocess.s11_load_calibration_bundle_path =
|
||||
optional_string(*preprocess_obj, "s11_load_calibration_bundle_path", "");
|
||||
config.preprocess.s11_reference_bundle_path = optional_string(*preprocess_obj, "s11_reference_bundle_path", "");
|
||||
const auto* s21_obj = as_object(required_field(*preprocess_obj, "s21"), "preprocess.s21");
|
||||
config.preprocess.s21.calibration = parse_preprocess_asset(*s21_obj, "calibration", "preprocess.s21");
|
||||
config.preprocess.s21.reference = parse_preprocess_asset(*s21_obj, "reference", "preprocess.s21");
|
||||
|
||||
const auto* s11_obj = as_object(required_field(*preprocess_obj, "s11"), "preprocess.s11");
|
||||
const auto* s11_calibration_obj =
|
||||
as_object(required_field(*s11_obj, "calibration"), "preprocess.s11.calibration");
|
||||
config.preprocess.s11.calibration.open =
|
||||
parse_preprocess_asset(*s11_calibration_obj, "open", "preprocess.s11.calibration");
|
||||
config.preprocess.s11.calibration.short_standard =
|
||||
parse_preprocess_asset(*s11_calibration_obj, "short", "preprocess.s11.calibration");
|
||||
config.preprocess.s11.calibration.load =
|
||||
parse_preprocess_asset(*s11_calibration_obj, "load", "preprocess.s11.calibration");
|
||||
config.preprocess.s11.reference = parse_preprocess_asset(*s11_obj, "reference", "preprocess.s11");
|
||||
}
|
||||
|
||||
if (const auto* gpr_value = optional_field(*root_obj, "gpr"); gpr_value != nullptr) {
|
||||
|
||||
@@ -65,16 +65,16 @@ int main(int argc, char** argv) {
|
||||
radar::preprocessing::CalibrationMaster calibration_master(
|
||||
radar::preprocessing::make_s21_through_calibrator()
|
||||
);
|
||||
calibration_master.load_s21_calibration_bundle(config.preprocess.s21_calibration_bundle_path);
|
||||
calibration_master.load_s21_calibration_bundle(config.preprocess.s21.calibration.bundle_path);
|
||||
calibration_master.load_s11_calibration_bundle(
|
||||
config.preprocess.s11_open_calibration_bundle_path,
|
||||
config.preprocess.s11_short_calibration_bundle_path,
|
||||
config.preprocess.s11_load_calibration_bundle_path
|
||||
config.preprocess.s11.calibration.open.bundle_path,
|
||||
config.preprocess.s11.calibration.short_standard.bundle_path,
|
||||
config.preprocess.s11.calibration.load.bundle_path
|
||||
);
|
||||
|
||||
radar::preprocessing::ReferenceMaster reference_master;
|
||||
reference_master.load_s21_reference_bundle(config.preprocess.s21_reference_bundle_path);
|
||||
reference_master.load_s11_reference_bundle(config.preprocess.s11_reference_bundle_path);
|
||||
reference_master.load_s21_reference_bundle(config.preprocess.s21.reference.bundle_path);
|
||||
reference_master.load_s11_reference_bundle(config.preprocess.s11.reference.bundle_path);
|
||||
reference_master.prepare_calibrated(calibration_master, config.run_combos);
|
||||
|
||||
radar::preprocessing::DataPreprocessor preprocessor(
|
||||
|
||||
@@ -23,6 +23,7 @@ struct ProcessingLiveConfig {
|
||||
float pass_through_y_min_db = -100.0F;
|
||||
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_gain = 1.0F;
|
||||
|
||||
@@ -30,11 +30,11 @@ using Json = nlohmann::json;
|
||||
throw std::runtime_error("processing.history_command must be one of: none, remove_last, clear_all");
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_pass_through_channel(const std::string& value) -> std::string {
|
||||
[[nodiscard]] auto parse_s_parameter_channel(const std::string& value, const std::string& field_name) -> std::string {
|
||||
if (value == "s21" || value == "s11") {
|
||||
return value;
|
||||
}
|
||||
throw std::runtime_error("processing.pass_through_channel must be one of: s21, s11");
|
||||
throw std::runtime_error(field_name + " must be one of: s21, s11");
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_u64_number(const Json& value, const std::string& field_name) -> std::uint64_t {
|
||||
@@ -112,7 +112,8 @@ using Json = nlohmann::json;
|
||||
if (!found->is_string()) {
|
||||
throw std::runtime_error("processing.pass_through_channel must be string");
|
||||
}
|
||||
config.pass_through_channel = parse_pass_through_channel(found->get<std::string>());
|
||||
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()) {
|
||||
@@ -138,6 +139,12 @@ using Json = nlohmann::json;
|
||||
}
|
||||
config.bscan_axis = found->get<std::string>();
|
||||
}
|
||||
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");
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -92,15 +93,28 @@ void fft_inplace(std::vector<std::complex<double>>& values, bool inverse) {
|
||||
return std::abs(sample);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto fallback_profile(const ipc::SweepTraceBlock& trace) -> BScanProfile {
|
||||
const std::size_t point_count = std::min(trace.frequency_hz.size(), trace.s21.size());
|
||||
[[nodiscard]] auto selected_trace_samples(
|
||||
const ipc::SweepTraceBlock& trace,
|
||||
const ProcessingLiveConfig& live_config
|
||||
) -> std::span<const ipc::Complex32> {
|
||||
if (live_config.bscan_channel == "s11") {
|
||||
return trace.s11;
|
||||
}
|
||||
return trace.s21;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto fallback_profile(
|
||||
const ipc::SweepTraceBlock& trace,
|
||||
std::span<const ipc::Complex32> selected_samples
|
||||
) -> BScanProfile {
|
||||
const std::size_t point_count = std::min(trace.frequency_hz.size(), selected_samples.size());
|
||||
BScanProfile fallback{};
|
||||
fallback.depth_m.reserve(point_count);
|
||||
fallback.response.reserve(point_count);
|
||||
|
||||
const float denominator = point_count > 1U ? static_cast<float>(point_count - 1U) : 1.0F;
|
||||
for (std::size_t index = 0U; index < point_count; ++index) {
|
||||
const auto& sample = trace.s21[index];
|
||||
const auto& sample = selected_samples[index];
|
||||
fallback.depth_m.push_back(static_cast<float>(static_cast<float>(index) / denominator));
|
||||
fallback.response.push_back(std::sqrt(sample.re * sample.re + sample.im * sample.im));
|
||||
}
|
||||
@@ -111,9 +125,10 @@ void fft_inplace(std::vector<std::complex<double>>& values, bool inverse) {
|
||||
const ipc::SweepTraceBlock& trace,
|
||||
const ProcessingLiveConfig& live_config
|
||||
) -> BScanProfile {
|
||||
const std::size_t point_count = std::min(trace.frequency_hz.size(), trace.s21.size());
|
||||
const auto selected_samples = selected_trace_samples(trace, live_config);
|
||||
const std::size_t point_count = std::min(trace.frequency_hz.size(), selected_samples.size());
|
||||
if (point_count < 2U) {
|
||||
return fallback_profile(trace);
|
||||
return fallback_profile(trace, selected_samples);
|
||||
}
|
||||
|
||||
const double configured_start_hz = static_cast<double>(live_config.bscan_start_freq_mhz) * 1'000'000.0;
|
||||
@@ -122,55 +137,55 @@ void fft_inplace(std::vector<std::complex<double>>& values, bool inverse) {
|
||||
const double stop_hz = std::max(configured_start_hz, configured_stop_hz);
|
||||
|
||||
std::vector<double> filtered_freq_hz{};
|
||||
std::vector<std::complex<double>> filtered_s21{};
|
||||
std::vector<std::complex<double>> filtered_samples{};
|
||||
filtered_freq_hz.reserve(point_count);
|
||||
filtered_s21.reserve(point_count);
|
||||
filtered_samples.reserve(point_count);
|
||||
|
||||
for (std::size_t index = 0U; index < point_count; ++index) {
|
||||
const double frequency_hz = static_cast<double>(trace.frequency_hz[index]);
|
||||
if (frequency_hz < start_hz || frequency_hz > stop_hz) {
|
||||
continue;
|
||||
}
|
||||
const auto& sample = trace.s21[index];
|
||||
const auto& sample = selected_samples[index];
|
||||
filtered_freq_hz.push_back(frequency_hz);
|
||||
filtered_s21.emplace_back(static_cast<double>(sample.re), static_cast<double>(sample.im));
|
||||
filtered_samples.emplace_back(static_cast<double>(sample.re), static_cast<double>(sample.im));
|
||||
}
|
||||
|
||||
if (filtered_freq_hz.size() < 2U) {
|
||||
return fallback_profile(trace);
|
||||
return fallback_profile(trace, selected_samples);
|
||||
}
|
||||
|
||||
const std::size_t filtered_count = filtered_freq_hz.size();
|
||||
const double df = (filtered_freq_hz.back() - filtered_freq_hz.front()) / static_cast<double>(filtered_count - 1U);
|
||||
if (df <= 0.0) {
|
||||
return fallback_profile(trace);
|
||||
return fallback_profile(trace, selected_samples);
|
||||
}
|
||||
|
||||
const auto start_bin = static_cast<std::int64_t>(std::llround(filtered_freq_hz.front() / df));
|
||||
if (start_bin < 0) {
|
||||
return fallback_profile(trace);
|
||||
return fallback_profile(trace, selected_samples);
|
||||
}
|
||||
|
||||
const auto start_index = static_cast<std::size_t>(start_bin);
|
||||
if (start_index > (std::numeric_limits<std::size_t>::max() / 2U)) {
|
||||
return fallback_profile(trace);
|
||||
return fallback_profile(trace, selected_samples);
|
||||
}
|
||||
if (start_index > (std::numeric_limits<std::size_t>::max() - filtered_count + 1U)) {
|
||||
return fallback_profile(trace);
|
||||
return fallback_profile(trace, selected_samples);
|
||||
}
|
||||
|
||||
const std::size_t min_fft_len = 2U * (start_index + filtered_count - 1U);
|
||||
const std::size_t fft_len = next_power_of_two(min_fft_len);
|
||||
if (fft_len < min_fft_len || (fft_len & (fft_len - 1U)) != 0U) {
|
||||
return fallback_profile(trace);
|
||||
return fallback_profile(trace, selected_samples);
|
||||
}
|
||||
if (start_index > fft_len || filtered_count > (fft_len - start_index)) {
|
||||
return fallback_profile(trace);
|
||||
return fallback_profile(trace, selected_samples);
|
||||
}
|
||||
|
||||
std::vector<std::complex<double>> spectrum(fft_len, std::complex<double>(0.0, 0.0));
|
||||
for (std::size_t index = 0U; index < filtered_count; ++index) {
|
||||
spectrum[start_index + index] = filtered_s21[index];
|
||||
spectrum[start_index + index] = filtered_samples[index];
|
||||
}
|
||||
|
||||
fft_inplace(spectrum, true);
|
||||
|
||||
Reference in New Issue
Block a user