87 lines
3.8 KiB
C++
87 lines
3.8 KiB
C++
#include "io/gpr/GprGeometry.hpp"
|
||
#include "io/gpr/IprHeader.hpp"
|
||
#include <gtest/gtest.h>
|
||
#include <algorithm>
|
||
#include <vector>
|
||
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, ChannelInterpDensifiesToTargetGrid) {
|
||
// 14 通道 ~均匀 0.105m,跨度 1.372m;targetDy=0.025 → ny=round(1.372/0.025)+1=56。
|
||
std::vector<double> off;
|
||
for (int i = 0; i < 14; ++i) off.push_back(-0.686 + i * 0.1055385); // -0.686..+0.686
|
||
auto rows = planChannelInterpolation(off, 0.025);
|
||
EXPECT_EQ(rows.size(), 56u);
|
||
// 首行=最左通道(无插值),末行=最右通道。
|
||
EXPECT_EQ(rows.front().a, 0);
|
||
EXPECT_NEAR(rows.front().wb, 0.0, 1e-9);
|
||
EXPECT_EQ(rows.back().a, 13);
|
||
// 中间存在真插值行(wb 落在 (0,1))。
|
||
bool sawInterp = false;
|
||
for (const auto& r : rows)
|
||
if (r.a != r.b && r.wb > 0.05 && r.wb < 0.95) sawInterp = true;
|
||
EXPECT_TRUE(sawInterp);
|
||
}
|
||
|
||
TEST(GprGeometry, ChannelInterpDynamicCountBySpacing) {
|
||
// 不同道间距 → 不同插值条数(动态,不写死)。两通道 0.10m 间距:
|
||
// targetDy=0.025 → ny=round(0.10/0.025)+1=5(端点 + 中间 3 条)。
|
||
auto r1 = planChannelInterpolation({0.0, 0.10}, 0.025);
|
||
EXPECT_EQ(r1.size(), 5u);
|
||
// targetDy=0.05 → ny=round(0.10/0.05)+1=3(端点 + 中间 1 条)。
|
||
auto r2 = planChannelInterpolation({0.0, 0.10}, 0.05);
|
||
EXPECT_EQ(r2.size(), 3u);
|
||
}
|
||
|
||
TEST(GprGeometry, ChannelInterpHandlesUnsortedOffsets) {
|
||
// 通道在文件里可能非按偏移有序(任意排布)。偏移乱序但物理跨度相同 → 网格行数、
|
||
// 端点、单调性应不受顺序影响(内部按偏移排序定位)。
|
||
std::vector<double> sorted = {-0.4, -0.2, 0.0, 0.2, 0.4};
|
||
std::vector<double> shuffled = {0.0, 0.4, -0.4, 0.2, -0.2}; // 同偏移集合,乱序
|
||
auto rs = planChannelInterpolation(sorted, 0.1);
|
||
auto ru = planChannelInterpolation(shuffled, 0.1);
|
||
ASSERT_EQ(rs.size(), ru.size()); // ny 仅取决于跨度,与顺序无关
|
||
EXPECT_EQ(rs.size(), 9u); // round(0.8/0.1)+1
|
||
// 真正的不变量:每行的【有效插值位置】=(1-wb)*off[a]+wb*off[b] 应等于网格位置
|
||
// p=min+j*targetDy(与通道在文件里的顺序无关)。末行恰落在 max 时会以 [次末,末] 夹
|
||
// 且 wb=1(值=末通道),故不能直接断言 a==末通道,要看有效位置。
|
||
auto effPos = [](const std::vector<double>& off, const ChannelInterpRow& r) {
|
||
return (1.0 - r.wb) * off[r.a] + r.wb * off[r.b];
|
||
};
|
||
for (std::size_t j = 0; j < ru.size(); ++j) {
|
||
const double p = -0.4 + static_cast<double>(j) * 0.1;
|
||
EXPECT_NEAR(effPos(sorted, rs[j]), p, 1e-9); // 有序
|
||
EXPECT_NEAR(effPos(shuffled, ru[j]), p, 1e-9); // 乱序:同一结果
|
||
// a/b 偏移夹住网格位置。
|
||
const double oa = shuffled[ru[j].a], ob = shuffled[ru[j].b];
|
||
EXPECT_LE(std::min(oa, ob) - 1e-9, p);
|
||
EXPECT_GE(std::max(oa, ob) + 1e-9, p);
|
||
}
|
||
}
|
||
|
||
TEST(GprGeometry, ChannelInterpDegenerateIdentity) {
|
||
// 单通道 / 已比 targetDy 密 / targetDy<=0 → 逐通道 identity。
|
||
EXPECT_EQ(planChannelInterpolation({0.5}, 0.025).size(), 1u);
|
||
auto dense = planChannelInterpolation({0.0, 0.01}, 0.025); // 跨度<targetDy/2
|
||
ASSERT_EQ(dense.size(), 2u);
|
||
EXPECT_EQ(dense[0].a, 0);
|
||
EXPECT_EQ(dense[1].a, 1);
|
||
EXPECT_EQ(planChannelInterpolation({0.0, 0.10}, -1.0).size(), 2u);
|
||
}
|
||
|
||
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);
|
||
}
|