move to new version
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
// Numeric check of the GPR backprojection frame-timing model against the
|
||||
// Multi_antenna_3D notebook (compute_pair_timestamps_for_snapshot +
|
||||
// apply_intra_sweep_phase_correction, 'segmented_interleaved').
|
||||
//
|
||||
// Builds a synthetic 4x4 collection with two segment windows taken from
|
||||
// per-trace capture timestamps, runs build_frame_timing and
|
||||
// apply_motion_correction from gpr_backprojection_processor.ipp, and prints
|
||||
// segment/slot assignment, frame centre, per-Tx pair dtau and the motion phase
|
||||
// at probe frequency indices. Compare against the Python replica
|
||||
// (scripts/gpr_timing_check_reference.py) — every printed number must match.
|
||||
//
|
||||
// Also prints the fallback (zero timestamps) and legacy 'interleaved' layouts.
|
||||
//
|
||||
// Usage: gpr_timing_check
|
||||
#include "gpr_processor.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <complex>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <queue>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace radar::processing {
|
||||
namespace {
|
||||
|
||||
#include "../processors/src/gpr_backprojection_processor.ipp"
|
||||
|
||||
constexpr double kSpeedMps = 1.42;
|
||||
constexpr double kLookAngleDeg = 19.5;
|
||||
constexpr double kDirectionSign = 1.0;
|
||||
constexpr std::size_t kFreqCount = 11U;
|
||||
|
||||
[[nodiscard]] auto make_run_config() -> config::RunConfig {
|
||||
config::RunConfig run_config{};
|
||||
for (std::uint32_t output = 0U; output < 4U; ++output) {
|
||||
for (std::uint32_t input = 0U; input < 4U; ++input) {
|
||||
run_config.run_combos.push_back(ipc::ComboKey{input, output});
|
||||
}
|
||||
}
|
||||
return run_config;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto make_collection(bool with_trace_timestamps) -> ipc::PreprocessedCollection {
|
||||
ipc::PreprocessedCollection collection{};
|
||||
collection.collection_id = 7U;
|
||||
// The collection-level capture starts 10 ms before the first trace — the
|
||||
// trace marks, not the collection window, must define the frame model.
|
||||
collection.capture_start_ns = 990'000'000ULL;
|
||||
collection.capture_end_ns = 1'483'000'000ULL;
|
||||
|
||||
// Segment A (Tx0, Tx1): 1.000..1.223 s; segment B (Tx2, Tx3): 1.223..1.483 s
|
||||
// (260 ms against the 241.5 ms norm -> stretched).
|
||||
const auto window_for_tx = [&](std::uint32_t output) -> std::pair<std::uint64_t, std::uint64_t> {
|
||||
if (!with_trace_timestamps) {
|
||||
return {0U, 0U};
|
||||
}
|
||||
return output < 2U
|
||||
? std::pair<std::uint64_t, std::uint64_t>{1'000'000'000ULL, 1'223'000'000ULL}
|
||||
: std::pair<std::uint64_t, std::uint64_t>{1'223'000'000ULL, 1'483'000'000ULL};
|
||||
};
|
||||
|
||||
for (std::uint32_t output = 0U; output < 4U; ++output) {
|
||||
for (std::uint32_t input = 0U; input < 4U; ++input) {
|
||||
ipc::SweepTraceBlock trace{};
|
||||
trace.combo = ipc::ComboKey{input, output};
|
||||
const auto window = window_for_tx(output);
|
||||
trace.capture_start_ns = window.first;
|
||||
trace.capture_end_ns = window.second;
|
||||
collection.traces.push_back(std::move(trace));
|
||||
}
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto make_trace(std::uint32_t output_pos) -> SelectedTrace {
|
||||
SelectedTrace trace{};
|
||||
trace.combo = ipc::ComboKey{0U, output_pos};
|
||||
trace.frequency_hz.resize(kFreqCount);
|
||||
trace.s21.assign(kFreqCount, std::complex<double>(1.0, 0.0));
|
||||
for (std::size_t index = 0U; index < kFreqCount; ++index) {
|
||||
trace.frequency_hz[index] =
|
||||
1.5e9 + (static_cast<double>(index) * (6.0e9 - 1.5e9) / static_cast<double>(kFreqCount - 1U));
|
||||
}
|
||||
return trace;
|
||||
}
|
||||
|
||||
void print_timing(const char* label, const FrameTiming& timing) {
|
||||
std::printf("%s: frame_center_s=%.9f from_timestamps=%d\n",
|
||||
label, timing.frame_center_s, timing.from_trace_timestamps ? 1 : 0);
|
||||
for (std::size_t index = 0U; index < timing.segments.size(); ++index) {
|
||||
const auto& segment = timing.segments[index];
|
||||
std::printf(" segment %zu: start=%.9f end=%.9f center=%.9f stretched=%d\n",
|
||||
index, segment.start_s, segment.end_s, segment.center_s,
|
||||
segment.stretched ? 1 : 0);
|
||||
}
|
||||
std::vector<std::uint32_t> outputs{};
|
||||
outputs.reserve(timing.by_output_pos.size());
|
||||
for (const auto& [output, _slot] : timing.by_output_pos) {
|
||||
outputs.push_back(output);
|
||||
}
|
||||
std::sort(outputs.begin(), outputs.end());
|
||||
for (const auto output : outputs) {
|
||||
const auto& slot = timing.by_output_pos.at(output);
|
||||
std::printf(" tx o%u: segment=%zu slot=%zu slot_count=%zu\n",
|
||||
output, slot.segment_index, slot.slot, slot.slot_count);
|
||||
}
|
||||
}
|
||||
|
||||
void print_motion_phase(const FrameTiming& timing, MotionMode motion_mode, const char* label) {
|
||||
for (std::uint32_t output = 0U; output < 4U; ++output) {
|
||||
auto trace = make_trace(output);
|
||||
const auto& slot = timing.by_output_pos.at(output);
|
||||
apply_motion_correction(
|
||||
trace,
|
||||
timing.segments[slot.segment_index],
|
||||
slot.slot,
|
||||
slot.slot_count,
|
||||
timing.frame_center_s,
|
||||
1.5e9,
|
||||
6.0e9,
|
||||
kSpeedOfLightMetersPerSec,
|
||||
kSpeedMps,
|
||||
kLookAngleDeg,
|
||||
kDirectionSign,
|
||||
motion_mode
|
||||
);
|
||||
// s21 started at 1+0j, so arg(s21[k]) = -phi[k] (notebook sign = -1).
|
||||
std::printf("%s tx o%u: phi(k=0)=%.12e phi(k=5)=%.12e phi(k=10)=%.12e\n",
|
||||
label, output,
|
||||
-std::arg(trace.s21[0]), -std::arg(trace.s21[5]), -std::arg(trace.s21[10]));
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] auto run_check() -> int {
|
||||
const auto run_config = make_run_config();
|
||||
|
||||
const auto timed = build_frame_timing(run_config, make_collection(true), 2U, true);
|
||||
print_timing("segmented+timestamps", timed);
|
||||
print_motion_phase(timed, MotionMode::IntMinus, "int_minus");
|
||||
print_motion_phase(timed, MotionMode::IntFocus, "int_focus");
|
||||
|
||||
const auto fallback = build_frame_timing(run_config, make_collection(false), 2U, true);
|
||||
print_timing("segmented+fallback", fallback);
|
||||
print_motion_phase(fallback, MotionMode::IntMinus, "fallback int_minus");
|
||||
|
||||
const auto interleaved = build_frame_timing(run_config, make_collection(true), 2U, false);
|
||||
print_timing("interleaved", interleaved);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace radar::processing
|
||||
|
||||
auto main() -> int {
|
||||
return radar::processing::run_check();
|
||||
}
|
||||
Reference in New Issue
Block a user