From 241319b84b693049192acb8bd7c385efbfd3b7c5 Mon Sep 17 00:00:00 2001 From: gaozheng Date: Mon, 29 Jun 2026 12:46:08 +0800 Subject: [PATCH] =?UTF-8?q?feat(radar):=20=E8=A7=84=E8=8C=83=E5=8C=96?= =?UTF-8?q?=E4=BD=93=20bridge=20buildLineVolumeFromNormalized?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将 RadarVolumeAssembler.cpp 从 geopro_gpr3dv_bridge 迁入纯 geopro_io_gpr, 避免 io_gpr 反链 bridge 形成循环依赖。新增 NormalizedRadarVolumeBridge 组合 reader(.head/.data) + assembleRadarVolume → BuiltI16(X=道/Y=通道/Z=采样)。 --- src/io/gpr/CMakeLists.txt | 4 +- src/io/gpr/NormalizedRadarVolumeBridge.cpp | 55 +++++++++++++++++++ src/io/gpr/NormalizedRadarVolumeBridge.hpp | 20 +++++++ tests/CMakeLists.txt | 2 + tests/io/gpr/test_normalized_radar_bridge.cpp | 28 ++++++++++ 5 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 src/io/gpr/NormalizedRadarVolumeBridge.cpp create mode 100644 src/io/gpr/NormalizedRadarVolumeBridge.hpp create mode 100644 tests/io/gpr/test_normalized_radar_bridge.cpp diff --git a/src/io/gpr/CMakeLists.txt b/src/io/gpr/CMakeLists.txt index e552813..0470637 100644 --- a/src/io/gpr/CMakeLists.txt +++ b/src/io/gpr/CMakeLists.txt @@ -1,6 +1,7 @@ add_library(geopro_io_gpr STATIC IprHeader.cpp IprbReader.cpp GprGeometry.cpp GprSurveyAssembler.cpp - GpsTrack.cpp NormalizedRadarReader.cpp) + GpsTrack.cpp NormalizedRadarReader.cpp + RadarVolumeAssembler.cpp NormalizedRadarVolumeBridge.cpp) target_include_directories(geopro_io_gpr PUBLIC ${CMAKE_SOURCE_DIR}/src) target_compile_features(geopro_io_gpr PUBLIC cxx_std_17) # GprSurveyAssembler 返回 geopro::core::GprSurvey(头文件内联,仅需 include 解析)。 @@ -16,7 +17,6 @@ set_target_properties(geopro_io_gpr PROPERTIES AUTOMOC OFF AUTOUIC OFF AUTORCC O # 额外用 GpsTrack(geopro_io_gpr) 解析 .gps。 add_library(geopro_gpr3dv_bridge STATIC Gpr3dvVolumeBridge.cpp - RadarVolumeAssembler.cpp Gpr3dvSurveyVolumeBridge.cpp) target_include_directories(geopro_gpr3dv_bridge PUBLIC ${CMAKE_SOURCE_DIR}/src) target_compile_features(geopro_gpr3dv_bridge PUBLIC cxx_std_17) diff --git a/src/io/gpr/NormalizedRadarVolumeBridge.cpp b/src/io/gpr/NormalizedRadarVolumeBridge.cpp new file mode 100644 index 0000000..ca021d6 --- /dev/null +++ b/src/io/gpr/NormalizedRadarVolumeBridge.cpp @@ -0,0 +1,55 @@ +#include "io/gpr/NormalizedRadarVolumeBridge.hpp" + +#include +#include +#include +#include +#include +#include + +#include "io/gpr/NormalizedRadarReader.hpp" +#include "io/gpr/RadarVolumeAssembler.hpp" + +namespace geopro::io::gpr { + +geopro::core::BuiltI16 buildLineVolumeFromNormalized(const std::string& lineDir, + const std::string& linePrefix, + int coarse, double targetDy) { + const std::string head = lineDir + "/" + linePrefix + ".head"; + const std::string data = lineDir + "/" + linePrefix + ".data"; + + std::string headText; + { + std::ifstream f(head); + if (!f) throw std::runtime_error("打开 .head 失败: " + head); + std::stringstream ss; + ss << f.rdbuf(); + headText = ss.str(); + } + + const RadarHeader h = parseRadarHead(headText); + const std::vector cube = readRadarDataCube(data, h); + const int M = h.channels; + const int N = h.samples; + + RadarCubeDesc d; + d.channels = M; + d.traces = h.traces; + d.samples = N; + d.chXOffsets = h.chXOffsets; + d.dxBase = h.distanceInterval > 1e-9 ? h.distanceInterval : 1.0; + d.dyWhenNotInterpolated = + (h.chXOffsets.size() >= 2 && M > 1) + ? (h.chXOffsets.back() - h.chXOffsets.front()) / (M - 1) + : 1.0; + d.dz = depthSpacingZ(h) > 1e-12 ? depthSpacingZ(h) : 1.0; + + // position-major 立方体索引 ((t*M + c)*N + s),与 readRadarDataCube 一致。 + CubeSampler sample = [&cube, M, N](int c, int t, int s) { + return static_cast(cube[(static_cast(t) * M + c) * N + s]); + }; + + return assembleRadarVolume(d, sample, coarse, targetDy); +} + +} // namespace geopro::io::gpr diff --git a/src/io/gpr/NormalizedRadarVolumeBridge.hpp b/src/io/gpr/NormalizedRadarVolumeBridge.hpp new file mode 100644 index 0000000..128e89f --- /dev/null +++ b/src/io/gpr/NormalizedRadarVolumeBridge.hpp @@ -0,0 +1,20 @@ +#ifndef GEOPRO_IO_GPR_NORMALIZED_RADAR_VOLUME_BRIDGE_HPP +#define GEOPRO_IO_GPR_NORMALIZED_RADAR_VOLUME_BRIDGE_HPP + +#include + +#include "core/algo/GprVolumeBuilder.hpp" // geopro::core::BuiltI16 + +namespace geopro::io::gpr { + +// 读 {lineDir}/{linePrefix}.head + .data → assembleRadarVolume → BuiltI16 +// (轴 X=道/Y=通道/Z=采样)。coarse 沿道下采样;targetDy 线内通道插值(读 .head +// CH_X_OFFSETS)。打开/解析失败抛 std::runtime_error。 +geopro::core::BuiltI16 buildLineVolumeFromNormalized(const std::string& lineDir, + const std::string& linePrefix, + int coarse = 4, + double targetDy = 0.025); + +} // namespace geopro::io::gpr + +#endif // GEOPRO_IO_GPR_NORMALIZED_RADAR_VOLUME_BRIDGE_HPP diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 21edb38..e55bd49 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -228,6 +228,8 @@ target_sources(geopro_tests PRIVATE io/gpr/test_gpr_survey_assembler.cpp) target_sources(geopro_tests PRIVATE io/gpr/test_gps_track.cpp) # NormalizedRadarReader:规范化 .head(KEY:VALUE) 解析(维度/字节序/通道偏移/波速/深度间距,纯 C++17)。 target_sources(geopro_tests PRIVATE io/gpr/test_normalized_radar_reader.cpp) +# NormalizedRadarVolumeBridge:组合 reader(.head/.data) + assembleRadarVolume → BuiltI16(轴 X=道/Y=通道/Z=采样)。 +target_sources(geopro_tests PRIVATE io/gpr/test_normalized_radar_bridge.cpp) target_link_libraries(geopro_tests PRIVATE geopro_io_gpr) # Gpr3dvVolumeBridge(P2):gpr3dv 处理后立方体 → geopro 量化体(轴 X=道/Y=通道/Z=样本)。 diff --git a/tests/io/gpr/test_normalized_radar_bridge.cpp b/tests/io/gpr/test_normalized_radar_bridge.cpp new file mode 100644 index 0000000..8e06d1f --- /dev/null +++ b/tests/io/gpr/test_normalized_radar_bridge.cpp @@ -0,0 +1,28 @@ +#include +#include +#include +#include +#include "core/algo/GprVolumeBuilder.hpp" +#include "io/gpr/NormalizedRadarVolumeBridge.hpp" +namespace fs = std::filesystem; + +TEST(NormalizedRadarBridge, BuildsVolumeWithExpectedAxes) { + // K=4 道, M=2 通道, N=3 采样, 无通道偏移(不插值), coarse=1。 + fs::path dir = fs::temp_directory_path() / "radar_bridge_test"; + fs::create_directories(dir); + { std::ofstream f(dir / "L.head"); + f << "SAMPLES:3\nNUMBER_OF_CH:2\nLAST_TRACE:8\nBITS:16\nENDIAN_TYPE:1\n" + "DISTANCE_INTERVAL:0.1\nTIMEWINDOW:30\nDIELECTRIC:9\n"; } + { std::ofstream f(dir / "L.data", std::ios::binary); + for (int t = 0; t < 4; ++t) for (int c = 0; c < 2; ++c) for (int s = 0; s < 3; ++s) { + std::int16_t v = static_cast(t * 10 + c * 100 + s); + f.write(reinterpret_cast(&v), 2); } } + const auto b = geopro::io::gpr::buildLineVolumeFromNormalized( + (dir).string(), "L", /*coarse=*/1, /*targetDy=*/0.0); // targetDy=0 不插值 + EXPECT_EQ(b.vol.nx(), 4); // 道 + EXPECT_EQ(b.vol.ny(), 2); // 通道 + EXPECT_EQ(b.vol.nz(), 3); // 采样 + EXPECT_DOUBLE_EQ(b.spacing[0], 0.1); // dx=DISTANCE_INTERVAL + EXPECT_GT(b.spacing[2], 0.0); // dz 由 timewindow/dielectric 求得 >0 + EXPECT_NEAR(b.quant.toPhys(b.vol.at(3, 1, 2)), 132.0, b.quant.scale); // t3c1s2=30+100+2 +}