feat(io/gpr): GPR 几何-通道横偏解析与采样深度换算

新增 geopro::io::gpr 两个纯 C++17 几何函数:
- parseChannelXOffsets: 解析 .ord 末列==1 的有效通道横向偏移
- depthOfSample: 按物理把采样序号换算为深度米(samples<=1 防除零)

含失败先行的单测,GprGeometry.cpp 接入 geopro_io_gpr,
test 接入 geopro_tests。
This commit is contained in:
gaozheng 2026-06-23 10:19:11 +08:00
parent 379875dff0
commit 0bbed9c0c3
5 changed files with 76 additions and 1 deletions

View File

@ -1,4 +1,4 @@
add_library(geopro_io_gpr STATIC IprHeader.cpp IprbReader.cpp) add_library(geopro_io_gpr STATIC IprHeader.cpp IprbReader.cpp GprGeometry.cpp)
target_include_directories(geopro_io_gpr PUBLIC ${CMAKE_SOURCE_DIR}/src) target_include_directories(geopro_io_gpr PUBLIC ${CMAKE_SOURCE_DIR}/src)
target_compile_features(geopro_io_gpr PUBLIC cxx_std_17) target_compile_features(geopro_io_gpr PUBLIC cxx_std_17)

View File

@ -0,0 +1,33 @@
#include "io/gpr/GprGeometry.hpp"
#include <sstream>
#include <string>
#include <vector>
namespace geopro::io::gpr {
std::vector<double> parseChannelXOffsets(const std::string& ordText) {
std::vector<double> offsets;
std::istringstream lines(ordText);
std::string line;
while (std::getline(lines, line)) {
std::istringstream tok(line);
std::vector<std::string> cols;
std::string col;
while (tok >> col) cols.push_back(col);
if (cols.size() < 4) continue; // 空行/列数不足,跳过
if (cols.back() != "1") continue; // 末列==1 才是有效通道
offsets.push_back(std::stod(cols[1]));
}
return offsets;
}
double depthOfSample(int s, const IprHeader& h) {
if (h.samples <= 1) return 0.0; // 防除零
const double timeNs = static_cast<double>(s) * h.timeWindowNs /
static_cast<double>(h.samples - 1);
const double timeSec = timeNs * 1e-9;
return h.soilVelocity * timeSec / 2.0;
}
} // namespace geopro::io::gpr

View File

@ -0,0 +1,20 @@
#ifndef GEOPRO_IO_GPR_GPRGEOMETRY_HPP
#define GEOPRO_IO_GPR_GPRGEOMETRY_HPP
#include <string>
#include <vector>
#include "io/gpr/IprHeader.hpp"
namespace geopro::io::gpr {
// 解析 .ord 文本,返回末列==1 的有效通道的横向偏移(第 2 列),按文件顺序。
std::vector<double> parseChannelXOffsets(const std::string& ordText);
// 采样序号 s → 深度(米)。depth = soilVelocity[m/s] * (s * timeWindowNs/(samples-1) * 1e-9) / 2。
// samples<=1 时返回 0 防除零。
double depthOfSample(int s, const IprHeader& h);
} // namespace geopro::io::gpr
#endif // GEOPRO_IO_GPR_GPRGEOMETRY_HPP

View File

@ -171,6 +171,7 @@ target_link_libraries(geopro_tests PRIVATE geopro_controller Qt6::Test)
# io/gpr .iprh + .iprb B-scan C++17 Qt/VTK # io/gpr .iprh + .iprb B-scan C++17 Qt/VTK
target_sources(geopro_tests PRIVATE io/gpr/test_ipr_header.cpp) target_sources(geopro_tests PRIVATE io/gpr/test_ipr_header.cpp)
target_sources(geopro_tests PRIVATE io/gpr/test_iprb_reader.cpp) target_sources(geopro_tests PRIVATE io/gpr/test_iprb_reader.cpp)
target_sources(geopro_tests PRIVATE io/gpr/test_gpr_geometry.cpp)
target_link_libraries(geopro_tests PRIVATE geopro_io_gpr) target_link_libraries(geopro_tests PRIVATE geopro_io_gpr)
add_subdirectory(spike) # spike S3: banded contour add_subdirectory(spike) # spike S3: banded contour

View File

@ -0,0 +1,21 @@
#include "io/gpr/GprGeometry.hpp"
#include "io/gpr/IprHeader.hpp"
#include <gtest/gtest.h>
using namespace geopro::io::gpr;
TEST(GprGeometry, ParsesActiveChannelOffsets) {
const std::string ord = "0 -0.686000 -1.5 1\n1 -0.581000 -1.5 1\n14 0 -1.5 0\n";
auto xs = parseChannelXOffsets(ord);
ASSERT_EQ(xs.size(), 2u); // 仅 2 个有效(末列=1)
EXPECT_NEAR(xs[0], -0.686, 1e-6);
EXPECT_NEAR(xs[1], -0.581, 1e-6);
}
TEST(GprGeometry, DepthOfLastSampleMatchesPhysics) {
IprHeader h{};
h.samples = 821;
h.timeWindowNs = 160.352;
h.soilVelocity = 1e8; // m/s
EXPECT_NEAR(depthOfSample(820, h), 8.0, 0.05); // ~8m
EXPECT_NEAR(depthOfSample(0, h), 0.0, 1e-9);
}