Newer
Older
/*=====================================================================
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 Head Up Display (HUD)
*
* @author Lorenz Meier <mavteam@student.ethz.ch>
*
*/
#include <QShowEvent>
pixhawk
committed
#include <QContextMenuEvent>
#include <QMenu>
Bryant Mairs
committed
#include <QStandardPaths>
pixhawk
committed
#include "UAS.h"
#include "QGC.h"
/**
* @warning The HUD widget will not start painting its content automatically
* to update the view, start the auto-update by calling HUD::start().
*
* @param width
* @param height
* @param parent
*/
HUD::HUD(int width, int height, QWidget* parent)
uas(NULL),
yawInt(0.0f),
mode(tr("UNKNOWN MODE")),
state(tr("UNKNOWN STATE")),
fuelStatus(tr("00.0V (00m:00s)")),
xCenterOffset(0.0f),
yCenterOffset(0.0f),
vwidth(200.0f),
vheight(150.0f),
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
vPitchPerDeg(6.0f), ///< 4 mm y translation per degree)
rawBuffer1(NULL),
rawBuffer2(NULL),
rawImage(NULL),
rawLastIndex(0),
rawExpectedBytes(0),
bytesPerLine(1),
imageStarted(false),
receivedDepth(8),
receivedChannels(1),
receivedWidth(640),
receivedHeight(480),
warningBlinkRate(5),
refreshTimer(new QTimer(this)),
noCamera(true),
hardwareAcceleration(true),
strongStrokeWidth(1.5f),
normalStrokeWidth(1.0f),
fineStrokeWidth(0.5f),
waypointName(""),
roll(0.0f),
pitch(0.0f),
yaw(0.0f),
rollLP(0.0f),
pitchLP(0.0f),
yawLP(0.0f),
yawDiff(0.0f),
xPos(0.0),
yPos(0.0),
zPos(0.0),
xSpeed(0.0),
ySpeed(0.0),
zSpeed(0.0),
lastSpeedUpdate(0),
totalSpeed(0.0),
totalAcc(0.0),
lat(0.0),
lon(0.0),
alt(0.0),
load(0.0f),
offlineDirectory(""),
nextOfflineImage(""),
Lorenz Meier
committed
videoEnabled(true),
pixhawk
committed
yImageFactor(1.0),
Don Gagne
committed
Q_UNUSED(width);
Q_UNUSED(height);
// Set auto fill to false
setAutoFillBackground(false);
// Set minimum size
setMinimumSize(80, 60);
// Set preferred size
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
// Set up the initial color theme. This can be updated by a styleChanged
// signal from MainWindow.
styleChanged(qgcApp()->styleIsDark());
refreshTimer->setInterval(updateInterval);
connect(refreshTimer, SIGNAL(timeout()), this, SLOT(repaint()));
Lorenz Meier
committed
QWidget::resize(this->width(), this->height());
const QString fontFileName = ":/res/fonts/vera.ttf"; ///< Font file is part of the QRC file and compiled into the app
const QString fontFamilyName = "Bitstream Vera Sans";
if(!QFile::exists(fontFileName)) qDebug() << "ERROR! font file: " << fontFileName << " DOES NOT EXIST!";
fontDatabase.addApplicationFont(fontFileName);
font = fontDatabase.font(fontFamilyName, "Roman", qMax(5,(int)(10.0f*scalingFactor*1.2f+0.5f)));
QFont* fontPtr = &font;
if (font.family() != fontFamilyName) qDebug() << "ERROR! WRONG FONT LOADED: " << fontFamilyName;
}
// Connect the themeChanged signal from the MainWindow to this widget, so it
// can change it's styling accordingly.
connect(qgcApp(), &QGCApplication::styleChanged, this, &HUD::styleChanged);
connect(MultiVehicleManager::instance(), &MultiVehicleManager::activeVehicleChanged, this, &HUD::_activeVehicleChanged);
pixhawk
committed
createActions();
_activeVehicleChanged(MultiVehicleManager::instance()->activeVehicle());
QSize HUD::sizeHint() const
{
lm
committed
return QSize(width(), (width()*3.0f)/4);
}
void HUD::styleChanged(bool styleIsDark)
{
// Generate a background image that's dependent on the current color scheme.
QImage fill = QImage(width(), height(), QImage::Format_Indexed8);
fill.fill(styleIsDark ? 0 : 255);
glImage = QGLWidget::convertToGLFormat(fill);
// Now set the other default colors based on the current color scheme.
{
defaultColor = QColor(70, 200, 70);
setPointColor = QColor(200, 20, 200);
warningColor = Qt::yellow;
criticalColor = Qt::red;
infoColor = QColor(20, 200, 20);
fuelColor = criticalColor;
}
else
{
defaultColor = QColor(0x01, 0x47, 0x01);
setPointColor = QColor(0x82, 0x17, 0x82);
warningColor = Qt::darkYellow;
criticalColor = Qt::darkRed;
infoColor = QColor(0x07, 0x82, 0x07);
fuelColor = criticalColor;
}
void HUD::showEvent(QShowEvent* event)
{
// React only to internal (pre-display)
// events
Lorenz Meier
committed
QWidget::showEvent(event);
refreshTimer->start(updateInterval);
}
void HUD::hideEvent(QHideEvent* event)
{
// React only to internal (pre-display)
// events
Lorenz Meier
committed
QWidget::hideEvent(event);
pixhawk
committed
void HUD::contextMenuEvent (QContextMenuEvent* event)
{
QMenu menu(this);
dongfang
committed
enableHUDAction->setChecked(HUDInstrumentsEnabled);
enableVideoAction->setChecked(videoEnabled);
pixhawk
committed
menu.addAction(enableHUDAction);
//menu.addAction(selectHUDColorAction);
menu.addAction(enableVideoAction);
menu.addAction(selectOfflineDirectoryAction);
menu.addAction(selectSaveDirectoryAction);
pixhawk
committed
menu.exec(event->globalPos());
}
void HUD::createActions()
{
enableHUDAction = new QAction(tr("Enable HUD"), this);
enableHUDAction->setStatusTip(tr("Show the HUD instruments in this window"));
enableHUDAction->setCheckable(true);
dongfang
committed
enableHUDAction->setChecked(HUDInstrumentsEnabled);
pixhawk
committed
connect(enableHUDAction, SIGNAL(triggered(bool)), this, SLOT(enableHUDInstruments(bool)));
enableVideoAction = new QAction(tr("Enable Video Live feed"), this);
enableVideoAction->setStatusTip(tr("Show the video live feed"));
enableVideoAction->setCheckable(true);
enableVideoAction->setChecked(videoEnabled);
connect(enableVideoAction, SIGNAL(triggered(bool)), this, SLOT(enableVideo(bool)));
selectOfflineDirectoryAction = new QAction(tr("Load image log"), this);
pixhawk
committed
selectOfflineDirectoryAction->setStatusTip(tr("Load previously logged images into simulation / replay"));
connect(selectOfflineDirectoryAction, SIGNAL(triggered()), this, SLOT(selectOfflineDirectory()));
selectSaveDirectoryAction = new QAction(tr("Save images to directory"), this);
selectSaveDirectoryAction->setStatusTip(tr("Save images from image stream to a directory"));
selectSaveDirectoryAction->setCheckable(true);
connect(selectSaveDirectoryAction, SIGNAL(triggered(bool)), this, SLOT(saveImages(bool)));
pixhawk
committed
}
/**
*
* @param uas the UAS/MAV to monitor/display with the HUD
*/
void HUD::_activeVehicleChanged(Vehicle* vehicle)
disconnect(this->uas, SIGNAL(attitudeChanged(UASInterface*, double, double, double, quint64)), this, SLOT(updateAttitude(UASInterface*, double, double, double, quint64)));
disconnect(this->uas, SIGNAL(attitudeChanged(UASInterface*,int, double, double, double, quint64)), this, SLOT(updateAttitude(UASInterface*,int,double, double, double, quint64)));
disconnect(this->uas, SIGNAL(batteryChanged(UASInterface*, double, double, double, int)), this, SLOT(updateBattery(UASInterface*, double, double, double, int)));
disconnect(this->uas, SIGNAL(statusChanged(UASInterface*,QString,QString)), this, SLOT(updateState(UASInterface*,QString)));
disconnect(this->uas, SIGNAL(modeChanged(int,QString,QString)), this, SLOT(updateMode(int,QString,QString)));
disconnect(this->uas, SIGNAL(heartbeat(UASInterface*)), this, SLOT(receiveHeartbeat(UASInterface*)));
disconnect(this->uas, SIGNAL(localPositionChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateLocalPosition(UASInterface*,double,double,double,quint64)));
disconnect(this->uas, SIGNAL(globalPositionChanged(UASInterface*,double,double,double,double,quint64)), this, SLOT(updateGlobalPosition(UASInterface*,double,double,double,double,quint64)));
disconnect(this->uas, SIGNAL(velocityChanged_NED(UASInterface*,double,double,double,quint64)), this, SLOT(updateSpeed(UASInterface*,double,double,double,quint64)));
disconnect(this->uas, SIGNAL(waypointSelected(int,int)), this, SLOT(selectWaypoint(int, int)));
pixhawk
committed
// Try to disconnect the image link
UAS* u = dynamic_cast<UAS*>(this->uas);
disconnect(u, SIGNAL(imageReady(UASInterface*)), this, SLOT(copyImage(UASInterface*)));
pixhawk
committed
}
}
this->uas = NULL;
if (vehicle) {
this->uas = vehicle->uas();
pixhawk
committed
// Now connect the new UAS
// Setup communication
connect(uas, SIGNAL(attitudeChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateAttitude(UASInterface*, double, double, double, quint64)));
connect(uas, SIGNAL(attitudeChanged(UASInterface*,int,double,double,double,quint64)), this, SLOT(updateAttitude(UASInterface*,int,double, double, double, quint64)));
connect(uas, SIGNAL(batteryChanged(UASInterface*, double, double, double, int)), this, SLOT(updateBattery(UASInterface*, double, double, double, int)));
pixhawk
committed
connect(uas, SIGNAL(statusChanged(UASInterface*,QString,QString)), this, SLOT(updateState(UASInterface*,QString)));
connect(uas, SIGNAL(modeChanged(int,QString,QString)), this, SLOT(updateMode(int,QString,QString)));
connect(uas, SIGNAL(heartbeat(UASInterface*)), this, SLOT(receiveHeartbeat(UASInterface*)));
connect(uas, SIGNAL(localPositionChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateLocalPosition(UASInterface*,double,double,double,quint64)));
connect(uas, SIGNAL(globalPositionChanged(UASInterface*,double,double,double,double,quint64)), this, SLOT(updateGlobalPosition(UASInterface*,double,double,double,double,quint64)));
connect(uas, SIGNAL(velocityChanged_NED(UASInterface*,double,double,double,quint64)), this, SLOT(updateSpeed(UASInterface*,double,double,double,quint64)));
pixhawk
committed
connect(uas, SIGNAL(waypointSelected(int,int)), this, SLOT(selectWaypoint(int, int)));
// Try to connect the image link
connect(u, SIGNAL(imageReady(UASInterface*)), this, SLOT(copyImage(UASInterface*)));
pixhawk
committed
}
}
//void HUD::updateAttitudeThrustSetPoint(UASInterface* uas, double rollDesired, double pitchDesired, double yawDesired, double thrustDesired, quint64 msec)
//{
//// updateValue(uas, "roll desired", rollDesired, msec);
//// updateValue(uas, "pitch desired", pitchDesired, msec);
//// updateValue(uas, "yaw desired", yawDesired, msec);
//// updateValue(uas, "thrust desired", thrustDesired, msec);
//}
void HUD::updateAttitude(UASInterface* uas, double roll, double pitch, double yaw, quint64 timestamp)
{
Q_UNUSED(uas);
Q_UNUSED(timestamp);
if (!isnan(roll) && !isinf(roll) && !isnan(pitch) && !isinf(pitch) && !isnan(yaw) && !isinf(yaw))
{
this->roll = roll;
this->pitch = pitch*3.35f; // Constant here is the 'focal length' of the projection onto the plane
this->yaw = yaw;
}
void HUD::updateAttitude(UASInterface* uas, int component, double roll, double pitch, double yaw, quint64 timestamp)
{
Q_UNUSED(uas);
Q_UNUSED(timestamp);
if (!isnan(roll) && !isinf(roll) && !isnan(pitch) && !isinf(pitch) && !isnan(yaw) && !isinf(yaw))
{
attitudes.insert(component, QVector3D(roll, pitch*3.35f, yaw)); // Constant here is the 'focal length' of the projection onto the plane
}
void HUD::updateBattery(UASInterface* uas, double voltage, double current, double percent, int seconds)
Bryant
committed
Q_UNUSED(seconds);
fuelStatus = tr("BAT [%1% | %2V]").arg(percent, 2, 'f', 0, QChar('0')).arg(voltage, 4, 'f', 1, QChar('0'));
fuelColor = infoColor;
}
}
void HUD::receiveHeartbeat(UASInterface*)
{
}
void HUD::updateThrust(UASInterface* uas, double thrust)
Q_UNUSED(uas);
Q_UNUSED(thrust);
// updateValue(uas, "thrust", thrust, MG::TIME::getGroundTimeNow());
}
void HUD::updateLocalPosition(UASInterface* uas,double x,double y,double z,quint64 timestamp)
{
Q_UNUSED(uas);
Q_UNUSED(timestamp);
this->xPos = x;
this->yPos = y;
this->zPos = z;
void HUD::updateGlobalPosition(UASInterface* uas,double lat, double lon, double altitudeAMSL, double altitudeWGS84, quint64 timestamp)
Q_UNUSED(timestamp);
this->lat = lat;
this->lon = lon;
}
void HUD::updateSpeed(UASInterface* uas,double x,double y,double z,quint64 timestamp)
{
Q_UNUSED(uas);
Q_UNUSED(timestamp);
this->xSpeed = x;
this->ySpeed = y;
this->zSpeed = z;
double newTotalSpeed = sqrt(xSpeed*xSpeed + ySpeed*ySpeed + zSpeed*zSpeed);
totalAcc = (newTotalSpeed - totalSpeed) / ((double)(lastSpeedUpdate - timestamp)/1000.0);
totalSpeed = newTotalSpeed;
}
/**
* Updates the current system state, but only if the uas matches the currently monitored uas.
*
* @param uas the system the state message originates from
* @param state short state text, displayed in HUD
*/
void HUD::updateState(UASInterface* uas,QString state)
// Only one UAS is connected at a time
Q_UNUSED(uas);
this->state = state;
}
/**
* Updates the current system mode, but only if the uas matches the currently monitored uas.
*
* @param uas the system the state message originates from
* @param mode short mode text, displayed in HUD
*/
void HUD::updateMode(int id,QString mode, QString description)
// Only one UAS is connected at a time
Q_UNUSED(id);
Q_UNUSED(description);
}
void HUD::updateLoad(UASInterface* uas, double load)
{
Q_UNUSED(uas);
this->load = load;
//updateValue(uas, "load", load, MG::TIME::getGroundTimeNow());
}
/**
* @param y coordinate in pixels to be converted to reference mm units
* @return the screen coordinate relative to the QGLWindow origin
*/
float HUD::refToScreenX(float x)
{
//qDebug() << "sX: " << (scalingFactor * x) << "Orig:" << x;
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
return (scalingFactor * x);
}
/**
* @param x coordinate in pixels to be converted to reference mm units
* @return the screen coordinate relative to the QGLWindow origin
*/
float HUD::refToScreenY(float y)
{
//qDebug() << "sY: " << (scalingFactor * y);
return (scalingFactor * y);
}
/**
* Paint text on top of the image and OpenGL drawings
*
* @param text chars to write
* @param color text color
* @param fontSize text size in mm
* @param refX position in reference units (mm of the real instrument). This is relative to the measurement unit position, NOT in pixels.
* @param refY position in reference units (mm of the real instrument). This is relative to the measurement unit position, NOT in pixels.
*/
void HUD::paintText(QString text, QColor color, float fontSize, float refX, float refY, QPainter* painter)
{
QPen prevPen = painter->pen();
float pPositionX = refToScreenX(refX) - (fontSize*scalingFactor*0.072f);
float pPositionY = refToScreenY(refY) - (fontSize*scalingFactor*0.212f);
QFont font("Bitstream Vera Sans");
// Enforce minimum font size of 5 pixels
int fSize = qMax(5, (int)(fontSize*scalingFactor*1.26f));
font.setPixelSize(fSize);
QFontMetrics metrics = QFontMetrics(font);
int border = qMax(4, metrics.leading());
QRect rect = metrics.boundingRect(0, 0, width() - 2*border, int(height()*0.125),
Qt::AlignLeft | Qt::TextWordWrap, text);
painter->setPen(color);
painter->setFont(font);
painter->setRenderHint(QPainter::TextAntialiasing);
painter->drawText(pPositionX, pPositionY,
rect.width(), rect.height(),
Qt::AlignCenter | Qt::TextWordWrap, text);
painter->setPen(prevPen);
}
/**
* @param referencePositionX horizontal position in the reference mm-unit space
* @param referencePositionY horizontal position in the reference mm-unit space
* @param referenceWidth width in the reference mm-unit space
* @param referenceHeight width in the reference mm-unit space
*/
void HUD::setupGLView(float referencePositionX, float referencePositionY, float referenceWidth, float referenceHeight)
{
Don Gagne
committed
Q_UNUSED(referencePositionX);
Q_UNUSED(referencePositionY);
Q_UNUSED(referenceWidth);
Q_UNUSED(referenceHeight);
#if 0
// code ifdef'ed out but left in to silence warnings
int pixelWidth = (int)(referenceWidth * scalingFactor);
int pixelHeight = (int)(referenceHeight * scalingFactor);
// Translate and scale the GL view in the virtual reference coordinate units on the screen
int pixelPositionX = (int)((referencePositionX * scalingFactor) + xCenterOffset);
int pixelPositionY = this->height() - (referencePositionY * scalingFactor) + yCenterOffset - pixelHeight;
Don Gagne
committed
#endif
Don Gagne
committed
Q_UNUSED(event);
}
void HUD::paintHUD()
{
// static quint64 interval = 0;
// qDebug() << "INTERVAL:" << MG::TIME::getGroundTimeNow() - interval << __FILE__ << __LINE__;
// interval = MG::TIME::getGroundTimeNow();
#if (QGC_EVENTLOOP_DEBUG)
qDebug() << "EVENTLOOP:" << __FILE__ << __LINE__;
#endif
// Read out most important values to limit hash table lookups
// Low-pass roll, pitch and yaw
rollLP = roll;//rollLP * 0.2f + 0.8f * roll;
pitchLP = pitch;//pitchLP * 0.2f + 0.8f * pitch;
yawLP = (!isinf(yaw) && !isnan(yaw)) ? yaw : yawLP;//yawLP * 0.2f + 0.8f * yaw;
// Translate for yaw
const float maxYawTrans = 60.0f;
float newYawDiff = yawDiff;
if (isinf(newYawDiff)) newYawDiff = yawDiff;
if (newYawDiff > M_PI) newYawDiff = newYawDiff - M_PI;
if (newYawDiff < -M_PI) newYawDiff = newYawDiff + M_PI;
newYawDiff = yawDiff * 0.8 + newYawDiff * 0.2;
if (yawInt > M_PI) yawInt = (float)M_PI;
if (yawInt < -M_PI) yawInt = (float)-M_PI;
float yawTrans = yawInt * (float)maxYawTrans;
if ((yawTrans < 5.0) && (yawTrans > -5.0)) yawTrans = 0;
// Negate to correct direction
yawTrans = -yawTrans;
//qDebug() << "yaw translation" << yawTrans << "integral" << yawInt << "difference" << yawDiff << "yaw" << yaw;
// Update scaling factor
// adjust scaling to fit both horizontally and vertically
scalingFactor = this->width()/vwidth;
double scalingFactorH = this->height()/vheight;
if (scalingFactorH < scalingFactor) scalingFactor = scalingFactorH;
// And if either video or the data stream is enabled, draw the next frame.
xImageFactor = width() / (float)glImage.width();
yImageFactor = height() / (float)glImage.height();
Lorenz Meier
committed
}
QPainter painter;
painter.begin(this);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setRenderHint(QPainter::HighQualityAntialiasing, true);
QPixmap pmap = QPixmap::fromImage(glImage).scaledToWidth(width());
painter.drawPixmap(0, (height() - pmap.height()) / 2, pmap);
pixhawk
committed
//glEnable(GL_MULTISAMPLE);
// QT PAINTING
//makeCurrent();
pixhawk
committed
painter.translate((this->vwidth/2.0+xCenterOffset)*scalingFactor, (this->vheight/2.0+yCenterOffset)*scalingFactor);
// COORDINATE FRAME IS NOW (0,0) at CENTER OF WIDGET
// Draw all fixed indicators
// BATTERY
paintText(fuelStatus, fuelColor, 6.0f, (-vwidth/2.0) + 10, -vheight/2.0 + 6, &painter);
pixhawk
committed
// Waypoint
paintText(waypointName, defaultColor, 6.0f, (-vwidth/3.0) + 10, +vheight/3.0 + 15, &painter);
QPen linePen(Qt::SolidLine);
linePen.setWidth(refLineWidthToPen(1.0f));
linePen.setColor(defaultColor);
painter.setBrush(Qt::NoBrush);
painter.setPen(linePen);
pixhawk
committed
// YAW INDICATOR
//
// .
// . .
// .......
//
const float yawIndicatorWidth = 12.0f;
const float yawIndicatorY = vheight/2.0f - 15.0f;
pixhawk
committed
QPolygon yawIndicator(4);
yawIndicator.setPoint(0, QPoint(refToScreenX(0.0f), refToScreenY(yawIndicatorY)));
yawIndicator.setPoint(1, QPoint(refToScreenX(yawIndicatorWidth/2.0f), refToScreenY(yawIndicatorY+yawIndicatorWidth)));
yawIndicator.setPoint(2, QPoint(refToScreenX(-yawIndicatorWidth/2.0f), refToScreenY(yawIndicatorY+yawIndicatorWidth)));
yawIndicator.setPoint(3, QPoint(refToScreenX(0.0f), refToScreenY(yawIndicatorY)));
painter.drawPolyline(yawIndicator);
painter.setPen(linePen);
pixhawk
committed
// CENTER
pixhawk
committed
// HEADING INDICATOR
//
// __ __
// \/\/
//
const float hIndicatorWidth = 20.0f;
pixhawk
committed
const float hIndicatorY = -25.0f;
const float hIndicatorYLow = hIndicatorY + hIndicatorWidth / 6.0f;
const float hIndicatorSegmentWidth = hIndicatorWidth / 7.0f;
QPolygon hIndicator(7);
hIndicator.setPoint(0, QPoint(refToScreenX(0.0f-hIndicatorWidth/2.0f), refToScreenY(hIndicatorY)));
hIndicator.setPoint(1, QPoint(refToScreenX(0.0f-hIndicatorWidth/2.0f+hIndicatorSegmentWidth*1.75f), refToScreenY(hIndicatorY)));
hIndicator.setPoint(2, QPoint(refToScreenX(0.0f-hIndicatorSegmentWidth*1.0f), refToScreenY(hIndicatorYLow)));
hIndicator.setPoint(3, QPoint(refToScreenX(0.0f), refToScreenY(hIndicatorY)));
hIndicator.setPoint(4, QPoint(refToScreenX(0.0f+hIndicatorSegmentWidth*1.0f), refToScreenY(hIndicatorYLow)));
hIndicator.setPoint(5, QPoint(refToScreenX(0.0f+hIndicatorWidth/2.0f-hIndicatorSegmentWidth*1.75f), refToScreenY(hIndicatorY)));
hIndicator.setPoint(6, QPoint(refToScreenX(0.0f+hIndicatorWidth/2.0f), refToScreenY(hIndicatorY)));
painter.drawPolyline(hIndicator);
pixhawk
committed
// SETPOINT
const float centerWidth = 8.0f;
pixhawk
committed
// TODO
//painter.drawEllipse(QPointF(refToScreenX(qMin(10.0f, values.value("roll desired", 0.0f) * 10.0f)), refToScreenY(qMin(10.0f, values.value("pitch desired", 0.0f) * 10.0f))), refToScreenX(centerWidth/2.0f), refToScreenX(centerWidth/2.0f));
const float centerCrossWidth = 20.0f;
pixhawk
committed
// left
painter.drawLine(QPointF(refToScreenX(-centerWidth / 2.0f), refToScreenY(0.0f)), QPointF(refToScreenX(-centerCrossWidth / 2.0f), refToScreenY(0.0f)));
// right
painter.drawLine(QPointF(refToScreenX(centerWidth / 2.0f), refToScreenY(0.0f)), QPointF(refToScreenX(centerCrossWidth / 2.0f), refToScreenY(0.0f)));
// top
painter.drawLine(QPointF(refToScreenX(0.0f), refToScreenY(-centerWidth / 2.0f)), QPointF(refToScreenX(0.0f), refToScreenY(-centerCrossWidth / 2.0f)));
pixhawk
committed
// COMPASS
const float compassY = -vheight/2.0f + 6.0f;
QRectF compassRect(QPointF(refToScreenX(-12.0f), refToScreenY(compassY)), QSizeF(refToScreenX(24.0f), refToScreenY(12.0f)));
pixhawk
committed
painter.setBrush(Qt::NoBrush);
painter.setPen(linePen);
painter.drawRoundedRect(compassRect, 3, 3);
pixhawk
committed
QString yawAngle;
pixhawk
committed
// const float yawDeg = ((values.value("yaw", 0.0f)/M_PI)*180.0f)+180.f;
// YAW is in compass-human readable format, so 0 .. 360 deg.
if (yawDeg < 0) yawDeg += 360;
if (yawDeg > 360) yawDeg -= 360;
/* final safeguard for really stupid systems */
int yawCompass = static_cast<int>(yawDeg) % 360;
yawAngle.sprintf("%03d", yawCompass);
paintText(yawAngle, defaultColor,8.5f, -9.8f, compassY+ 1.7f, &painter);
painter.setBrush(Qt::NoBrush);
painter.setPen(linePen);
pixhawk
committed
// CHANGE RATE STRIPS
drawChangeRateStrip(-95.0f, -60.0f, 40.0f, -10.0f, 10.0f, -zSpeed, &painter);
pixhawk
committed
// CHANGE RATE STRIPS
drawChangeRateStrip(95.0f, -60.0f, 40.0f, -10.0f, 10.0f, totalAcc, &painter,true);
pixhawk
committed
// GAUGES
pixhawk
committed
// Left altitude gauge
painter.setBrush(Qt::NoBrush);
painter.setPen(linePen);
drawChangeIndicatorGauge(-vGaugeSpacing, 35.0f, 15.0f, 10.0f, gaugeAltitude, defaultColor, &painter, false);
paintText("alt m", defaultColor, 5.5f, -73.0f, 50, &painter);
pixhawk
committed
// Right speed gauge
drawChangeIndicatorGauge(vGaugeSpacing, 35.0f, 15.0f, 10.0f, totalSpeed, defaultColor, &painter, false);
paintText("v m/s", defaultColor, 5.5f, 55.0f, 50, &painter);
pixhawk
committed
// Waypoint name
if (waypointName != "") paintText(waypointName, defaultColor, 2.0f, (-vwidth/3.0) + 10, +vheight/3.0 + 15, &painter);
// MOVING PARTS
painter.translate(refToScreenX(yawTrans), 0);
// Old single-component pitch drawing
// // Rotate view and draw all roll-dependent indicators
// painter.rotate((rollLP/M_PI)* -180.0f);
// painter.translate(0, (-pitchLP/(float)M_PI)* -180.0f * refToScreenY(1.8f));
// //qDebug() << "ROLL" << roll << "PITCH" << pitch << "YAW DIFF" << valuesDot.value("roll", 0.0f);
pixhawk
committed
pixhawk
committed
// paintPitchLines(pitchLP, &painter);
pixhawk
committed
QColor attColor = painter.pen().color();
// Draw multi-component attitude
foreach (QVector3D att, attitudes.values())
{
attColor = attColor.darker(200);
painter.setPen(attColor);
// Rotate view and draw all roll-dependent indicators
painter.rotate((att.x()/M_PI)* -180.0f);
painter.translate(0, (-att.y()/(float)M_PI)* -180.0f * refToScreenY(1.8f));
//qDebug() << "ROLL" << roll << "PITCH" << pitch << "YAW DIFF" << valuesDot.value("roll", 0.0f);
// PITCH
paintPitchLines(att.y(), &painter);
painter.translate(0, -(-att.y()/(float)M_PI)* -180.0f * refToScreenY(1.8f));
painter.rotate(-(att.x()/M_PI)* -180.0f);
pixhawk
committed
}
/**
* @param pitch pitch angle in degrees (-180 to 180)
*/
void HUD::paintPitchLines(float pitch, QPainter* painter)
{
QString label;
const float yDeg = vPitchPerDeg;
const float lineDistance = 5.0f; ///< One pitch line every 10 degrees
const float posIncrement = yDeg * lineDistance;
float posY = posIncrement;
const float posLimit = sqrt(pow(vwidth, 2.0f) + pow(vheight, 2.0f))*3.0f;
const float offsetAbs = pitch * yDeg;
float offset = pitch;
if (offset < 0) offset = -offset;
int offsetCount = 0;
while (offset > lineDistance) {
offset -= lineDistance;
offsetCount++;
}
int iPos = (int)(0.5f + lineDistance); ///< The first line
int iNeg = (int)(-0.5f - lineDistance); ///< The first line
offset *= yDeg;
painter->setPen(defaultColor);
posY = -offsetAbs + posIncrement; //+ 100;// + lineDistance;
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
paintPitchLinePos(label.sprintf("%3d", iPos), 0.0f, -posY, painter);
posY += posIncrement;
iPos += (int)lineDistance;
}
// HORIZON
//
// ------------ ------------
//
const float pitchWidth = 30.0f;
const float pitchGap = pitchWidth / 2.5f;
const QColor horizonColor = defaultColor;
const float diagonal = sqrt(pow(vwidth, 2.0f) + pow(vheight, 2.0f));
const float lineWidth = refLineWidthToPen(0.5f);
// Left horizon
drawLine(0.0f-diagonal, offsetAbs, 0.0f-pitchGap/2.0f, offsetAbs, lineWidth, horizonColor, painter);
// Right horizon
drawLine(0.0f+pitchGap/2.0f, offsetAbs, 0.0f+diagonal, offsetAbs, lineWidth, horizonColor, painter);
label.clear();
posY = offsetAbs + posIncrement;
paintPitchLineNeg(label.sprintf("%3d", iNeg), 0.0f, posY, painter);
posY += posIncrement;
iNeg -= (int)lineDistance;
}
}
void HUD::paintPitchLinePos(QString text, float refPosX, float refPosY, QPainter* painter)
{
//painter->setPen(QPen(QBrush, normalStrokeWidth));
const float pitchWidth = 30.0f;
const float pitchGap = pitchWidth / 2.5f;
const float pitchHeight = pitchWidth / 12.0f;
const float textSize = pitchHeight * 1.6f;
const float lineWidth = 1.5f;
// Positive pitch indicator:
//
// _______ _______
// |10 |
//
// Left vertical line
drawLine(refPosX-pitchWidth/2.0f, refPosY, refPosX-pitchWidth/2.0f, refPosY+pitchHeight, lineWidth, defaultColor, painter);
// Left horizontal line
drawLine(refPosX-pitchWidth/2.0f, refPosY, refPosX-pitchGap/2.0f, refPosY, lineWidth, defaultColor, painter);
// Text left
paintText(text, defaultColor, textSize, refPosX-pitchWidth/2.0 + 0.75f, refPosY + pitchHeight - 1.3f, painter);
// Right vertical line
drawLine(refPosX+pitchWidth/2.0f, refPosY, refPosX+pitchWidth/2.0f, refPosY+pitchHeight, lineWidth, defaultColor, painter);
// Right horizontal line
drawLine(refPosX+pitchWidth/2.0f, refPosY, refPosX+pitchGap/2.0f, refPosY, lineWidth, defaultColor, painter);
}
void HUD::paintPitchLineNeg(QString text, float refPosX, float refPosY, QPainter* painter)
{
const float pitchWidth = 30.0f;
const float pitchGap = pitchWidth / 2.5f;
const float pitchHeight = pitchWidth / 12.0f;
const float textSize = pitchHeight * 1.6f;
const float segmentWidth = ((pitchWidth - pitchGap)/2.0f) / 7.0f; ///< Four lines and three gaps -> 7 segments
const float lineWidth = 1.5f;
// Negative pitch indicator:
//
// -10
// _ _ _ _| |_ _ _ _
//
//
// Left vertical line
drawLine(refPosX-pitchGap/2.0, refPosY, refPosX-pitchGap/2.0, refPosY-pitchHeight, lineWidth, defaultColor, painter);
// Left horizontal line with four segments
drawLine(refPosX-pitchWidth/2.0+(i*segmentWidth), refPosY, refPosX-pitchWidth/2.0+(i*segmentWidth)+segmentWidth, refPosY, lineWidth, defaultColor, painter);
}
// Text left
paintText(text, defaultColor, textSize, refPosX-pitchWidth/2.0f + 0.75f, refPosY + pitchHeight - 1.3f, painter);
// Right vertical line
drawLine(refPosX+pitchGap/2.0, refPosY, refPosX+pitchGap/2.0, refPosY-pitchHeight, lineWidth, defaultColor, painter);
// Right horizontal line with four segments
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
drawLine(refPosX+pitchWidth/2.0f-(i*segmentWidth), refPosY, refPosX+pitchWidth/2.0f-(i*segmentWidth)-segmentWidth, refPosY, lineWidth, defaultColor, painter);
}
}
void rotatePointClockWise(QPointF& p, float angle)
{
// Standard 2x2 rotation matrix, counter-clockwise
//
// | cos(phi) sin(phi) |
// | -sin(phi) cos(phi) |
//
//p.setX(cos(angle) * p.x() + sin(angle) * p.y());
//p.setY(-sin(angle) * p.x() + cos(angle) * p.y());
p.setX(cos(angle) * p.x() + sin(angle)* p.y());
p.setY((-1.0f * sin(angle) * p.x()) + cos(angle) * p.y());
}
float HUD::refLineWidthToPen(float line)
{
return line * 2.50f;
}
/**
* Rotate a polygon around a point
*
* @param p polygon to rotate
* @param origin the rotation center
* @param angle rotation angle, in radians
* @return p Polygon p rotated by angle around the origin point
*/
void HUD::rotatePolygonClockWiseRad(QPolygonF& p, float angle, QPointF origin)
{
// Standard 2x2 rotation matrix, counter-clockwise
//
// | cos(phi) sin(phi) |
// | -sin(phi) cos(phi) |
//
for (int i = 0; i < p.size(); i++) {
QPointF curr = p.at(i);
const float x = curr.x();
const float y = curr.y();
curr.setX(((cos(angle) * (x-origin.x())) + (-sin(angle) * (y-origin.y()))) + origin.x());
curr.setY(((sin(angle) * (x-origin.x())) + (cos(angle) * (y-origin.y()))) + origin.y());
p.replace(i, curr);
}
}
void HUD::drawPolygon(QPolygonF refPolygon, QPainter* painter)
{
// Scale coordinates
QPolygonF draw(refPolygon.size());
for (int i = 0; i < refPolygon.size(); i++) {
QPointF curr;
curr.setX(refToScreenX(refPolygon.at(i).x()));
curr.setY(refToScreenY(refPolygon.at(i).y()));
draw.replace(i, curr);
}
painter->drawPolygon(draw);
}
void HUD::drawChangeRateStrip(float xRef, float yRef, float height, float minRate, float maxRate, float value, QPainter* painter,bool reverse)
{
float scaledValue = value;
// Saturate value
if (value > maxRate) scaledValue = maxRate;
if (value < minRate) scaledValue = minRate;
// x (Origin: xRef, yRef)
// -
// |
// |
// |
// =
// |
// -0.005 >|
// |
// -
const float width = height / 8.0f;
const float lineWidth = 1.5f;
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
if (reverse)
{
drawLine(xRef, yRef, xRef-width, yRef, lineWidth, defaultColor, painter);
// Vertical main line
drawLine(xRef-width/2.0f, yRef, xRef-width/2.0f, yRef+height, lineWidth, defaultColor, painter);
// Zero mark
drawLine(xRef, yRef+height/2.0f, xRef-width, yRef+height/2.0f, lineWidth, defaultColor, painter);
// Horizontal bottom line
drawLine(xRef, yRef+height, xRef-width, yRef+height, lineWidth, defaultColor, painter);
// Text
QString label;
label.sprintf("%+06.2f >", value);
QFont font("Bitstream Vera Sans");
// Enforce minimum font size of 5 pixels
//int fSize = qMax(5, (int)(6.0f*scalingFactor*1.26f));
font.setPixelSize(6.0f * 1.26f);
QFontMetrics metrics = QFontMetrics(font);
paintText(label, defaultColor, 6.0f, (xRef-width) - metrics.width(label), yRef+height-((scaledValue - minRate)/(maxRate-minRate))*height - 1.6f, painter);
}
else
{
drawLine(xRef, yRef, xRef+width, yRef, lineWidth, defaultColor, painter);
// Vertical main line
drawLine(xRef+width/2.0f, yRef, xRef+width/2.0f, yRef+height, lineWidth, defaultColor, painter);
// Zero mark
drawLine(xRef, yRef+height/2.0f, xRef+width, yRef+height/2.0f, lineWidth, defaultColor, painter);
// Horizontal bottom line
drawLine(xRef, yRef+height, xRef+width, yRef+height, lineWidth, defaultColor, painter);
// Text
QString label;
label.sprintf("< %+06.2f", value);
paintText(label, defaultColor, 6.0f, xRef+width/2.0f, yRef+height-((scaledValue - minRate)/(maxRate-minRate))*height - 1.6f, painter);
}
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
//void HUD::drawSystemIndicator(float xRef, float yRef, int maxNum, float maxWidth, float maxHeight, QPainter* painter)
//{
// Q_UNUSED(maxWidth);
// Q_UNUSED(maxHeight);
// if (values.size() > 0)
// {
// QString selectedKey = values.begin().key();
// // | | | | | |
// // | | | | | |
// // x speed: 2.54
// // One column per value
// QMapIterator<QString, float> value(values);
// float x = xRef;
// float y = yRef;
// const float vspacing = 1.0f;
// float width = 1.5f;
// float height = 1.5f;
// const float hspacing = 0.6f;
// // TODO ensure that instrument stays smaller than maxWidth and maxHeight
// int i = 0;
// while (value.hasNext() && i < maxNum)
// {
// value.next();
// QBrush brush(Qt::SolidPattern);
// if (value.value() < 0.01f && value.value() > -0.01f)
// {
// brush.setColor(Qt::gray);
// }
// else if (value.value() > 0.01f)
// {
// brush.setColor(Qt::blue);
// }
// else
// {
// brush.setColor(Qt::yellow);
// }
// painter->setBrush(brush);
// painter->setPen(Qt::NoPen);
// // Draw current value colormap
// painter->drawRect(refToScreenX(x), refToScreenY(y), refToScreenX(width), refToScreenY(height));
// // Draw change rate colormap
// painter->drawRect(refToScreenX(x), refToScreenY(y+height+hspacing), refToScreenX(width), refToScreenY(height));
// // Draw mean value colormap
// painter->drawRect(refToScreenX(x), refToScreenY(y+2.0f*(height+hspacing)), refToScreenX(width), refToScreenY(height));
// // Add spacing
// x += width+vspacing;
// // Iterate
// i++;
// }
// // Draw detail label
// QString detail = "NO DATA AVAILABLE";
// if (values.contains(selectedKey))
// {
// detail = values.find(selectedKey).key();
// detail.append(": ");
// detail.append(QString::number(values.find(selectedKey).value()));
// }
// paintText(detail, QColor(255, 255, 255), 3.0f, xRef, yRef+3.0f*(height+hspacing)+1.0f, painter);
// }
//}
void HUD::drawChangeIndicatorGauge(float xRef, float yRef, float radius, float expectedMaxChange, float value, const QColor& color, QPainter* painter, bool solid)
{
// Draw the circle
QPen circlePen(Qt::SolidLine);
if (!solid) circlePen.setStyle(Qt::DotLine);
circlePen.setColor(defaultColor);
circlePen.setWidth(refLineWidthToPen(2.0f));
painter->setBrush(Qt::NoBrush);
painter->setPen(circlePen);
drawCircle(xRef, yRef, radius, 200.0f, 170.0f, 1.5f, color, painter);
float textSize = radius / 2.5;
paintText(label, color, textSize, xRef-textSize*1.7f, yRef-textSize*0.4f, painter);
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
// Draw the needle
// Scale the rotation so that the gauge does one revolution
// per max. change
const float rangeScale = (2.0f * M_PI) / expectedMaxChange;
const float maxWidth = radius / 10.0f;
const float minWidth = maxWidth * 0.3f;
QPolygonF p(6);
p.replace(0, QPointF(xRef-maxWidth/2.0f, yRef-radius * 0.5f));
p.replace(1, QPointF(xRef-minWidth/2.0f, yRef-radius * 0.9f));
p.replace(2, QPointF(xRef+minWidth/2.0f, yRef-radius * 0.9f));
p.replace(3, QPointF(xRef+maxWidth/2.0f, yRef-radius * 0.5f));
p.replace(4, QPointF(xRef, yRef-radius * 0.46f));
p.replace(5, QPointF(xRef-maxWidth/2.0f, yRef-radius * 0.5f));
rotatePolygonClockWiseRad(p, value*rangeScale, QPointF(xRef, yRef));
QBrush indexBrush;
indexBrush.setColor(defaultColor);
indexBrush.setStyle(Qt::SolidPattern);
painter->setPen(Qt::SolidLine);
painter->setPen(defaultColor);
painter->setBrush(indexBrush);
drawPolygon(p, painter);
}
void HUD::drawLine(float refX1, float refY1, float refX2, float refY2, float width, const QColor& color, QPainter* painter)
{
QPen pen(Qt::SolidLine);
pen.setWidth(refLineWidthToPen(width));
pen.setColor(color);
painter->setPen(pen);
painter->drawLine(QPoint(refToScreenX(refX1), refToScreenY(refY1)), QPoint(refToScreenX(refX2), refToScreenY(refY2)));
}
void HUD::drawEllipse(float refX, float refY, float radiusX, float radiusY, float startDeg, float endDeg, float lineWidth, const QColor& color, QPainter* painter)
{
QPen pen(painter->pen().style());
pen.setWidth(refLineWidthToPen(lineWidth));
pen.setColor(color);
painter->setPen(pen);
painter->drawEllipse(QPointF(refToScreenX(refX), refToScreenY(refY)), refToScreenX(radiusX), refToScreenY(radiusY));
}
void HUD::drawCircle(float refX, float refY, float radius, float startDeg, float endDeg, float lineWidth, const QColor& color, QPainter* painter)
{
drawEllipse(refX, refY, radius, radius, startDeg, endDeg, lineWidth, color, painter);
}
void HUD::selectWaypoint(int uasId, int id)
Q_UNUSED(uasId);
waypointName = tr("WP") + QString::number(id);
}
void HUD::setImageSize(int width, int height, int depth, int channels)
{
// Allocate raw image in correct size
if (width != receivedWidth || height != receivedHeight || depth != receivedDepth || channels != receivedChannels || image == NULL) {
// Set new size
if (width > 0) receivedWidth = width;
if (height > 0) receivedHeight = height;
if (depth > 1) receivedDepth = depth;
if (channels > 1) receivedChannels = channels;
rawExpectedBytes = (receivedWidth * receivedHeight * receivedDepth * receivedChannels) / 8;
bytesPerLine = rawExpectedBytes / receivedHeight;
// Delete old buffers if necessary
rawImage = NULL;
if (rawBuffer1 != NULL) delete rawBuffer1;
if (rawBuffer2 != NULL) delete rawBuffer2;
rawBuffer1 = (unsigned char*)malloc(rawExpectedBytes);
rawBuffer2 = (unsigned char*)malloc(rawExpectedBytes);
rawImage = rawBuffer1;
if (depth <= 8 && channels == 1) {
image = new QImage(receivedWidth, receivedHeight, QImage::Format_Indexed8);
// Create matching color table
image->setColorCount(256);
image->setColor(i, qRgb(i, i, i));
//qDebug() << __FILE__ << __LINE__ << std::hex << i;
}
}
// 32 BIT COLOR IMAGE WITH ALPHA VALUES (#ARGB)
image = new QImage(receivedWidth, receivedHeight, QImage::Format_ARGB32);
}
// Fill first channel of image with black pixels
image->fill(qgcApp()->styleIsDark() ? 0 : 255);
Lorenz Meier
committed
glImage = *image;
qDebug() << __FILE__ << __LINE__ << "Setting up image";
// Set size once
setFixedSize(receivedWidth, receivedHeight);
setMinimumSize(receivedWidth, receivedHeight);
setMaximumSize(receivedWidth, receivedHeight);
// Lock down the size
//setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
//resize(receivedWidth, receivedHeight);
}
}
void HUD::startImage(int imgid, int width, int height, int depth, int channels)
{
Q_UNUSED(imgid);
//qDebug() << "HUD: starting image (" << width << "x" << height << ", " << depth << "bits) with " << channels << "channels";
// Copy previous image to screen if it hasn't been finished properly
finishImage();
// Reset image size if necessary
setImageSize(width, height, depth, channels);
imageStarted = true;
}
void HUD::finishImage()
{
commitRawDataToGL();
imageStarted = false;
}
}
void HUD::commitRawDataToGL()
{
qDebug() << __FILE__ << __LINE__ << "Copying raw data to GL buffer:" << rawImage << receivedWidth << receivedHeight << image->format();
QImage::Format format = image->format();
QImage* newImage = new QImage(rawImage, receivedWidth, receivedHeight, format);
if (format == QImage::Format_Indexed8) {
newImage->setColorCount(256);
newImage->setColor(i, qRgb(i, i, i));
//qDebug() << __FILE__ << __LINE__ << std::hex << i;
}
}
Lorenz Meier
committed
glImage = *newImage;
rawImage = rawBuffer1;
//qDebug() << "Now buffer 1";
}
}
}
void HUD::saveImage(QString fileName)
{
image->save(fileName);
}
void HUD::saveImage()
{
//Bring up popup
QString fileName = "output.png";
saveImage(fileName);
}
pixhawk
committed
void HUD::selectOfflineDirectory()
{
QString fileName = QGCFileDialog::getExistingDirectory(this, tr("Select image directory"), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
pixhawk
committed
offlineDirectory = fileName;
}
}
void HUD::enableHUDInstruments(bool enabled)
{
dongfang
committed
HUDInstrumentsEnabled = enabled;
pixhawk
committed
}
void HUD::enableVideo(bool enabled)
{
videoEnabled = enabled;
}
void HUD::setPixels(int imgid, const unsigned char* imageData, int length, int startIndex)
{
Q_UNUSED(imgid);
// qDebug() << "at" << __FILE__ << __LINE__ << ": Received startindex" << startIndex << "and length" << length << "(" << startIndex+length << "of" << rawExpectedBytes << "bytes)";
LM
committed
if (imageStarted)
{
//if (rawLastIndex != startIndex) qDebug() << "PACKET LOSS!";
LM
committed
if (startIndex+length > rawExpectedBytes)
{
qDebug() << "HUD: OVERFLOW! startIndex:" << startIndex << "length:" << length << "image raw size" << ((receivedWidth * receivedHeight * receivedChannels * receivedDepth) / 8) - 1;
LM
committed
}
else
{
memcpy(rawImage+startIndex, imageData, length);
rawLastIndex = startIndex+length;
// Check if we just reached the end of the image
LM
committed
if (startIndex+length == rawExpectedBytes)
{
//qDebug() << "HUD: END OF IMAGE REACHED!";
finishImage();
rawLastIndex = 0;
}
}
// for (int i = 0; i < length; i++)
// {
// for (int j = 0; j < receivedChannels; j++)
// {
// unsigned int x = (startIndex+i) % receivedWidth;
// unsigned int y = (unsigned int)((startIndex+i) / receivedWidth);
// qDebug() << "Setting pixel" << x << "," << y << "to" << (unsigned int)*(rawImage+startIndex+i);
// }
// }
}
}
if (temp_im.byteCount() > 0)
{
// Save to directory if logging is enabled
if (imageLoggingEnabled)
{
temp_im.save(QString("%1/%2.png").arg(imageLogDirectory).arg(imageLogCounter));
imageLogCounter++;
}
}
}
}
void HUD::saveImages(bool save)
{
if (save)
{
imageLogDirectory = QGCFileDialog::getExistingDirectory(this, tr("Select image log directory"), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
qDebug() << "Logging to:" << imageLogDirectory;
if (imageLogDirectory != "")
{
imageLogCounter = 0;
imageLoggingEnabled = true;
qDebug() << "Logging on";
}
else
{
imageLoggingEnabled = false;
selectSaveDirectoryAction->setChecked(false);
}
else
{
imageLoggingEnabled = false;
selectSaveDirectoryAction->setChecked(false);
}