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
/*
*@TODO:
dongfang
committed
* repaint on demand multiple canvases
* multi implementation with shared model class
*/
double PrimaryFlightDisplay_round(double value, int digits=0)
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),
uas(NULL),
dongfang
committed
altimeterMode(GPS_MAIN),
altimeterFrame(ASL),
speedMode(GROUND_MAIN),
roll(UNKNOWN_ATTITUDE),
pitch(UNKNOWN_ATTITUDE),
// heading(NAN),
heading(UNKNOWN_ATTITUDE),
primaryAltitude(UNKNOWN_ALTITUDE),
GPSAltitude(UNKNOWN_ALTITUDE),
aboveHomeAltitude(UNKNOWN_ALTITUDE),
primarySpeed(UNKNOWN_SPEED),
groundspeed(UNKNOWN_SPEED),
verticalVelocity(UNKNOWN_ALTITUDE),
dongfang
committed
font("Bitstream Vera Sans"),
refreshTimer(new QTimer(this)),
navigationCrosstrackError(0),
navigationTargetBearing(UNKNOWN_ATTITUDE),
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)),
dongfang
committed
instrumentBackground(QColor::fromHsvF(0, 0, 0.3, 0.3)),
instrumentOpagueBackground(QColor::fromHsvF(0, 0, 0.3, 1.0))
{
dongfang
committed
setMinimumSize(120, 80);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
// Connect with UAS
connect(UASManager::instance(), SIGNAL(activeUASSet(UASInterface*)), this, SLOT(setActiveUAS(UASInterface*)));
if (UASManager::instance()->getActiveUAS() != NULL) setActiveUAS(UASManager::instance()->getActiveUAS());
// Refresh timer
refreshTimer->setInterval(updateInterval);
connect(refreshTimer, SIGNAL(timeout()), this, SLOT(update()));
dongfang
committed
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
}
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);
}
qreal constrain(qreal value, qreal min, qreal max) {
if (value<min) value=min;
else if(value>max) value=max;
return value;
}
void PrimaryFlightDisplay::resizeEvent(QResizeEvent *e) {
QWidget::resizeEvent(e);
qreal size = e->size().width();
fineLineWidth = constrain(size*LINEWIDTH*2/3, 1, 2);
instrumentEdgePen.setWidthF(fineLineWidth);
//AIEdgePen.setWidthF(fineLineWidth);
smallTestSize = 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)
{
// Event is not needed
// the event is ignored as this widget
// is refreshed automatically
Q_UNUSED(event);
//makeDummyData();
doPaint();
}
dongfang
committed
dongfang
committed
/**
*
* @param uas the UAS/MAV to monitor/display with the HUD
*/
void PrimaryFlightDisplay::setActiveUAS(UASInterface* uas)
{
if (this->uas != NULL) {
// Disconnect any previously connected active MAV
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(waypointSelected(int,int)), this, SLOT(selectWaypoint(int, int)));
disconnect(this->uas, SIGNAL(primarySpeedChanged(UASInterface*, double, quint64)), this, SLOT(updatePrimarySpeed(UASInterface*,double,quint64)));
disconnect(this->uas, SIGNAL(gpsSpeedChanged(UASInterface*, double, quint64)), this, SLOT(updateGPSSpeed(UASInterface*,double,quint64)));
disconnect(this->uas, SIGNAL(climbRateChanged(UASInterface*, double, quint64)), this, SLOT(updateClimbRate(UASInterface*, AltitudeMeasurementSource, double, quint64)));
disconnect(this->uas, SIGNAL(primaryAltitudeChanged(UASInterface*, double, quint64)), this, SLOT(updatePrimaryAltitude(UASInterface*, double, quint64)));
disconnect(this->uas, SIGNAL(gpsAltitudeChanged(UASInterface*, double, quint64)), this, SLOT(updateGPSAltitude(UASInterface*, double, quint64)));
disconnect(this->uas, SIGNAL(navigationControllerErrorsChanged(UASInterface*, double, double, double)), this, SLOT(updateNavigationControllerErrors(UASInterface*, double, double, double)));
//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(armingChanged(bool)), this, SLOT(updateArmed(bool)));
//disconnect(this->uas, SIGNAL(satelliteCountChanged(double, QString)), this, SLOT(updateSatelliteCount(double, QString)));
//disconnect(this->uas, SIGNAL(localizationChanged(UASInterface* uas, int fix)), this, SLOT(updateGPSFixType(UASInterface*,int)));
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)));
//connect(uas, SIGNAL(batteryChanged(UASInterface*, double, double, double, int)), this, SLOT(updateBattery(UASInterface*, double, double, double, int)));
//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(armingChanged(bool)), this, SLOT(updateArmed(bool)));
//connect(uas, SIGNAL(satelliteCountChanged(double, QString)), this, SLOT(updateSatelliteCount(double, QString)));
dongfang
committed
//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,quint64)), this, SLOT(updateGlobalPosition(UASInterface*,double,double,double,quint64)));
dongfang
committed
connect(uas, SIGNAL(waypointSelected(int,int)), this, SLOT(selectWaypoint(int, int)));
connect(uas, SIGNAL(primarySpeedChanged(UASInterface*, double, quint64)), this, SLOT(updatePrimarySpeed(UASInterface*,double,quint64)));
connect(uas, SIGNAL(gpsSpeedChanged(UASInterface*, double, quint64)), this, SLOT(updateGPSSpeed(UASInterface*,double,quint64)));
connect(uas, SIGNAL(climbRateChanged(UASInterface*, double, quint64)), this, SLOT(updateClimbRate(UASInterface*, AltitudeMeasurementSource, double, quint64)));
connect(uas, SIGNAL(primaryAltitudeChanged(UASInterface*, double, quint64)), this, SLOT(updatePrimaryAltitude(UASInterface*, double, quint64)));
connect(uas, SIGNAL(gpsAltitudeChanged(UASInterface*, double, quint64)), this, SLOT(updateGPSAltitude(UASInterface*, 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);
if (!isnan(roll) && !isinf(roll) && !isnan(pitch) && !isinf(pitch) && !isnan(yaw) && !isinf(yaw))
{
this->roll = roll * (180.0 / M_PI);
this->pitch = pitch * (180.0 / M_PI);
yaw = yaw * (180.0 / M_PI);
if (yaw<0) yaw+=360;
this->heading = yaw;
dongfang
committed
}
// TODO: Else-part. We really should have an "attitude bad or unknown" indication instead of just freezing.
dongfang
committed
//qDebug("r,p,y: %f,%f,%f", roll, pitch, yaw);
dongfang
committed
}
/*
* TODO! Implementation or removal of this.
* Currently a dummy.
*/
void PrimaryFlightDisplay::updateAttitude(UASInterface* uas, int component, double roll, double pitch, double yaw, quint64 timestamp)
{
Q_UNUSED(uas);
Q_UNUSED(component);
Q_UNUSED(timestamp);
if (!isnan(roll) && !isinf(roll) && !isnan(pitch) && !isinf(pitch) && !isnan(yaw) && !isinf(yaw)) {
this->roll = roll * (180.0 / M_PI);
this->pitch = pitch * (180.0 / M_PI);
yaw = yaw * (180.0 / M_PI);
if (yaw<0) yaw+=360;
this->heading = yaw;
}
// qDebug("(2) r,p,y: %f,%f,%f", roll, pitch, yaw);
dongfang
committed
dongfang
committed
}
/*
* TODO! Examine what data comes with this call, should we consider it airspeed, ground speed or
* should we not consider it at all?
*/
void PrimaryFlightDisplay::updatePrimarySpeed(UASInterface* uas, double speed, quint64 timestamp)
{
Q_UNUSED(uas);
Q_UNUSED(timestamp);
primarySpeed = speed;
didReceivePrimarySpeed = true;
}
void PrimaryFlightDisplay::updateGPSSpeed(UASInterface* uas, double speed, quint64 timestamp)
dongfang
committed
{
Q_UNUSED(uas);
Q_UNUSED(timestamp);
groundspeed = speed;
if (!didReceivePrimarySpeed)
primarySpeed = speed;
}
dongfang
committed
void PrimaryFlightDisplay::updateClimbRate(UASInterface* uas, double climbRate, quint64 timestamp) {
Q_UNUSED(uas);
Q_UNUSED(timestamp);
verticalVelocity = climbRate;
}
void PrimaryFlightDisplay::updatePrimaryAltitude(UASInterface* uas, double altitude, quint64 timestamp) {
Q_UNUSED(uas);
Q_UNUSED(timestamp);
primaryAltitude = altitude;
didReceivePrimaryAltitude = true;
}
void PrimaryFlightDisplay::updateGPSAltitude(UASInterface* uas, double altitude, quint64 timestamp) {
Q_UNUSED(uas);
Q_UNUSED(timestamp);
GPSAltitude = altitude;
if (!didReceivePrimaryAltitude)
primaryAltitude = altitude;
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) {
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
float length = 0.15;
float side = 0.5;
// 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));
float rel = length/qSqrt(2);
// 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
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.rotate(-roll);
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();
// TODO: The gradient is wrong now.
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
) {
// 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)(pitch/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, pitch-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) {
QString s_number; //= QString("%d").arg(degrees);
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,
dongfang
committed
// To save computations, we do these transformations once for both scales:
painter.resetTransform();
painter.translate(area.center());
painter.rotate(-roll);
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 start = heading - halfspan;
float end = heading + 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 - heading;
// 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 (displayTick==30 || displayTick==60 ||
displayTick==120 || displayTick==150 ||
displayTick==210 || displayTick==240 ||
dongfang
committed
displayTick==300 || displayTick==330) {
// draw a number
QString s_number;
s_number.sprintf("%d", displayTick/10);
painter.setPen(scalePen);
drawTextCenter(painter, s_number, /*COMPASS_SCALE_TEXT_SIZE*radius*/ smallTestSize, 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 (displayTick%90 == 0) {
// 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
/* final safeguard for really stupid systems */
int digitalCompassValue = static_cast<int>(qRound((double)heading)) % 360;
dongfang
committed
QString s_digitalCompass;
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
if (shouldDisplayNavigationData() && navigationTargetBearing != UNKNOWN_ATTITUDE && !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);
painter.setPen(pen);
painter.drawLine(QPointF(x, y), QPointF(x, -y));
}
}
/*
void PrimaryFlightDisplay::drawSeparateCompassDisk(QPainter& painter, QRectF area) {
float radius = area.width()/2;
float innerRadius = radius * 0.96;
painter.resetTransform();
painter.setBrush(instrumentOpagueBackground);
painter.setPen(instrumentEdgePen);
painter.drawEllipse(area);
painter.setBrush(Qt::NoBrush);
QPen scalePen(Qt::white);
scalePen.setWidthF(fineLineWidth);
for (int displayTick=0; displayTick<360; displayTick+=COMPASS_SEPARATE_DISK_RESOLUTION) {
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
// yaw is in center.
float off = displayTick - heading;
// wrap that to ]-180..180]
if (off<=-180) off+= 360; else if (off>180) off -= 360;
painter.translate(area.center());
painter.rotate(off);
bool drewArrow = false;
bool isMajor = displayTick % COMPASS_DISK_MAJORTICK == 0;
if (displayTick==30 || displayTick==60 ||
displayTick==120 || displayTick==150 ||
displayTick==210 || displayTick==240 ||
displayTick==300 || displayTick==330) {
// draw a number
QString s_number;
s_number.sprintf("%d", displayTick/10);
painter.setPen(scalePen);
drawTextCenter(painter, s_number, mediumTextSize, 0, -innerRadius*0.75);
} else {
if (displayTick % COMPASS_DISK_ARROWTICK == 0) {
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();
painter.setPen(scalePen);
painter.setBrush(Qt::SolidPattern);
painter.drawPath(markerPath);
painter.setBrush(Qt::NoBrush);
drewArrow = true;
}
if (displayTick%90 == 0) {
// Also draw a label
QString name = compassWindNames[displayTick / 45];
painter.setPen(scalePen);
drawTextCenter(painter, name, mediumTextSize, 0, -innerRadius*0.75);
}
}
}
// draw the scale lines. If an arrow was drawn, stay off from it.
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);
painter.setPen(scalePen);
painter.drawLine(p_start, p_end);
painter.resetTransform();
}
painter.setPen(scalePen);
//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);
QRectF headingNumberRect(-radius/3, radius*0.12, radius*2/3, radius*0.28);
painter.setPen(instrumentEdgePen);
painter.drawRoundedRect(headingNumberRect, instrumentEdgePen.widthF()*2/3, instrumentEdgePen.widthF()*2/3);
// if (heading < 0) heading += 360;
// else if (heading >= 360) heading -= 360;
int yawCompass = static_cast<int>(heading) % 360;
QString yawAngle;
yawAngle.sprintf("%03d", yawCompass);
QPen pen;
pen.setWidthF(lineWidth);
pen.setColor(Qt::white);
painter.setPen(pen);
drawTextCenter(painter, yawAngle, largeTextSize, 0, radius/4);
}
dongfang
committed
void PrimaryFlightDisplay::drawAltimeter(
QPainter& painter,
QRectF area, // the area where to draw the tape.
float secondaryAltitude,
dongfang
committed
float vv
) {
dongfang
committed
painter.resetTransform();
fillInstrumentBackground(painter, area);
dongfang
committed
dongfang
committed
float secondaryAltitudeBoxHeight = mediumTextSize * 2;
// not yet implemented: Display of secondary altitude.
// if (isAirplane())
// 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;
float markerTip = (tickmarkLeft*2+tickmarkRightMajor)/3;
float scaleCenterAltitude = primaryAltitude == UNKNOWN_ALTITUDE ? 0 : primaryAltitude;
dongfang
committed
// altitude scale
#ifdef ALTIMETER_PROJECTED
float range = 1.2;
float start = scaleCenterAltitude - ALTIMETER_PROJECTED_SPAN/2;
float end = scaleCenterAltitude + ALTIMETER_PROJECTED_SPAN/2;
dongfang
committed
int firstTick = ceil(start / ALTIMETER_PROJECTED_RESOLUTION) * ALTIMETER_PROJECTED_RESOLUTION;
int lastTick = floor(end / ALTIMETER_PROJECTED_RESOLUTION) * ALTIMETER_PROJECTED_RESOLUTION;
for (int tickAlt = firstTick; tickAlt <= lastTick; tickAlt += ALTIMETER_PROJECTED_RESOLUTION) {
// a number between 0 and 1. Use as radians directly.
float r = range*(tickAlt-altitude)/(ALTIMETER_PROJECTED_SPAN/2);
float y = effectiveHalfHeight * sin(r);
scale = cos(r);
if (scale<0) scale = -scale;
bool hasText = tickAlt % ALTIMETER_PROJECTED_MAJOR_RESOLUTION == 0;
#else
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
#endif
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);