From 0d7f64694169e3ec2edc97aaf7165d56ee6a1095 Mon Sep 17 00:00:00 2001 From: gaozheng Date: Tue, 23 Jun 2026 10:00:08 +0800 Subject: [PATCH] =?UTF-8?q?feat(io/gpr):=20=E5=AE=9E=E7=8E=B0=20.iprb=20B-?= =?UTF-8?q?scan=20=E4=BA=8C=E8=BF=9B=E5=88=B6=E8=AF=BB=E5=8F=96=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit readIprb 读取 int16 雷达剖面(布局 [trace*samples + s]): traces=lastTrace+1;校验文件字节数=samples*traces*2, 不符或打不开抛 std::runtime_error。纯 C++17,零 Qt/VTK。 --- src/io/gpr/CMakeLists.txt | 2 +- src/io/gpr/IprbReader.cpp | 37 +++++++++++++++++++++++++++++++ src/io/gpr/IprbReader.hpp | 25 +++++++++++++++++++++ tests/CMakeLists.txt | 3 ++- tests/io/gpr/test_iprb_reader.cpp | 37 +++++++++++++++++++++++++++++++ 5 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 src/io/gpr/IprbReader.cpp create mode 100644 src/io/gpr/IprbReader.hpp create mode 100644 tests/io/gpr/test_iprb_reader.cpp diff --git a/src/io/gpr/CMakeLists.txt b/src/io/gpr/CMakeLists.txt index b33b1b6..eee22e7 100644 --- a/src/io/gpr/CMakeLists.txt +++ b/src/io/gpr/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library(geopro_io_gpr STATIC IprHeader.cpp) +add_library(geopro_io_gpr STATIC IprHeader.cpp IprbReader.cpp) target_include_directories(geopro_io_gpr PUBLIC ${CMAKE_SOURCE_DIR}/src) target_compile_features(geopro_io_gpr PUBLIC cxx_std_17) diff --git a/src/io/gpr/IprbReader.cpp b/src/io/gpr/IprbReader.cpp new file mode 100644 index 0000000..a41541d --- /dev/null +++ b/src/io/gpr/IprbReader.cpp @@ -0,0 +1,37 @@ +#include "io/gpr/IprbReader.hpp" + +#include +#include +#include + +namespace geopro::io::gpr { + +BScan readIprb(const std::string& path, const IprHeader& h) { + std::ifstream f(path, std::ios::binary | std::ios::ate); + if (!f) { + throw std::runtime_error("readIprb: 无法打开文件: " + path); + } + + const std::streamsize fileSize = f.tellg(); + const long traces = h.lastTrace + 1; + const long count = static_cast(h.samples) * traces; + const std::streamsize expected = static_cast(count) * 2; + if (fileSize != expected) { + throw std::runtime_error("readIprb: 文件大小与 samples*traces*2 不符: " + path); + } + + BScan b; + b.samples = h.samples; + b.traces = traces; + b.data.resize(static_cast(count)); + + f.seekg(0, std::ios::beg); + f.read(reinterpret_cast(b.data.data()), expected); + if (!f) { + throw std::runtime_error("readIprb: 读取数据失败: " + path); + } + + return b; +} + +} // namespace geopro::io::gpr diff --git a/src/io/gpr/IprbReader.hpp b/src/io/gpr/IprbReader.hpp new file mode 100644 index 0000000..90aa5b5 --- /dev/null +++ b/src/io/gpr/IprbReader.hpp @@ -0,0 +1,25 @@ +#ifndef GEOPRO_IO_GPR_IPRBREADER_HPP +#define GEOPRO_IO_GPR_IPRBREADER_HPP + +#include +#include +#include + +#include "io/gpr/IprHeader.hpp" + +namespace geopro::io::gpr { + +// 一个通道一条测线的雷达 B-scan 剖面(int16 存储,布局 [trace*samples + s],s 最快)。 +struct BScan { + int samples = 0; + long traces = 0; + std::vector data; // 大小 = samples*traces +}; + +// 读取 .iprb 二进制 B-scan:traces = h.lastTrace+1; +// 校验 samples*traces*2 == 文件字节数,不符抛 std::runtime_error;文件打不开抛 std::runtime_error。 +BScan readIprb(const std::string& path, const IprHeader& h); + +} // namespace geopro::io::gpr + +#endif // GEOPRO_IO_GPR_IPRBREADER_HPP diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2e6b32a..bd84fb5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -168,8 +168,9 @@ 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)。 +# 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_iprb_reader.cpp) target_link_libraries(geopro_tests PRIVATE geopro_io_gpr) add_subdirectory(spike) # spike S3: banded contour 渲染验证 diff --git a/tests/io/gpr/test_iprb_reader.cpp b/tests/io/gpr/test_iprb_reader.cpp new file mode 100644 index 0000000..7d2cbbe --- /dev/null +++ b/tests/io/gpr/test_iprb_reader.cpp @@ -0,0 +1,37 @@ +#include "io/gpr/IprbReader.hpp" +#include "io/gpr/IprHeader.hpp" +#include +#include +#include +namespace { +std::string writeTmp(const std::vector& v) { + std::string p = std::tmpnam(nullptr); + std::ofstream f(p, std::ios::binary); + f.write(reinterpret_cast(v.data()), + static_cast(v.size()*sizeof(int16_t))); + return p; +} +} +using namespace geopro::io::gpr; +TEST(IprbReader, ReadsInt16AndLayout) { + std::vector raw{0,1,2, 10,11,12, 20,21,22, 30,31,32}; // 4 道×3 采样 + auto path = writeTmp(raw); + IprHeader h{}; h.samples = 3; h.lastTrace = 3; // traces = 4 + auto b = readIprb(path, h); + EXPECT_EQ(b.samples, 3); + EXPECT_EQ(b.traces, 4); + EXPECT_EQ(b.data.size(), 12u); + EXPECT_EQ(b.data[1*3 + 2], 12); // 第1道第2采样 + std::remove(path.c_str()); +} +TEST(IprbReader, ThrowsOnSizeMismatch) { + std::vector raw{0,1,2,3,4}; // 5 个,与 samples*traces 不符 + auto path = writeTmp(raw); + IprHeader h{}; h.samples = 3; h.lastTrace = 3; // 期望 12 + EXPECT_THROW(readIprb(path, h), std::runtime_error); + std::remove(path.c_str()); +} +TEST(IprbReader, ThrowsOnMissingFile) { + IprHeader h{}; h.samples = 3; h.lastTrace = 3; + EXPECT_THROW(readIprb("____no_such_file____.iprb", h), std::runtime_error); +}