297 lines
9.9 KiB
C++
297 lines
9.9 KiB
C++
#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
|