160 lines
6.4 KiB
C++
160 lines
6.4 KiB
C++
#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
|