geopro/external/gpr3dviewer/GPRDataModel.h

297 lines
9.9 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.

#ifndef GPRDATAMODEL_H
#define GPRDATAMODEL_H
#include <QVector>
#include <QString>
#include <QMap>
#include <QVector3D>
#include <QMetaType>
#include <utility>
#include "SurveyGeometry.h"
struct RadarTrace {
QVector<short> amplitudes; // 【重要】改为 short (16-bit),之前是 float
double startTime = 0.0;
double timeStep = 0.0;
// 三维位置信息
QVector3D position; // 该道的三维空间位置
int channelNumber = 0; // 通道号
RadarTrace() = default;
RadarTrace(const RadarTrace& other)
: amplitudes(QVector<short>(other.amplitudes.cbegin(), other.amplitudes.cend()))
, startTime(other.startTime)
, timeStep(other.timeStep)
, position(other.position)
, channelNumber(other.channelNumber)
{}
RadarTrace& operator=(const RadarTrace& other) {
if (this != &other) {
amplitudes = QVector<short>(other.amplitudes.cbegin(), other.amplitudes.cend());
startTime = other.startTime;
timeStep = other.timeStep;
position = other.position;
channelNumber = other.channelNumber;
}
return *this;
}
RadarTrace(RadarTrace&& other) noexcept
: amplitudes(std::move(other.amplitudes))
, startTime(other.startTime)
, timeStep(other.timeStep)
, position(other.position)
, channelNumber(other.channelNumber)
{}
RadarTrace& operator=(RadarTrace&& other) noexcept {
if (this != &other) {
amplitudes = std::move(other.amplitudes);
startTime = other.startTime;
timeStep = other.timeStep;
position = other.position;
channelNumber = other.channelNumber;
}
return *this;
}
};
class GPRDataModel {
public:
struct Header {
int numTraces = 0; // LAST TRACE
int samplesPerTrace = 0; // SAMPLES
double timeWindowNs = 0.0; // TIMEWINDOW (ns)
double distanceInc = 0.0; // DISTANCE INTERVAL (m)
double antennaFreq = 0.0; // FREQUENCY (MHz)
QString antennaType; // ANTENNAS
QString date; // DATE
QString timeStr; // TIME
// 三维相关参数
int numberOfChannels = 1; // NUMBER_OF_CH
QVector<float> chXOffsets; // CH_X_OFFSETS
QVector<float> chYOffsets; // CH_Y_OFFSETS
QString units; // UNITS
double startPosition = 0.0; // START POSITION
double stopPosition = 0.0; // STOP POSITION
double waveVelocity = 0.1; // 波速,用于深度计算
double timeIntervalNs = 0.0; // TIME INTERVAL (ns),用于计算时窗
// 原始映射,用于调试或扩展
QMap<QString, QString> rawParams;
};
Header header;
QVector<RadarTrace> traces;
// 三维数据相关属性
int tracesPerChannel = 0; // 每个通道的道数
int channels = 0; // 实际通道数
double totalDistance = 0.0; // 总距离
// 三维数据体 - 存储为[channel][trace][sample]格式
QVector<QVector<QVector<short>>> volumeData; // [channel][trace][sample]
bool isEmpty() const { return traces.isEmpty(); }
void clear() {
traces.clear();
header = Header{};
tracesPerChannel = 0;
channels = 0;
totalDistance = 0.0;
volumeData.clear();
}
// 获取指定通道的道数
int getTracesPerChannel() const {
if (header.numTraces <= 0) {
return 0;
}
if (header.numberOfChannels > 0) {
return header.numTraces / header.numberOfChannels;
}
return header.numTraces;
}
int getTraceIndex(int channel, int traceInChannel) const {
const int nChannels = header.numberOfChannels > 0 ? header.numberOfChannels : 1;
if (channel < 0 || channel >= nChannels ||
traceInChannel < 0 || traceInChannel >= getTracesPerChannel()) {
return -1;
}
return traceInChannel * nChannels + channel;
}
// 获取指定通道和道号的数据
const RadarTrace* getTrace(int channel, int traceInChannel) const {
const int index = getTraceIndex(channel, traceInChannel);
if (index >= 0 && index < traces.size()) {
return &traces[index];
}
return nullptr;
}
// 计算深度(米)
double calculateDepth(int sampleIndex) const {
if (header.samplesPerTrace > 0) {
// 使用默认波速0.1m/ns除以2是因为往返时间
double timePerSample = header.timeWindowNs / header.samplesPerTrace;
double timeNs = sampleIndex * timePerSample;
return timeNs * header.waveVelocity / 2.0; // 深度 = 时间 * 波速 / 2
}
return 0.0;
}
// 计算距离(米)
double calculateDistance(int traceIndex) const {
if (header.distanceInc > 0) {
return traceIndex * header.distanceInc;
}
return 0.0;
}
// 构建三维数据体
void buildVolumeData() {
if (traces.isEmpty()) return;
int nChannels = header.numberOfChannels > 0 ? header.numberOfChannels : 1;
int nTracesPerChannel = getTracesPerChannel();
int nSamples = header.samplesPerTrace;
volumeData.resize(nChannels);
for (int c = 0; c < nChannels; ++c) {
volumeData[c].resize(nTracesPerChannel);
for (int t = 0; t < nTracesPerChannel; ++t) {
volumeData[c][t].resize(nSamples);
}
}
// 填充数据
for (int i = 0; i < traces.size(); ++i) {
int channel = i % nChannels;
int traceInChannel = i / nChannels;
if (channel < volumeData.size() && traceInChannel < volumeData[channel].size()) {
for (int s = 0; s < traces[i].amplitudes.size() && s < nSamples; ++s) {
volumeData[channel][traceInChannel][s] = traces[i].amplitudes[s];
}
}
}
}
// 获取三维数据点
short getVolumeValue(int channel, int trace, int sample) const {
if (channel < 0 || channel >= volumeData.size() ||
trace < 0 || trace >= volumeData[channel].size() ||
sample < 0 || sample >= volumeData[channel][trace].size()) {
return 0;
}
return volumeData[channel][trace][sample];
}
// 获取全局最小最大值
void getGlobalMinMax(short& minVal, short& maxVal) const {
if (volumeData.isEmpty()) {
minVal = 0;
maxVal = 0;
return;
}
minVal = 32767;
maxVal = -32768;
for (const auto& channelData : volumeData) {
for (const auto& traceData : channelData) {
for (short val : traceData) {
if (val < minVal) minVal = val;
if (val > maxVal) maxVal = val;
}
}
}
}
// 获取通道数
int getChannelCount() const {
if (!volumeData.isEmpty()) return volumeData.size();
return qMax(1, header.numberOfChannels);
}
// 获取每通道的迹数
int getTraceCountPerChannel() const {
if (!volumeData.isEmpty() && !volumeData[0].isEmpty()) return volumeData[0].size();
int nCh = qMax(1, header.numberOfChannels);
return header.numTraces / nCh;
}
// 获取每迹的样本数
int getSampleCount() const {
if (!volumeData.isEmpty() && !volumeData[0].isEmpty()) return volumeData[0][0].size();
return header.samplesPerTrace;
}
GPRDataModel() = default;
GPRDataModel(const GPRDataModel&) = default;
GPRDataModel& operator=(const GPRDataModel&) = default;
GPRDataModel(GPRDataModel&&) = default;
GPRDataModel& operator=(GPRDataModel&&) = default;
};
struct TrajectoryEditPoint {
QVector3D rawPosition;
QVector3D editedPosition;
bool enabled = true;
bool manuallyEdited = false;
bool distanceOutlier = false;
bool speedOutlier = false;
bool angleOutlier = false;
};
struct TrajectoryEditSettings {
bool distanceFilterEnabled = true;
double maxSegmentDistanceM = 1.0;
bool speedFilterEnabled = false;
double maxSpeedMps = 5.0;
double traceIntervalSec = 0.05;
bool angleFilterEnabled = true;
double maxTurnAngleDeg = 60.0;
int interpolationMode = 0; // 0 linear, 1 spline
bool preserveManualEdits = true;
};
// 单条测线结构含GPS
struct SurveyLine {
QString name; // 测线编号,如 "_000", "_001"
QString displayName; // 显示名称,如 "碧新路_000"
QString radFilePath; // .rad 文件完整路径
GPRDataModel data; // 雷达数据
QVector<QVector3D> gpsPositions; // 每个trace的GPS坐标 (X,Y,Z)
QVector<TrajectoryEditPoint> trajectoryEditPoints; // 可编辑中心线轨迹点
TrajectoryEditSettings trajectoryEditSettings;
// 三维轨迹相关(新增)
SurveyGeometry geometry; // 天线几何参数
QVector<QVector<QVector3D>> channelTrajectories; // [channel][trace] 绝对坐标
bool hasGps() const { return !gpsPositions.isEmpty(); }
bool hasValidTrajectories() const { return !channelTrajectories.isEmpty() && !channelTrajectories[0].isEmpty(); }
bool isEmpty() const { return data.isEmpty(); }
void clear() {
name.clear();
displayName.clear();
radFilePath.clear();
data.clear();
gpsPositions.clear();
trajectoryEditPoints.clear();
trajectoryEditSettings = TrajectoryEditSettings{};
geometry = SurveyGeometry{};
channelTrajectories.clear();
}
SurveyLine() = default;
SurveyLine(const SurveyLine&) = default;
SurveyLine& operator=(const SurveyLine&) = default;
SurveyLine(SurveyLine&&) = default;
SurveyLine& operator=(SurveyLine&&) = default;
};
Q_DECLARE_METATYPE(SurveyLine)
#endif // GPRDATAMODEL_H