fix(io/gpr): readIprb 以文件大小为权威推导道数

真实明星路数据规律为「道数 == LAST TRACE」(非 lastTrace+1),旧实现硬假设
traces=lastTrace+1 并严格校验文件字节相等,导致真实 .iprb 装配抛错。

改为 traces = fileBytes / (samples*2),要求字节数为 samples*2 整数倍(否则抛),
lastTrace 仅作 header 提示不再决定道数。更新单测:新增
FileSizeIsAuthoritativeNotLastTracePlusOne(lastTrace=N 但文件含 N 道 → traces==N),
ReadsInt16AndLayout/ThrowsOnSizeMismatch 语义不变仍通过。
This commit is contained in:
gaozheng 2026-06-23 12:37:42 +08:00
parent bfd7d4aafd
commit d75a52e519
3 changed files with 32 additions and 12 deletions

View File

@ -12,21 +12,28 @@ BScan readIprb(const std::string& path, const IprHeader& h) {
throw std::runtime_error("readIprb: 无法打开文件: " + path);
}
const std::streamsize fileSize = f.tellg();
const std::int64_t traces = static_cast<std::int64_t>(h.lastTrace) + 1;
const std::int64_t count = static_cast<std::int64_t>(h.samples) * traces;
const std::int64_t expected = count * 2;
if (fileSize != expected) {
throw std::runtime_error("readIprb: 文件大小与 samples*traces*2 不符: " + path);
// 文件大小为权威:道数由实测字节数推导,而非 header 的 LAST TRACE。
// 真实数据规律:实际道数 = LAST TRACE非 LAST TRACE+1故不能硬假设 +1。
const std::int64_t fileBytes = static_cast<std::int64_t>(f.tellg());
const std::int64_t samples = static_cast<std::int64_t>(h.samples);
if (samples <= 0) {
throw std::runtime_error("readIprb: samples 非法(<=0): " + path);
}
const std::int64_t bytesPerTrace = samples * 2; // int16
if (fileBytes % bytesPerTrace != 0) {
throw std::runtime_error(
"readIprb: 文件大小不是 samples*2 的整数倍: " + path);
}
const std::int64_t traces = fileBytes / bytesPerTrace;
BScan b;
b.samples = h.samples;
b.traces = traces;
b.data.resize(static_cast<std::size_t>(h.samples) * traces);
b.data.resize(static_cast<std::size_t>(samples * traces));
f.seekg(0, std::ios::beg);
f.read(reinterpret_cast<char*>(b.data.data()), static_cast<std::streamsize>(expected));
f.read(reinterpret_cast<char*>(b.data.data()),
static_cast<std::streamsize>(fileBytes));
if (!f) {
throw std::runtime_error("readIprb: 读取数据失败: " + path);
}

View File

@ -16,8 +16,9 @@ struct BScan {
std::vector<int16_t> data; // 大小 = samples*traces
};
// 读取 .iprb 二进制 B-scantraces = h.lastTrace+1
// 校验 samples*traces*2 == 文件字节数,不符抛 std::runtime_error文件打不开抛 std::runtime_error。
// 读取 .iprb 二进制 B-scan以文件大小为权威traces = fileBytes / (samples*2)。
// 要求 fileBytes 是 samples*2 的整数倍,否则抛 std::runtime_error文件打不开抛 std::runtime_error。
// 注意h.lastTrace 仅作 header 提示,不决定道数(真实数据规律为道数==lastTrace非 +1
BScan readIprb(const std::string& path, const IprHeader& h);
} // namespace geopro::io::gpr

View File

@ -25,12 +25,24 @@ TEST(IprbReader, ReadsInt16AndLayout) {
std::remove(path.c_str());
}
TEST(IprbReader, ThrowsOnSizeMismatch) {
std::vector<int16_t> raw{0,1,2,3,4}; // 5 个,与 samples*traces 不符
std::vector<int16_t> raw{0,1,2,3,4}; // 5 个,非 samples 整数倍 → 抛
auto path = writeTmp(raw);
IprHeader h{}; h.samples = 3; h.lastTrace = 3; // 期望 12
IprHeader h{}; h.samples = 3; h.lastTrace = 3; // 5*2=10, 10%6!=0
EXPECT_THROW(readIprb(path, h), std::runtime_error);
std::remove(path.c_str());
}
// 真实数据规律LAST TRACE=N 但文件恰含 N 道(非 N+1)。
// 文件大小为权威 → traces 应等于文件实含道数 N而非 N+1。
TEST(IprbReader, FileSizeIsAuthoritativeNotLastTracePlusOne) {
// 4 道×3 采样 = 12 个 int16文件恰含 4 道。
std::vector<int16_t> raw{0,1,2, 10,11,12, 20,21,22, 30,31,32};
auto path = writeTmp(raw);
IprHeader h{}; h.samples = 3; h.lastTrace = 4; // 假设 +1 会得 5 道(错)
auto b = readIprb(path, h);
EXPECT_EQ(b.traces, 4); // 以文件大小为准 → 4复刻真实数据 traces==lastTrace 规律
EXPECT_EQ(b.data.size(), 12u);
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);