Newer
Older
dongfang
committed
#include "PrimaryFlightDisplay.h"
#include "UASManager.h"
//#include "ui_primaryflightdisplay.h"
#include <QDebug>
#include <QRectF>
#include <cmath>
#include <QPen>
#include <QPainter>
#include <QPainterPath>
#include <QResizeEvent>
dongfang
committed
#if 0
// Left in but ifdef'ed out since there is commented out code below that uses it
static const float SEPARATE_COMPASS_ASPECTRATIO = 3.0f/4.0f;
static const float LINEWIDTH = 0.0036f;
static const float SMALL_TEXT_SIZE = 0.028f;
static const float MEDIUM_TEXT_SIZE = SMALL_TEXT_SIZE*1.2f;
static const float LARGE_TEXT_SIZE = MEDIUM_TEXT_SIZE*1.2f;
static const bool SHOW_ZERO_ON_SCALES = true;
// all in units of display height
static const float ROLL_SCALE_RADIUS = 0.42f;
static const float ROLL_SCALE_TICKMARKLENGTH = 0.04f;
static const float ROLL_SCALE_MARKERWIDTH = 0.06f;
static const float ROLL_SCALE_MARKERHEIGHT = 0.04f;
static const int ROLL_SCALE_RANGE = 60;
// fraction of height to translate for each degree of pitch.
static const float PITCHTRANSLATION = 65;
// 5 degrees for each line
static const int PITCH_SCALE_RESOLUTION = 5;
static const float PITCH_SCALE_MAJORWIDTH = 0.1f;
static const float PITCH_SCALE_MINORWIDTH = 0.066f;
// Beginning from PITCH_SCALE_WIDTHREDUCTION_FROM degrees of +/- pitch, the
// width of the lines is reduced, down to PITCH_SCALE_WIDTHREDUCTION times
// the normal width. This helps keep orientation in extreme attitudes.
static const int PITCH_SCALE_WIDTHREDUCTION_FROM = 30;
static const float PITCH_SCALE_WIDTHREDUCTION = 0.3f;
static const int PITCH_SCALE_HALFRANGE = 15;
// The number of degrees to either side of the heading to draw the compass disk.
// 180 is valid, this will draw a complete disk. If the disk is partly clipped
// away, less will do.
static const int COMPASS_DISK_MAJORTICK = 10;
static const int COMPASS_DISK_ARROWTICK = 45;
static const int COMPASS_DISK_RESOLUTION = 10;
static const float COMPASS_DISK_MARKERWIDTH = 0.2f;
static const float COMPASS_DISK_MARKERHEIGHT = 0.133f;
static const int CROSSTRACK_MAX = 1000;
static const float TAPE_GAUGES_TICKWIDTH_MAJOR = 0.25f;
static const float TAPE_GAUGES_TICKWIDTH_MINOR = 0.15f;
// The altitude difference between top and bottom of scale
static const int ALTIMETER_LINEAR_SPAN = 50;
// every 5 meters there is a tick mark
static const int ALTIMETER_LINEAR_RESOLUTION = 5;
// every 10 meters there is a number
static const int ALTIMETER_LINEAR_MAJOR_RESOLUTION = 10;
// min. and max. vertical velocity
static const int ALTIMETER_VVI_SPAN = 5;
static const float ALTIMETER_VVI_WIDTH = 0.2f;
// Now the same thing for airspeed!
static const int AIRSPEED_LINEAR_SPAN = 15;
static const int AIRSPEED_LINEAR_RESOLUTION = 1;
static const int AIRSPEED_LINEAR_MAJOR_RESOLUTION = 5;
dongfang
committed
/*
*@TODO:
dongfang
committed
* repaint on demand multiple canvases
* multi implementation with shared model class
*/
double PrimaryFlightDisplay_round(double value, int digits=0)
return floor(value * pow(10.0, digits) + 0.5) / pow(10.0, digits);
dongfang
committed
qreal PrimaryFlightDisplay_constrain(qreal value, qreal min, qreal max) {
if (value<min) value=min;
else if(value>max) value=max;
return value;
}
dongfang
committed
const int PrimaryFlightDisplay::tickValues[] = {10, 20, 30, 45, 60};
const QString PrimaryFlightDisplay::compassWindNames[] = {
QString("N"),
QString("NE"),
QString("E"),
QString("SE"),
QString("S"),
QString("SW"),
QString("W"),
QString("NW")
};
PrimaryFlightDisplay::PrimaryFlightDisplay(int width, int height, QWidget *parent) :
QWidget(parent),
_valuesChanged(false),
_valuesLastPainted(QGC::groundTimeMilliseconds()),
uas(NULL),
dongfang
committed
Anton Babushkin
committed
roll(0),
pitch(0),
heading(0),
altitudeAMSL(std::numeric_limits<double>::quiet_NaN()),
altitudeRelative(std::numeric_limits<double>::quiet_NaN()),
groundSpeed(std::numeric_limits<double>::quiet_NaN()),
airSpeed(std::numeric_limits<double>::quiet_NaN()),
climbRate(std::numeric_limits<double>::quiet_NaN()),
navigationCrosstrackError(0),
navigationTargetBearing(std::numeric_limits<double>::quiet_NaN()),
layout(COMPASS_INTEGRATED),
style(OVERLAY_HSI),
dongfang
committed
amberColor(QColor::fromHsvF(0.12, 0.6, 1.0)),
greenColor(QColor::fromHsvF(0.25, 0.8, 0.8)),
lineWidth(2),
fineLineWidth(1),
instrumentEdgePen(QColor::fromHsvF(0, 0, 0.65, 0.5)),
instrumentOpagueBackground(QColor::fromHsvF(0, 0, 0.3, 1.0)),
font("Bitstream Vera Sans"),
Q_UNUSED(width);
Q_UNUSED(height);
dongfang
committed
setMinimumSize(120, 80);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
setActiveUAS(UASManager::instance()->getActiveUAS());
// Connect with UAS signal
//connect(UASManager::instance(), SIGNAL(UASCreated(UASInterface*)), this, SLOT(addUAS(UASInterface*)));
connect(UASManager::instance(), SIGNAL(UASDeleted(UASInterface*)), this, SLOT(forgetUAS(UASInterface*)));
dongfang
committed
connect(UASManager::instance(), SIGNAL(activeUASSet(UASInterface*)), this, SLOT(setActiveUAS(UASInterface*)));
dongfang
committed
// Refresh timer
refreshTimer->setInterval(updateInterval);
connect(refreshTimer, SIGNAL(timeout()), this, SLOT(checkUpdate()));
dongfang
committed
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
}
PrimaryFlightDisplay::~PrimaryFlightDisplay()
{
refreshTimer->stop();
}
QSize PrimaryFlightDisplay::sizeHint() const
{
return QSize(width(), (width()*3.0f)/4);
}
void PrimaryFlightDisplay::showEvent(QShowEvent* event)
{
// React only to internal (pre-display)
// events
QWidget::showEvent(event);
refreshTimer->start(updateInterval);
emit visibilityChanged(true);
}
void PrimaryFlightDisplay::hideEvent(QHideEvent* event)
{
// React only to internal (pre-display)
// events
refreshTimer->stop();
QWidget::hideEvent(event);
emit visibilityChanged(false);
}
void PrimaryFlightDisplay::resizeEvent(QResizeEvent *e) {
QWidget::resizeEvent(e);
qreal size = e->size().width();
lineWidth = PrimaryFlightDisplay_constrain(size*LINEWIDTH, 1, 6);
fineLineWidth = PrimaryFlightDisplay_constrain(size*LINEWIDTH*2/3, 1, 2);
instrumentEdgePen.setWidthF(fineLineWidth);
//AIEdgePen.setWidthF(fineLineWidth);
smallTextSize = size * SMALL_TEXT_SIZE;
mediumTextSize = size * MEDIUM_TEXT_SIZE;
largeTextSize = size * LARGE_TEXT_SIZE;
/*
* Try without layout Change-O-Matic. It was too complicated.
qreal aspect = e->size().width() / e->size().height();
if (aspect <= SEPARATE_COMPASS_ASPECTRATIO)
// qDebug("Width %d height %d decision %d", e->size().width(), e->size().height(), layout);
dongfang
committed
void PrimaryFlightDisplay::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
doPaint();
}
dongfang
committed
void PrimaryFlightDisplay::checkUpdate()
{
if (uas && (_valuesChanged || (QGC::groundTimeMilliseconds() - _valuesLastPainted) > 260)) {
update();
_valuesChanged = false;
_valuesLastPainted = QGC::groundTimeMilliseconds();
}
}
///*
// * Interface towards qgroundcontrol
// */
//void PrimaryFlightDisplay::addUAS(UASInterface* uas)
//{
// if (uas)
// {
// if (!this->uas)
// {
// setActiveUAS(uas);
// }
// }
//}
void PrimaryFlightDisplay::forgetUAS(UASInterface* uas)
if (this->uas != NULL && this->uas == uas) {
dongfang
committed
// Disconnect any previously connected active MAV
Anton Babushkin
committed
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(speedChanged(UASInterface*, double, double, quint64)), this, SLOT(updateSpeed(UASInterface*, double, double, quint64)));
disconnect(this->uas, SIGNAL(altitudeChanged(UASInterface*, double, double, double, quint64)), this, SLOT(updateAltitude(UASInterface*, double, double, double, quint64)));
disconnect(this->uas, SIGNAL(navigationControllerErrorsChanged(UASInterface*, double, double, double)), this, SLOT(updateNavigationControllerErrors(UASInterface*, double, double, double)));
dongfang
committed
}
}
/**
*
* @param uas the UAS/MAV to monitor/display with the HUD
*/
void PrimaryFlightDisplay::setActiveUAS(UASInterface* uas)
{
if (uas == this->uas)
return; //no need to rewire
// Disconnect the previous one (if any)
forgetUAS(this->uas);
dongfang
committed
if (uas) {
// 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)));
Anton Babushkin
committed
connect(uas, SIGNAL(speedChanged(UASInterface*, double, double, quint64)), this, SLOT(updateSpeed(UASInterface*, double, double, quint64)));
connect(uas, SIGNAL(altitudeChanged(UASInterface*, double, double, double, quint64)), this, SLOT(updateAltitude(UASInterface*, double, double, double, quint64)));
connect(uas, SIGNAL(navigationControllerErrorsChanged(UASInterface*, double, double, double)), this, SLOT(updateNavigationControllerErrors(UASInterface*, double, double, double)));
dongfang
committed
// Set new UAS
this->uas = uas;
}
}
void PrimaryFlightDisplay::updateAttitude(UASInterface* uas, double roll, double pitch, double yaw, quint64 timestamp)
{
Q_UNUSED(uas);
Q_UNUSED(timestamp);
Anton Babushkin
committed
if (isinf(roll)) {
this->roll = std::numeric_limits<double>::quiet_NaN();
float rolldeg = roll * (180.0 / M_PI);
if (fabsf(roll - rolldeg) > 2.5f) {
_valuesChanged = true;
}
this->roll = rolldeg;
dongfang
committed
Anton Babushkin
committed
if (isinf(pitch)) {
this->pitch = std::numeric_limits<double>::quiet_NaN();
float pitchdeg = pitch * (180.0 / M_PI);
if (fabsf(pitch - pitchdeg) > 2.5f) {
_valuesChanged = true;
}
this->pitch = pitchdeg;
Anton Babushkin
committed
if (isinf(yaw)) {
this->heading = std::numeric_limits<double>::quiet_NaN();
yaw = yaw * (180.0 / M_PI);
if (yaw<0) yaw+=360;
if (fabsf(heading - yaw) > 10.0f) {
_valuesChanged = true;
}
dongfang
committed
}
void PrimaryFlightDisplay::updateAttitude(UASInterface* uas, int component, double roll, double pitch, double yaw, quint64 timestamp)
{
Q_UNUSED(component);
this->updateAttitude(uas, roll, pitch, yaw, timestamp);
dongfang
committed
}
Anton Babushkin
committed
void PrimaryFlightDisplay::updateSpeed(UASInterface* uas, double _groundSpeed, double _airSpeed, quint64 timestamp)
dongfang
committed
{
Q_UNUSED(uas);
Q_UNUSED(timestamp);
if (fabsf(groundSpeed - _groundSpeed) > 0.5f) {
_valuesChanged = true;
}
if (fabsf(airSpeed - _airSpeed) > 1.0f) {
_valuesChanged = true;
}
Anton Babushkin
committed
groundSpeed = _groundSpeed;
airSpeed = _airSpeed;
dongfang
committed
Anton Babushkin
committed
void PrimaryFlightDisplay::updateAltitude(UASInterface* uas, double _altitudeAMSL, double _altitudeRelative, double _climbRate, quint64 timestamp) {
Q_UNUSED(uas);
Q_UNUSED(timestamp);
if (fabsf(altitudeAMSL - _altitudeAMSL) > 0.5f) {
_valuesChanged = true;
}
if (fabsf(altitudeRelative - _altitudeRelative) > 0.5f) {
_valuesChanged = true;
}
if (fabsf(climbRate - _climbRate) > 0.5f) {
_valuesChanged = true;
}
Anton Babushkin
committed
altitudeAMSL = _altitudeAMSL;
altitudeRelative = _altitudeRelative;
climbRate = _climbRate;
dongfang
committed
}
void PrimaryFlightDisplay::updateNavigationControllerErrors(UASInterface* uas, double altitudeError, double speedError, double xtrackError) {
Q_UNUSED(uas);
this->navigationAltitudeError = altitudeError;
this->navigationSpeedError = speedError;
this->navigationCrosstrackError = xtrackError;
}
dongfang
committed
/*
* Private and such
*/
// TODO: Move to UAS. Real working implementation.
bool PrimaryFlightDisplay::isAirplane() {
if (!this->uas)
return false;
switch(this->uas->getSystemType()) {
case MAV_TYPE_GENERIC:
case MAV_TYPE_FIXED_WING:
case MAV_TYPE_AIRSHIP:
case MAV_TYPE_FLAPPING_WING:
return true;
default:
return false;
}
}
// TODO: Implement. Should return true when navigating.
// That would be (APM) in AUTO and RTL modes.
// This could forward to a virtual on UAS bool isNavigatingAutonomusly() or whatever.
bool PrimaryFlightDisplay::shouldDisplayNavigationData() {
return true;
}
dongfang
committed
void PrimaryFlightDisplay::drawTextCenter (
QPainter& painter,
QString text,
dongfang
committed
float x,
float y)
{
dongfang
committed
painter.setFont(font);
QFontMetrics metrics = QFontMetrics(font);
QRect bounds = metrics.boundingRect(text);
int flags = Qt::AlignCenter | Qt::TextDontClip; // For some reason the bounds rect is too small!
painter.drawText(x /*+bounds.x()*/ -bounds.width()/2, y /*+bounds.y()*/ -bounds.height()/2, bounds.width(), bounds.height(), flags, text);
}
void PrimaryFlightDisplay::drawTextLeftCenter (
QPainter& painter,
QString text,
dongfang
committed
float x,
float y)
{
dongfang
committed
painter.setFont(font);
QFontMetrics metrics = QFontMetrics(font);
QRect bounds = metrics.boundingRect(text);
int flags = Qt::AlignLeft | Qt::TextDontClip; // For some reason the bounds rect is too small!
painter.drawText(x /*+bounds.x()*/, y /*+bounds.y()*/ -bounds.height()/2, bounds.width(), bounds.height(), flags, text);
}
void PrimaryFlightDisplay::drawTextRightCenter (
QPainter& painter,
QString text,
dongfang
committed
float x,
float y)
{
dongfang
committed
painter.setFont(font);
QFontMetrics metrics = QFontMetrics(font);
QRect bounds = metrics.boundingRect(text);
int flags = Qt::AlignRight | Qt::TextDontClip; // For some reason the bounds rect is too small!
painter.drawText(x /*+bounds.x()*/ -bounds.width(), y /*+bounds.y()*/ -bounds.height()/2, bounds.width(), bounds.height(), flags, text);
}
void PrimaryFlightDisplay::drawTextCenterTop (
QPainter& painter,
QString text,
dongfang
committed
float x,
float y)
{
dongfang
committed
painter.setFont(font);
QFontMetrics metrics = QFontMetrics(font);
QRect bounds = metrics.boundingRect(text);
int flags = Qt::AlignCenter | Qt::TextDontClip; // For some reason the bounds rect is too small!
painter.drawText(x /*+bounds.x()*/ -bounds.width()/2, y+bounds.height() /*+bounds.y()*/, bounds.width(), bounds.height(), flags, text);
}
void PrimaryFlightDisplay::drawTextCenterBottom (
QPainter& painter,
QString text,
dongfang
committed
float x,
float y)
{
dongfang
committed
painter.setFont(font);
QFontMetrics metrics = QFontMetrics(font);
QRect bounds = metrics.boundingRect(text);
int flags = Qt::AlignCenter;
painter.drawText(x /*+bounds.x()*/ -bounds.width()/2, y /*+bounds.y()*/, bounds.width(), bounds.height(), flags, text);
}
void PrimaryFlightDisplay::drawInstrumentBackground(QPainter& painter, QRectF edge) {
painter.setPen(instrumentEdgePen);
painter.drawRect(edge);
}
void PrimaryFlightDisplay::fillInstrumentBackground(QPainter& painter, QRectF edge) {
painter.setPen(instrumentEdgePen);
painter.setBrush(instrumentBackground);
painter.drawRect(edge);
painter.setBrush(Qt::NoBrush);
}
void PrimaryFlightDisplay::fillInstrumentOpagueBackground(QPainter& painter, QRectF edge) {
painter.setPen(instrumentEdgePen);
painter.setBrush(instrumentOpagueBackground);
painter.drawRect(edge);
painter.setBrush(Qt::NoBrush);
}
dongfang
committed
qreal pitchAngleToTranslation(qreal viewHeight, float pitch) {
Anton Babushkin
committed
if (isnan(pitch))
return pitch * viewHeight / PITCHTRANSLATION;
dongfang
committed
}
void PrimaryFlightDisplay::drawAIAirframeFixedFeatures(QPainter& painter, QRectF area) {
// red line from -7/10 to -5/10 half-width
// red line from 7/10 to 5/10 half-width
// red slanted line from -2/10 half-width to 0
// red slanted line from 2/10 half-width to 0
// red arrow thing under roll scale
// prepareTransform(painter, width, height);
painter.resetTransform();
painter.translate(area.center());
qreal w = area.width();
qreal h = area.height();
dongfang
committed
// The 2 lines at sides.
painter.drawLine(QPointF(-side*w, 0), QPointF(-(side-length)*w, 0));
painter.drawLine(QPointF(side*w, 0), QPointF((side-length)*w, 0));
// The gull
painter.drawLine(QPointF(rel*w, rel*w/2), QPoint(0, 0));
painter.drawLine(QPointF(-rel*w, rel*w/2), QPoint(0, 0));
// The roll scale marker.
QPainterPath markerPath(QPointF(0, -w*ROLL_SCALE_RADIUS+1));
markerPath.lineTo(-h*ROLL_SCALE_MARKERWIDTH/2, -w*(ROLL_SCALE_RADIUS-ROLL_SCALE_MARKERHEIGHT)+1);
markerPath.lineTo(h*ROLL_SCALE_MARKERWIDTH/2, -w*(ROLL_SCALE_RADIUS-ROLL_SCALE_MARKERHEIGHT)+1);
dongfang
committed
markerPath.closeSubpath();
painter.drawPath(markerPath);
}
inline qreal min4(qreal a, qreal b, qreal c, qreal d) {
if(b<a) a=b;
if(c<a) a=c;
if(d<a) a=d;
return a;
}
inline qreal max4(qreal a, qreal b, qreal c, qreal d) {
if(b>a) a=b;
if(c>a) a=c;
if(d>a) a=d;
return a;
}
dongfang
committed
void PrimaryFlightDisplay::drawAIGlobalFeatures(
QPainter& painter,
QRectF mainArea,
QRectF paintArea) {
dongfang
committed
Anton Babushkin
committed
if (isnan(displayRoll))
dongfang
committed
painter.resetTransform();
painter.translate(mainArea.center());
dongfang
committed
qreal pitchPixels = pitchAngleToTranslation(mainArea.height(), pitch);
qreal gradientEnd = pitchAngleToTranslation(mainArea.height(), 60);
dongfang
committed
//painter.rotate(-roll);
//painter.translate(0, pitchPixels);
dongfang
committed
// QTransform forwardTransform;
//forwardTransform.translate(mainArea.center().x(), mainArea.center().y());
painter.translate(0, pitchPixels);
// Calculate the radius of area we need to paint to cover all.
QTransform rtx = painter.transform().inverted();
QPointF topLeft = rtx.map(paintArea.topLeft());
QPointF topRight = rtx.map(paintArea.topRight());
QPointF bottomLeft = rtx.map(paintArea.bottomLeft());
QPointF bottomRight = rtx.map(paintArea.bottomRight());
// Just KISS... make a rectangluar basis.
qreal minx = min4(topLeft.x(), topRight.x(), bottomLeft.x(), bottomRight.x());
qreal maxx = max4(topLeft.x(), topRight.x(), bottomLeft.x(), bottomRight.x());
qreal miny = min4(topLeft.y(), topRight.y(), bottomLeft.y(), bottomRight.y());
qreal maxy = max4(topLeft.y(), topRight.y(), bottomLeft.y(), bottomRight.y());
QPointF hzonLeft = QPoint(minx, 0);
QPointF hzonRight = QPoint(maxx, 0);
QPainterPath skyPath(hzonLeft);
skyPath.lineTo(QPointF(minx, miny));
skyPath.lineTo(QPointF(maxx, miny));
skyPath.lineTo(hzonRight);
dongfang
committed
skyPath.closeSubpath();
QLinearGradient skyGradient(0, -gradientEnd, 0, 0);
dongfang
committed
skyGradient.setColorAt(0, QColor::fromHsvF(0.6, 1.0, 0.7));
skyGradient.setColorAt(1, QColor::fromHsvF(0.6, 0.25, 0.9));
QBrush skyBrush(skyGradient);
painter.fillPath(skyPath, skyBrush);
QPainterPath groundPath(hzonRight);
groundPath.lineTo(maxx, maxy);
groundPath.lineTo(minx, maxy);
groundPath.lineTo(hzonLeft);
dongfang
committed
groundPath.closeSubpath();
QLinearGradient groundGradient(0, gradientEnd, 0, 0);
dongfang
committed
groundGradient.setColorAt(0, QColor::fromHsvF(0.25, 1, 0.5));
groundGradient.setColorAt(1, QColor::fromHsvF(0.25, 0.25, 0.5));
QBrush groundBrush(groundGradient);
painter.fillPath(groundPath, groundBrush);
QPen pen;
pen.setWidthF(lineWidth);
pen.setColor(greenColor);
painter.setPen(pen);
QPointF start(-mainArea.width(), 0);
QPoint end(mainArea.width(), 0);
dongfang
committed
painter.drawLine(start, end);
}
void PrimaryFlightDisplay::drawPitchScale(
QPainter& painter,
QRectF area,
dongfang
committed
bool drawNumbersLeft,
bool drawNumbersRight
) {
Don Gagne
committed
Q_UNUSED(intrusion);
float displayPitch = this->pitch;
Anton Babushkin
committed
if (isnan(displayPitch))
// The area should be quadratic but if not width is the major size.
qreal w = area.width();
if (w<area.height()) w = area.height();
dongfang
committed
QPen pen;
pen.setWidthF(lineWidth);
pen.setColor(Qt::white);
painter.setPen(pen);
dongfang
committed
QTransform savedTransform = painter.transform();
// find the mark nearest center
int snap = qRound((double)(displayPitch/PITCH_SCALE_RESOLUTION))*PITCH_SCALE_RESOLUTION;
dongfang
committed
int _min = snap-PITCH_SCALE_HALFRANGE;
int _max = snap+PITCH_SCALE_HALFRANGE;
for (int degrees=_min; degrees<=_max; degrees+=PITCH_SCALE_RESOLUTION) {
bool isMajor = degrees % (PITCH_SCALE_RESOLUTION*2) == 0;
float linewidth = isMajor ? PITCH_SCALE_MAJORWIDTH : PITCH_SCALE_MINORWIDTH;
if (abs(degrees) > PITCH_SCALE_WIDTHREDUCTION_FROM) {
// we want: 1 at PITCH_SCALE_WIDTHREDUCTION_FROM and PITCH_SCALE_WIDTHREDUCTION at 90.
// That is PITCH_SCALE_WIDTHREDUCTION + (1-PITCH_SCALE_WIDTHREDUCTION) * f(pitch)
// where f(90)=0 and f(PITCH_SCALE_WIDTHREDUCTION_FROM)=1
// f(p) = (90-p) * 1/(90-PITCH_SCALE_WIDTHREDUCTION_FROM)
// or PITCH_SCALE_WIDTHREDUCTION + f(pitch) - f(pitch) * PITCH_SCALE_WIDTHREDUCTION
// or PITCH_SCALE_WIDTHREDUCTION (1-f(pitch)) + f(pitch)
int fromVertical = abs(pitch>=0 ? 90-pitch : -90-pitch);
float temp = fromVertical * 1/(90.0f-PITCH_SCALE_WIDTHREDUCTION_FROM);
linewidth *= (PITCH_SCALE_WIDTHREDUCTION * (1-temp) + temp);
}
float shift = pitchAngleToTranslation(w, displayPitch-degrees);
// TODO: Intrusion detection and evasion. That is, don't draw
// where the compass has intruded.
dongfang
committed
painter.translate(0, shift);
dongfang
committed
painter.drawLine(start, end);
if (isMajor && (drawNumbersLeft||drawNumbersRight)) {
int displayDegrees = degrees;
if(displayDegrees>90) displayDegrees = 180-displayDegrees;
else if (displayDegrees<-90) displayDegrees = -180 - displayDegrees;
if (SHOW_ZERO_ON_SCALES || degrees) {
Anton Babushkin
committed
if (isnan(this->pitch))
s_number.sprintf("-");
else
s_number.sprintf("%d", displayDegrees);
if (drawNumbersLeft) drawTextRightCenter(painter, s_number, mediumTextSize, -PITCH_SCALE_MAJORWIDTH * w-10, 0);
if (drawNumbersRight) drawTextLeftCenter(painter, s_number, mediumTextSize, PITCH_SCALE_MAJORWIDTH * w+10, 0);
dongfang
committed
}
}
painter.setTransform(savedTransform);
}
}
void PrimaryFlightDisplay::drawRollScale(
QPainter& painter,
QRectF area,
bool drawTicks,
bool drawNumbers) {
dongfang
committed
QPen pen;
pen.setWidthF(lineWidth);
pen.setColor(Qt::white);
painter.setPen(pen);
dongfang
committed
// We should really do these transforms but they are assumed done by caller.
// painter.resetTransform();
// painter.translate(area.center());
// painter.rotate(roll);
qreal _size = w * ROLL_SCALE_RADIUS*2;
dongfang
committed
painter.drawArc(arcArea, (90-ROLL_SCALE_RANGE)*16, ROLL_SCALE_RANGE*2*16);
dongfang
committed
if (drawTicks) {
int length = sizeof(tickValues)/sizeof(int);
qreal previousRotation = 0;
for (int i=0; i<length*2+1; i++) {
int degrees = (i==length) ? 0 : (i>length) ?-tickValues[i-length-1] : tickValues[i];
//degrees = 180 - degrees;
painter.rotate(degrees - previousRotation);
previousRotation = degrees;
QPointF start(0, -_size/2);
QPointF end(0, -(1.0+ROLL_SCALE_TICKMARKLENGTH)*_size/2);
painter.drawLine(start, end);
QString s_number; //= QString("%d").arg(degrees);
if (SHOW_ZERO_ON_SCALES || degrees)
s_number.sprintf("%d", abs(degrees));
if (drawNumbers) {
drawTextCenterBottom(painter, s_number, mediumTextSize, 0, -(ROLL_SCALE_RADIUS+ROLL_SCALE_TICKMARKLENGTH*1.7)*w);
dongfang
committed
}
}
}
}
void PrimaryFlightDisplay::drawAIAttitudeScales(
QPainter& painter,
Anton Babushkin
committed
if (isnan(displayRoll))
dongfang
committed
// To save computations, we do these transformations once for both scales:
painter.resetTransform();
painter.translate(area.center());
dongfang
committed
QTransform saved = painter.transform();
drawRollScale(painter, area, true, true);
painter.setTransform(saved);
dongfang
committed
}
void PrimaryFlightDisplay::drawAICompassDisk(QPainter& painter, QRectF area, float halfspan) {
float displayHeading = this->heading;
Anton Babushkin
committed
if (isnan(displayHeading))
displayHeading = 0;
float start = displayHeading - halfspan;
float end = displayHeading + halfspan;
int firstTick = ceil(start / COMPASS_DISK_RESOLUTION) * COMPASS_DISK_RESOLUTION;
int lastTick = floor(end / COMPASS_DISK_RESOLUTION) * COMPASS_DISK_RESOLUTION;
dongfang
committed
float radius = area.width()/2;
float innerRadius = radius * 0.96;
painter.resetTransform();
painter.setBrush(instrumentBackground);
painter.setPen(instrumentEdgePen);
dongfang
committed
painter.drawEllipse(area);
painter.setBrush(Qt::NoBrush);
QPen scalePen(Qt::black);
scalePen.setWidthF(fineLineWidth);
dongfang
committed
for (int tickYaw = firstTick; tickYaw <= lastTick; tickYaw += COMPASS_DISK_RESOLUTION) {
dongfang
committed
int displayTick = tickYaw;
if (displayTick < 0) displayTick+=360;
else if (displayTick>=360) displayTick-=360;
// yaw is in center.
float off = tickYaw - displayHeading;
dongfang
committed
// wrap that to ]-180..180]
dongfang
committed
painter.translate(area.center());
painter.rotate(off);
bool drewArrow = false;
bool isMajor = displayTick % COMPASS_DISK_MAJORTICK == 0;
dongfang
committed
// If heading unknown, still draw marks but no numbers.
Anton Babushkin
committed
if (!isnan(this->heading) &&
(displayTick==30 || displayTick==60 ||
displayTick==120 || displayTick==150 ||
displayTick==210 || displayTick==240 ||
displayTick==300 || displayTick==330)
) {
dongfang
committed
// draw a number
QString s_number;
s_number.sprintf("%d", displayTick/10);
drawTextCenter(painter, s_number, smallTextSize, 0, -innerRadius*0.75);
dongfang
committed
} else {
dongfang
committed
if (displayTick!=0) {
QPainterPath markerPath(QPointF(0, -innerRadius*(1-COMPASS_DISK_MARKERHEIGHT/2)));
markerPath.lineTo(innerRadius*COMPASS_DISK_MARKERWIDTH/4, -innerRadius);
markerPath.lineTo(-innerRadius*COMPASS_DISK_MARKERWIDTH/4, -innerRadius);
markerPath.closeSubpath();
dongfang
committed
painter.setBrush(Qt::SolidPattern);
painter.drawPath(markerPath);
painter.setBrush(Qt::NoBrush);
drewArrow = true;
}
// If heading unknown, still draw marks but no N S E W.
Anton Babushkin
committed
if (!isnan(this->heading) && displayTick%90 == 0) {
dongfang
committed
// Also draw a label
QString name = compassWindNames[displayTick / 45];
painter.setPen(scalePen);
drawTextCenter(painter, name, mediumTextSize, 0, -innerRadius*0.75);
dongfang
committed
}
dongfang
committed
}
dongfang
committed
QPointF p_start = drewArrow ? QPoint(0, -innerRadius*0.94) : QPoint(0, -innerRadius);
QPoint p_end = isMajor ? QPoint(0, -innerRadius*0.86) : QPoint(0, -innerRadius*0.90);
dongfang
committed
dongfang
committed
painter.drawLine(p_start, p_end);
painter.resetTransform();
}
dongfang
committed
//painter.setBrush(Qt::SolidPattern);
painter.translate(area.center());
QPainterPath markerPath(QPointF(0, -radius-2));
markerPath.lineTo(radius*COMPASS_DISK_MARKERWIDTH/2, -radius-radius*COMPASS_DISK_MARKERHEIGHT-2);
markerPath.lineTo(-radius*COMPASS_DISK_MARKERWIDTH/2, -radius-radius*COMPASS_DISK_MARKERHEIGHT-2);
markerPath.closeSubpath();
painter.drawPath(markerPath);
qreal digitalCompassYCenter = -radius*0.52;
qreal digitalCompassHeight = radius*0.28;
QPointF digitalCompassBottom(0, digitalCompassYCenter+digitalCompassHeight);
QPointF digitalCompassAbsoluteBottom = painter.transform().map(digitalCompassBottom);
qreal digitalCompassUpshift = digitalCompassAbsoluteBottom.y()>height() ? digitalCompassAbsoluteBottom.y()-height() : 0;
QRectF digitalCompassRect(-radius/3, -radius*0.52-digitalCompassUpshift, radius*2/3, radius*0.28);
dongfang
committed
painter.setPen(instrumentEdgePen);
painter.drawRoundedRect(digitalCompassRect, instrumentEdgePen.widthF()*2/3, instrumentEdgePen.widthF()*2/3);
dongfang
committed
Anton Babushkin
committed
if (isnan(this->heading))
s_digitalCompass.sprintf("---");
else {
/* final safeguard for really stupid systems */
int digitalCompassValue = static_cast<int>(qRound((double)heading)) % 360;
s_digitalCompass.sprintf("%03d", digitalCompassValue);
}
dongfang
committed
QPen pen;
pen.setWidthF(lineWidth);
pen.setColor(Qt::white);
painter.setPen(pen);
dongfang
committed
drawTextCenter(painter, s_digitalCompass, largeTextSize, 0, -radius*0.38-digitalCompassUpshift);
dongfang
committed
// dummy
// navigationTargetBearing = 10;
// navigationCrosstrackError = 500;
// The CDI
Anton Babushkin
committed
if (shouldDisplayNavigationData() && !isnan(navigationTargetBearing) && !isinf(navigationCrosstrackError)) {
painter.resetTransform();
painter.translate(area.center());
// TODO : Sign might be wrong?
// TODO : The case where error exceeds max. Truncate to max. and make that visible somehow.
bool errorBeyondRadius = false;
if (abs(navigationCrosstrackError) > CROSSTRACK_MAX) {
errorBeyondRadius = true;
navigationCrosstrackError = navigationCrosstrackError>0 ? CROSSTRACK_MAX : -CROSSTRACK_MAX;
}
float r = radius * CROSSTRACK_RADIUS;
float x = navigationCrosstrackError / CROSSTRACK_MAX * r;
float y = qSqrt(r*r - x*x); // the positive y, there is also a negative.
float sillyHeading = 0;
float angle = sillyHeading - navigationTargetBearing; // TODO: sign.
painter.rotate(-angle);
QPen pen;
pen.setWidthF(lineWidth);
pen.setColor(Qt::black);
if(errorBeyondRadius) {
pen.setStyle(Qt::DotLine);
}
painter.setPen(pen);
painter.drawLine(QPointF(x, y), QPointF(x, -y));
}
}
dongfang
committed
void PrimaryFlightDisplay::drawAltimeter(
QPainter& painter,
Anton Babushkin
committed
QRectF area
) {
dongfang
committed
Anton Babushkin
committed
float primaryAltitude = altitudeAMSL;
float secondaryAltitude = 0;
dongfang
committed
painter.resetTransform();
fillInstrumentBackground(painter, area);
dongfang
committed
dongfang
committed
float secondaryAltitudeBoxHeight = mediumTextSize * 2;
// not yet implemented: Display of secondary altitude.
Anton Babushkin
committed
if (!isnan(secondaryAltitude)) {
effectiveHalfHeight-= secondaryAltitudeBoxHeight;
}
float markerHalfHeight = mediumTextSize*0.8;
float leftEdge = instrumentEdgePen.widthF()*2;
float rightEdge = w-leftEdge;
float tickmarkLeft = leftEdge;
float tickmarkRightMajor = tickmarkLeft+TAPE_GAUGES_TICKWIDTH_MAJOR*w;
float tickmarkRightMinor = tickmarkLeft+TAPE_GAUGES_TICKWIDTH_MINOR*w;
Anton Babushkin
committed
float scaleCenterAltitude = isnan(primaryAltitude) ? 0 : primaryAltitude;
dongfang
committed
// altitude scale
float start = scaleCenterAltitude - ALTIMETER_LINEAR_SPAN/2;
float end = scaleCenterAltitude + ALTIMETER_LINEAR_SPAN/2;
dongfang
committed
int firstTick = ceil(start / ALTIMETER_LINEAR_RESOLUTION) * ALTIMETER_LINEAR_RESOLUTION;
int lastTick = floor(end / ALTIMETER_LINEAR_RESOLUTION) * ALTIMETER_LINEAR_RESOLUTION;
for (int tickAlt = firstTick; tickAlt <= lastTick; tickAlt += ALTIMETER_LINEAR_RESOLUTION) {
float y = (tickAlt-scaleCenterAltitude)*effectiveHalfHeight/(ALTIMETER_LINEAR_SPAN/2);
bool isMajor = tickAlt % ALTIMETER_LINEAR_MAJOR_RESOLUTION == 0;
dongfang
committed
painter.resetTransform();
painter.translate(area.left(), area.center().y() - y);
pen.setColor(tickAlt<0 ? redColor : Qt::white);
painter.setPen(pen);
if (isMajor) {
painter.drawLine(tickmarkLeft, 0, tickmarkRightMajor, 0);
dongfang
committed
QString s_alt;
drawTextLeftCenter(painter, s_alt, mediumTextSize, numbersLeft, 0);
} else {
painter.drawLine(tickmarkLeft, 0, tickmarkRightMinor, 0);