|
|
|
@ -27,6 +27,7 @@ QGCDataPlot2D::QGCDataPlot2D(QWidget *parent) :
@@ -27,6 +27,7 @@ QGCDataPlot2D::QGCDataPlot2D(QWidget *parent) :
|
|
|
|
|
QHBoxLayout* layout = new QHBoxLayout(ui->plotFrame); |
|
|
|
|
layout->addWidget(plot); |
|
|
|
|
ui->plotFrame->setLayout(layout); |
|
|
|
|
ui->gridCheckBox->setChecked(plot->gridEnabled()); |
|
|
|
|
|
|
|
|
|
// Connect user actions
|
|
|
|
|
connect(ui->selectFileButton, SIGNAL(clicked()), this, SLOT(selectFile())); |
|
|
|
@ -35,6 +36,9 @@ QGCDataPlot2D::QGCDataPlot2D(QWidget *parent) :
@@ -35,6 +36,9 @@ QGCDataPlot2D::QGCDataPlot2D(QWidget *parent) :
|
|
|
|
|
connect(ui->savePlotButton, SIGNAL(clicked()), this, SLOT(savePlot())); |
|
|
|
|
connect(ui->printButton, SIGNAL(clicked()), this, SLOT(print())); |
|
|
|
|
connect(ui->legendCheckBox, SIGNAL(clicked(bool)), plot, SLOT(showLegend(bool))); |
|
|
|
|
connect(ui->symmetricCheckBox, SIGNAL(clicked(bool)), plot, SLOT(setSymmetric(bool))); |
|
|
|
|
connect(ui->gridCheckBox, SIGNAL(clicked(bool)), plot, SLOT(showGrid(bool))); |
|
|
|
|
connect(ui->regressionButton, SIGNAL(clicked()), this, SLOT(calculateRegression())); |
|
|
|
|
connect(ui->style, SIGNAL(currentIndexChanged(QString)), plot, SLOT(setStyleText(QString))); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -68,31 +72,54 @@ void QGCDataPlot2D::loadFile()
@@ -68,31 +72,54 @@ void QGCDataPlot2D::loadFile()
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void QGCDataPlot2D::loadFile(QString file) |
|
|
|
|
{ |
|
|
|
|
fileName = file; |
|
|
|
|
if (QFileInfo(fileName).isReadable()) |
|
|
|
|
{ |
|
|
|
|
if (fileName.contains(".raw") || fileName.contains(".imu")) |
|
|
|
|
{ |
|
|
|
|
loadRawLog(fileName); |
|
|
|
|
} |
|
|
|
|
else if (fileName.contains(".txt") || fileName.contains(".csv")) |
|
|
|
|
{ |
|
|
|
|
loadCsvLog(fileName); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This function brings up a file name dialog and exports to either PDF or SVG, depending on the filename |
|
|
|
|
*/ |
|
|
|
|
void QGCDataPlot2D::savePlot() |
|
|
|
|
{ |
|
|
|
|
QString fileName = "plot.svg"; |
|
|
|
|
fileName = QFileDialog::getSaveFileName( |
|
|
|
|
this, "Export File Name", QDesktopServices::storageLocation(QDesktopServices::DesktopLocation), |
|
|
|
|
"SVG Documents (*.svg);;"); |
|
|
|
|
while(!fileName.endsWith("svg")) |
|
|
|
|
"PDF Documents (*.pdf);;SVG Images (*.svg)"); |
|
|
|
|
while(!(fileName.endsWith(".svg") || fileName.endsWith(".pdf"))) |
|
|
|
|
{ |
|
|
|
|
QMessageBox msgBox; |
|
|
|
|
msgBox.setIcon(QMessageBox::Critical); |
|
|
|
|
msgBox.setText("Unsuitable file extension for SVG"); |
|
|
|
|
msgBox.setInformativeText("Please choose .svg as file extension. Click OK to change the file extension, cancel to not save the file."); |
|
|
|
|
msgBox.setText("Unsuitable file extension for PDF or SVG"); |
|
|
|
|
msgBox.setInformativeText("Please choose .pdf or .svg as file extension. Click OK to change the file extension, cancel to not save the file."); |
|
|
|
|
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); |
|
|
|
|
msgBox.setDefaultButton(QMessageBox::Ok); |
|
|
|
|
if(msgBox.exec() == QMessageBox::Cancel) break; |
|
|
|
|
// Abort if cancelled
|
|
|
|
|
if(msgBox.exec() == QMessageBox::Cancel) return; |
|
|
|
|
fileName = QFileDialog::getSaveFileName( |
|
|
|
|
this, "Export File Name", QDesktopServices::storageLocation(QDesktopServices::DesktopLocation), |
|
|
|
|
"SVG Documents (*.svg);;"); |
|
|
|
|
"PDF Documents (*.pdf);;SVG Images (*.svg)"); |
|
|
|
|
} |
|
|
|
|
exportSVG(fileName); |
|
|
|
|
|
|
|
|
|
// else if (fileName.endsWith("pdf"))
|
|
|
|
|
// {
|
|
|
|
|
// print(fileName);
|
|
|
|
|
// }
|
|
|
|
|
if (fileName.endsWith(".pdf")) |
|
|
|
|
{ |
|
|
|
|
exportPDF(fileName); |
|
|
|
|
} |
|
|
|
|
else if (fileName.endsWith(".svg")) |
|
|
|
|
{ |
|
|
|
|
exportSVG(fileName); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -137,10 +164,51 @@ void QGCDataPlot2D::print()
@@ -137,10 +164,51 @@ void QGCDataPlot2D::print()
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void QGCDataPlot2D::exportPDF(QString fileName) |
|
|
|
|
{ |
|
|
|
|
QPrinter printer; |
|
|
|
|
printer.setOutputFormat(QPrinter::PdfFormat); |
|
|
|
|
printer.setOutputFileName(fileName); |
|
|
|
|
//printer.setFullPage(true);
|
|
|
|
|
printer.setPageMargins(10.0, 10.0, 10.0, 10.0, QPrinter::Millimeter); |
|
|
|
|
printer.setPageSize(QPrinter::A4); |
|
|
|
|
|
|
|
|
|
QString docName = plot->title().text(); |
|
|
|
|
if ( !docName.isEmpty() ) |
|
|
|
|
{ |
|
|
|
|
docName.replace (QRegExp (QString::fromLatin1 ("\n")), tr (" -- ")); |
|
|
|
|
printer.setDocName (docName); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
printer.setCreator("QGroundControl"); |
|
|
|
|
printer.setOrientation(QPrinter::Landscape); |
|
|
|
|
|
|
|
|
|
plot->setStyleSheet("QWidget { background-color: #FFFFFF; color: #000000; background-clip: border; font-size: 10pt;}"); |
|
|
|
|
// plot->setCanvasBackground(Qt::white);
|
|
|
|
|
// QwtPlotPrintFilter filter;
|
|
|
|
|
// filter.color(Qt::white, QwtPlotPrintFilter::CanvasBackground);
|
|
|
|
|
// filter.color(Qt::black, QwtPlotPrintFilter::AxisScale);
|
|
|
|
|
// filter.color(Qt::black, QwtPlotPrintFilter::AxisTitle);
|
|
|
|
|
// filter.color(Qt::black, QwtPlotPrintFilter::MajorGrid);
|
|
|
|
|
// filter.color(Qt::black, QwtPlotPrintFilter::MinorGrid);
|
|
|
|
|
// if ( printer.colorMode() == QPrinter::GrayScale )
|
|
|
|
|
// {
|
|
|
|
|
// int options = QwtPlotPrintFilter::PrintAll;
|
|
|
|
|
// options &= ~QwtPlotPrintFilter::PrintBackground;
|
|
|
|
|
// options |= QwtPlotPrintFilter::PrintFrameWithScales;
|
|
|
|
|
// filter.setOptions(options);
|
|
|
|
|
// }
|
|
|
|
|
plot->print(printer);//, filter);
|
|
|
|
|
plot->setStyleSheet("QWidget { background-color: #050508; color: #DDDDDF; background-clip: border; font-size: 11pt;}"); |
|
|
|
|
//plot->setCanvasBackground(QColor(5, 5, 8));
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void QGCDataPlot2D::exportSVG(QString fileName) |
|
|
|
|
{ |
|
|
|
|
if ( !fileName.isEmpty() ) |
|
|
|
|
{ |
|
|
|
|
plot->setStyleSheet("QWidget { background-color: #FFFFFF; color: #000000; background-clip: border; font-size: 10pt;}"); |
|
|
|
|
//plot->setCanvasBackground(Qt::white);
|
|
|
|
|
QSvgGenerator generator; |
|
|
|
|
generator.setFileName(fileName); |
|
|
|
|
generator.setSize(QSize(800, 600)); |
|
|
|
@ -153,6 +221,7 @@ void QGCDataPlot2D::exportSVG(QString fileName)
@@ -153,6 +221,7 @@ void QGCDataPlot2D::exportSVG(QString fileName)
|
|
|
|
|
filter.color(Qt::black, QwtPlotPrintFilter::MinorGrid); |
|
|
|
|
|
|
|
|
|
plot->print(generator, filter); |
|
|
|
|
plot->setStyleSheet("QWidget { background-color: #050508; color: #DDDDDF; background-clip: border; font-size: 11pt;}"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -199,6 +268,7 @@ void QGCDataPlot2D::loadRawLog(QString file, QString xAxisName, QString yAxisFil
@@ -199,6 +268,7 @@ void QGCDataPlot2D::loadRawLog(QString file, QString xAxisName, QString yAxisFil
|
|
|
|
|
// Postprocess log file
|
|
|
|
|
logFile = new QTemporaryFile(); |
|
|
|
|
compressor = new LogCompressor(file, logFile->fileName()); |
|
|
|
|
compressor->startCompression(); |
|
|
|
|
|
|
|
|
|
// Block UI
|
|
|
|
|
QProgressDialog progress("Transforming RAW log file to CSV", "Abort Transformation", 0, 1, this); |
|
|
|
@ -242,6 +312,7 @@ void QGCDataPlot2D::loadCsvLog(QString file, QString xAxisName, QString yAxisFil
@@ -242,6 +312,7 @@ void QGCDataPlot2D::loadCsvLog(QString file, QString xAxisName, QString yAxisFil
|
|
|
|
|
{ |
|
|
|
|
logFile->close(); |
|
|
|
|
delete logFile; |
|
|
|
|
curveNames.clear(); |
|
|
|
|
} |
|
|
|
|
logFile = new QFile(file); |
|
|
|
|
|
|
|
|
@ -249,6 +320,11 @@ void QGCDataPlot2D::loadCsvLog(QString file, QString xAxisName, QString yAxisFil
@@ -249,6 +320,11 @@ void QGCDataPlot2D::loadCsvLog(QString file, QString xAxisName, QString yAxisFil
|
|
|
|
|
if (!logFile->open(QIODevice::ReadOnly | QIODevice::Text)) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
// Set plot title
|
|
|
|
|
if (ui->plotTitle->text() != "") plot->setTitle(ui->plotTitle->text()); |
|
|
|
|
if (ui->plotXAxisLabel->text() != "") plot->setAxisTitle(QwtPlot::xBottom, ui->plotXAxisLabel->text()); |
|
|
|
|
if (ui->plotYAxisLabel->text() != "") plot->setAxisTitle(QwtPlot::yLeft, ui->plotYAxisLabel->text()); |
|
|
|
|
|
|
|
|
|
// Extract header
|
|
|
|
|
|
|
|
|
|
// Read in values
|
|
|
|
@ -302,12 +378,15 @@ void QGCDataPlot2D::loadCsvLog(QString file, QString xAxisName, QString yAxisFil
@@ -302,12 +378,15 @@ void QGCDataPlot2D::loadCsvLog(QString file, QString xAxisName, QString yAxisFil
|
|
|
|
|
QVector<double> xValues; |
|
|
|
|
QMap<QString, QVector<double>* > yValues; |
|
|
|
|
|
|
|
|
|
QStringList curveNames = header.split(separator, QString::SkipEmptyParts); |
|
|
|
|
curveNames.append(header.split(separator, QString::SkipEmptyParts)); |
|
|
|
|
QString curveName; |
|
|
|
|
|
|
|
|
|
// Clear UI elements
|
|
|
|
|
ui->xAxis->clear(); |
|
|
|
|
ui->yAxis->clear(); |
|
|
|
|
ui->xRegressionComboBox->clear(); |
|
|
|
|
ui->yRegressionComboBox->clear(); |
|
|
|
|
ui->regressionOutput->clear(); |
|
|
|
|
|
|
|
|
|
int curveNameIndex = 0; |
|
|
|
|
|
|
|
|
@ -325,14 +404,18 @@ void QGCDataPlot2D::loadCsvLog(QString file, QString xAxisName, QString yAxisFil
@@ -325,14 +404,18 @@ void QGCDataPlot2D::loadCsvLog(QString file, QString xAxisName, QString yAxisFil
|
|
|
|
|
|
|
|
|
|
foreach(curveName, curveNames) |
|
|
|
|
{ |
|
|
|
|
// Add to plot x axis selection
|
|
|
|
|
ui->xAxis->addItem(curveName); |
|
|
|
|
// Add to regression selection
|
|
|
|
|
ui->xRegressionComboBox->addItem(curveName); |
|
|
|
|
ui->yRegressionComboBox->addItem(curveName); |
|
|
|
|
if (curveName != xAxisFilter) |
|
|
|
|
{ |
|
|
|
|
if ((yAxisFilter == "") || yAxisFilter.contains(curveName)) |
|
|
|
|
{ |
|
|
|
|
yValues.insert(curveName, new QVector<double>()); |
|
|
|
|
// Add separator starting with second item
|
|
|
|
|
if (curveNameIndex > 0 && curveNameIndex < curveNames.size()) |
|
|
|
|
if (curveNameIndex > 0 && curveNameIndex < curveNames.count()) |
|
|
|
|
{ |
|
|
|
|
ui->yAxis->setText(ui->yAxis->text()+"|"); |
|
|
|
|
} |
|
|
|
@ -364,8 +447,7 @@ void QGCDataPlot2D::loadCsvLog(QString file, QString xAxisName, QString yAxisFil
@@ -364,8 +447,7 @@ void QGCDataPlot2D::loadCsvLog(QString file, QString xAxisName, QString yAxisFil
|
|
|
|
|
|
|
|
|
|
// Take this value as x if it is selected
|
|
|
|
|
x = values.at(curveNames.indexOf(curveName)).toDouble(&okx); |
|
|
|
|
xValues.append(x - 1270125570000LL); |
|
|
|
|
qDebug() << "x" << x - 1270125570000LL; |
|
|
|
|
xValues.append(x);// - 1270125570000ULL);
|
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
@ -374,11 +456,11 @@ void QGCDataPlot2D::loadCsvLog(QString file, QString xAxisName, QString yAxisFil
@@ -374,11 +456,11 @@ void QGCDataPlot2D::loadCsvLog(QString file, QString xAxisName, QString yAxisFil
|
|
|
|
|
if(yAxisFilter == "" || yAxisFilter.contains(curveName)) |
|
|
|
|
{ |
|
|
|
|
// Only append y values where a valid x value is present
|
|
|
|
|
if (yValues.value(curveName)->size() == xValues.size() - 1) |
|
|
|
|
if (yValues.value(curveName)->count() == xValues.count() - 1) |
|
|
|
|
{ |
|
|
|
|
bool oky; |
|
|
|
|
int curveNameIndex = curveNames.indexOf(curveName); |
|
|
|
|
if (values.size() > curveNameIndex) |
|
|
|
|
if (values.count() > curveNameIndex) |
|
|
|
|
{ |
|
|
|
|
y = values.at(curveNameIndex).toDouble(&oky); |
|
|
|
|
yValues.value(curveName)->append(y); |
|
|
|
@ -391,10 +473,87 @@ void QGCDataPlot2D::loadCsvLog(QString file, QString xAxisName, QString yAxisFil
@@ -391,10 +473,87 @@ void QGCDataPlot2D::loadCsvLog(QString file, QString xAxisName, QString yAxisFil
|
|
|
|
|
|
|
|
|
|
// Add data array of each curve to the plot at once (fast)
|
|
|
|
|
// Iterates through all x-y curve combinations
|
|
|
|
|
for (int i = 0; i < yValues.size(); i++) |
|
|
|
|
for (int i = 0; i < yValues.count(); i++) |
|
|
|
|
{ |
|
|
|
|
plot->appendData(yValues.keys().at(i), xValues.data(), yValues.values().at(i)->data(), xValues.count()); |
|
|
|
|
} |
|
|
|
|
plot->setStyleText(ui->style->currentText()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool QGCDataPlot2D::calculateRegression() |
|
|
|
|
{ |
|
|
|
|
// TODO Add support for quadratic / cubic curve fitting
|
|
|
|
|
return calculateRegression(ui->xRegressionComboBox->currentText(), ui->yRegressionComboBox->currentText(), "linear"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param xName Name of the x dimension |
|
|
|
|
* @param yName Name of the y dimension |
|
|
|
|
* @param method Regression method, either "linear", "quadratic" or "cubic". Only linear is supported at this point |
|
|
|
|
*/ |
|
|
|
|
bool QGCDataPlot2D::calculateRegression(QString xName, QString yName, QString method) |
|
|
|
|
{ |
|
|
|
|
bool result = false; |
|
|
|
|
QString function; |
|
|
|
|
if (xName != yName) |
|
|
|
|
{ |
|
|
|
|
if (QFileInfo(fileName).isReadable()) |
|
|
|
|
{ |
|
|
|
|
loadCsvLog(fileName, xName, yName); |
|
|
|
|
ui->xRegressionComboBox->setCurrentIndex(curveNames.indexOf(xName)); |
|
|
|
|
ui->yRegressionComboBox->setCurrentIndex(curveNames.indexOf(yName)); |
|
|
|
|
} |
|
|
|
|
const int size = 100000; |
|
|
|
|
double x[size]; |
|
|
|
|
double y[size]; |
|
|
|
|
int copied = plot->data(yName, x, y, size); |
|
|
|
|
|
|
|
|
|
if (method == "linear") |
|
|
|
|
{ |
|
|
|
|
double a; // Y-axis crossing
|
|
|
|
|
double b; // Slope
|
|
|
|
|
double r; // Regression coefficient
|
|
|
|
|
int lin = linearRegression(x, y, copied, &a, &b, &r); |
|
|
|
|
if(lin == 1) |
|
|
|
|
{ |
|
|
|
|
function = tr("%1 = %2 * %3 + %4 | R-coefficient: %5").arg(yName, QString::number(b), xName, QString::number(a), QString::number(r)); |
|
|
|
|
|
|
|
|
|
// Plot curve
|
|
|
|
|
// y-axis crossing (x = 0)
|
|
|
|
|
// Set plotting to lines only
|
|
|
|
|
plot->appendData(tr("regression %1-%2").arg(xName, yName), 0.0, a); |
|
|
|
|
plot->setStyleText("lines"); |
|
|
|
|
// x-value of the current rightmost x position in the plot
|
|
|
|
|
plot->appendData(tr("regression %1-%2").arg(xName, yName), plot->invTransform(QwtPlot::xBottom, plot->width() - plot->width()*0.08f), (a + b*plot->invTransform(QwtPlot::xBottom, plot->width() - plot->width() * 0.08f))); |
|
|
|
|
result = true; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
function = tr("Linear regression failed. (Limit: %1 data points. Try with less)").arg(size); |
|
|
|
|
result = false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else if (method == "quadratic") |
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
else if (method == "cubic") |
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
function = tr("Regression method %1 not found").arg(method); |
|
|
|
|
result = false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
plot->appendData(yValues.keys().at(i), xValues.data(), yValues.values().at(i)->data(), xValues.size()); |
|
|
|
|
// xName == yName
|
|
|
|
|
function = tr("Please select different X and Y dimensions, not %1 = %2").arg(xName, yName); |
|
|
|
|
} |
|
|
|
|
ui->regressionOutput->setText(function); |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|