Skip to content
Snippets Groups Projects
HUD.cc 51.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • pixhawk's avatar
    pixhawk committed
    /*=====================================================================
    
    
    QGroundControl Open Source Ground Control Station
    
    pixhawk's avatar
    pixhawk committed
    
    
    (c) 2009, 2010 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
    
    pixhawk's avatar
    pixhawk committed
    
    
    This file is part of the QGROUNDCONTROL project
    
    pixhawk's avatar
    pixhawk committed
    
    
        QGROUNDCONTROL is free software: you can redistribute it and/or modify
    
    pixhawk's avatar
    pixhawk committed
        it under the terms of the GNU General Public License as published by
        the Free Software Foundation, either version 3 of the License, or
        (at your option) any later version.
    
    
        QGROUNDCONTROL is distributed in the hope that it will be useful,
    
    pixhawk's avatar
    pixhawk committed
        but WITHOUT ANY WARRANTY; without even the implied warranty of
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        GNU General Public License for more details.
    
        You should have received a copy of the GNU General Public License
    
        along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
    
    pixhawk's avatar
    pixhawk committed
    
    ======================================================================*/
    
    /**
     * @file
     *   @brief Head Up Display (HUD)
     *
     *   @author Lorenz Meier <mavteam@student.ethz.ch>
     *
     */
    
    #include <QDebug>
    #include <cmath>
    
    pixhawk's avatar
    pixhawk committed
    #include <limits>
    
    pixhawk's avatar
    pixhawk committed
    
    #include "UASManager.h"
    #include "HUD.h"
    #include "MG.h"
    
    // Fix for some platforms, e.g. windows
    #ifndef GL_MULTISAMPLE
    #define GL_MULTISAMPLE  0x809D
    #endif
    
    
    pixhawk's avatar
    pixhawk committed
    template<typename T>
    inline bool isnan(T value)
    {
    
        return value != value;
    
    pixhawk's avatar
    pixhawk committed
    
    }
    
    // requires #include <limits>
    template<typename T>
    inline bool isinf(T value)
    {
        return std::numeric_limits<T>::has_infinity && (value == std::numeric_limits<T>::infinity() || (-1*value) == std::numeric_limits<T>::infinity());
    }
    
    
    pixhawk's avatar
    pixhawk committed
    /**
     * @warning The HUD widget will not start painting its content automatically
     *          to update the view, start the auto-update by calling HUD::start().
     *
     * @param width
     * @param height
     * @param parent
     */
    HUD::HUD(int width, int height, QWidget* parent)
    
        : QGLWidget(QGLFormat(QGL::SampleBuffers), parent),
    
        uas(NULL),
        values(QMap<QString, float>()),
        valuesDot(QMap<QString, float>()),
        valuesMean(QMap<QString, float>()),
        valuesCount(QMap<QString, int>()),
        lastUpdate(QMap<QString, quint64>()),
        yawInt(0.0f),
        mode(tr("UNKNOWN MODE")),
        state(tr("UNKNOWN STATE")),
        fuelStatus(tr("00.0V (00m:00s)")),
        xCenterOffset(0.0f),
        yCenterOffset(0.0f),
        vwidth(200.0f),
        vheight(150.0f),
        vGaugeSpacing(50.0f),
        vPitchPerDeg(6.0f), ///< 4 mm y translation per degree)
        rawBuffer1(NULL),
        rawBuffer2(NULL),
        rawImage(NULL),
        rawLastIndex(0),
        rawExpectedBytes(0),
        bytesPerLine(1),
        imageStarted(false),
        receivedDepth(8),
        receivedChannels(1),
        receivedWidth(640),
        receivedHeight(480),
        defaultColor(QColor(70, 200, 70)),
        setPointColor(QColor(200, 20, 200)),
        warningColor(Qt::yellow),
        criticalColor(Qt::red),
        infoColor(QColor(20, 200, 20)),
        fuelColor(criticalColor),
        warningBlinkRate(5),
        refreshTimer(new QTimer(this)),
        noCamera(true),
        hardwareAcceleration(true),
        strongStrokeWidth(1.5f),
        normalStrokeWidth(1.0f),
        fineStrokeWidth(0.5f),
        waypointName("")
    
    pixhawk's avatar
    pixhawk committed
    {
        // Set auto fill to false
        setAutoFillBackground(false);
    
        // Fill with black background
        QImage fill = QImage(width, height, QImage::Format_Indexed8);
        fill.setNumColors(3);
        fill.setColor(0, qRgb(0, 0, 0));
        fill.setColor(1, qRgb(0, 0, 0));
        fill.setColor(2, qRgb(0, 0, 0));
        fill.fill(0);
    
        //QString imagePath = MG::DIR::getIconDirectory() + "hud-template.png";
        //qDebug() << __FILE__ << __LINE__ << "template image:" << imagePath;
        //fill = QImage(imagePath);
    
        glImage = QGLWidget::convertToGLFormat(fill);
    
        // Refresh timer
    
        refreshTimer->setInterval(50); // 20 Hz
        //connect(refreshTimer, SIGNAL(timeout()), this, SLOT(update()));
        connect(refreshTimer, SIGNAL(timeout()), this, SLOT(paintHUD()));
    
    pixhawk's avatar
    pixhawk committed
    
        // Resize to correct size and fill with image
        resize(fill.size());
        glDrawPixels(glImage.width(), glImage.height(), GL_RGBA, GL_UNSIGNED_BYTE, glImage.bits());
    
        // Set size once
        //setFixedSize(fill.size());
        //setMinimumSize(fill.size());
        //setMaximumSize(fill.size());
        // Lock down the size
        //setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
    
        fontDatabase = QFontDatabase();
        const QString fontFileName = ":/general/vera.ttf"; ///< Font file is part of the QRC file and compiled into the app
        const QString fontFamilyName = "Bitstream Vera Sans";
        if(!QFile::exists(fontFileName)) qDebug() << "ERROR! font file: " << fontFileName << " DOES NOT EXIST!";
    
        fontDatabase.addApplicationFont(fontFileName);
        font = fontDatabase.font(fontFamilyName, "Roman", (int)(10*scalingFactor*1.2f+0.5f));
        if (font.family() != fontFamilyName) qDebug() << "ERROR! Font not loaded: " << fontFamilyName;
    
        // Connect with UAS
        UASManager* manager = UASManager::instance();
        connect(manager, SIGNAL(activeUASSet(UASInterface*)), this, SLOT(setActiveUAS(UASInterface*)));
    
    pixhawk's avatar
    pixhawk committed
    }
    
    HUD::~HUD()
    {
    
    }
    
    void HUD::start()
    {
        refreshTimer->start();
    }
    
    void HUD::stop()
    {
        refreshTimer->stop();
    }
    
    void HUD::updateValue(UASInterface* uas, QString name, double value, quint64 msec)
    {
    
        // UAS is not needed
        Q_UNUSED(uas);
    
    pixhawk's avatar
    pixhawk committed
        if (!isnan(value) && !isinf(value))
        {
    
    pixhawk's avatar
    pixhawk committed
            // Update mean
            const float oldMean = valuesMean.value(name, 0.0f);
            const int meanCount = valuesCount.value(name, 0);
    
    pixhawk's avatar
    pixhawk committed
            double mean = (oldMean * meanCount +  value) / (meanCount + 1);
            if (isnan(mean) || isinf(mean)) mean = 0.0;
            valuesMean.insert(name, mean);
    
    pixhawk's avatar
    pixhawk committed
            valuesCount.insert(name, meanCount + 1);
            // Two-value sliding average
    
    pixhawk's avatar
    pixhawk committed
            double dot = (valuesDot.value(name) + (value - values.value(name, 0.0f)) / ((msec - lastUpdate.value(name, 0))/1000.0f))/2.0f;
    
            if (isnan(dot) || isinf(dot))
            {
                dot = 0.0;
            }
    
    pixhawk's avatar
    pixhawk committed
            valuesDot.insert(name, dot);
    
    pixhawk's avatar
    pixhawk committed
            values.insert(name, value);
            lastUpdate.insert(name, msec);
    
            //qDebug() << __FILE__ << __LINE__ << "VALUE:" << value << "MEAN:" << mean << "DOT:" << dot << "COUNT:" << meanCount;
    
    pixhawk's avatar
    pixhawk committed
        }
    
    pixhawk's avatar
    pixhawk committed
    }
    
    /**
     *
     * @param uas the UAS/MAV to monitor/display with the HUD
     */
    void HUD::setActiveUAS(UASInterface* uas)
    {
        qDebug() << "ATTEMPTING TO SET UAS";
        if (this->uas != NULL && this->uas != uas)
        {
            // Disconnect any previously connected active MAV
            disconnect(uas, SIGNAL(attitudeChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateAttitude(UASInterface*, double, double, double, quint64)));
            disconnect(uas, SIGNAL(batteryChanged(UASInterface*, double, double, int)), this, SLOT(updateBattery(UASInterface*, double, double, int)));
            disconnect(uas, SIGNAL(heartbeat(UASInterface*)), this, SLOT(receiveHeartbeat(UASInterface*)));
            disconnect(uas, SIGNAL(thrustChanged(UASInterface*, double)), this, SLOT(updateThrust(UASInterface*, double)));
            disconnect(uas, SIGNAL(localPositionChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateLocalPosition(UASInterface*,double,double,double,quint64)));
            disconnect(uas, SIGNAL(globalPositionChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateGlobalPosition(UASInterface*,double,double,double,quint64)));
            disconnect(uas, SIGNAL(speedChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateSpeed(UASInterface*,double,double,double,quint64)));
    
            disconnect(uas, SIGNAL(statusChanged(UASInterface*,QString,QString)), this, SLOT(updateState(UASInterface*,QString)));
    
            disconnect(uas, SIGNAL(modeChanged(int,QString,QString)), this, SLOT(updateMode(int,QString,QString)));
    
    pixhawk's avatar
    pixhawk committed
            disconnect(uas, SIGNAL(loadChanged(UASInterface*, double)), this, SLOT(updateLoad(UASInterface*, double)));
            disconnect(uas, SIGNAL(attitudeThrustSetPointChanged(UASInterface*,double,double,double,double,quint64)), this, SLOT(updateAttitudeThrustSetPoint(UASInterface*,double,double,double,double,quint64)));
            disconnect(uas, SIGNAL(valueChanged(UASInterface*,QString,double,quint64)), this, SLOT(updateValue(UASInterface*,QString,double,quint64)));
        }
    
        // Now connect the new UAS
    
        //if (this->uas != uas)
        // {
        qDebug() << "UAS SET!" << "ID:" << uas->getUASID();
        // Setup communication
        connect(uas, SIGNAL(attitudeChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateAttitude(UASInterface*, double, double, double, quint64)));
    
        connect(uas, SIGNAL(batteryChanged(UASInterface*, double, double, int)), this, SLOT(updateBattery(UASInterface*, 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*)));
    
    pixhawk's avatar
    pixhawk committed
        //connect(uas, SIGNAL(thrustChanged(UASInterface*, double)), this, SLOT(updateThrust(UASInterface*, double)));
        //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)));
        //connect(uas, SIGNAL(speedChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateSpeed(UASInterface*,double,double,double,quint64)));
        //connect(uas, SIGNAL(statusChanged(UASInterface*,QString,QString)), this, SLOT(updateState(UASInterface*,QString,QString)));
        //connect(uas, SIGNAL(loadChanged(UASInterface*, double)), this, SLOT(updateLoad(UASInterface*, double)));
        //connect(uas, SIGNAL(attitudeThrustSetPointChanged(UASInterface*,double,double,double,double,quint64)), this, SLOT(updateAttitudeThrustSetPoint(UASInterface*,double,double,double,double,quint64)));
        //connect(uas, SIGNAL(valueChanged(UASInterface*,QString,double,quint64)), this, SLOT(updateValue(UASInterface*,QString,double,quint64)));
        //}
    }
    
    void HUD::updateAttitudeThrustSetPoint(UASInterface*, double rollDesired, double pitchDesired, double yawDesired, double thrustDesired, quint64 msec)
    {
        updateValue(uas, "roll desired", rollDesired, msec);
        updateValue(uas, "pitch desired", pitchDesired, msec);
        updateValue(uas, "yaw desired", yawDesired, msec);
        updateValue(uas, "thrust desired", thrustDesired, msec);
    }
    
    void HUD::updateAttitude(UASInterface* uas, double roll, double pitch, double yaw, quint64 timestamp)
    {
    
        //qDebug() << __FILE__ << __LINE__ << "ROLL" << roll;
    
    pixhawk's avatar
    pixhawk committed
        updateValue(uas, "roll", roll, timestamp);
        updateValue(uas, "pitch", pitch, timestamp);
        updateValue(uas, "yaw", yaw, timestamp);
    }
    
    void HUD::updateBattery(UASInterface* uas, double voltage, double percent, int seconds)
    {
        updateValue(uas, "voltage", voltage, MG::TIME::getGroundTimeNow());
        updateValue(uas, "time remaining", seconds, MG::TIME::getGroundTimeNow());
        updateValue(uas, "charge level", percent, MG::TIME::getGroundTimeNow());
    
    
        fuelStatus.sprintf("BAT [%02.0f \%% | %05.2fV] (%02dm:%02ds)", percent, voltage, seconds/60, seconds%60);
    
    
    pixhawk's avatar
    pixhawk committed
        if (percent < 20.0f)
        {
            fuelColor = warningColor;
        }
        else if (percent < 10.0f)
        {
            fuelColor = criticalColor;
        }
        else
        {
            fuelColor = infoColor;
        }
    }
    
    void HUD::receiveHeartbeat(UASInterface*)
    {
    }
    
    void HUD::updateThrust(UASInterface*, double thrust)
    {
        updateValue(uas, "thrust", thrust, MG::TIME::getGroundTimeNow());
    }
    
    void HUD::updateLocalPosition(UASInterface* uas,double x,double y,double z,quint64 timestamp)
    {
        updateValue(uas, "x", x, timestamp);
        updateValue(uas, "y", y, timestamp);
        updateValue(uas, "z", z, timestamp);
    }
    
    void HUD::updateGlobalPosition(UASInterface*,double lat, double lon, double altitude, quint64 timestamp)
    {
        updateValue(uas, "lat", lat, timestamp);
        updateValue(uas, "lon", lon, timestamp);
        updateValue(uas, "altitude", altitude, timestamp);
    }
    
    void HUD::updateSpeed(UASInterface* uas,double x,double y,double z,quint64 timestamp)
    {
        updateValue(uas, "xSpeed", x, timestamp);
        updateValue(uas, "ySpeed", y, timestamp);
        updateValue(uas, "zSpeed", z, timestamp);
    }
    
    /**
     * Updates the current system state, but only if the uas matches the currently monitored uas.
     *
     * @param uas the system the state message originates from
     * @param state short state text, displayed in HUD
     */
    
    void HUD::updateState(UASInterface* uas,QString state)
    
    pixhawk's avatar
    pixhawk committed
    {
    
        // Only one UAS is connected at a time
        Q_UNUSED(uas);
    
        this->state = state;
    }
    
    /**
     * Updates the current system mode, but only if the uas matches the currently monitored uas.
     *
     * @param uas the system the state message originates from
     * @param mode short mode text, displayed in HUD
     */
    
    void HUD::updateMode(int id,QString mode, QString description)
    
        // Only one UAS is connected at a time
    
        this->mode = mode;
    
    pixhawk's avatar
    pixhawk committed
    }
    
    void HUD::updateLoad(UASInterface* uas, double load)
    {
        updateValue(uas, "load", load, MG::TIME::getGroundTimeNow());
    }
    
    /**
     * @param y coordinate in pixels to be converted to reference mm units
     * @return the screen coordinate relative to the QGLWindow origin
     */
    float HUD::refToScreenX(float x)
    {
        //qDebug() << "sX: " << (scalingFactor * x);
        return (scalingFactor * x);
    }
    /**
     * @param x coordinate in pixels to be converted to reference mm units
     * @return the screen coordinate relative to the QGLWindow origin
     */
    float HUD::refToScreenY(float y)
    {
        //qDebug() << "sY: " << (scalingFactor * y);
        return (scalingFactor * y);
    }
    
    /**
     * This functions works in the OpenGL view, which is already translated by
     * the x and y center offsets.
     *
     */
    void HUD::paintCenterBackground(float roll, float pitch, float yaw)
    {
        // Center indicator is 100 mm wide
        float referenceWidth = 70.0;
        float referenceHeight = 70.0;
    
        // HUD is assumed to be 200 x 150 mm
        // so that positions can be hardcoded
        // but can of course be scaled.
    
        double referencePositionX = vwidth / 2.0 - referenceWidth/2.0;
        double referencePositionY = vheight / 2.0 - referenceHeight/2.0;
    
        //this->width()/2.0+(xCenterOffset*scalingFactor), this->height()/2.0+(yCenterOffset*scalingFactor);
    
        setupGLView(referencePositionX, referencePositionY, referenceWidth, referenceHeight);
    
        // Store current position in the model view
        // the position will be restored after drawing
        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
    
        // Move to the center of the window
        glTranslatef(referenceWidth/2.0f,referenceHeight/2.0f,0);
    
    
        // Move based on the yaw difference
        glTranslatef(yaw, 0.0f, 0.0f);
    
    
    pixhawk's avatar
    pixhawk committed
        // Rotate based on the bank
    
        glRotatef((roll/M_PI)*180.0f, 0.0f, 0.0f, 1.0f);
    
    pixhawk's avatar
    pixhawk committed
    
        // Translate in the direction of the rotation based
        // on the pitch. On the 777, a pitch of 1 degree = 2 mm
    
        //glTranslatef(0, ((-pitch/M_PI)*180.0f * vPitchPerDeg), 0);
    
    pixhawk's avatar
    pixhawk committed
        glTranslatef(0.0f, (-pitch * vPitchPerDeg * 16.5f), 0.0f);
    
    pixhawk's avatar
    pixhawk committed
    
        // Ground
        glColor3ub(179,102,0);
    
        glBegin(GL_POLYGON);
        glVertex2f(-300,-300);
        glVertex2f(-300,0);
        glVertex2f(300,0);
        glVertex2f(300,-300);
        glVertex2f(-300,-300);
        glEnd();
    
        // Sky
        glColor3ub(0,153,204);
    
        glBegin(GL_POLYGON);
        glVertex2f(-300,0);
        glVertex2f(-300,300);
        glVertex2f(300,300);
        glVertex2f(300,0);
        glVertex2f(-300,0);
    
        glEnd();
    }
    
    /**
     * Paint text on top of the image and OpenGL drawings
     *
     * @param text chars to write
     * @param color text color
     * @param fontSize text size in mm
     * @param refX position in reference units (mm of the real instrument). This is relative to the measurement unit position, NOT in pixels.
     * @param refY position in reference units (mm of the real instrument). This is relative to the measurement unit position, NOT in pixels.
     */
    void HUD::paintText(QString text, QColor color, float fontSize, float refX, float refY, QPainter* painter)
    {
        QPen prevPen = painter->pen();
        float pPositionX = refToScreenX(refX) - (fontSize*scalingFactor*0.072f);
        float pPositionY = refToScreenY(refY) - (fontSize*scalingFactor*0.212f);
    
    
        QFont font("Bitstream Vera Sans");
        // Enforce minimum font size of 5 pixels
    
        int fSize = qMax(5, (int)(fontSize*scalingFactor*1.26f));
    
    pixhawk's avatar
    pixhawk committed
    
        QFontMetrics metrics = QFontMetrics(font);
        int border = qMax(4, metrics.leading());
        QRect rect = metrics.boundingRect(0, 0, width() - 2*border, int(height()*0.125),
                                          Qt::AlignLeft | Qt::TextWordWrap, text);
        painter->setPen(color);
        painter->setFont(font);
        painter->setRenderHint(QPainter::TextAntialiasing);
        painter->drawText(pPositionX, pPositionY,
                          rect.width(), rect.height(),
                          Qt::AlignCenter | Qt::TextWordWrap, text);
        painter->setPen(prevPen);
    }
    
    void HUD::initializeGL()
    {
        bool antialiasing = true;
    
        // Antialiasing setup
        if(antialiasing)
        {
    
    pixhawk's avatar
    pixhawk committed
            glEnable(GL_MULTISAMPLE);
    
    pixhawk's avatar
    pixhawk committed
            glEnable(GL_BLEND);
    
            glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
    
            glEnable(GL_POINT_SMOOTH);
            glEnable(GL_LINE_SMOOTH);
    
            glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
            glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
        }
        else
        {
    
            glDisable(GL_BLEND);
            glDisable(GL_POINT_SMOOTH);
            glDisable(GL_LINE_SMOOTH);
    
    pixhawk's avatar
    pixhawk committed
        }
    }
    
    /**
     * @param referencePositionX horizontal position in the reference mm-unit space
     * @param referencePositionY horizontal position in the reference mm-unit space
     * @param referenceWidth width in the reference mm-unit space
     * @param referenceHeight width in the reference mm-unit space
     */
    void HUD::setupGLView(float referencePositionX, float referencePositionY, float referenceWidth, float referenceHeight)
    {
        int pixelWidth  = (int)(referenceWidth * scalingFactor);
        int pixelHeight = (int)(referenceHeight * scalingFactor);
        // Translate and scale the GL view in the virtual reference coordinate units on the screen
        int pixelPositionX = (int)((referencePositionX * scalingFactor) + xCenterOffset);
        int pixelPositionY = this->height() - (referencePositionY * scalingFactor) + yCenterOffset - pixelHeight;
    
        //qDebug() << "Pixel x" << pixelPositionX << "pixelY" << pixelPositionY;
        //qDebug() << "xCenterOffset:" << xCenterOffset << "yCenterOffest" << yCenterOffset
    
    
        //The viewport is established at the correct pixel position and clips everything
        // out of the desired instrument location
        glViewport(pixelPositionX, pixelPositionY, pixelWidth, pixelHeight);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
    
        // The ortho projection is setup in a way that so that the drawing is done in the
        // reference coordinate space
        glOrtho(0, referenceWidth, 0, referenceHeight, -1, 1);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        //glScalef(scaleX, scaleY, 1.0f);
    }
    
    void HUD::paintRollPitchStrips()
    {
    }
    
    
    
    pixhawk's avatar
    pixhawk committed
    void HUD::paintEvent(QPaintEvent *event)
    
    pixhawk's avatar
    pixhawk committed
    {
    
        // Event is not needed
    
        // the event is ignored as this widget
        // is refreshed automatically
    
        Q_UNUSED(event);
    
    }
    
    void HUD::paintHUD()
    {
    //    static quint64 interval = 0;
    //    qDebug() << "INTERVAL:" << MG::TIME::getGroundTimeNow() - interval << __FILE__ << __LINE__;
    //    interval = MG::TIME::getGroundTimeNow();
    
    lm's avatar
    lm committed
    
    
        // Read out most important values to limit hash table lookups
    
        static float roll = 0.0f;
        static float pitch = 0.0f;
        static float yaw = 0.0f;
    
    pixhawk's avatar
    pixhawk committed
    
    
        // Low-pass roll, pitch and yaw
        roll = roll * 0.2f + 0.8f * values.value("roll", 0.0f);
        pitch = pitch * 0.2f + 0.8f * values.value("pitch", 0.0f);
        yaw = yaw * 0.2f + 0.8f * values.value("yaw", 0.0f);
    
    pixhawk's avatar
    pixhawk committed
    
    
        // Translate for yaw
        const float maxYawTrans = 60.0f;
        static float yawDiff = 0.0f;
        float newYawDiff = valuesDot.value("yaw", 0.0f);
        if (isinf(newYawDiff)) newYawDiff = yawDiff;
        if (newYawDiff > M_PI) newYawDiff = newYawDiff - M_PI;
    
        if (newYawDiff < -M_PI) newYawDiff = newYawDiff + M_PI;
    
        newYawDiff = yawDiff * 0.8 + newYawDiff * 0.2;
    
        yawDiff = newYawDiff;
    
        yawInt += newYawDiff;
    
        if (yawInt > M_PI) yawInt = M_PI;
        if (yawInt < -M_PI) yawInt = -M_PI;
    
        float yawTrans = yawInt * (double)maxYawTrans;
        yawInt *= 0.6f;
    
    lm's avatar
    lm committed
    
        if ((yawTrans < 5.0) && (yawTrans > -5.0)) yawTrans = 0;
    
    
        //qDebug() << "yaw translation" << yawTrans << "integral" << yawInt << "difference" << yawDiff << "yaw" << yaw;
    
    pixhawk's avatar
    pixhawk committed
        // Update scaling factor
        // adjust scaling to fit both horizontally and vertically
        scalingFactor = this->width()/vwidth;
        double scalingFactorH = this->height()/vheight;
        if (scalingFactorH < scalingFactor) scalingFactor = scalingFactorH;
    
    
    
        // OPEN GL PAINTING
    
        // Store model view matrix to be able to reset it to the previous state
    
    pixhawk's avatar
    pixhawk committed
        makeCurrent();
    
        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
    
    pixhawk's avatar
    pixhawk committed
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
        // Blue / Brown background
    
        if (noCamera) paintCenterBackground(roll, pitch, yawTrans);
    
        glMatrixMode(GL_MODELVIEW);
        glPopMatrix();
    
    pixhawk's avatar
    pixhawk committed
    
    
        // END OF OPENGL PAINTING
    
    
        //glEnable(GL_MULTISAMPLE);
    
    
    pixhawk's avatar
    pixhawk committed
        // QT PAINTING
    
    pixhawk's avatar
    pixhawk committed
        QPainter painter;
        painter.begin(this);
        painter.setRenderHint(QPainter::Antialiasing, true);
    
        painter.setRenderHint(QPainter::HighQualityAntialiasing, true);
    
    pixhawk's avatar
    pixhawk committed
        painter.translate((this->vwidth/2.0+xCenterOffset)*scalingFactor, (this->vheight/2.0+yCenterOffset)*scalingFactor);
    
    
    
        // COORDINATE FRAME IS NOW (0,0) at CENTER OF WIDGET
    
    
        // Draw all fixed indicators
        // MODE
        paintText(mode, infoColor, 2.0f, (-vwidth/2.0) + 10, -vheight/2.0 + 10, &painter);
        // STATE
        paintText(state, infoColor, 2.0f, (-vwidth/2.0) + 10, -vheight/2.0 + 15, &painter);
        // BATTERY
        paintText(fuelStatus, fuelColor, 2.0f, (-vwidth/2.0) + 10, -vheight/2.0 + 20, &painter);
        // Waypoint
        paintText(waypointName, defaultColor, 2.0f, (-vwidth/3.0) + 10, +vheight/3.0 + 15, &painter);
    
        // YAW INDICATOR
        //
        //      .
        //    .   .
        //   .......
        //
        const float yawIndicatorWidth = 4.0f;
        const float yawIndicatorY = vheight/2.0f - 10.0f;
        QPolygon yawIndicator(4);
        yawIndicator.setPoint(0, QPoint(refToScreenX(0.0f), refToScreenY(yawIndicatorY)));
        yawIndicator.setPoint(1, QPoint(refToScreenX(yawIndicatorWidth/2.0f), refToScreenY(yawIndicatorY+yawIndicatorWidth)));
        yawIndicator.setPoint(2, QPoint(refToScreenX(-yawIndicatorWidth/2.0f), refToScreenY(yawIndicatorY+yawIndicatorWidth)));
        yawIndicator.setPoint(3, QPoint(refToScreenX(0.0f), refToScreenY(yawIndicatorY)));
        painter.setPen(defaultColor);
        painter.drawPolyline(yawIndicator);
    
        // CENTER
    
        // HEADING INDICATOR
        //
        //    __      __
        //       \/\/
        //
        const float hIndicatorWidth = 7.0f;
        const float hIndicatorY = -25.0f;
        const float hIndicatorYLow = hIndicatorY + hIndicatorWidth / 6.0f;
        const float hIndicatorSegmentWidth = hIndicatorWidth / 7.0f;
        QPolygon hIndicator(7);
        hIndicator.setPoint(0, QPoint(refToScreenX(0.0f-hIndicatorWidth/2.0f), refToScreenY(hIndicatorY)));
        hIndicator.setPoint(1, QPoint(refToScreenX(0.0f-hIndicatorWidth/2.0f+hIndicatorSegmentWidth*1.75f), refToScreenY(hIndicatorY)));
        hIndicator.setPoint(2, QPoint(refToScreenX(0.0f-hIndicatorSegmentWidth*1.0f), refToScreenY(hIndicatorYLow)));
        hIndicator.setPoint(3, QPoint(refToScreenX(0.0f), refToScreenY(hIndicatorY)));
        hIndicator.setPoint(4, QPoint(refToScreenX(0.0f+hIndicatorSegmentWidth*1.0f), refToScreenY(hIndicatorYLow)));
        hIndicator.setPoint(5, QPoint(refToScreenX(0.0f+hIndicatorWidth/2.0f-hIndicatorSegmentWidth*1.75f), refToScreenY(hIndicatorY)));
        hIndicator.setPoint(6, QPoint(refToScreenX(0.0f+hIndicatorWidth/2.0f), refToScreenY(hIndicatorY)));
        painter.setPen(defaultColor);
        painter.drawPolyline(hIndicator);
    
    
        // SETPOINT
        const float centerWidth = 4.0f;
        painter.setPen(defaultColor);
        painter.setBrush(Qt::NoBrush);
        // TODO
        //painter.drawEllipse(QPointF(refToScreenX(qMin(10.0f, values.value("roll desired", 0.0f) * 10.0f)), refToScreenY(qMin(10.0f, values.value("pitch desired", 0.0f) * 10.0f))), refToScreenX(centerWidth/2.0f), refToScreenX(centerWidth/2.0f));
    
        const float centerCrossWidth = 10.0f;
        // left
        painter.drawLine(QPointF(refToScreenX(-centerWidth / 2.0f), refToScreenY(0.0f)), QPointF(refToScreenX(-centerCrossWidth / 2.0f), refToScreenY(0.0f)));
        // right
        painter.drawLine(QPointF(refToScreenX(centerWidth / 2.0f), refToScreenY(0.0f)), QPointF(refToScreenX(centerCrossWidth / 2.0f), refToScreenY(0.0f)));
        // top
        painter.drawLine(QPointF(refToScreenX(0.0f), refToScreenY(-centerWidth / 2.0f)), QPointF(refToScreenX(0.0f), refToScreenY(-centerCrossWidth / 2.0f)));
    
    
    
        // COMPASS
        const float compassY = -vheight/2.0f + 10.0f;
        QRectF compassRect(QPointF(refToScreenX(-5.0f), refToScreenY(compassY)), QSizeF(refToScreenX(10.0f), refToScreenY(5.0f)));
        painter.setBrush(Qt::NoBrush);
        painter.setPen(Qt::SolidLine);
        painter.setPen(defaultColor);
        painter.drawRoundedRect(compassRect, 2, 2);
        QString yawAngle;
    
        const float yawDeg = ((values.value("yaw", 0.0f)/M_PI)*180.0f)+180.f;
        //qDebug() << "YAW: " << yawDeg;
        yawAngle.sprintf("%03d", (int)yawDeg);
        paintText(yawAngle, defaultColor, 3.5f, -3.7f, compassY+ 0.9f, &painter);
    
        // CHANGE RATE STRIPS
        drawChangeRateStrip(-51.0f, -50.0f, 15.0f, -1.0f, 1.0f, valuesDot.value("z", 0.0f), &painter);
    
        // CHANGE RATE STRIPS
        drawChangeRateStrip(49.0f, -50.0f, 15.0f, -1.0f, 1.0f, valuesDot.value("x", 0.0f), &painter);
    
        // GAUGES
    
        // Left altitude gauge
        drawChangeIndicatorGauge(-vGaugeSpacing, -15.0f, 10.0f, 2.0f, -values.value("z", 0.0f), defaultColor, &painter, false);
    
        // Right speed gauge
        drawChangeIndicatorGauge(vGaugeSpacing, -15.0f, 10.0f, 5.0f, values.value("xSpeed", 0.0f), defaultColor, &painter, false);
    
    
        // MOVING PARTS
    
    
    
        painter.translate(refToScreenX(yawTrans), 0);
    
    pixhawk's avatar
    pixhawk committed
    
        // Rotate view and draw all roll-dependent indicators
        painter.rotate((roll/M_PI)* -180.0f);
    
    
    pixhawk's avatar
    pixhawk committed
        painter.translate(0, (-pitch/M_PI)* -180.0f * refToScreenY(1.8));
    
    pixhawk's avatar
    pixhawk committed
        //qDebug() << "ROLL" << roll << "PITCH" << pitch << "YAW DIFF" << valuesDot.value("roll", 0.0f);
    
        // PITCH
    
    
        paintPitchLines(pitch, &painter);
    
    pixhawk's avatar
    pixhawk committed
        painter.end();
    
        //glDisable(GL_MULTISAMPLE);
    
    pixhawk's avatar
    pixhawk committed
    
    
        //glFlush();
    
    pixhawk's avatar
    pixhawk committed
    }
    
    /*
    void HUD::paintGL()
    {
        static float roll = 0.0;
        static float pitch = 0.0;
        static float yaw = 0.0;
    
        // Read out most important values to limit hash table lookups
        roll = roll * 0.5 + 0.5 * values.value("roll", 0.0f);
        pitch = pitch * 0.5 + 0.5 * values.value("pitch", 0.0f);
        yaw = yaw * 0.5 + 0.5 * values.value("yaw", 0.0f);
    
    pixhawk's avatar
    pixhawk committed
    
        //qDebug() << __FILE__ << __LINE__ << "ROLL:" << roll << "PITCH:" << pitch << "YAW:" << yaw;
    
    
        // Update scaling factor
        // adjust scaling to fit both horizontally and vertically
        scalingFactor = this->width()/vwidth;
        double scalingFactorH = this->height()/vheight;
        if (scalingFactorH < scalingFactor) scalingFactor = scalingFactorH;
    
    
    pixhawk's avatar
    pixhawk committed
        makeCurrent();
    
    pixhawk's avatar
    pixhawk committed
        glClear(GL_COLOR_BUFFER_BIT);
        //if(!noCamera) glDrawPixels(glImage.width(), glImage.height(), GL_RGBA, GL_UNSIGNED_BYTE, glImage.bits());
    
    pixhawk's avatar
    pixhawk committed
        //glDrawPixels(glImage.width(), glImage.height(), GL_RGBA, GL_UNSIGNED_BYTE, glImage.bits()); // FIXME Remove after testing
    
    pixhawk's avatar
    pixhawk committed
    
    
        // Blue / Brown background
        if (noCamera) paintCenterBackground(roll, pitch, yaw);
    
    
    pixhawk's avatar
    pixhawk committed
        glFlush();
    
    pixhawk's avatar
    pixhawk committed
    
        //    // Store current GL model view
        //    glMatrixMode(GL_MODELVIEW);
        //    glPushMatrix();
        //
        //    // Setup GL view
        //    setupGLView(0.0f, 0.0f, vwidth, vheight);
        //
        //    // Restore previous view
        //    glPopMatrix();
    
        // Now draw QPainter overlay
    
        //painter.setRenderHint(QPainter::Antialiasing);
    
        // Position the coordinate frame according to the setup
    
    
    pixhawk's avatar
    pixhawk committed
        makeOverlayCurrent();
    
    pixhawk's avatar
    pixhawk committed
        QPainter painter(this);
    
    pixhawk's avatar
    pixhawk committed
        //painter.setRenderHint(QPainter::Antialiasing, true);
    
    pixhawk's avatar
    pixhawk committed
        //painter.setRenderHint(QPainter::HighQualityAntialiasing, true);
    
    pixhawk's avatar
    pixhawk committed
        painter.translate((this->vwidth/2.0+xCenterOffset)*scalingFactor, (this->vheight/2.0+yCenterOffset)*scalingFactor);
    
        // COORDINATE FRAME IS NOW (0,0) at CENTER OF WIDGET
    
    
        // Draw all fixed indicators
        // MODE
        paintText(mode, infoColor, 2.0f, (-vwidth/2.0) + 10, -vheight/2.0 + 10, &painter);
    
        // STATE
        paintText(state, infoColor, 2.0f, (-vwidth/2.0) + 10, -vheight/2.0 + 15, &painter);
    
    pixhawk's avatar
    pixhawk committed
        // BATTERY
    
        paintText(fuelStatus, fuelColor, 2.0f, (-vwidth/2.0) + 10, -vheight/2.0 + 20, &painter);
    
    pixhawk's avatar
    pixhawk committed
        // Waypoint
        paintText(waypointName, defaultColor, 2.0f, (-vwidth/3.0) + 10, +vheight/3.0 + 15, &painter);
    
        // YAW INDICATOR
        //
        //      .
        //    .   .
        //   .......
        //
        const float yawIndicatorWidth = 4.0f;
        const float yawIndicatorY = vheight/2.0f - 10.0f;
        QPolygon yawIndicator(4);
        yawIndicator.setPoint(0, QPoint(refToScreenX(0.0f), refToScreenY(yawIndicatorY)));
        yawIndicator.setPoint(1, QPoint(refToScreenX(yawIndicatorWidth/2.0f), refToScreenY(yawIndicatorY+yawIndicatorWidth)));
        yawIndicator.setPoint(2, QPoint(refToScreenX(-yawIndicatorWidth/2.0f), refToScreenY(yawIndicatorY+yawIndicatorWidth)));
        yawIndicator.setPoint(3, QPoint(refToScreenX(0.0f), refToScreenY(yawIndicatorY)));
        painter.setPen(defaultColor);
        painter.drawPolyline(yawIndicator);
    
    
    lm's avatar
    lm committed
        // CENTER
    
    
    pixhawk's avatar
    pixhawk committed
        // HEADING INDICATOR
        //
        //    __      __
        //       \/\/
        //
        const float hIndicatorWidth = 7.0f;
        const float hIndicatorY = -25.0f;
        const float hIndicatorYLow = hIndicatorY + hIndicatorWidth / 6.0f;
        const float hIndicatorSegmentWidth = hIndicatorWidth / 7.0f;
        QPolygon hIndicator(7);
        hIndicator.setPoint(0, QPoint(refToScreenX(0.0f-hIndicatorWidth/2.0f), refToScreenY(hIndicatorY)));
        hIndicator.setPoint(1, QPoint(refToScreenX(0.0f-hIndicatorWidth/2.0f+hIndicatorSegmentWidth*1.75f), refToScreenY(hIndicatorY)));
        hIndicator.setPoint(2, QPoint(refToScreenX(0.0f-hIndicatorSegmentWidth*1.0f), refToScreenY(hIndicatorYLow)));
        hIndicator.setPoint(3, QPoint(refToScreenX(0.0f), refToScreenY(hIndicatorY)));
        hIndicator.setPoint(4, QPoint(refToScreenX(0.0f+hIndicatorSegmentWidth*1.0f), refToScreenY(hIndicatorYLow)));
        hIndicator.setPoint(5, QPoint(refToScreenX(0.0f+hIndicatorWidth/2.0f-hIndicatorSegmentWidth*1.75f), refToScreenY(hIndicatorY)));
        hIndicator.setPoint(6, QPoint(refToScreenX(0.0f+hIndicatorWidth/2.0f), refToScreenY(hIndicatorY)));
        painter.setPen(defaultColor);
        painter.drawPolyline(hIndicator);
    
    
    
    lm's avatar
    lm committed
        // SETPOINT
        const float centerWidth = 4.0f;
        painter.setPen(defaultColor);
        painter.setBrush(Qt::NoBrush);
        // TODO
        //painter.drawEllipse(QPointF(refToScreenX(qMin(10.0f, values.value("roll desired", 0.0f) * 10.0f)), refToScreenY(qMin(10.0f, values.value("pitch desired", 0.0f) * 10.0f))), refToScreenX(centerWidth/2.0f), refToScreenX(centerWidth/2.0f));
    
        const float centerCrossWidth = 10.0f;
        // left
        painter.drawLine(QPointF(refToScreenX(-centerWidth / 2.0f), refToScreenY(0.0f)), QPointF(refToScreenX(-centerCrossWidth / 2.0f), refToScreenY(0.0f)));
        // right
        painter.drawLine(QPointF(refToScreenX(centerWidth / 2.0f), refToScreenY(0.0f)), QPointF(refToScreenX(centerCrossWidth / 2.0f), refToScreenY(0.0f)));
        // top
        painter.drawLine(QPointF(refToScreenX(0.0f), refToScreenY(-centerWidth / 2.0f)), QPointF(refToScreenX(0.0f), refToScreenY(-centerCrossWidth / 2.0f)));
    
    
    
    
    pixhawk's avatar
    pixhawk committed
        // COMPASS
        const float compassY = -vheight/2.0f + 10.0f;
        QRectF compassRect(QPointF(refToScreenX(-5.0f), refToScreenY(compassY)), QSizeF(refToScreenX(10.0f), refToScreenY(5.0f)));
        painter.setBrush(Qt::NoBrush);
        painter.setPen(Qt::SolidLine);
        painter.setPen(defaultColor);
        painter.drawRoundedRect(compassRect, 2, 2);
        QString yawAngle;
    
        const float yawDeg = ((values.value("yaw", 0.0f)/M_PI)*180.0f)+180.f;
        //qDebug() << "YAW: " << yawDeg;
        yawAngle.sprintf("%03d", (int)yawDeg);
        paintText(yawAngle, defaultColor, 3.5f, -3.7f, compassY+ 0.9f, &painter);
    
        // CHANGE RATE STRIPS
        drawChangeRateStrip(-51.0f, -50.0f, 15.0f, -1.0f, 1.0f, valuesDot.value("z", 0.0f), &painter);
    
        // CHANGE RATE STRIPS
        drawChangeRateStrip(49.0f, -50.0f, 15.0f, -1.0f, 1.0f, valuesDot.value("x", 0.0f), &painter);
    
        // GAUGES
    
        // Left altitude gauge
        drawChangeIndicatorGauge(-vGaugeSpacing, -15.0f, 10.0f, 2.0f, -values.value("z", 0.0f), defaultColor, &painter, false);
    
        // Right speed gauge
        drawChangeIndicatorGauge(vGaugeSpacing, -15.0f, 10.0f, 5.0f, values.value("xSpeed", 0.0f), defaultColor, &painter, false);
    
    
    pixhawk's avatar
    pixhawk committed
        glFlush();
    
    pixhawk's avatar
    pixhawk committed
    
    
        // MOVING PARTS
    
        // Translate for yaw
    
    pixhawk's avatar
    pixhawk committed
        const float maxYawTrans = 60.0f;
    
    pixhawk's avatar
    pixhawk committed
        float yawDiff = valuesDot.value("yaw", 0.0f);
    
    pixhawk's avatar
    pixhawk committed
        if (isinf(yawDiff)) yawDiff = 0.0f;
        if (yawDiff > M_PI) yawDiff = yawDiff - M_PI;
    
    pixhawk's avatar
    pixhawk committed
    
    
    pixhawk's avatar
    pixhawk committed
        if (yawDiff < -M_PI) yawDiff = yawDiff + M_PI;
    
    pixhawk's avatar
    pixhawk committed
    
        yawInt += yawDiff;
    
        if (yawInt > M_PI) yawInt = M_PI;
        if (yawInt < -M_PI) yawInt = -M_PI;
    
        float yawTrans = yawInt * (double)maxYawTrans;
        yawInt *= 0.6f;
        //qDebug() << "yaw translation" << yawTrans << "integral" << yawInt << "difference" << yawDiff << "yaw" << yaw << "asin(yawInt)" << asinYaw;
    
    
    pixhawk's avatar
    pixhawk committed
        painter.translate(0, (pitch/M_PI)* -180.0f * refToScreenY(1.8));
    
    pixhawk's avatar
    pixhawk committed
        painter.translate(refToScreenX(yawTrans), 0);
    
    
    pixhawk's avatar
    pixhawk committed
        // Rotate view and draw all roll-dependent indicators
        painter.rotate((roll/M_PI)* -180.0f);
    
    
    lm's avatar
    lm committed
        //qDebug() << "ROLL" << roll << "PITCH" << pitch << "YAW DIFF" << valuesDot.value("roll", 0.0f);
    
    pixhawk's avatar
    pixhawk committed
        // PITCH
    
        paintPitchLines((pitch/M_PI)*180.0f, &painter);
    
        painter.end();
    
        glFlush();
    
    
    
    pixhawk's avatar
    pixhawk committed
    }*/
    
    
    pixhawk's avatar
    pixhawk committed
    
    /**
     * @param pitch pitch angle in degrees (-180 to 180)
     */
    void HUD::paintPitchLines(float pitch, QPainter* painter)
    {
        QString label;
    
        const float yDeg = vPitchPerDeg;
        const float lineDistance = 5.0f; ///< One pitch line every 10 degrees
        const float posIncrement = yDeg * lineDistance;
        float posY = posIncrement;
        const float posLimit = sqrt(pow(vwidth, 2.0f) + pow(vheight, 2.0f));
    
        const float offsetAbs = pitch * yDeg;
    
        float offset = pitch;
        if (offset < 0) offset = -offset;
        int offsetCount = 0;
        while (offset > lineDistance)
        {
            offset -= lineDistance;
            offsetCount++;
        }
    
        int iPos = (int)(0.5f + lineDistance); ///< The first line
        int iNeg = (int)(-0.5f - lineDistance); ///< The first line
    
        offset *= yDeg;
    
    
        painter->setPen(defaultColor);
    
        posY = -offsetAbs + posIncrement; //+ 100;// + lineDistance;
    
        while (posY < posLimit)
        {
            paintPitchLinePos(label.sprintf("%3d", iPos), 0.0f, -posY, painter);
            posY += posIncrement;
            iPos += (int)lineDistance;
        }
    
    
    
        // HORIZON
        //
        //    ------------    ------------
        //
        const float pitchWidth = 30.0f;
        const float pitchGap = pitchWidth / 2.5f;
        const QColor horizonColor = defaultColor;
        const float diagonal = sqrt(pow(vwidth, 2.0f) + pow(vheight, 2.0f));
        const float lineWidth = refLineWidthToPen(0.5f);
    
        // Left horizon
        drawLine(0.0f-diagonal, offsetAbs, 0.0f-pitchGap/2.0f, offsetAbs, lineWidth, horizonColor, painter);
        // Right horizon
        drawLine(0.0f+pitchGap/2.0f, offsetAbs, 0.0f+diagonal, offsetAbs, lineWidth, horizonColor, painter);
    
    
    
        label.clear();
    
        posY = offsetAbs  + posIncrement;
    
    
        while (posY < posLimit)
        {
            paintPitchLineNeg(label.sprintf("%3d", iNeg), 0.0f, posY, painter);
            posY += posIncrement;
            iNeg -= (int)lineDistance;
        }