Skip to content
Snippets Groups Projects
HUD.cc 56.3 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 <QContextMenuEvent>
    #include <QMenu>
    #include <QDesktopServices>
    #include <QFileDialog>
    
    pixhawk's avatar
    pixhawk committed
    #include <QDebug>
    #include <cmath>
    
    pixhawk's avatar
    pixhawk committed
    #include <qmath.h>
    
    pixhawk's avatar
    pixhawk committed
    #include <limits>
    
    pixhawk's avatar
    pixhawk committed
    
    #include "UASManager.h"
    
    pixhawk's avatar
    pixhawk committed
    #include "HUD.h"
    #include "MG.h"
    
    pixhawk's avatar
    pixhawk committed
    
    // 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),
          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)),
    
          imageTimer(new QTimer(this)),
    
          noCamera(true),
          hardwareAcceleration(true),
          strongStrokeWidth(1.5f),
          normalStrokeWidth(1.0f),
          fineStrokeWidth(0.5f),
          waypointName(""),
          roll(0.0f),
          pitch(0.0f),
          yaw(0.0f),
          rollLP(0.0f),
          pitchLP(0.0f),
          yawLP(0.0f),
          yawDiff(0.0f),
          xPos(0.0),
          yPos(0.0),
          zPos(0.0),
          xSpeed(0.0),
          ySpeed(0.0),
          zSpeed(0.0),
          lastSpeedUpdate(0),
          totalSpeed(0.0),
          totalAcc(0.0),
          lat(0.0),
          lon(0.0),
          alt(0.0),
          load(0.0f),
          offlineDirectory(""),
          nextOfflineImage(""),
          hudInstrumentsEnabled(true),
          videoEnabled(false),
          xImageFactor(1.0),
    
    pixhawk's avatar
    pixhawk committed
    {
        // Set auto fill to false
        setAutoFillBackground(false);
    
    
        // Set minimum size
        setMinimumSize(80, 60);
    
        // Set preferred size
        setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    
    pixhawk's avatar
    pixhawk committed
        // 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 = "/Users/user/Desktop/frame0000.png";
        //qDebug() << __FILE__ << __LINE__ << "template image:" << imagePath;
        //fill = QImage(imagePath);
    
    pixhawk's avatar
    pixhawk committed
    
    
        //glImage = QGLWidget::convertToGLFormat(fill);
    
    pixhawk's avatar
    pixhawk committed
    
        // Refresh timer
    
        refreshTimer->setInterval(updateInterval);
    
    pixhawk's avatar
    pixhawk committed
        imageTimer->setInterval(250);
    
        //connect(refreshTimer, SIGNAL(timeout()), this, SLOT(update()));
        connect(refreshTimer, SIGNAL(timeout()), this, SLOT(paintHUD()));
    
       // connect(imageTimer, SIGNAL(timeout()), this, SLOT(requestNewImage())); TODO
    
    pixhawk's avatar
    pixhawk committed
    
        // Resize to correct size and fill with image
    
        //glDrawPixels(glImage.width(), glImage.height(), GL_RGBA, GL_UNSIGNED_BYTE, glImage.bits());
    
    pixhawk's avatar
    pixhawk committed
    
        // 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", qMax(5,(int)(10.0f*scalingFactor*1.2f+0.5f)));
        QFont* fontPtr = &font;
    
        if (!fontPtr) {
    
            qDebug() << "ERROR! FONT NOT LOADED!";
    
            if (font.family() != fontFamilyName) qDebug() << "ERROR! WRONG FONT LOADED: " << fontFamilyName;
        }
    
    pixhawk's avatar
    pixhawk committed
    
        // Connect with UAS
    
        connect(UASManager::instance(), SIGNAL(activeUASSet(UASInterface*)), this, SLOT(setActiveUAS(UASInterface*)));
    
    
        createActions();
    
        if (UASManager::instance()->getActiveUAS() != NULL) setActiveUAS(UASManager::instance()->getActiveUAS());
    
    
    pixhawk's avatar
    pixhawk committed
    }
    
    HUD::~HUD()
    {
    
        refreshTimer->stop();
    
    pixhawk's avatar
    pixhawk committed
    }
    
    
        return QSize(width(), (width()*3.0f)/4);
    
    void HUD::showEvent(QShowEvent* event)
    {
    
        // React only to internal (pre-display)
        // events
    
        refreshTimer->start(updateInterval);
    
    }
    
    void HUD::hideEvent(QHideEvent* event)
    {
        // React only to internal (pre-display)
        // events
    
        Q_UNUSED(event);
        refreshTimer->stop();
    
    pixhawk's avatar
    pixhawk committed
    }
    
    
    void HUD::contextMenuEvent (QContextMenuEvent* event)
    {
        QMenu menu(this);
    
        // Update actions
        enableHUDAction->setChecked(hudInstrumentsEnabled);
        enableVideoAction->setChecked(videoEnabled);
    
    
        menu.addAction(enableHUDAction);
        //menu.addAction(selectHUDColorAction);
        menu.addAction(enableVideoAction);
        menu.addAction(selectOfflineDirectoryAction);
        //menu.addAction(selectVideoChannelAction);
        menu.exec(event->globalPos());
    }
    
    void HUD::createActions()
    {
        enableHUDAction = new QAction(tr("Enable HUD"), this);
        enableHUDAction->setStatusTip(tr("Show the HUD instruments in this window"));
        enableHUDAction->setCheckable(true);
        enableHUDAction->setChecked(hudInstrumentsEnabled);
        connect(enableHUDAction, SIGNAL(triggered(bool)), this, SLOT(enableHUDInstruments(bool)));
    
        enableVideoAction = new QAction(tr("Enable Video Live feed"), this);
        enableVideoAction->setStatusTip(tr("Show the video live feed"));
        enableVideoAction->setCheckable(true);
        enableVideoAction->setChecked(videoEnabled);
        connect(enableVideoAction, SIGNAL(triggered(bool)), this, SLOT(enableVideo(bool)));
    
        selectOfflineDirectoryAction = new QAction(tr("Select image log"), this);
        selectOfflineDirectoryAction->setStatusTip(tr("Load previously logged images into simulation / replay"));
        connect(selectOfflineDirectoryAction, SIGNAL(triggered()), this, SLOT(selectOfflineDirectory()));
    }
    
    
    pixhawk's avatar
    pixhawk committed
    /**
     *
     * @param uas the UAS/MAV to monitor/display with the HUD
     */
    void HUD::setActiveUAS(UASInterface* uas)
    {
    
        if (this->uas != NULL) {
    
    pixhawk's avatar
    pixhawk committed
            // 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(batteryChanged(UASInterface*, double, double, int)), this, SLOT(updateBattery(UASInterface*, double, double, int)));
            disconnect(this->uas, SIGNAL(statusChanged(UASInterface*,QString,QString)), this, SLOT(updateState(UASInterface*,QString)));
            disconnect(this->uas, SIGNAL(modeChanged(int,QString,QString)), this, SLOT(updateMode(int,QString,QString)));
            disconnect(this->uas, SIGNAL(heartbeat(UASInterface*)), this, SLOT(receiveHeartbeat(UASInterface*)));
    
            disconnect(this->uas, SIGNAL(localPositionChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateLocalPosition(UASInterface*,double,double,double,quint64)));
    
    pixhawk's avatar
    pixhawk committed
            disconnect(this->uas, SIGNAL(globalPositionChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateGlobalPosition(UASInterface*,double,double,double,quint64)));
    
            disconnect(this->uas, SIGNAL(speedChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateSpeed(UASInterface*,double,double,double,quint64)));
            disconnect(this->uas, SIGNAL(waypointSelected(int,int)), this, SLOT(selectWaypoint(int, int)));
    
    pixhawk's avatar
    pixhawk committed
    
    
            UAS* u = dynamic_cast<UAS*>(this->uas);
    
                disconnect(u, SIGNAL(imageStarted(quint64)), this, SLOT(startImage(quint64)));
    
                disconnect(u, SIGNAL(imageReady(UASInterface*)), this, SLOT(requestNewImage()));
    
            // 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(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*)));
    
            connect(uas, SIGNAL(localPositionChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateLocalPosition(UASInterface*,double,double,double,quint64)));
    
    pixhawk's avatar
    pixhawk committed
            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(waypointSelected(int,int)), this, SLOT(selectWaypoint(int, int)));
    
            // Try to connect the image link
            UAS* u = dynamic_cast<UAS*>(uas);
    
                connect(u, SIGNAL(imageStarted(quint64)), this, SLOT(startImage(quint64)));
    
                connect(u, SIGNAL(imageReady(UASInterface*)), this, SLOT(requestNewImage()));
    
            this->u = dynamic_cast<UAS*>(this->uas);
    
    pixhawk's avatar
    pixhawk committed
    }
    
    
    //void HUD::updateAttitudeThrustSetPoint(UASInterface* uas, double rollDesired, double pitchDesired, double yawDesired, double thrustDesired, quint64 msec)
    //{
    ////    updateValue(uas, "roll desired", rollDesired, msec);
    ////    updateValue(uas, "pitch desired", pitchDesired, msec);
    ////    updateValue(uas, "yaw desired", yawDesired, msec);
    ////    updateValue(uas, "thrust desired", thrustDesired, msec);
    //}
    
    pixhawk's avatar
    pixhawk committed
    
    void HUD::updateAttitude(UASInterface* uas, double roll, double pitch, double yaw, quint64 timestamp)
    {
    
        Q_UNUSED(uas);
        Q_UNUSED(timestamp);
        this->roll = roll;
        this->pitch = pitch;
        this->yaw = yaw;
    
    pixhawk's avatar
    pixhawk committed
    }
    
    void HUD::updateBattery(UASInterface* uas, double voltage, double percent, int seconds)
    {
    
    pixhawk's avatar
    pixhawk committed
        fuelStatus = tr("BAT [%1% | %2V] (%3:%4)").arg(percent, 2, 'f', 0, QChar('0')).arg(voltage, 4, 'f', 1, QChar('0')).arg(seconds/60, 2, 10, QChar('0')).arg(seconds%60, 2, 10, QChar('0'));
    
        if (percent < 20.0f) {
    
    pixhawk's avatar
    pixhawk committed
            fuelColor = warningColor;
    
        } else if (percent < 10.0f) {
    
    pixhawk's avatar
    pixhawk committed
            fuelColor = criticalColor;
    
    pixhawk's avatar
    pixhawk committed
            fuelColor = infoColor;
        }
    }
    
    void HUD::receiveHeartbeat(UASInterface*)
    {
    }
    
    
    void HUD::updateThrust(UASInterface* uas, double thrust)
    
    pixhawk's avatar
    pixhawk committed
    {
    
        Q_UNUSED(uas);
        Q_UNUSED(thrust);
    //    updateValue(uas, "thrust", thrust, MG::TIME::getGroundTimeNow());
    
    pixhawk's avatar
    pixhawk committed
    }
    
    void HUD::updateLocalPosition(UASInterface* uas,double x,double y,double z,quint64 timestamp)
    {
    
        Q_UNUSED(uas);
        Q_UNUSED(timestamp);
        this->xPos = x;
        this->yPos = y;
        this->zPos = z;
    
    pixhawk's avatar
    pixhawk committed
    }
    
    
    void HUD::updateGlobalPosition(UASInterface* uas,double lat, double lon, double altitude, quint64 timestamp)
    
    pixhawk's avatar
    pixhawk committed
    {
    
        Q_UNUSED(uas);
        Q_UNUSED(timestamp);
        this->lat = lat;
        this->lon = lon;
        this->alt = altitude;
    
    pixhawk's avatar
    pixhawk committed
    }
    
    void HUD::updateSpeed(UASInterface* uas,double x,double y,double z,quint64 timestamp)
    {
    
        Q_UNUSED(uas);
        Q_UNUSED(timestamp);
        this->xSpeed = x;
        this->ySpeed = y;
        this->zSpeed = z;
    
    pixhawk's avatar
    pixhawk committed
        double newTotalSpeed = sqrt(xSpeed*xSpeed + ySpeed*ySpeed + zSpeed*zSpeed);
        totalAcc = (newTotalSpeed - totalSpeed) / ((double)(lastSpeedUpdate - timestamp)/1000.0);
        totalSpeed = newTotalSpeed;
    
    pixhawk's avatar
    pixhawk committed
    }
    
    /**
     * 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)
    {
    
        Q_UNUSED(uas);
        this->load = load;
        //updateValue(uas, "load", load, MG::TIME::getGroundTimeNow());
    
    pixhawk's avatar
    pixhawk committed
    }
    
    /**
     * @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);
    
            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);
    
        if (isVisible()) {
    
            //    static quint64 interval = 0;
            //    qDebug() << "INTERVAL:" << MG::TIME::getGroundTimeNow() - interval << __FILE__ << __LINE__;
            //    interval = MG::TIME::getGroundTimeNow();
    
    lm's avatar
    lm committed
    
    
            qDebug() << "EVENTLOOP:" << __FILE__ << __LINE__;
    
            // Read out most important values to limit hash table lookups
            // Low-pass roll, pitch and yaw
            rollLP = rollLP * 0.2f + 0.8f * roll;
            pitchLP = pitchLP * 0.2f + 0.8f * pitch;
            yawLP = yawLP * 0.2f + 0.8f * yaw;
    
    pixhawk's avatar
    pixhawk committed
    
    
            // Translate for yaw
            const float maxYawTrans = 60.0f;
    
            float newYawDiff = yawDiff;
            if (isinf(newYawDiff)) newYawDiff = yawDiff;
            if (newYawDiff > M_PI) newYawDiff = newYawDiff - M_PI;
    
            if (newYawDiff < -M_PI) newYawDiff = newYawDiff + M_PI;
    
            newYawDiff = yawDiff * 0.8 + newYawDiff * 0.2;
    
            yawDiff = newYawDiff;
    
            yawInt += newYawDiff;
    
            if (yawInt > M_PI) yawInt = (float)M_PI;
            if (yawInt < -M_PI) yawInt = (float)-M_PI;
    
            float yawTrans = yawInt * (float)maxYawTrans;
    
            yawInt *= 0.6f;
    
    lm's avatar
    lm committed
    
    
            if ((yawTrans < 5.0) && (yawTrans > -5.0)) yawTrans = 0;
    
    lm's avatar
    lm committed
    
    
            // Negate to correct direction
            yawTrans = -yawTrans;
    
    lm's avatar
    lm committed
    
    
            //qDebug() << "yaw translation" << yawTrans << "integral" << yawInt << "difference" << yawDiff << "yaw" << yaw;
    
            // Update scaling factor
            // adjust scaling to fit both horizontally and vertically
            scalingFactor = this->width()/vwidth;
            double scalingFactorH = this->height()/vheight;
            if (scalingFactorH < scalingFactor) scalingFactor = scalingFactorH;
    
            // OPEN GL PAINTING
            // Store model view matrix to be able to reset it to the previous state
            makeCurrent();
            glMatrixMode(GL_MODELVIEW);
            glPushMatrix();
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
            // Fill with black background
    
            if (videoEnabled) {
                if (nextOfflineImage != "" && QFileInfo(nextOfflineImage).exists()) {
    
                    qDebug() << __FILE__ << __LINE__ << "template image:" << nextOfflineImage;
                    QImage fill = QImage(nextOfflineImage);
    
                    xImageFactor = width() / (float)fill.width();
                    yImageFactor = height() / (float)fill.height();
    
                    glImage = QGLWidget::convertToGLFormat(fill);
    
                    // Reset to save load efforts
                    nextOfflineImage = "";
                }
    
    pixhawk's avatar
    pixhawk committed
                glRasterPos2i(0, 0);
    
    
                // Resize to correct size and fill with image
                glDrawPixels(glImage.width(), glImage.height(), GL_RGBA, GL_UNSIGNED_BYTE, glImage.bits());
    
                // Blue / brown background
                paintCenterBackground(roll, pitch, yawTrans);
    
            }
    
            glMatrixMode(GL_MODELVIEW);
            glPopMatrix();
    
            // END OF OPENGL PAINTING
    
    
            if (hudInstrumentsEnabled) {
    
                //glEnable(GL_MULTISAMPLE);
    
                // QT PAINTING
                //makeCurrent();
                QPainter painter;
                painter.begin(this);
                painter.setRenderHint(QPainter::Antialiasing, true);
                painter.setRenderHint(QPainter::HighQualityAntialiasing, true);
                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);
    
    pixhawk's avatar
    pixhawk committed
    
    
    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);
    
                // 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));
    
    pixhawk's avatar
    pixhawk committed
    
    
                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
    
    
    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;
    
    pixhawk's avatar
    pixhawk committed
    
    
                //    const float yawDeg = ((values.value("yaw", 0.0f)/M_PI)*180.0f)+180.f;
    
    pixhawk's avatar
    pixhawk committed
    
    
                // YAW is in compass-human readable format, so 0 - 360deg. This is normal in aviation, not -180 - +180.
    
                const float yawDeg = ((yawLP/M_PI)*180.0f)+180.0f+180.0f;
                int yawCompass = static_cast<int>(yawDeg) % 360;
                yawAngle.sprintf("%03d", yawCompass);
                paintText(yawAngle, defaultColor, 3.5f, -4.3f, compassY+ 0.97f, &painter);
    
    pixhawk's avatar
    pixhawk committed
    
    
                // CHANGE RATE STRIPS
                drawChangeRateStrip(-51.0f, -50.0f, 15.0f, -1.0f, 1.0f, -zSpeed, &painter);
    
    pixhawk's avatar
    pixhawk committed
    
    
    pixhawk's avatar
    pixhawk committed
                drawChangeRateStrip(49.0f, -50.0f, 15.0f, -1.0f, 1.0f, totalAcc, &painter);
    
    pixhawk's avatar
    pixhawk committed
    
    
    pixhawk's avatar
    pixhawk committed
                float gaugeAltitude;
    
    
                if (this->alt != 0) {
    
    pixhawk's avatar
    pixhawk committed
                    gaugeAltitude = alt;
    
    pixhawk's avatar
    pixhawk committed
                    gaugeAltitude = -zPos;
                }
    
                drawChangeIndicatorGauge(-vGaugeSpacing, -15.0f, 10.0f, 2.0f, gaugeAltitude, defaultColor, &painter, false);
    
    pixhawk's avatar
    pixhawk committed
                drawChangeIndicatorGauge(vGaugeSpacing, -15.0f, 10.0f, 5.0f, totalSpeed, defaultColor, &painter, false);
    
                // Waypoint name
                if (waypointName != "") paintText(waypointName, defaultColor, 2.0f, (-vwidth/3.0) + 10, +vheight/3.0 + 15, &painter);
    
                // MOVING PARTS
    
    
                painter.translate(refToScreenX(yawTrans), 0);
    
                // Rotate view and draw all roll-dependent indicators
                painter.rotate((rollLP/M_PI)* -180.0f);
    
    
                painter.translate(0, (-pitchLP/(float)M_PI)* -180.0f * refToScreenY(1.8f));
    
    
                //qDebug() << "ROLL" << roll << "PITCH" << pitch << "YAW DIFF" << valuesDot.value("roll", 0.0f);
    
                // PITCH
    
                paintPitchLines(pitchLP, &painter);
                painter.end();
    
            //glDisable(GL_MULTISAMPLE);
    
    
            //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