Add fusion trajectory replay view
Ubuntu build / build-and-test (ubuntu-22.04) (push) Has been cancelled
Ubuntu build / build-and-test (ubuntu-24.04) (push) Has been cancelled

This commit is contained in:
2026-07-16 23:49:55 +03:00
parent 2856c5a560
commit 851642296a
10 changed files with 348 additions and 29 deletions
+76 -9
View File
@@ -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;
}