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;
}