Add fusion trajectory replay view
This commit is contained in:
+76
-9
@@ -14,6 +14,8 @@
|
||||
#include <QDir>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QFormLayout>
|
||||
#include <QFile>
|
||||
#include <QFileDialog>
|
||||
#include <QGridLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QHeaderView>
|
||||
@@ -155,6 +157,17 @@ LocationAnalysis analyzeLocations(const QVector<TelemetryPoint> &points)
|
||||
return analysis;
|
||||
}
|
||||
|
||||
QVector<TelemetryPoint> preferredAnalysisPoints(const QVector<TelemetryPoint> &points)
|
||||
{
|
||||
QVector<TelemetryPoint> corrected;
|
||||
for (const TelemetryPoint &point : points) {
|
||||
if (point.source == LocationSource::ImuCorrected) {
|
||||
corrected.push_back(point);
|
||||
}
|
||||
}
|
||||
return corrected.isEmpty() ? points : corrected;
|
||||
}
|
||||
|
||||
QString metersText(double meters)
|
||||
{
|
||||
if (!std::isfinite(meters)) {
|
||||
@@ -255,7 +268,7 @@ public:
|
||||
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override
|
||||
{
|
||||
return parent.isValid() ? 0 : 6;
|
||||
return parent.isValid() ? 0 : 7;
|
||||
}
|
||||
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
|
||||
@@ -275,14 +288,16 @@ public:
|
||||
case 0:
|
||||
return point.receivedAt.toLocalTime().toString(QStringLiteral("HH:mm:ss.zzz"));
|
||||
case 1:
|
||||
return coordinateText(point.latitude, point.latitudeText);
|
||||
return locationSourceText(point.source);
|
||||
case 2:
|
||||
return coordinateText(point.longitude, point.longitudeText);
|
||||
return coordinateText(point.latitude, point.latitudeText);
|
||||
case 3:
|
||||
return optionalDoubleText(point.hheight, 2);
|
||||
return coordinateText(point.longitude, point.longitudeText);
|
||||
case 4:
|
||||
return optionalIntText(point.satellites);
|
||||
return optionalDoubleText(point.hheight, 2);
|
||||
case 5:
|
||||
return optionalIntText(point.satellites);
|
||||
case 6:
|
||||
return optionalDopText(point.dop.pdop);
|
||||
default:
|
||||
return {};
|
||||
@@ -296,7 +311,7 @@ public:
|
||||
return {};
|
||||
}
|
||||
static const QStringList headers = {
|
||||
QStringLiteral("Saat"), QStringLiteral("Lat"), QStringLiteral("Long"),
|
||||
QStringLiteral("Saat"), QStringLiteral("Kaynak"), QStringLiteral("Lat"), QStringLiteral("Long"),
|
||||
QStringLiteral("HHeight"), QStringLiteral("Uydu"), QStringLiteral("PDOP")};
|
||||
return section >= 0 && section < headers.size() ? headers[section] : QVariant();
|
||||
}
|
||||
@@ -366,6 +381,50 @@ void MainWindow::clearPoints()
|
||||
resetTelemetryState();
|
||||
}
|
||||
|
||||
bool MainWindow::loadTelemetryLog(const QString &filePath)
|
||||
{
|
||||
QFile file(filePath);
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
const QString message = QStringLiteral("Log acilamadi: %1").arg(file.errorString());
|
||||
m_statusLabel->setText(message);
|
||||
statusBar()->showMessage(message, 8000);
|
||||
return false;
|
||||
}
|
||||
const QList<QByteArray> lines = file.readAll().split('\n');
|
||||
resetTelemetryState();
|
||||
const int replayCapacity = static_cast<int>(std::min<qsizetype>(10000, lines.size()));
|
||||
m_maxPointsSpin->setValue(std::max(10, replayCapacity));
|
||||
int parsedLocations = 0;
|
||||
for (const QByteArray &rawLine : lines) {
|
||||
const QString line = QString::fromUtf8(rawLine).trimmed();
|
||||
if (line.isEmpty() || line.startsWith(QLatin1Char('#'))) {
|
||||
continue;
|
||||
}
|
||||
const auto telemetry = LocationParser::parseLine(line);
|
||||
if (telemetry.has_value() && telemetry->hasLocation()) {
|
||||
++parsedLocations;
|
||||
}
|
||||
handleSerialLine(line);
|
||||
}
|
||||
flushUiUpdates();
|
||||
m_map->fitToPoints();
|
||||
const QString message = QStringLiteral("Replay yuklendi: %1 konum - %2")
|
||||
.arg(parsedLocations).arg(filePath);
|
||||
m_statusLabel->setText(message);
|
||||
statusBar()->showMessage(message, 8000);
|
||||
return parsedLocations > 0;
|
||||
}
|
||||
|
||||
void MainWindow::openTelemetryLog()
|
||||
{
|
||||
const QString filePath = QFileDialog::getOpenFileName(
|
||||
this, QStringLiteral("Sentetik telemetri logunu ac"), QString(),
|
||||
QStringLiteral("Telemetry logs (*.log *.txt);;All files (*)"));
|
||||
if (!filePath.isEmpty()) {
|
||||
loadTelemetryLog(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::handleSerialLine(const QString &line)
|
||||
{
|
||||
static const QRegularExpression solveStartRegex(
|
||||
@@ -698,6 +757,9 @@ void MainWindow::buildUi()
|
||||
m_connectButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
|
||||
m_clearButton = new QPushButton(QStringLiteral("Temizle"), this);
|
||||
m_clearButton->setIcon(style()->standardIcon(QStyle::SP_TrashIcon));
|
||||
m_openLogButton = new QPushButton(QStringLiteral("Senaryo Logu Ac"), this);
|
||||
m_openLogButton->setObjectName(QStringLiteral("openLogButton"));
|
||||
m_openLogButton->setIcon(style()->standardIcon(QStyle::SP_DialogOpenButton));
|
||||
|
||||
m_autoFollowCheck = new QCheckBox(QStringLiteral("Kamerayi takip et"), this);
|
||||
m_autoFollowCheck->setChecked(true);
|
||||
@@ -716,6 +778,7 @@ void MainWindow::buildUi()
|
||||
toolbarLayout->addWidget(new QLabel(QStringLiteral("Max"), this));
|
||||
toolbarLayout->addWidget(m_maxPointsSpin);
|
||||
toolbarLayout->addWidget(m_connectButton);
|
||||
toolbarLayout->addWidget(m_openLogButton);
|
||||
toolbarLayout->addWidget(m_clearButton);
|
||||
toolbarLayout->addWidget(m_autoFollowCheck);
|
||||
toolbarLayout->addWidget(zoomOutButton);
|
||||
@@ -932,6 +995,7 @@ void MainWindow::buildUi()
|
||||
|
||||
connect(m_connectButton, &QPushButton::clicked, this, &MainWindow::toggleSerial);
|
||||
connect(m_clearButton, &QPushButton::clicked, this, &MainWindow::clearPoints);
|
||||
connect(m_openLogButton, &QPushButton::clicked, this, &MainWindow::openTelemetryLog);
|
||||
connect(m_maxPointsSpin, qOverload<int>(&QSpinBox::valueChanged), this,
|
||||
&MainWindow::trimToMaxPoints);
|
||||
connect(m_autoFollowCheck, &QCheckBox::toggled, m_map, &MapWidget::setAutoFollow);
|
||||
@@ -974,6 +1038,7 @@ void MainWindow::updateConnectionControls()
|
||||
void MainWindow::addTelemetryPoint(const ParsedTelemetry &telemetry)
|
||||
{
|
||||
TelemetryPoint point;
|
||||
point.source = telemetry.source;
|
||||
point.latitude = *telemetry.latitude;
|
||||
point.longitude = *telemetry.longitude;
|
||||
point.latitudeText = telemetry.latitudeText;
|
||||
@@ -1028,7 +1093,8 @@ void MainWindow::updateDopLabels()
|
||||
|
||||
void MainWindow::updateAnalysis()
|
||||
{
|
||||
const LocationAnalysis analysis = analyzeLocations(m_points);
|
||||
const QVector<TelemetryPoint> analysisPoints = preferredAnalysisPoints(m_points);
|
||||
const LocationAnalysis analysis = analyzeLocations(analysisPoints);
|
||||
if (!analysis.valid) {
|
||||
m_cepLabel->setText(QStringLiteral("-"));
|
||||
m_meanLocationLabel->setText(QStringLiteral("-"));
|
||||
@@ -1051,11 +1117,11 @@ void MainWindow::updateAnalysis()
|
||||
m_map->setAverageMarker(analysis.averageLat, analysis.averageLon,
|
||||
analysis.rmsScatterMeters);
|
||||
|
||||
const double cep50 = calculateCep50(m_points, analysis.averageLat,
|
||||
const double cep50 = calculateCep50(analysisPoints, analysis.averageLat,
|
||||
analysis.averageLon);
|
||||
m_cepLabel->setText(QStringLiteral("%1 (P50, N=%2)")
|
||||
.arg(metersText(cep50))
|
||||
.arg(m_points.size()));
|
||||
.arg(analysisPoints.size()));
|
||||
m_map->setCepMarker(analysis.averageLat, analysis.averageLon, cep50);
|
||||
}
|
||||
|
||||
@@ -1078,6 +1144,7 @@ void MainWindow::mergePendingTelemetry(const ParsedTelemetry &telemetry)
|
||||
}
|
||||
|
||||
if (telemetry.latitude.has_value()) {
|
||||
m_pendingTelemetry.source = telemetry.source;
|
||||
m_pendingTelemetry.latitude = telemetry.latitude;
|
||||
m_pendingTelemetry.latitudeText = telemetry.latitudeText;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user