diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 46b32b6..10d163c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,6 +9,7 @@ # add_subdirectory(controller) # 联动编排 # add_subdirectory(core) +add_subdirectory(io) add_subdirectory(data) add_subdirectory(net) add_subdirectory(render) diff --git a/src/io/CMakeLists.txt b/src/io/CMakeLists.txt new file mode 100644 index 0000000..e86a626 --- /dev/null +++ b/src/io/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(gpr) diff --git a/src/io/gpr/CMakeLists.txt b/src/io/gpr/CMakeLists.txt new file mode 100644 index 0000000..b33b1b6 --- /dev/null +++ b/src/io/gpr/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(geopro_io_gpr STATIC IprHeader.cpp) +target_include_directories(geopro_io_gpr PUBLIC ${CMAKE_SOURCE_DIR}/src) +target_compile_features(geopro_io_gpr PUBLIC cxx_std_17) + +# 纯 C++17 解析层,零 Qt/VTK。顶层全局开启了 AUTOMOC/UIC/RCC,这里显式关闭保持纯净。 +set_target_properties(geopro_io_gpr PROPERTIES AUTOMOC OFF AUTOUIC OFF AUTORCC OFF) diff --git a/src/io/gpr/IprHeader.cpp b/src/io/gpr/IprHeader.cpp new file mode 100644 index 0000000..54eb194 --- /dev/null +++ b/src/io/gpr/IprHeader.cpp @@ -0,0 +1,64 @@ +#include "io/gpr/IprHeader.hpp" + +#include +#include +#include + +namespace geopro::io::gpr { + +namespace { + +// SOIL VELOCITY 原文单位 m/µs,本层统一换算成 m/s 存储。 +constexpr double kSoilVelocityToMetersPerSecond = 1e6; + +std::string trim(const std::string& s) { + std::size_t b = 0; + std::size_t e = s.size(); + while (b < e && std::isspace(static_cast(s[b]))) ++b; + while (e > b && std::isspace(static_cast(s[e - 1]))) --e; + return s.substr(b, e - b); +} + +} // namespace + +IprHeader parseIprHeader(const std::string& text) { + IprHeader header; + bool hasSamples = false; + bool hasLastTrace = false; + bool hasChannels = false; + + std::istringstream stream(text); + std::string line; + while (std::getline(stream, line)) { + const std::size_t colon = line.find(':'); + if (colon == std::string::npos) continue; + + const std::string key = trim(line.substr(0, colon)); + const std::string value = trim(line.substr(colon + 1)); + if (value.empty()) continue; + + if (key == "SAMPLES") { + header.samples = std::stoi(value); + hasSamples = true; + } else if (key == "LAST TRACE") { + header.lastTrace = std::stol(value); + hasLastTrace = true; + } else if (key == "CHANNELS") { + header.channels = std::stoi(value); + hasChannels = true; + } else if (key == "TIMEWINDOW") { + header.timeWindowNs = std::stod(value); + } else if (key == "SOIL VELOCITY") { + header.soilVelocity = std::stod(value) * kSoilVelocityToMetersPerSecond; + } else if (key == "DISTANCE INTERVAL") { + header.distanceInterval = std::stod(value); + } + } + + if (!hasSamples || !hasLastTrace || !hasChannels) { + throw std::runtime_error("parseIprHeader: missing required field(s) (SAMPLES/LAST TRACE/CHANNELS)"); + } + return header; +} + +} // namespace geopro::io::gpr diff --git a/src/io/gpr/IprHeader.hpp b/src/io/gpr/IprHeader.hpp new file mode 100644 index 0000000..49112ab --- /dev/null +++ b/src/io/gpr/IprHeader.hpp @@ -0,0 +1,23 @@ +#ifndef GEOPRO_IO_GPR_IPRHEADER_HPP +#define GEOPRO_IO_GPR_IPRHEADER_HPP + +#include + +namespace geopro::io::gpr { + +// 探地雷达 .iprh 文本头的关键字段(纯解析,零 Qt/VTK 依赖)。 +struct IprHeader { + int samples = 0; // 每道采样点数 (SAMPLES) + long lastTrace = 0; // 最后一道索引 (LAST TRACE);道数 = lastTrace+1 + int channels = 0; // 通道数 (CHANNELS) + double timeWindowNs = 0; // 时窗 ns (TIMEWINDOW) + double soilVelocity = 0; // 土速,统一存 m/s(原文 SOIL VELOCITY 单位 m/µs,读入后 ×1e6) + double distanceInterval = 0; // 道距 m (DISTANCE INTERVAL) +}; + +// 解析 .iprh 头文本。samples/lastTrace/channels 缺任一抛 std::runtime_error。 +IprHeader parseIprHeader(const std::string& text); + +} // namespace geopro::io::gpr + +#endif // GEOPRO_IO_GPR_IPRHEADER_HPP diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 753159f..2e6b32a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -168,4 +168,8 @@ target_sources(geopro_tests PRIVATE controller/test_workbench_nav_controller.cpp target_sources(geopro_tests PRIVATE controller/test_vtk_scene_controller.cpp) target_link_libraries(geopro_tests PRIVATE geopro_controller Qt6::Test) +# io/gpr 层:.iprh 头解析(纯 C++17,零 Qt/VTK)。 +target_sources(geopro_tests PRIVATE io/gpr/test_ipr_header.cpp) +target_link_libraries(geopro_tests PRIVATE geopro_io_gpr) + add_subdirectory(spike) # spike S3: banded contour 渲染验证 diff --git a/tests/io/gpr/test_ipr_header.cpp b/tests/io/gpr/test_ipr_header.cpp new file mode 100644 index 0000000..3ba229a --- /dev/null +++ b/tests/io/gpr/test_ipr_header.cpp @@ -0,0 +1,18 @@ +#include "io/gpr/IprHeader.hpp" +#include +using geopro::io::gpr::parseIprHeader; +TEST(IprHeader, ParsesKeyFieldsFromRealSample) { + const std::string t = + "SAMPLES: 821\nLAST TRACE: 45305\nCHANNELS: 14\n" + "TIMEWINDOW: 160.352\nSOIL VELOCITY: 100.000000\nDISTANCE INTERVAL: 0.049084\n"; + auto h = parseIprHeader(t); + EXPECT_EQ(h.samples, 821); + EXPECT_EQ(h.lastTrace, 45305); + EXPECT_EQ(h.channels, 14); + EXPECT_DOUBLE_EQ(h.timeWindowNs, 160.352); + EXPECT_DOUBLE_EQ(h.soilVelocity, 1e8); // 100 m/µs → 1e8 m/s + EXPECT_NEAR(h.distanceInterval, 0.049084, 1e-9); +} +TEST(IprHeader, ThrowsOnMissingSamples) { + EXPECT_THROW(parseIprHeader("CHANNELS: 14\n"), std::runtime_error); +}