Newer
Older
dongfang
committed
#include "PrimaryFlightDisplay.h"
#include "UASManager.h"
#include <QDebug>
#include <QRectF>
#include <cmath>
#include <QPen>
#include <QPainter>
#include <QPainterPath>
#include <QResizeEvent>
dongfang
committed
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(QWidget *parent) :
dongfang
committed
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"),
dongfang
committed
setMinimumSize(120, 80);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
setActiveUAS(UASManager::instance()->getActiveUAS());
// Connect with UAS signal
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
}
PrimaryFlightDisplay::~PrimaryFlightDisplay()
{
refreshTimer->stop();
}
QSize PrimaryFlightDisplay::sizeHint() const
{
return QSize(width(), (int)(width() * 3.0f / 4.0f));
dongfang
committed
}
dongfang
committed
void PrimaryFlightDisplay::showEvent(QShowEvent* event)
{
// React only to internal (pre-display) events
dongfang
committed
QWidget::showEvent(event);
refreshTimer->start(updateInterval);
emit visibilityChanged(true);
}
void PrimaryFlightDisplay::hideEvent(QHideEvent* event)
{
// React only to internal (pre-display) events
dongfang
committed
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);
smallTextSize = size * SMALL_TEXT_SIZE;
mediumTextSize = size * MEDIUM_TEXT_SIZE;
largeTextSize = size * LARGE_TEXT_SIZE;
}
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();
}
}
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, double, quint64)), this, SLOT(updateAltitude(UASInterface*, double, double, double, quint64)));
Anton Babushkin
committed
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, double, quint64)), this, SLOT(updateAltitude(UASInterface*, double, 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
void PrimaryFlightDisplay::updateAltitude(UASInterface* uas, double _altitudeAMSL, double _altitudeWGS84, double _altitudeRelative, double _climbRate, quint64 timestamp) {
Q_UNUSED(uas);
Q_UNUSED(timestamp);
if (fabsf(altitudeAMSL - _altitudeAMSL) > 0.5f) {
_valuesChanged = true;
}
if (fabsf(altitudeWGS84 - _altitudeWGS84) > 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;
Anton Babushkin
committed
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.width()/2, y - bounds.height()/2, bounds.width(), bounds.height(), flags, text);
dongfang
committed
}
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, y - bounds.height()/2, bounds.width(), bounds.height(), flags, text);
dongfang
committed
}
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.width(), y - bounds.height()/2, bounds.width(), bounds.height(), flags, text);
dongfang
committed
}
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.width()/2, y+bounds.height(), bounds.width(), bounds.height(), flags, text);
dongfang
committed
}
void PrimaryFlightDisplay::drawTextCenterBottom (
QPainter& painter,
QString text,
dongfang
committed
float x,
float y)
{
dongfang
committed
painter.setFont(font);
dongfang
committed
QRect bounds = metrics.boundingRect(text);
int flags = Qt::AlignCenter;
painter.drawText(x - bounds.width()/2, y, bounds.width(), bounds.height(), flags, text);
dongfang
committed
}
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.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:
dongfang
committed
// 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);
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];
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;
dongfang
committed
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;
// 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.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
// 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 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);
dongfang
committed
}
}
QPainterPath markerPath(QPoint(markerTip, 0));
markerPath.lineTo(markerTip+markerHalfHeight, markerHalfHeight);
markerPath.lineTo(rightEdge, markerHalfHeight);
markerPath.lineTo(rightEdge, -markerHalfHeight);
markerPath.lineTo(markerTip+markerHalfHeight, -markerHalfHeight);
markerPath.closeSubpath();
painter.resetTransform();
painter.translate(area.left(), area.center().y());
dongfang
committed
painter.setBrush(Qt::SolidPattern);
painter.drawPath(markerPath);
painter.setBrush(Qt::NoBrush);
dongfang
committed
QString s_alt;
Anton Babushkin
committed
if (isnan(primaryAltitude))
s_alt.sprintf("---");
else
s_alt.sprintf("%3.0f", primaryAltitude);
dongfang
committed
float xCenter = (markerTip+rightEdge)/2;
drawTextCenter(painter, s_alt, mediumTextSize, xCenter, 0);
Anton Babushkin
committed
if (!isnan(climbRate)) {
float vvPixHeight = -climbRate/ALTIMETER_VVI_SPAN * effectiveHalfHeight;
if (abs (vvPixHeight) < markerHalfHeight)
return; // hidden behind marker.
Anton Babushkin
committed
float vvSign = vvPixHeight>0 ? 1 : -1; // reverse y sign
Anton Babushkin
committed
QPointF vvArrowBegin(rightEdge - w*ALTIMETER_VVI_WIDTH/2, markerHalfHeight*vvSign);
QPointF vvArrowEnd(rightEdge - w*ALTIMETER_VVI_WIDTH/2, vvPixHeight);
painter.drawLine(vvArrowBegin, vvArrowEnd);
// Yeah this is a repetition of above code but we are going to trash it all anyway, so no fix.
Anton Babushkin
committed
float vvArowHeadSize = abs(vvPixHeight - markerHalfHeight*vvSign);
if (vvArowHeadSize > w*ALTIMETER_VVI_WIDTH/3) vvArowHeadSize = w*ALTIMETER_VVI_WIDTH/3;