feat(io/gpr): 新增 .iprh 头解析器(纯 C++17,零 Qt/VTK)
- IprHeader/parseIprHeader:按行解析 KEY: value,支持含空格键名 (LAST TRACE/SOIL VELOCITY/DISTANCE INTERVAL) - SOIL VELOCITY 由 m/µs 统一换算为 m/s 存储(×1e6) - 缺 SAMPLES/LAST TRACE/CHANNELS 任一抛 std::runtime_error - CMake 接线:src/io(gpr) 静态库 geopro_io_gpr + tests 链接 - TDD:2 个新用例,全测试套件 100% 通过
This commit is contained in:
parent
b509795ffd
commit
c395921ca8
|
|
@ -9,6 +9,7 @@
|
||||||
# add_subdirectory(controller) # 联动编排
|
# add_subdirectory(controller) # 联动编排
|
||||||
#
|
#
|
||||||
add_subdirectory(core)
|
add_subdirectory(core)
|
||||||
|
add_subdirectory(io)
|
||||||
add_subdirectory(data)
|
add_subdirectory(data)
|
||||||
add_subdirectory(net)
|
add_subdirectory(net)
|
||||||
add_subdirectory(render)
|
add_subdirectory(render)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
add_subdirectory(gpr)
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
#include "io/gpr/IprHeader.hpp"
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
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<unsigned char>(s[b]))) ++b;
|
||||||
|
while (e > b && std::isspace(static_cast<unsigned char>(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
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef GEOPRO_IO_GPR_IPRHEADER_HPP
|
||||||
|
#define GEOPRO_IO_GPR_IPRHEADER_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
@ -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_sources(geopro_tests PRIVATE controller/test_vtk_scene_controller.cpp)
|
||||||
target_link_libraries(geopro_tests PRIVATE geopro_controller Qt6::Test)
|
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 渲染验证
|
add_subdirectory(spike) # spike S3: banded contour 渲染验证
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
#include "io/gpr/IprHeader.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
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);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue