geopro/src/io/gpr/Gpr3dvVolumeBridge.cpp

160 lines
6.4 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "io/gpr/Gpr3dvVolumeBridge.hpp"
#include <algorithm>
#include <chrono>
#include <cmath>
#include <stdexcept>
#include <vector>
#include <QString>
#include "GPRDataModel.h"
#include "IprhParser.h"
#include "RadarProcessor.h"
#include "core/model/ScalarVolumeI16.hpp"
#include "io/gpr/RadarVolumeAssembler.hpp" // assembleRadarVolume(共享建体)
namespace geopro::io::gpr {
namespace {
// 全体 traces 平均绝对幅值——朴素的「处理是否生效」标量证据(与 P1 冒烟同口径)。
double meanAbsAmplitude(const GPRDataModel& model) {
long double sum = 0.0L;
long long count = 0;
for (const RadarTrace& tr : model.traces) {
for (short v : tr.amplitudes) {
sum += static_cast<long double>(v < 0 ? -v : v);
++count;
}
}
return count > 0 ? static_cast<double>(sum / count) : 0.0;
}
// 通道横向间距(米):优先 chYOffsets 跨度 /(通道数-1);取不到退路 ≈1.37/(通道数-1)。
double channelSpacingY(const GPRDataModel::Header& h, int channels) {
if (channels <= 1) return 1.0;
const auto& off = h.chYOffsets;
if (off.size() >= 2) {
float lo = off.front(), hi = off.front();
for (float v : off) {
lo = std::min(lo, v);
hi = std::max(hi, v);
}
const double span = static_cast<double>(hi) - static_cast<double>(lo);
if (span > 1e-9) return span / (channels - 1);
}
// 退路:明星路 14 通道横向跨度 ≈1.37m(-0.686~+0.686)。
constexpr double kArrayWidthM = 1.37;
return kArrayWidthM / (channels - 1);
}
// 深度采样间距(米)(timeWindow/samples) ns × 波速(m/ns) / 2(往返)。
// 与 GPRDataModel::calculateDepth 同式。取不到 → 1.0(单位间距)。
double depthSpacingZ(const GPRDataModel::Header& h) {
if (h.samplesPerTrace <= 0 || h.timeWindowNs <= 0.0) return 1.0;
const double timePerSample = h.timeWindowNs / h.samplesPerTrace; // ns
const double vel = h.waveVelocity > 0.0 ? h.waveVelocity : 0.1; // m/ns
const double dz = timePerSample * vel / 2.0;
return dz > 1e-12 ? dz : 1.0;
}
double nowMs(std::chrono::steady_clock::time_point t0) {
const auto t1 = std::chrono::steady_clock::now();
return std::chrono::duration<double, std::milli>(t1 - t0).count();
}
} // namespace
geopro::core::BuiltI16 buildLineVolumeFromGpr3dv(const std::string& lineDir,
const std::string& linePrefix,
BridgeMetrics* metricsOut,
int coarse, double targetDy) {
const QString dir = QString::fromLocal8Bit(lineDir.c_str());
const QString base = QString::fromLocal8Bit(linePrefix.c_str());
// 1) 走 P1 链load → buildVolumeData(处理前) → runPipeline → 再 buildVolumeData。
const auto tLoad = std::chrono::steady_clock::now();
GPRDataModel model;
if (!IprhParser::loadImpulseMultiChannel(dir, base, model)) {
throw std::runtime_error("loadImpulseMultiChannel 失败: " + lineDir + " / " +
linePrefix);
}
model.buildVolumeData(); // 处理前立方体(用于 before 统计)
const double meanBefore = meanAbsAmplitude(model);
const double loadMs = nowMs(tLoad);
const auto tPipe = std::chrono::steady_clock::now();
RadarProcessor::ProcPipeline pipeline;
pipeline.setDefaultFlow();
GPRDataModel processed = RadarProcessor::runPipeline(model, pipeline);
// ★关键runPipeline 原位变换 traces 且零时校正会改 samplesPerTrace
// 不重建 volumeData故必须再建一次拿处理后立方体。
processed.buildVolumeData();
const double meanAfter = meanAbsAmplitude(processed);
const double pipelineMs = nowMs(tPipe);
// 2) 立方体维度volumeData[通道][道][样本] → 轴 X=道/Y=通道/Z=样本。
const int channels = processed.volumeData.size();
const int traces = channels > 0 ? processed.volumeData[0].size() : 0;
const int samples =
(channels > 0 && traces > 0) ? processed.volumeData[0][0].size() : 0;
if (channels <= 0 || traces <= 0 || samples <= 0) {
throw std::runtime_error("处理后立方体为空(通道/道/样本 = " +
std::to_string(channels) + "/" +
std::to_string(traces) + "/" +
std::to_string(samples) + ")");
}
// §1 线内通道插值偏移:读各通道真实横向偏移(header.chXOffsets)。绝不跨线;间距/
// 通道数从数据来,不假设。空/不等长 → 不启用通道插值(helper 内退路=逐通道 identity)。
std::vector<double> latOff;
const auto& chx = processed.header.chXOffsets;
if (chx.size() == channels)
for (int c = 0; c < channels; ++c)
latOff.push_back(static_cast<double>(chx[c]));
// 3) 组立方体描述 + 采样器(volumeData[通道][道][样本],越界取 0),调共享建体 helper。
// 扫值域→Quant(中点 offset)→通道插值(规则化 Y 到 targetDy)→逐体素填→spacing 均在
// helper 内完成(与后续规范化 .head/.data 路共用同一逻辑,零漂移)。
const GPRDataModel::Header& h = processed.header;
RadarCubeDesc desc;
desc.channels = channels;
desc.traces = traces;
desc.samples = samples;
desc.chXOffsets = latOff;
desc.dxBase = h.distanceInc > 1e-9 ? h.distanceInc : 1.0; // 道距(米)
desc.dyWhenNotInterpolated = channelSpacingY(h, channels); // 原通道横距(米)
desc.dz = depthSpacingZ(h); // 深度采样距(米)
CubeSampler sample = [&processed](int c, int t, int s) -> double {
const auto& ch = processed.volumeData[c];
if (t >= static_cast<int>(ch.size())) return 0.0;
const auto& tr = ch[t];
return s < static_cast<int>(tr.size()) ? static_cast<double>(tr[s]) : 0.0;
};
const auto tFill = std::chrono::steady_clock::now();
geopro::core::BuiltI16 built = assembleRadarVolume(desc, sample, coarse, targetDy);
const double fillMs = nowMs(tFill);
if (metricsOut) {
metricsOut->nx = built.vol.nx();
metricsOut->ny = built.vol.ny();
metricsOut->nz = built.vol.nz();
metricsOut->meanAbsBefore = meanBefore;
metricsOut->meanAbsAfter = meanAfter;
metricsOut->vminPhys = built.vminPhys;
metricsOut->vmaxPhys = built.vmaxPhys;
metricsOut->dx = built.spacing[0];
metricsOut->dy = built.spacing[1];
metricsOut->dz = built.spacing[2];
metricsOut->loadMs = loadMs;
metricsOut->pipelineMs = pipelineMs;
metricsOut->fillMs = fillMs;
}
return built;
}
} // namespace geopro::io::gpr