Commit b135d321 authored by pixhawk's avatar pixhawk

Significantly improved plotting, now capturing data in linechart allows...

Significantly improved plotting, now capturing data in linechart allows limmediate processing / analysis of the whole flight
parent 96947527
price quantity
210 81
250 73
280 64
300 61
320 50
340 46
360 45
380 44
400 43
420 39
440 36
......@@ -36,6 +36,9 @@ This file is part of the QGROUNDCONTROL project
#include <QDebug>
/**
* It will only get active upon calling startCompression()
*/
LogCompressor::LogCompressor(QString logFileName, QString outFileName, int uasid) :
logFileName(logFileName),
outFileName(outFileName),
......@@ -44,7 +47,6 @@ LogCompressor::LogCompressor(QString logFileName, QString outFileName, int uasid
dataLines(1),
uasid(uasid)
{
start();
}
void LogCompressor::run()
......@@ -174,9 +176,15 @@ void LogCompressor::run()
dataLines = 1;
delete keys;
qDebug() << "Done with logfile processing";
emit finishedFile(outfile.fileName());
running = false;
}
void LogCompressor::startCompression()
{
start();
}
bool LogCompressor::isFinished()
{
return !running;
......
......@@ -5,11 +5,15 @@
class LogCompressor : public QThread
{
Q_OBJECT
public:
/** @brief Create the log compressor. It will only get active upon calling startCompression() */
LogCompressor(QString logFileName, QString outFileName="", int uasid = 0);
void startCompression();
bool isFinished();
int getDataLines();
int getCurrentLine();
protected:
void run();
QString logFileName;
......@@ -18,6 +22,12 @@ protected:
int currentDataLine;
int dataLines;
int uasid;
signals:
/** @brief This signal is emitted once a logfile has been finished writing
* @param fileName The name out the output (CSV) file
*/
void finishedFile(QString fileName);
};
#endif // LOGCOMPRESSOR_H
......@@ -145,6 +145,7 @@ void MainWindow::connectWidgets()
{
connect(UASManager::instance(), SIGNAL(UASCreated(UASInterface*)), linechart, SLOT(addSystem(UASInterface*)));
connect(UASManager::instance(), SIGNAL(activeUASSet(int)), linechart, SLOT(selectSystem(int)));
connect(linechart, SIGNAL(logfileWritten(QString)), this, SLOT(loadDataView(QString)));
connect(mavlink, SIGNAL(receiveLossChanged(int, float)), info, SLOT(updateSendLoss(int, float)));
}
......@@ -544,6 +545,13 @@ void MainWindow::loadDataView()
centerStack->setCurrentWidget(dataplot);
}
void MainWindow::loadDataView(QString fileName)
{
clearView();
centerStack->setCurrentWidget(dataplot);
dataplot->loadFile(fileName);
}
void MainWindow::loadPilotView()
{
clearView();
......
......@@ -119,6 +119,8 @@ public slots:
void loadMAVLinkView();
/** @brief Load data view, allowing to plot flight data */
void loadDataView();
/** @brief Load data view, allowing to plot flight data */
void loadDataView(QString fileName);
/** @brief Show the online help for users */
void showHelp();
......
This diff is collapsed.
......@@ -16,11 +16,17 @@ public:
QGCDataPlot2D(QWidget *parent = 0);
~QGCDataPlot2D();
/** @brief Calculate and display regression function*/
bool calculateRegression(QString xName, QString yName, QString method="linear");
/** @brief Linear regression over data points */
int linearRegression(double *x,double *y,int n,double *a,double *b,double *r);
int linearRegression(double* x,double* y,int n,double* a,double* b,double* r);
public slots:
/** @brief Load previously selected file */
void loadFile();
/** @brief Load file with this name */
void loadFile(QString file);
/** @brief Reload a file, with filtering enabled */
void reloadFile();
void selectFile();
......@@ -29,10 +35,14 @@ public slots:
void saveCsvLog();
/** @brief Save plot to PDF or SVG */
void savePlot();
/** @brief Export PDF file */
void exportPDF(QString fileName);
/** @brief Export SVG file */
void exportSVG(QString file);
/** @brief Print or save PDF file (MacOS/Linux) */
void print();
/** @brief Calculate and display regression function*/
bool calculateRegression();
protected:
void changeEvent(QEvent *e);
......@@ -40,6 +50,7 @@ protected:
LogCompressor* compressor;
QFile* logFile;
QString fileName;
QStringList curveNames;
private:
Ui::QGCDataPlot2D *ui;
......
......@@ -6,22 +6,22 @@
<rect>
<x>0</x>
<y>0</y>
<width>807</width>
<width>1073</width>
<height>308</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout" columnstretch="2,10,2,0,0,0,0,0,0,0,0,0,0">
<item row="0" column="0" colspan="2">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>X</string>
</property>
</widget>
</item>
<item row="0" column="2">
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="xAxis"/>
</item>
<item row="0" column="3">
......@@ -53,7 +53,12 @@
</item>
<item>
<property name="text">
<string>Only dots</string>
<string>Only rectangles</string>
</property>
</item>
<item>
<property name="text">
<string>Only symbols</string>
</property>
</item>
<item>
......@@ -68,7 +73,12 @@
</item>
<item>
<property name="text">
<string>Lines and dots</string>
<string>Lines and rects</string>
</property>
</item>
<item>
<property name="text">
<string>Lines and symbols</string>
</property>
</item>
<item>
......@@ -83,7 +93,7 @@
</item>
<item>
<property name="text">
<string>Dotted lines and dots</string>
<string>Dotted lines and rects</string>
</property>
</item>
<item>
......@@ -98,39 +108,91 @@
</item>
<item>
<property name="text">
<string>Dashed lines and dots</string>
<string>Dashed lines and rects</string>
</property>
</item>
</widget>
</item>
<item row="0" column="9">
<item row="0" column="7" colspan="2">
<widget class="QPushButton" name="reloadButton">
<property name="text">
<string>Reload</string>
<string>Replot</string>
</property>
</widget>
</item>
<item row="0" column="10">
<spacer name="horizontalSpacer_2">
<item row="0" column="11">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</widget>
</item>
<item row="0" column="11">
<item row="0" column="12" colspan="2">
<widget class="QPushButton" name="savePlotButton">
<property name="text">
<string>Save Plot</string>
<string>Save Image</string>
</property>
</widget>
</item>
<item row="0" column="19">
<widget class="QPushButton" name="printButton">
<property name="text">
<string>Print</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Title</string>
</property>
</widget>
</item>
<item row="1" column="2" colspan="3">
<widget class="QLineEdit" name="plotTitle"/>
</item>
<item row="1" column="5">
<widget class="QLabel" name="label_5">
<property name="text">
<string>X label</string>
</property>
</widget>
</item>
<item row="1" column="6" colspan="2">
<widget class="QLineEdit" name="plotXAxisLabel"/>
</item>
<item row="1" column="8">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Y label</string>
</property>
</widget>
</item>
<item row="1" column="9" colspan="3">
<widget class="QLineEdit" name="plotYAxisLabel"/>
</item>
<item row="1" column="12" colspan="2">
<widget class="QCheckBox" name="symmetricCheckBox">
<property name="text">
<string>Symmetric</string>
</property>
</widget>
</item>
<item row="1" column="14" colspan="2">
<widget class="QCheckBox" name="legendCheckBox">
<property name="text">
<string>Legend</string>
</property>
</widget>
</item>
<item row="1" column="16">
<widget class="QCheckBox" name="gridCheckBox">
<property name="text">
<string>Grid</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="13">
<item row="2" column="0" colspan="20">
<widget class="QFrame" name="plotFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
......@@ -140,14 +202,14 @@
</property>
</widget>
</item>
<item row="2" column="0">
<item row="3" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>File</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<item row="3" column="1" colspan="2">
<widget class="QComboBox" name="inputFileType">
<item>
<property name="text">
......@@ -166,14 +228,21 @@
</item>
</widget>
</item>
<item row="2" column="3" colspan="4">
<item row="3" column="3" colspan="4">
<widget class="QLabel" name="filenameLabel">
<property name="text">
<string>Please select input file..</string>
</property>
</widget>
</item>
<item row="2" column="10">
<item row="3" column="8" colspan="2">
<widget class="QPushButton" name="selectFileButton">
<property name="text">
<string>Select file</string>
</property>
</widget>
</item>
<item row="3" column="10" colspan="2">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
......@@ -186,41 +255,60 @@
</property>
</spacer>
</item>
<item row="0" column="12">
<widget class="QPushButton" name="printButton">
<property name="text">
<string>Print</string>
<item row="3" column="12">
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="2" column="12">
<widget class="QPushButton" name="saveCsvButton">
<item row="3" column="13">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Save CSV</string>
<string>Regression</string>
</property>
</widget>
</item>
<item row="2" column="9">
<widget class="QPushButton" name="selectFileButton">
<item row="3" column="14" colspan="2">
<widget class="QComboBox" name="xRegressionComboBox"/>
</item>
<item row="3" column="16" colspan="2">
<widget class="QComboBox" name="yRegressionComboBox"/>
</item>
<item row="3" column="19">
<widget class="QPushButton" name="regressionButton">
<property name="text">
<string>Select file</string>
<string>Calculate</string>
</property>
</widget>
</item>
<item row="0" column="7">
<widget class="QCheckBox" name="symmetricCheckBox">
<property name="text">
<string>Symmetric</string>
<item row="3" column="18">
<widget class="QLineEdit" name="regressionOutput">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="8">
<widget class="QCheckBox" name="legendCheckBox">
<item row="0" column="14">
<widget class="QPushButton" name="saveCsvButton">
<property name="text">
<string>Legend</string>
<string>Save Data</string>
</property>
</widget>
</item>
<item row="0" column="9" colspan="2">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
......
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2010 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
QGROUNDCONTROL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
/**
* @file
* @brief Implementation of class IncrementalPlot
* @author Lorenz Meier <mavteam@student.ethz.ch>
*
*/
#include <qwt_plot.h>
#include <qwt_plot_canvas.h>
#include <qwt_plot_curve.h>
......@@ -9,9 +39,9 @@
#include <Scrollbar.h>
#include <ScrollZoomer.h>
#include <float.h>
#if QT_VERSION >= 0x040000
#include <qpaintengine.h>
#endif
#include <QDebug>
CurveData::CurveData():
d_count(0)
......@@ -45,18 +75,23 @@ int CurveData::size() const
return d_x.size();
}
const double *CurveData::x() const
const double* CurveData::x() const
{
return d_x.data();
}
const double *CurveData::y() const
const double* CurveData::y() const
{
return d_y.data();
}
IncrementalPlot::IncrementalPlot(QWidget *parent):
QwtPlot(parent)
QwtPlot(parent),
symbolWidth(1.2f),
curveWidth(1.0f),
gridWidth(0.8f),
scaleWidth(1.0f),
symmetric(false)
{
setAutoReplot(false);
......@@ -67,8 +102,8 @@ IncrementalPlot::IncrementalPlot(QWidget *parent):
plotLayout()->setAlignCanvasToScales(true);
QwtPlotGrid *grid = new QwtPlotGrid;
grid->setMajPen(QPen(Qt::gray, 0, Qt::DotLine));
grid = new QwtPlotGrid;
grid->setMajPen(QPen(Qt::gray, 0.8f, Qt::DotLine));
grid->attach(this);
QwtLinearScaleEngine* yScaleEngine = new QwtLinearScaleEngine();
......@@ -82,7 +117,7 @@ IncrementalPlot::IncrementalPlot(QWidget *parent):
// enable zooming
zoomer = new ScrollZoomer(canvas());
zoomer->setRubberBandPen(QPen(Qt::red, 2, Qt::DotLine));
zoomer->setRubberBandPen(QPen(Qt::red, 1.5f, Qt::DotLine));
zoomer->setTrackerPen(QPen(Qt::red));
//zoomer->setZoomBase(QwtDoubleRect());
legend = NULL;
......@@ -112,6 +147,8 @@ IncrementalPlot::IncrementalPlot(QWidget *parent):
colors.append(QColor(161,252,116));
colors.append(QColor(87,231,246));
colors.append(QColor(230,126,23));
connect(this, SIGNAL(legendChecked(QwtPlotItem*,bool)), this, SLOT(handleLegendClick(QwtPlotItem*,bool)));
}
IncrementalPlot::~IncrementalPlot()
......@@ -119,6 +156,23 @@ IncrementalPlot::~IncrementalPlot()
}
/**
* @param symmetric true will enforce that both axes have the same interval,
* centered around the data plot. A circle will thus remain a circle if true,
* if set to false it might become an ellipse because of axis scaling.
*/
void IncrementalPlot::setSymmetric(bool symmetric)
{
this->symmetric = symmetric;
updateScale(); // Updates the scaling at replots
}
void IncrementalPlot::handleLegendClick(QwtPlotItem* item, bool on)
{
item->setVisible(!on);
replot();
}
void IncrementalPlot::showLegend(bool show)
{
if (show)
......@@ -127,6 +181,7 @@ void IncrementalPlot::showLegend(bool show)
{
legend = new QwtLegend;
legend->setFrameStyle(QFrame::Box);
legend->setItemMode(QwtLegend::CheckableItem);
}
insertLegend(legend, QwtPlot::RightLegend);
}
......@@ -135,7 +190,7 @@ void IncrementalPlot::showLegend(bool show)
delete legend;
legend = NULL;
}
replot();
updateScale(); // Updates the scaling at replots
}
/**
......@@ -159,19 +214,25 @@ void IncrementalPlot::setStyleText(QString style)
if (style.toLower().contains("circles"))
{
curve->setSymbol(QwtSymbol(QwtSymbol::Ellipse,
QBrush(curve->pen().color()), curve->pen(), QSize(5, 5)) );
Qt::NoBrush, QPen(QBrush(curve->symbol().pen().color()), symbolWidth), QSize(6, 6)) );
}
else if (style.toLower().contains("crosses"))
{
curve->setSymbol(QwtSymbol(QwtSymbol::XCross,
QBrush(curve->pen().color()), curve->pen(), QSize(5, 5)) );
Qt::NoBrush, QPen(QBrush(curve->symbol().pen().color()), symbolWidth), QSize(5, 5)) );
}
else // Always show dots (style.toLower().contains("dots"))
else if (style.toLower().contains("rect"))
{
curve->setSymbol(QwtSymbol(QwtSymbol::Rect,
QBrush(curve->pen().color()), curve->pen(), QSize(1, 1)) );
Qt::NoBrush, QPen(QBrush(curve->symbol().pen().color()), symbolWidth), QSize(6, 6)) );
}
else if (style.toLower().contains("line")) // Show no symbol
{
curve->setSymbol(QwtSymbol(QwtSymbol::NoSymbol,
Qt::NoBrush, QPen(QBrush(curve->symbol().pen().color()), symbolWidth), QSize(6, 6)) );
}
curve->setPen(QPen(QBrush(curve->symbol().pen().color().darker()), curveWidth));
// Style of lines
if (style.toLower().contains("dotted"))
{
......@@ -181,19 +242,25 @@ void IncrementalPlot::setStyleText(QString style)
{
curve->setStyle(QwtPlotCurve::Lines);
}
else if (style.toLower().contains("dashed") || style.toLower().contains("solid"))
{
curve->setStyle(QwtPlotCurve::Steps);
}
else
{
curve->setStyle(QwtPlotCurve::NoCurve);
}
}
replot();
}
void IncrementalPlot::resetScaling()
{
xmin = 0;
xmax = 500;
ymin = 0;
ymax = 500;
ymin = xmin;
ymax = xmax;
setAxisScale(xBottom, xmin+xmin*0.05, xmax+xmax*0.05);
setAxisScale(yLeft, ymin+ymin*0.05, ymax+ymax*0.05);
......@@ -207,6 +274,46 @@ void IncrementalPlot::resetScaling()
ymax = DBL_MIN;
}
/**
* Updates the scale calculation and re-plots the whole plot
*/
void IncrementalPlot::updateScale()
{
const double margin = 0.05;
double xMinRange = xmin+(xmin*margin);
double xMaxRange = xmax+(xmax*margin);
double yMinRange = ymin+(ymin*margin);
double yMaxRange = ymax+(ymax*margin);
if (symmetric)
{
double xRange = xMaxRange - xMinRange;
double yRange = yMaxRange - yMinRange;
// Get the aspect ratio of the plot
float xSize = width();
if (legend != NULL) xSize -= legend->width();
float ySize = height();
float aspectRatio = xSize / ySize;
if (xRange > yRange)
{
double yCenter = yMinRange + yRange/2.0;
yMinRange = yCenter - xRange/2.0;
yMaxRange = yCenter + xRange/2.0;
}
else
{
double xCenter = xMinRange + xRange/2.0;
xMinRange = xCenter - yRange/2.0;
xMaxRange = xCenter + yRange/2.0;
}
}
setAxisScale(xBottom, xMinRange, xMaxRange);
setAxisScale(yLeft, yMinRange, yMaxRange);
zoomer->setZoomBase(true);
}
void IncrementalPlot::appendData(QString key, double x, double y)
{
appendData(key, &x, &y, 1);
......@@ -235,7 +342,7 @@ void IncrementalPlot::appendData(QString key, double *x, double *y, int size)
const QColor &c = getNextColor();
curve->setSymbol(QwtSymbol(QwtSymbol::XCross,
QBrush(c), QPen(c), QSize(5, 5)) );
QBrush(c), QPen(c, 1.2f), QSize(5, 5)) );
curve->attach(this);
}
......@@ -285,9 +392,7 @@ void IncrementalPlot::appendData(QString key, double *x, double *y, int size)
if(scaleChanged)
{
setAxisScale(xBottom, xmin+xmin*0.05, xmax+xmax*0.05);
setAxisScale(yLeft, ymin+ymin*0.05, ymax+ymax*0.05);
zoomer->setZoomBase(true);
updateScale();
}
else
{
......@@ -316,9 +421,44 @@ void IncrementalPlot::appendData(QString key, double *x, double *y, int size)
#if QT_VERSION >= 0x040000 && defined(Q_WS_X11)
canvas()->setAttribute(Qt::WA_PaintOutsidePaintEvent, false);
#endif
}
}
/**
* @return Number of copied data points, 0 on failure
*/
int IncrementalPlot::data(QString key, double* r_x, double* r_y, int maxSize)
{
int result = 0;
if (d_data.contains(key))
{
CurveData* d = d_data.value(key);
if (maxSize >= d->count())
{
result = d->count();
memcpy(r_x, d->x(), sizeof(double) * d->count());
memcpy(r_y, d->y(), sizeof(double) * d->count());
}
else
{
result = 0;
}
}
return result;
}
/**
* @param show true to show the grid, false else
*/
void IncrementalPlot::showGrid(bool show)
{
grid->setVisible(show);