Commit 8e6b633a authored by pixhawk's avatar pixhawk

Added NEW data plot view, fixed zoom issue in linechart

parent f6490e90
......@@ -73,7 +73,8 @@ FORMS += src/ui/MainWindow.ui \
src/ui/watchdog/WatchdogProcessView.ui \
src/ui/watchdog/WatchdogView.ui \
src/ui/QGCFirmwareUpdate.ui \
src/ui/QGCPxImuFirmwareUpdate.ui
src/ui/QGCPxImuFirmwareUpdate.ui \
src/ui/QGCDataPlot2D.ui
INCLUDEPATH += src \
src/ui \
src/ui/linechart \
......@@ -150,7 +151,9 @@ HEADERS += src/MG.h \
src/QGC.h \
src/ui/QGCFirmwareUpdate.h \
src/ui/QGCPxImuFirmwareUpdate.h \
src/comm/MAVLinkLightProtocol.h
src/comm/MAVLinkLightProtocol.h \
src/ui/QGCDataPlot2D.h \
src/ui/linechart/IncrementalPlot.h
SOURCES += src/main.cc \
src/Core.cc \
src/uas/UASManager.cc \
......@@ -209,5 +212,7 @@ SOURCES += src/main.cc \
src/QGC.cc \
src/ui/QGCFirmwareUpdate.cc \
src/ui/QGCPxImuFirmwareUpdate.cc \
src/comm/MAVLinkLightProtocol.cc
src/comm/MAVLinkLightProtocol.cc \
src/ui/QGCDataPlot2D.cc \
src/ui/linechart/IncrementalPlot.cc
RESOURCES = mavground.qrc
#include <QFile>
#include <QTextStream>
#include <QStringList>
#include <QFileInfo>
#include "LogCompressor.h"
#include <QDebug>
LogCompressor::LogCompressor(QString logFileName, int uasid) :
LogCompressor::LogCompressor(QString logFileName, QString outFileName, int uasid) :
logFileName(logFileName),
outFileName(outFileName),
running(true),
currentDataLine(0),
dataLines(1),
uasid(uasid)
{
start();
......@@ -17,13 +22,23 @@ void LogCompressor::run()
QString separator = "\t";
QString fileName = logFileName;
QFile file(fileName);
QFile outfile(outFileName);
QStringList* keys = new QStringList();
QStringList* times = new QStringList();
if (!file.exists()) return;
if (!file.open(QIODevice::ReadWrite | QIODevice::Text))
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return;
if (outFileName != "")
{
// Check if file is writeable
if (!QFileInfo(outfile).isWritable())
{
return;
}
}
// Find all keys
QTextStream in(&file);
while (!in.atEnd()) {
......@@ -43,9 +58,9 @@ void LogCompressor::run()
spacer += " " + separator;
}
qDebug() << header;
//qDebug() << header;
qDebug() << "NOW READING TIMES";
//qDebug() << "NOW READING TIMES";
// Find all times
//in.reset();
......@@ -62,6 +77,9 @@ void LogCompressor::run()
times->append(time);
}
}
dataLines = times->length();
times->sort();
// Create lines
......@@ -74,7 +92,10 @@ void LogCompressor::run()
// Fill in the values for all keys
file.reset();
QTextStream data(&file);
int linecounter = 0;
while (!data.atEnd()) {
linecounter++;
currentDataLine = linecounter;
QString line = data.readLine();
QStringList parts = line.split(separator);
// Get time
......@@ -100,20 +121,43 @@ void LogCompressor::run()
// Add header, write out file
file.close();
QFile::remove(file.fileName());
if (!file.open(QIODevice::ReadWrite | QIODevice::Text))
if (outFileName == "")
{
QFile::remove(file.fileName());
outfile.setFileName(file.fileName());
}
if (!outfile.open(QIODevice::WriteOnly | QIODevice::Text))
return;
file.write(QString(QString("unix_timestamp") + separator + header.replace(" ", "_") + QString("\n")).toLatin1());
outfile.write(QString(QString("unix_timestamp") + separator + header.replace(" ", "_") + QString("\n")).toLatin1());
//QString fileHeader = QString("unix_timestamp") + header.replace(" ", "_") + QString("\n");
// Debug output
// File output
for (int i = 0; i < outLines->length(); i++)
{
//qDebug() << outLines->at(i);
file.write(QString(outLines->at(i) + "\n").toLatin1());
outfile.write(QString(outLines->at(i) + "\n").toLatin1());
}
currentDataLine = 0;
dataLines = 1;
delete keys;
qDebug() << "Done with logfile processing";
running = false;
}
bool LogCompressor::isFinished()
{
return !running;
}
int LogCompressor::getCurrentLine()
{
return currentDataLine;
}
int LogCompressor::getDataLines()
{
return dataLines;
}
......@@ -6,10 +6,17 @@
class LogCompressor : public QThread
{
public:
LogCompressor(QString logFileName, int uasid = 0);
LogCompressor(QString logFileName, QString outFileName="", int uasid = 0);
bool isFinished();
int getDataLines();
int getCurrentLine();
protected:
void run();
QString logFileName;
QString outFileName;
bool running;
int currentDataLine;
int dataLines;
int uasid;
};
......
......@@ -138,6 +138,7 @@ void MainWindow::buildWidgets()
headDown1 = new HDDisplay(acceptList, this);
headDown2 = new HDDisplay(acceptList2, this);
joystick = new JoystickInput();
dataplot = new QGCDataPlot2D();
}
void MainWindow::connectWidgets()
......@@ -156,6 +157,7 @@ void MainWindow::arrangeCenterStack()
centerStack->addWidget(protocol);
centerStack->addWidget(map);
centerStack->addWidget(hud);
centerStack->addWidget(dataplot);
setCentralWidget(centerStack);
}
......@@ -296,12 +298,59 @@ void MainWindow::connectActions()
connect(ui.actionSettingsView, SIGNAL(triggered()), this, SLOT(loadSettingsView()));
connect(ui.actionShow_full_view, SIGNAL(triggered()), this, SLOT(loadAllView()));
connect(ui.actionShow_MAVLink_view, SIGNAL(triggered()), this, SLOT(loadMAVLinkView()));
connect(ui.actionShow_data_analysis_view, SIGNAL(triggered()), this, SLOT(loadDataView()));
connect(ui.actionStyleConfig, SIGNAL(triggered()), this, SLOT(reloadStylesheet()));
connect(ui.actionOnline_documentation, SIGNAL(triggered()), this, SLOT(showHelp()));
connect(ui.actionCredits_Developers, SIGNAL(triggered()), this, SLOT(showCredits()));
connect(ui.actionProject_Roadmap, SIGNAL(triggered()), this, SLOT(showRoadMap()));
// Joystick configuration
connect(ui.actionJoystickSettings, SIGNAL(triggered()), this, SLOT(configure()));
}
void MainWindow::showHelp()
{
if(!QDesktopServices::openUrl(QUrl("http://qgroundcontrol.org/user_guide")))
{
QMessageBox msgBox;
msgBox.setIcon(QMessageBox::Critical);
msgBox.setText("Could not open help in browser");
msgBox.setInformativeText("To get to the online help, please open http://qgroundcontrol.org/user_guide in a browser.");
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.exec();
}
}
void MainWindow::showCredits()
{
if(!QDesktopServices::openUrl(QUrl("http://qgroundcontrol.org/credits")))
{
QMessageBox msgBox;
msgBox.setIcon(QMessageBox::Critical);
msgBox.setText("Could not open credits in browser");
msgBox.setInformativeText("To get to the online help, please open http://qgroundcontrol.org/credits in a browser.");
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.exec();
}
}
void MainWindow::showRoadMap()
{
if(!QDesktopServices::openUrl(QUrl("http://qgroundcontrol.org/roadmap")))
{
QMessageBox msgBox;
msgBox.setIcon(QMessageBox::Critical);
msgBox.setText("Could not open roadmap in browser");
msgBox.setInformativeText("To get to the online help, please open http://qgroundcontrol.org/roadmap in a browser.");
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.exec();
}
}
void MainWindow::configure()
{
joystickWidget = new JoystickWidget(joystick, this);
......@@ -489,6 +538,12 @@ void MainWindow::loadPixhawkView()
this->show();
}
void MainWindow::loadDataView()
{
clearView();
centerStack->setCurrentWidget(dataplot);
}
void MainWindow::loadPilotView()
{
clearView();
......
......@@ -62,6 +62,7 @@ This file is part of the PIXHAWK project
#include "HDDisplay.h"
#include "WatchdogControl.h"
#include "HSIDisplay.h"
#include "QGCDataPlot2D.h"
#include "LogCompressor.h"
......@@ -116,6 +117,15 @@ public slots:
void loadAllView();
/** @brief Load MAVLink XML generator view */
void loadMAVLinkView();
/** @brief Load data view, allowing to plot flight data */
void loadDataView();
/** @brief Show the online help for users */
void showHelp();
/** @brief Show the authors / credits */
void showCredits();
/** @brief Show the project roadmap */
void showRoadMap();
// Fixme find a nicer solution that scales to more AP types
void loadSlugsView();
......@@ -158,6 +168,7 @@ protected:
HDDisplay* headDown2;
WatchdogControl* watchdogControl;
HSIDisplay* hsi;
QGCDataPlot2D* dataplot;
// Popup widgets
JoystickWidget* joystickWidget;
......
......@@ -35,7 +35,7 @@
<x>0</x>
<y>0</y>
<width>1000</width>
<height>25</height>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuMGround">
......@@ -75,13 +75,23 @@
<addaction name="actionSettingsView"/>
<addaction name="separator"/>
<addaction name="actionShow_MAVLink_view"/>
<addaction name="actionShow_data_analysis_view"/>
<addaction name="actionShow_full_view"/>
<addaction name="actionStyleConfig"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>Help</string>
</property>
<addaction name="actionOnline_documentation"/>
<addaction name="actionProject_Roadmap"/>
<addaction name="actionCredits_Developers"/>
</widget>
<addaction name="menuMGround"/>
<addaction name="menuNetwork"/>
<addaction name="menuUnmanned_System"/>
<addaction name="menuWindow"/>
<addaction name="menuHelp"/>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
......@@ -263,6 +273,42 @@
<string>Show MAVLink view</string>
</property>
</action>
<action name="actionOnline_documentation">
<property name="icon">
<iconset resource="../../mavground.qrc">
<normaloff>:/images/categories/applications-internet.svg</normaloff>:/images/categories/applications-internet.svg</iconset>
</property>
<property name="text">
<string>Online documentation</string>
</property>
</action>
<action name="actionShow_data_analysis_view">
<property name="icon">
<iconset resource="../../mavground.qrc">
<normaloff>:/images/apps/utilities-system-monitor.svg</normaloff>:/images/apps/utilities-system-monitor.svg</iconset>
</property>
<property name="text">
<string>Show data analysis view</string>
</property>
</action>
<action name="actionProject_Roadmap">
<property name="icon">
<iconset resource="../../mavground.qrc">
<normaloff>:/images/status/software-update-available.svg</normaloff>:/images/status/software-update-available.svg</iconset>
</property>
<property name="text">
<string>Project Roadmap</string>
</property>
</action>
<action name="actionCredits_Developers">
<property name="icon">
<iconset resource="../../mavground.qrc">
<normaloff>:/images/categories/preferences-system.svg</normaloff>:/images/categories/preferences-system.svg</iconset>
</property>
<property name="text">
<string>Credits / Developers</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>
......
This diff is collapsed.
#ifndef QGCDATAPLOT2D_H
#define QGCDATAPLOT2D_H
#include <QWidget>
#include <QFile>
#include "IncrementalPlot.h"
#include "LogCompressor.h"
namespace Ui {
class QGCDataPlot2D;
}
class QGCDataPlot2D : public QWidget {
Q_OBJECT
public:
QGCDataPlot2D(QWidget *parent = 0);
~QGCDataPlot2D();
/** @brief Linear regression over data points */
int linearRegression(double *x,double *y,int n,double *a,double *b,double *r);
public slots:
void loadFile();
/** @brief Reload a file, with filtering enabled */
void reloadFile();
void selectFile();
void loadCsvLog(QString file, QString xAxisName="", QString yAxisFilter="");
void loadRawLog(QString file, QString xAxisName="", QString yAxisFilter="");
void saveCsvLog();
/** @brief Save plot to PDF or SVG */
void savePlot();
/** @brief Export SVG file */
void exportSVG(QString file);
/** @brief Print or save PDF file (MacOS/Linux) */
void print();
protected:
void changeEvent(QEvent *e);
IncrementalPlot* plot;
LogCompressor* compressor;
QFile* logFile;
QString fileName;
private:
Ui::QGCDataPlot2D *ui;
};
#endif // QGCDATAPLOT2D_H
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QGCDataPlot2D</class>
<widget class="QWidget" name="QGCDataPlot2D">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>736</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">
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_2">
<property name="text">
<string>X</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QComboBox" name="xAxis"/>
</item>
<item row="0" column="3">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Y</string>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QLineEdit" name="yAxis"/>
</item>
<item row="0" column="5" colspan="2">
<widget class="QComboBox" name="style">
<item>
<property name="text">
<string>Style..</string>
</property>
</item>
<item>
<property name="text">
<string>Dots</string>
</property>
</item>
<item>
<property name="text">
<string>Lines</string>
</property>
</item>
</widget>
</item>
<item row="0" column="8">
<widget class="QPushButton" name="reloadButton">
<property name="text">
<string>Reload</string>
</property>
</widget>
</item>
<item row="0" column="9">
<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>
<item row="0" column="10">
<widget class="QPushButton" name="savePlotButton">
<property name="text">
<string>Save Plot</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="12">
<widget class="QFrame" name="plotFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>File</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QComboBox" name="inputFileType">
<item>
<property name="text">
<string>CSV</string>
</property>
</item>
<item>
<property name="text">
<string>pxIMU</string>
</property>
</item>
<item>
<property name="text">
<string>RAW</string>
</property>
</item>
</widget>
</item>
<item row="2" 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="9">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="11">
<widget class="QPushButton" name="printButton">
<property name="text">
<string>Print</string>
</property>
</widget>
</item>
<item row="2" column="11">
<widget class="QPushButton" name="saveCsvButton">
<property name="text">
<string>Save CSV</string>
</property>
</widget>
</item>
<item row="2" column="8">
<widget class="QPushButton" name="selectFileButton">
<property name="text">
<string>Select file</string>
</property>
</widget>
</item>
<item row="0" column="7">
<widget class="QCheckBox" name="symmetricCheckBox">
<property name="text">
<string>Symmetric</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
......@@ -405,7 +405,7 @@ void QGCParamWidget::loadParameters()
changedValues.value(wpParams.at(1).toInt())->insert(wpParams.at(2), (float)wpParams.at(3).toDouble());
qDebug() << "MARKING COMP" << wpParams.at(1).toInt() << "PARAM" << wpParams.at(2) << "VALUE" << (float)wpParams.at(3).toDouble() << "AS CHANGED";
//qDebug() << "MARKING COMP" << wpParams.at(1).toInt() << "PARAM" << wpParams.at(2) << "VALUE" << (float)wpParams.at(3).toDouble() << "AS CHANGED";
// Mark in UI
......
#include <qwt_plot.h>
#include <qwt_plot_canvas.h>
#include <qwt_plot_curve.h>
#include <qwt_symbol.h>
#include <qwt_plot_layout.h>
#include <qwt_plot_grid.h>
#include <qwt_scale_engine.h>
#include "IncrementalPlot.h"
#include <Scrollbar.h>
#include <ScrollZoomer.h>
#include <float.h>
#if QT_VERSION >= 0x040000
#include <qpaintengine.h>
#endif
CurveData::CurveData():
d_count(0)
{
}
void CurveData::append(double *x, double *y, int count)
{
int newSize = ( (d_count + count) / 1000 + 1 ) * 1000;
if ( newSize > size() )
{
d_x.resize(newSize);
d_y.resize(newSize);
}
for ( register int i = 0; i < count; i++ )
{
d_x[d_count + i] = x[i];
d_y[d_count + i] = y[i];
}
d_count += count;
}
int CurveData::count() const
{
return d_count;
}
int CurveData::size() const
{
return d_x.size();
}
const double *CurveData::x() const
{
return d_x.data();
}
const double *CurveData::y() const
{
return d_y.data();
}
IncrementalPlot::IncrementalPlot(QWidget *parent):
QwtPlot(parent)
{
setAutoReplot(false);
setFrameStyle(QFrame::NoFrame);
setLineWidth(0);
setCanvasLineWidth(2);
plotLayout()->setAlignCanvasToScales(true);
QwtPlotGrid *grid = new QwtPlotGrid;
grid->setMajPen(QPen(Qt::gray, 0, Qt::DotLine));
grid->attach(this);
QwtLinearScaleEngine* yScaleEngine = new QwtLinearScaleEngine();
setAxisScaleEngine(QwtPlot::yLeft, yScaleEngine);
setAxisAutoScale(xBottom);
setAxisAutoScale(yLeft);
resetScaling();
// enable zooming
zoomer = new ScrollZoomer(canvas());
zoomer->setRubberBandPen(QPen(Qt::red, 2, Qt::DotLine));
zoomer->setTrackerPen(QPen(Qt::red));
//zoomer->setZoomBase(QwtDoubleRect());
colors = QList<QColor>();
nextColor = 0;
///> Color map for plots, includes 20 colors
///> Map will start from beginning when the first 20 colors are exceeded
colors.append(QColor(242,255,128));
colors.append(QColor(70,80,242));
colors.append(QColor(232,33,47));
colors.append(QColor(116,251,110));
colors.append(QColor(81,183,244));
colors.append(QColor(234,38,107));
colors.append(QColor(92,247,217));
colors.append(QColor(151,59,239));
colors.append(QColor(231,72,28));
colors.append(QColor(236,48,221));
colors.append(QColor(75,133,243));
colors.append(QColor(203,254,121));
colors.append(QColor(104,64,240));
colors.append(QColor(200,54,238));
colors.append(QColor(104,250,138));
colors.append(QColor(235,43,165));
colors.append(QColor(98,248,176));
colors.append(QColor(161,252,116));
colors.append(QColor(87,231,246));
colors.append(QColor(230,126,23));
}
IncrementalPlot::~IncrementalPlot()
{
}
void IncrementalPlot::resetScaling()
{
xmin = 0;
xmax = 500;
ymin = 0;
ymax = 500;
setAxisScale(xBottom, xmin+xmin*0.05, xmax+xmax*0.05);
setAxisScale(yLeft, ymin+ymin*0.05, ymax+ymax*0.05);
replot();
// Make sure the first data access hits these
xmin = DBL_MAX;
xmax = DBL_MIN;
ymin = DBL_MAX;
ymax = DBL_MIN;
}
void IncrementalPlot::appendData(QString key, double x, double y)
{
appendData(key, &x, &y, 1);
}
void IncrementalPlot::appendData(QString key, double *x, double *y, int size)
{
CurveData* data;
QwtPlotCurve* curve;
if (!d_data.contains(key))
{
data = new CurveData;
d_data.insert(key, data);
}
else
{
data = d_data.value(key);
}
if (!d_curve.contains(key))
{
curve = new QwtPlotCurve(key);
d_curve.insert(key, curve);
curve->setStyle(QwtPlotCurve::NoCurve);
curve->setPaintAttribute(QwtPlotCurve::PaintFiltered);
const QColor &c = getNextColor();
curve->setSymbol(QwtSymbol(QwtSymbol::XCross,
QBrush(c), QPen(c), QSize(5, 5)) );
curve->attach(this);
}
else
{
curve = d_curve.value(key);
}
data->append(x, y, size);
curve->setRawData(data->x(), data->y(), data->count());
bool scaleChanged = false;
// Update scales
for (int i = 0; i<size; i++)
{
if (x[i] < xmin)
{
xmin = x[i];
scaleChanged = true;
}
if (x[i] > xmax)
{
xmax = x[i];
scaleChanged = true;
}
if (y[i] < ymin)
{
ymin = y[i];
scaleChanged = true;
}
if (y[i] > ymax)
{
ymax = y[i];
scaleChanged = true;
}
}
// setAxisScale(xBottom, xmin+xmin*0.05, xmax+xmax*0.05);
// setAxisScale(yLeft, ymin+ymin*0.05, ymax+ymax*0.05);
//#ifdef __GNUC__
//#warning better use QwtData
//#endif
//replot();
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);
}
else
{
const bool cacheMode =
canvas()->testPaintAttribute(QwtPlotCanvas::PaintCached);
#if QT_VERSION >= 0x040000 && defined(Q_WS_X11)
// Even if not recommended by TrollTech, Qt::WA_PaintOutsidePaintEvent
// works on X11. This has an tremendous effect on the performance..
canvas()->setAttribute(Qt::WA_PaintOutsidePaintEvent, true);
#endif
canvas()->setPaintAttribute(QwtPlotCanvas::PaintCached, false);
// FIXME Check if here all curves should be drawn
// QwtPlotCurve* plotCurve;
// foreach(plotCurve, d_curve)
// {
// plotCurve->draw(0, curve->dataSize()-1);
// }
curve->draw(curve->dataSize() - size, curve->dataSize() - 1);
canvas()->setPaintAttribute(QwtPlotCanvas::PaintCached, cacheMode);
#if QT_VERSION >= 0x040000 && defined(Q_WS_X11)
canvas()->setAttribute(Qt::WA_PaintOutsidePaintEvent, false);
#endif
}
}
QList<QColor> IncrementalPlot::getColorMap()
{
return colors;
}
QColor IncrementalPlot::getNextColor()
{
/* Return current color and increment counter for next round */
nextColor++;
if(nextColor >= colors.size()) nextColor = 0;
return colors[nextColor++];
}
QColor IncrementalPlot::getColorForCurve(QString id)
{
return d_curve.value(id)->pen().color();
}
void IncrementalPlot::removeData()
{
d_curve.clear();
d_data.clear();
resetScaling();
replot();
}
#ifndef INCREMENTALPLOT_H
#define INCREMENTALPLOT_H
#include <QTimer>
#include <qwt_array.h>
#include <qwt_plot.h>
#include <QMap>
#include "ScrollZoomer.h"
class QwtPlotCurve;
class CurveData
{
// A container class for growing data
public:
CurveData();
void append(double *x, double *y, int count);
int count() const;
int size() const;
const double *x() const;
const double *y() const;
private:
int d_count;
QwtArray<double> d_x;
QwtArray<double> d_y;
QTimer *d_timer;
int d_timerCount;
};
class IncrementalPlot : public QwtPlot
{
Q_OBJECT
public:
IncrementalPlot(QWidget *parent = NULL);
virtual ~IncrementalPlot();
void appendData(QString key, double x, double y);
void appendData(QString key, double *x, double *y, int size);
void resetScaling();
void removeData();
/** @brief Get color map of this plot */
QList<QColor> IncrementalPlot::getColorMap();
/** @brief Get next color of color map */
QColor IncrementalPlot::getNextColor();
/** @brief Get color for curve id */
QColor IncrementalPlot::getColorForCurve(QString id);
protected:
QList<QColor> colors;
int nextColor;
ScrollZoomer* zoomer;
double xmin;
double xmax;
double ymin;
double ymax;
private:
QMap<QString, CurveData* > d_data;
QMap<QString, QwtPlotCurve* > d_curve;
};
#endif /* INCREMENTALPLOT_H */
......@@ -134,7 +134,8 @@ d_curve(NULL)
setCanvasBackground(QColor(40, 40, 40));
// Enable zooming
Zoomer *zoomer = new Zoomer(canvas());
//zoomer = new Zoomer(canvas());
zoomer = new ScrollZoomer(canvas());
zoomer->setRubberBandPen(QPen(Qt::blue, 2, Qt::DotLine));
zoomer->setTrackerPen(QPen(Qt::blue));
......@@ -581,16 +582,23 @@ void LinechartPlot::paintRealtime()
{
// Update plot window value to new max time if the last time was also the max time
windowLock.lock();
if (automaticScrollActive) {
if (MG::TIME::getGroundTimeNow() > maxTime && abs(MG::TIME::getGroundTimeNow() - maxTime) < 5000000)
{
plotPosition = MG::TIME::getGroundTimeNow();
}
else
{
if (automaticScrollActive)
{
// FIXME Check, but commenting this out should have been
// beneficial (does only add complexity)
// if (MG::TIME::getGroundTimeNow() > maxTime && abs(MG::TIME::getGroundTimeNow() - maxTime) < 5000000)
// {
// plotPosition = MG::TIME::getGroundTimeNow();
// }
// else
// {
plotPosition = maxTime;// + lastMaxTimeAdded.msec();
}
// }
setAxisScale(QwtPlot::xBottom, plotPosition - plotInterval, plotPosition, timeScaleStep);
// FIXME Last fix for scroll zoomer is here
//setAxisScale(QwtPlot::yLeft, minValue + minValue * 0.05, maxValue + maxValue * 0.05f, (maxValue - minValue) / 10.0);
/* Notify about change. Even if the window position was not changed
* itself, the relative position of the window to the interval must
* have changed, as the interval likely increased in length */
......@@ -618,8 +626,17 @@ void LinechartPlot::paintRealtime()
canvas()->setAttribute(Qt::WA_PaintOutsidePaintEvent, directPaint);
#endif
// Only set current view as zoombase if zoomer is not active
// else we could not zoom out any more
replot();
if(zoomer->zoomStack().size() < 2)
{
zoomer->setZoomBase(true);
}
else
{
replot();
}
#ifndef _WIN32
canvas()->setAttribute(Qt::WA_PaintOutsidePaintEvent, oldDirectPaint);
......@@ -726,10 +743,11 @@ void TimeSeriesData::append(quint64 ms, double value)
{
dataMutex.lock();
// Pre- allocate new space
// FIXME Check this for validity
if(static_cast<quint64>(size()) < (count + 100))
{
this->ms.resize(size() + 1000);
this->value.resize(size() + 1000);
this->ms.resize(size() + 10000);
this->value.resize(size() + 10000);
}
this->ms[count] = ms;
this->value[count] = value;
......@@ -779,16 +797,16 @@ void TimeSeriesData::append(quint64 ms, double value)
if(maxInterval > 0)
{ // maxInterval = 0 means infinite
if(interval >= maxInterval && !this->ms.isEmpty() && !this->value.isEmpty())
if(interval > maxInterval && !this->ms.isEmpty() && !this->value.isEmpty())
{
// The time at which this time series should be cut
quint64 minTime = stopTime - maxInterval;
double minTime = stopTime - maxInterval;
// Delete elements from the start of the list as long the time
// value of this elements is before the cut time
while(this->ms.first() < minTime)
{
this->ms.remove(0);
this->value.remove(0);
this->ms.pop_front();
this->value.pop_front();
}
}
}
......
......@@ -265,6 +265,7 @@ protected:
QMap<QString, QwtPlotCurve*> curves;
QMap<QString, TimeSeriesData*> data;
QMap<QString, QwtScaleMap*> scaleMaps;
ScrollZoomer* zoomer;
QList<QColor> colors;
int nextColor;
......
......@@ -42,6 +42,8 @@ This file is part of the PIXHAWK project
#include <QColor>
#include <QPalette>
#include <QFileDialog>
#include <QDesktopServices>
#include <QMessageBox>
#include "LinechartWidget.h"
#include "LinechartPlot.h"
......@@ -259,20 +261,39 @@ void LinechartWidget::startLogging()
// Let user select the log file name
QDate date(QDate::currentDate());
// QString("./pixhawk-log-" + date.toString("yyyy-MM-dd") + "-" + QString::number(logindex) + ".log")
QString fileName = QFileDialog::getSaveFileName(this, tr("Specify log file name"), tr("."), tr("Logfile (*.txt)"));
QString fileName = QFileDialog::getSaveFileName(this, tr("Specify log file name"), QDesktopServices::storageLocation(QDesktopServices::DesktopLocation), tr("Logfile (*.txt, *.csv);;"));
// Store reference to file
logFile = new QFile(fileName);
if (logFile->open(QIODevice::WriteOnly | QIODevice::Text))
// Append correct file ending if needed
bool abort = false;
while (!(fileName.endsWith(".txt") || fileName.endsWith(".csv")))
{
logging = true;
logindex++;
logButton->setText(tr("Stop logging"));
disconnect(logButton, SIGNAL(clicked()), this, SLOT(startLogging()));
connect(logButton, SIGNAL(clicked()), this, SLOT(stopLogging()));
QMessageBox msgBox;
msgBox.setIcon(QMessageBox::Critical);
msgBox.setText("Unsuitable file extension for logfile");
msgBox.setInformativeText("Please choose .txt or .csv as file extension. Click OK to change the file extension, cancel to not start logging.");
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Ok);
if(msgBox.exec() == QMessageBox::Cancel)
{
abort = true;
break;
}
fileName = QFileDialog::getSaveFileName(this, tr("Specify log file name"), QDesktopServices::storageLocation(QDesktopServices::DesktopLocation), tr("Logfile (*.txt, *.csv);;"));
}
else
// Check if the user did not abort the file save dialog
if (!abort && fileName != "")
{
return;
logFile = new QFile(fileName);
if (logFile->open(QIODevice::WriteOnly | QIODevice::Text))
{
logging = true;
logindex++;
logButton->setText(tr("Stop logging"));
disconnect(logButton, SIGNAL(clicked()), this, SLOT(startLogging()));
connect(logButton, SIGNAL(clicked()), this, SLOT(stopLogging()));
}
}
}
......@@ -418,7 +439,7 @@ void LinechartWidget::removeCurve(QString curve)
Q_UNUSED(curve)
//TODO @todo Ensure that the button for a curve gets deleted when the original curve is deleted
// Remove name
}
}
void LinechartWidget::setActive(bool active)
{
......
x y z
0 1 2
1 2 3
2 3 4
3 4 5
4 5 6
5 10 15
6 11 16
7 12 19
8 14 21
9 15 21
10 17 25
200 200 200
250 250 280
300 320 340
\ No newline at end of file
unix_timestamp Accel._X Accel._Y Accel._Z Battery Bottom_Rotor CPU_Load Ground_Dist. Gyro_Phi Gyro_Psi Gyro_Theta Left_Servo Mag._X Mag._Y Mag._Z Pressure Right_Servo Temperature Top_Rotor pitch_IMU roll_IMU yaw_IMU
1270125571544 5 -197 982 0 29685 29866 29888 0 0 0 96186 354 -0.112812 0.13585 -1.94192
1270125571564 6 -198 983 0 29781 29995 29871 0 0 0 96183 354 -0.112085 0.137193 -1.94139
1270125571584 10 -197 982 0 29739 30018 29867 0 0 0 96171 354 -0.111693 0.136574 -1.94353
1270125571604 7 -198 982 0 29802 29911 29803 0 0 0 96177 354 -0.112179 0.135778 -1.94617
1270125571645 4 -197 984 0 29782 29844 29844 0 0 0 96180 354 -0.113056 0.135965 -1.94715
1270125571665 4 -200 985 0 29685 29867 29884 0 0 0 96183 354 -0.113231 0.135327 -1.94586
1270125571685 7 -198 983 0 29757 30018 29829 0 0 0 96177 354 -0.112604 0.136857 -1.94508
1270125571706 6 -198 982 0 29759 29992 29867 0 0 0 96183 354 -0.1126 0.136602 -1.94915
1270125571746 7 -196 985 0 29803 29910 29800 0 0 0 96183 354 -0.112392 0.136709 -1.95079
1270125571766 4 -198 981 0 29750 29828 29888 0 0 0 96186 354 -0.113342 0.135532 -1.95064
1270125571787 5 -197 984 0 29759 29931 29881 0 0 0 96174 354 -0.112628 0.135657 -1.9488
1270125571807 10 -197 982 0 29739 29958 29819 0 0 0 96174 354 -0.112506 0.135777 -1.95001
1270125571847 4 -198 981 0 29786 29829 29864 0 0 0 96232 356 -0.112359 0.135253 -1.94825
1270125571867 5 -201 978 0 29719 29856 29890 0 0 0 96218 356 -0.11218 0.134669 -1.94657
1270125571888 7 -198 986 0 29782 29813 29845 0 0 0 96223 356 -0.111451 0.135688 -1.9455
1270125571908 10 -200 984 0 29693 30048 29885 0 0 0 96215 356 -0.110745 0.1365 -1.94668
1270125571948 5 -197 982 0 29757 29899 29844 0 0 0 96229 356 -0.110004 0.135859 -1.94573
1270125571969 6 -197 983 0 29727 29829 29814 0 0 0 96249 356 -0.110316 0.135751 -1.94565
1270125571989 6 -197 978 0 29790 29828 29815 0 0 0 96220 356 -0.111159 0.136244 -1.94416
1270125572009 5 -194 983 0 29718 29856 29890 0 0 0 96229 356 -0.111216 0.135992 -1.94196
1270125572050 3 -196 985 0 29739 29978 29827 0 0 0 96223 356 -0.110727 0.136577 -1.94402
1270125572070 8 -198 982 0 29782 29829 29815 0 0 0 96235 356 -0.111423 0.136697 -1.94566
1270125572090 8 -195 982 0 29687 29865 29894 0 0 0 96218 356 -0.112088 0.136097 -1.9442
1270125572130 8 -198 986 0 29793 29859 29844 0 0 0 96244 356 -0.110282 0.137491 -1.94235
1270125572171 6 -198 982 0 29790 29856 29879 0 0 0 96244 356 -0.112179 0.136971 -1.94365
1270125572191 3 -198 980 0 29759 30018 29829 0 0 0 96218 356 -0.111623 0.136194 -1.94286
1270125572211 7 -198 984 0 29758 29829 29846 0 0 0 96229 356 -0.112275 0.136007 -1.94387
1270125572252 8 -197 980 0 29743 30018 29824 0 0 0 96215 356 -0.111515 0.135539 -1.94281
1270125572272 11 -195 984 0 29741 29867 29844 0 0 0 96249 356 -0.111995 0.135753 -1.94527
1270125572292 4 -200 984 0 29771 29980 29865 0 0 0 96215 356 -0.111913 0.135829 -1.94455
1270125572312 6 -198 983 0 29792 29837 29845 0 0 0 96229 356 -0.111884 0.134861 -1.94478
1270125572353 5 -196 983 0 29685 29867 29884 0 0 0 96238 356 -0.111332 0.135644 -1.94784
1270125572373 5 -198 982 0 29693 29852 29904 0 0 0 96238 356 -0.110772 0.136927 -1.94698
1270125572393 6 -195 982 0 29738 29791 29818 0 0 0 96229 356 -0.109851 0.138106 -1.94592
1270125572414 8 -198 986 0 29782 29844 29829 0 0 0 96223 356 -0.111077 0.137396 -1.9428
1270125572454 11 -198 983 0 29743 29986 29833 0 0 0 96220 356 -0.110257 0.136744 -1.94155
1270125572474 6 -198 983 0 29749 29866 29813 0 0 0 96223 356 -0.110497 0.136917 -1.94351
1270125572495 6 -197 984 0 29719 29828 29890 0 0 0 96223 356 -0.111349 0.136959 -1.94295
1270125572515 7 -197 983 0 29760 29791 29818 0 0 0 96232 356 -0.111323 0.137251 -1.93972
1270125572555 10 -198 984 0 29800 30016 29857 0 0 0 96241 356 -0.110873 0.137041 -1.94196
1270125572575 5 -200 982 0 29760 29962 29866 0 0 0 96232 356 -0.110648 0.136038 -1.9445
1270125572596 4 -197 983 0 29740 30024 29867 0 0 0 96238 356 -0.110486 0.136046 -1.94576
1270125572616 8 -196 985 0 29802 29783 29808 0 0 0 96235 356 -0.111227 0.135128 -1.94567
1270125572656 5 -198 986 0 29771 29972 29867 0 0 0 96229 356 -0.111182 0.133833 -1.94823
1270125572697 8 -196 980 0 29739 29978 29803 0 0 0 96238 356 -0.111457 0.133602 -1.94682
1270125572717 4 -196 983 0 29749 30026 29867 0 0 0 96235 356 -0.116038 0.131922 -1.93861
1270125592944 5 -195 985 0 29757 29994 29871 0 0 0 96220 356 -0.115848 0.1323 -1.93916
1270125593025 5 -200 986 0 29780 29844 29845 0 0 0 96235 356 -0.115273 0.132222 -1.93576
1270125593045 5 -197 982 0 29757 29995 29871 0 0 0 96229 356 -0.114713 0.132137 -1.93723
1270125593086 4 -197 984 0 29802 29847 29819 0 0 0 96241 356 -0.114815 0.131529 -1.93626
1270125593106 6 -202 984 0 29802 29783 29803 0 0 0 96247 356 -0.115379 0.130754 -1.93546
1270125593126 8 -196 982 0 29803 29918 29793 0 0 0 96229 356 -0.116136 0.130336 -1.93339
1270125593146 6 -196 987 0 29802 29825 29802 0 0 0 96244 356 -0.117635 0.128822 -1.93292
1270125593187 7 -197 978 0 29802 29825 29864 0 0 0 96238 356 -0.117592 0.128252 -1.93468
1270125593207 7 -197 980 0 29803 29918 29771 0 0 0 96235 356 -0.116961 0.127734 -1.93337
1270125593227 6 -200 982 0 29733 29994 29867 0 0 0 96241 356 -0.11821 0.127208 -1.93399
1270125593247 7 -194 984 0 29739 29982 29888 0 0 0 96235 356 -0.116428 0.128791 -1.9378
1270125593288 10 -197 985 0 29771 30000 29865 0 0 0 96238 356 -0.116243 0.128666 -1.93672
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment