157 lines
5.6 KiB
C++
157 lines
5.6 KiB
C++
// GprVolumeRepository:逐线 GPR int16 量化体 → app 渲染链 float 体(VolumeGrid)。
|
||
// 1) builtI16ToVolumeGrid 纯适配器:维度/反量化值/spacing/origin/vmin-vmax/kBlank→NaN。
|
||
// 2) createGprVolumeGrid 全链:合成多通道 .iprb 走真 P1/P2 链 → 反量化体维度/spacing 自洽。
|
||
|
||
#include <gtest/gtest.h>
|
||
|
||
#include <cmath>
|
||
#include <cstdint>
|
||
#include <filesystem>
|
||
#include <fstream>
|
||
#include <string>
|
||
|
||
#include "core/algo/GprVolumeBuilder.hpp"
|
||
#include "core/model/ScalarVolumeI16.hpp"
|
||
#include "data/GprVolumeRepository.hpp"
|
||
|
||
namespace fs = std::filesystem;
|
||
|
||
namespace {
|
||
|
||
// 适配器单测:手搭一个 2x1x2 的 BuiltI16(已知 quant/spacing/origin) → 校验反量化逐值。
|
||
TEST(GprVolumeRepositoryAdapter, DequantDimsSpacingAndBlank) {
|
||
geopro::core::BuiltI16 built;
|
||
built.vol = geopro::core::ScalarVolumeI16(2, 1, 2);
|
||
// Quant: phys = q*scale + offset = q*0.5 + 10。
|
||
built.quant.scale = 0.5;
|
||
built.quant.offset = 10.0;
|
||
built.origin = {1.0, 2.0, 3.0};
|
||
built.spacing = {0.2, 1.37, 0.05};
|
||
built.vminPhys = 5.0;
|
||
built.vmaxPhys = 20.0;
|
||
|
||
// 填 4 个体素:3 个正常值 + 1 个 kBlank。
|
||
built.vol.at(0, 0, 0) = 4; // phys = 4*0.5+10 = 12
|
||
built.vol.at(1, 0, 0) = -2; // phys = -2*0.5+10 = 9
|
||
built.vol.at(0, 0, 1) = 20; // phys = 20*0.5+10 = 20
|
||
built.vol.at(1, 0, 1) = geopro::core::ScalarVolumeI16::kBlank; // → NaN
|
||
|
||
const geopro::data::VolumeGrid g = geopro::data::builtI16ToVolumeGrid(built);
|
||
|
||
// 维度。
|
||
EXPECT_EQ(g.vol.nx(), 2);
|
||
EXPECT_EQ(g.vol.ny(), 1);
|
||
EXPECT_EQ(g.vol.nz(), 2);
|
||
|
||
// 反量化逐值。
|
||
EXPECT_DOUBLE_EQ(g.vol.at(0, 0, 0), 12.0);
|
||
EXPECT_DOUBLE_EQ(g.vol.at(1, 0, 0), 9.0);
|
||
EXPECT_DOUBLE_EQ(g.vol.at(0, 0, 1), 20.0);
|
||
EXPECT_TRUE(std::isnan(g.vol.at(1, 0, 1))); // kBlank → NaN(下游透明)
|
||
|
||
// origin/spacing 原样搬运。
|
||
EXPECT_DOUBLE_EQ(g.origin[0], 1.0);
|
||
EXPECT_DOUBLE_EQ(g.origin[1], 2.0);
|
||
EXPECT_DOUBLE_EQ(g.origin[2], 3.0);
|
||
EXPECT_DOUBLE_EQ(g.spacing[0], 0.2);
|
||
EXPECT_DOUBLE_EQ(g.spacing[1], 1.37);
|
||
EXPECT_DOUBLE_EQ(g.spacing[2], 0.05);
|
||
|
||
// 物理值域 = BuiltI16 的 vminPhys/vmaxPhys。
|
||
EXPECT_DOUBLE_EQ(g.vmin, 5.0);
|
||
EXPECT_DOUBLE_EQ(g.vmax, 20.0);
|
||
EXPECT_TRUE(g.valid());
|
||
}
|
||
|
||
// 写一个合成通道:.iprh 文本头 + .iprb 纯 int16 波形([trace*samples + s],s 最快)。
|
||
// 与 test_gpr3dv_volume_bridge 同口径,确保 createGprVolumeGrid 走真 P1/P2 链。
|
||
void writeSyntheticChannel(const fs::path& iprhPath, int samples, int traces,
|
||
std::int16_t base, double chYOffset,
|
||
double distanceInterval, double timeWindowNs,
|
||
double soilVelocity, int channels) {
|
||
std::ofstream h(iprhPath);
|
||
h << "SAMPLES: " << samples << "\n";
|
||
h << "LAST TRACE: " << (traces - 1) << "\n";
|
||
h << "CHANNELS: " << channels << "\n";
|
||
h << "TIMEWINDOW: " << timeWindowNs << "\n";
|
||
h << "SOIL VELOCITY: " << soilVelocity << "\n";
|
||
h << "DISTANCE INTERVAL: " << distanceInterval << "\n";
|
||
h << "CH_Y_OFFSET: " << chYOffset << "\n";
|
||
h.close();
|
||
|
||
fs::path iprbPath = iprhPath;
|
||
iprbPath.replace_extension(".iprb");
|
||
std::ofstream b(iprbPath, std::ios::binary);
|
||
for (int t = 0; t < traces; ++t) {
|
||
for (int s = 0; s < samples; ++s) {
|
||
const std::int16_t v = static_cast<std::int16_t>(base + t + s);
|
||
b.write(reinterpret_cast<const char*>(&v), sizeof(v));
|
||
}
|
||
}
|
||
}
|
||
|
||
class GprVolumeRepositoryChainTest : public ::testing::Test {
|
||
protected:
|
||
void SetUp() override {
|
||
dir_ = fs::temp_directory_path() / "gpr_volume_repo_test";
|
||
std::error_code ec;
|
||
fs::remove_all(dir_, ec);
|
||
fs::create_directories(dir_);
|
||
}
|
||
void TearDown() override {
|
||
std::error_code ec;
|
||
fs::remove_all(dir_, ec);
|
||
}
|
||
fs::path dir_;
|
||
};
|
||
|
||
TEST_F(GprVolumeRepositoryChainTest, FullChainProducesValidVolumeGrid) {
|
||
const int samples = 64;
|
||
const int traces = 40;
|
||
const int channels = 2;
|
||
const double dxHeader = 0.05;
|
||
const double timeWindowNs = 100.0;
|
||
const double soilVel = 0.1;
|
||
|
||
writeSyntheticChannel(dir_ / "syn_001_A01.iprh", samples, traces,
|
||
/*base=*/100, /*chYOffset=*/-0.5, dxHeader, timeWindowNs,
|
||
soilVel, channels);
|
||
writeSyntheticChannel(dir_ / "syn_001_A02.iprh", samples, traces,
|
||
/*base=*/300, /*chYOffset=*/0.5, dxHeader, timeWindowNs,
|
||
soilVel, channels);
|
||
|
||
// coarse=2:沿测线下采样,dx ×2 保形。
|
||
geopro::data::VolumeGrid g;
|
||
ASSERT_NO_THROW(
|
||
{ g = geopro::data::createGprVolumeGrid(dir_.string(), "syn_001", 2); });
|
||
|
||
// 维度:Y=通道数;X/Z 正。
|
||
EXPECT_EQ(g.vol.ny(), channels);
|
||
EXPECT_GT(g.vol.nx(), 0);
|
||
EXPECT_GT(g.vol.nz(), 0);
|
||
|
||
// spacing:X=道距×coarse、Y=通道横距(跨度1.0/(2-1)=1.0)、Z=深度采样距>0。
|
||
EXPECT_DOUBLE_EQ(g.spacing[0], dxHeader * 2);
|
||
EXPECT_NEAR(g.spacing[1], 1.0, 1e-6);
|
||
EXPECT_GT(g.spacing[2], 0.0);
|
||
|
||
// origin=0;值域自洽(vmin<=vmax,搬自 BuiltI16.vminPhys/vmaxPhys,处理后极小合成
|
||
// 数据可能退化为相等区间 → 与 io::gpr 桥接同口径,不强求严格 <)。
|
||
EXPECT_DOUBLE_EQ(g.origin[0], 0.0);
|
||
EXPECT_DOUBLE_EQ(g.origin[1], 0.0);
|
||
EXPECT_DOUBLE_EQ(g.origin[2], 0.0);
|
||
EXPECT_LE(g.vmin, g.vmax);
|
||
|
||
// 稠密体:抽查角点为有限值(非 NaN)——GPR 立方体每体素有值,反量化后无空洞。
|
||
EXPECT_TRUE(std::isfinite(g.vol.at(0, 0, 0)));
|
||
EXPECT_TRUE(std::isfinite(g.vol.at(g.vol.nx() - 1, g.vol.ny() - 1,
|
||
g.vol.nz() - 1)));
|
||
}
|
||
|
||
TEST_F(GprVolumeRepositoryChainTest, ThrowsOnMissingLine) {
|
||
EXPECT_THROW(geopro::data::createGprVolumeGrid(dir_.string(), "nope", 1),
|
||
std::runtime_error);
|
||
}
|
||
|
||
} // namespace
|