#include #include "core/algo/GprVolumeBuilder.hpp" #include "io/gpr/RadarVolumeAssembler.hpp" using geopro::io::gpr::RadarCubeDesc; using geopro::io::gpr::assembleRadarVolume; // 2 道 × 3 通道 × 4 采样,值 = 100*c + 10*t + s。不插值(chXOffsets 空)、coarse=1。 TEST(RadarVolumeAssembler, AxisMapAndQuantRoundTrip) { RadarCubeDesc d; d.channels = 3; d.traces = 2; d.samples = 4; d.dxBase = 0.1; d.dyWhenNotInterpolated = 0.5; d.dz = 0.05; auto sampler = [](int c, int t, int s) { return 100.0 * c + 10.0 * t + s; }; const geopro::core::BuiltI16 b = assembleRadarVolume(d, sampler, /*coarse=*/1, /*targetDy=*/0.0); EXPECT_EQ(b.vol.nx(), 2); // 道 EXPECT_EQ(b.vol.ny(), 3); // 通道(未插值=原通道数) EXPECT_EQ(b.vol.nz(), 4); // 采样 EXPECT_DOUBLE_EQ(b.spacing[0], 0.1); EXPECT_DOUBLE_EQ(b.spacing[1], 0.5); EXPECT_DOUBLE_EQ(b.spacing[2], 0.05); EXPECT_NEAR(b.vminPhys, 0.0, 1e-9); // c0,t0,s0 EXPECT_NEAR(b.vmaxPhys, 213.0, 1e-9); // c2,t1,s3 = 200+10+3 // 反量化对位:体素(道 t=1, 通道 c=2, 采样 s=3) 应≈213(量化误差内)。 const double recon = b.quant.toPhys(b.vol.at(1, 2, 3)); EXPECT_NEAR(recon, 213.0, b.quant.scale); } // coarse=2:4 道 → nxOut=2,dx×2。 TEST(RadarVolumeAssembler, CoarseDownsamplesTracesAndScalesDx) { RadarCubeDesc d; d.channels = 1; d.traces = 4; d.samples = 2; d.dxBase = 0.1; auto sampler = [](int, int t, int s) { return 10.0 * t + s; }; const geopro::core::BuiltI16 b = assembleRadarVolume(d, sampler, /*coarse=*/2, 0.0); EXPECT_EQ(b.vol.nx(), 2); EXPECT_DOUBLE_EQ(b.spacing[0], 0.2); EXPECT_NEAR(b.quant.toPhys(b.vol.at(1, 0, 0)), 20.0, b.quant.scale); // 输出道1 = 源道2 } // ── 合成靶标:在【装配出的体】里验通道插值的几何忠实度 ────────────────────── // 现有 test_gpr_geometry 只验 planChannelInterpolation 的【行规划】;这两个测试验 // assembleRadarVolume 把规划【应用到体】是否正确——即用户要判断的"通道插值在体里 // 对不对、会不会造缝"。 // 布局:3 通道偏移 {0, 0.10, 0.20},targetDy=0.05 → ny=round(0.2/0.05)+1=5: // 行 j0=ch0 / j1=blend(ch0,ch1,0.5) / j2=ch1 / j3=blend(ch1,ch2,0.5) / j4=ch2。 namespace { RadarCubeDesc make3ChDesc() { RadarCubeDesc d; d.channels = 3; d.traces = 2; d.samples = 3; d.dxBase = 0.1; d.dz = 0.05; d.chXOffsets = {0.0, 0.10, 0.20}; // 触发通道插值 return d; } } // namespace // 平层反射(同一深度 s=2 全通道等值 500)穿过通道插值后【全部行仍等于 500】—— // 不出现"插值行衰减/锯齿/横向缝"(用户最担心的 10cm 缝就是这条若失败)。 TEST(RadarVolumeAssembler, FlatReflectorStaysFlatAcrossInterpolatedRows) { const RadarCubeDesc d = make3ChDesc(); // s==2:平层反射(全通道 500);s==0:逐通道阶梯(ch0=100/ch1=200/ch2=300)验混合;其余 0。 auto sampler = [](int c, int /*t*/, int s) { if (s == 2) return 500.0; if (s == 0) return 100.0 * (c + 1); return 0.0; }; const geopro::core::BuiltI16 b = assembleRadarVolume(d, sampler, /*coarse=*/1, /*targetDy=*/0.05); ASSERT_EQ(b.vol.ny(), 5); // 3 通道 → 5 行(含 2 条插值) ASSERT_EQ(b.vol.nx(), 2); ASSERT_EQ(b.vol.nz(), 3); EXPECT_DOUBLE_EQ(b.spacing[1], 0.05); // 插值后 dy=targetDy // 平层在【每一行、每一道】都应保持 500(插值不破坏横向连续)。 for (int j = 0; j < b.vol.ny(); ++j) for (int to = 0; to < b.vol.nx(); ++to) EXPECT_NEAR(b.quant.toPhys(b.vol.at(to, j, 2)), 500.0, b.quant.scale) << "行 j=" << j << " 道 to=" << to << " 处平层被插值破坏"; } // 插值行 = 相邻两通道的正确线性混合(j1 在 ch0=100/ch1=200 之间 wb=0.5 → 150); // 纯通道行 = 原通道值(j0=100 / j2=200 / j4=300)。验"插值没造假峰、没错位"。 TEST(RadarVolumeAssembler, InterpolatedRowIsCorrectLinearBlend) { const RadarCubeDesc d = make3ChDesc(); auto sampler = [](int c, int /*t*/, int s) { return s == 0 ? 100.0 * (c + 1) : 0.0; }; const geopro::core::BuiltI16 b = assembleRadarVolume(d, sampler, /*coarse=*/1, /*targetDy=*/0.05); ASSERT_EQ(b.vol.ny(), 5); const double tol = b.quant.scale; EXPECT_NEAR(b.quant.toPhys(b.vol.at(0, 0, 0)), 100.0, tol); // j0 = ch0 EXPECT_NEAR(b.quant.toPhys(b.vol.at(0, 1, 0)), 150.0, tol); // j1 = blend(100,200,0.5) EXPECT_NEAR(b.quant.toPhys(b.vol.at(0, 2, 0)), 200.0, tol); // j2 = ch1 EXPECT_NEAR(b.quant.toPhys(b.vol.at(0, 3, 0)), 250.0, tol); // j3 = blend(200,300,0.5) EXPECT_NEAR(b.quant.toPhys(b.vol.at(0, 4, 0)), 300.0, tol); // j4 = ch2 }